1. 理论知识

1.1 SVM 模型的基本理论

在之前的课程中讨论的分类器都是线性的,而在实际问题中,很多数据并不是线性可分的,也就是说找不到这样的超平面,能完全区分不同的数据。所以,需要在分类器中引入非线性成分,使得模型更好地贴合数据,让分类更加准确。为了使模型非线性化,我们可以通过基函数将原始特征x变换到另一个空间。问题变为了原始最大裕度优化问题:
min⁡w,b,ξ12∣∣w∣∣2+C∑n=1Nξns.t.:y(n)⋅(wTϕ(x(n))+b)≥1−ξn\min_{\bold w,b,\bold \xi}\frac12||\bold w||^2+C\sum^N_{n=1}\xi_n\\ s.t.:y^{(n)}\cdot(\bold w^T\bold \phi(\bold x^{(n)})+b)\geq1-\xi_n w,b,ξmin​21​∣∣w∣∣2+Cn=1∑N​ξn​s.t.:y(n)⋅(wTϕ(x(n))+b)≥1−ξn​
得到的分类器为:
y^(x)=sign(w∗Tϕ(x(n))+b∗)\hat y(\bold x)=sign(\bold w^{*T}\phi(\bold x^{(n)})+b^*) y^​(x)=sign(w∗Tϕ(x(n))+b∗)
从直观上看,数据在高维空间中更容易分离。为了获得更好的性能,我们希望映射后的x到更高维的空间。然而太高的话代价也很大。使用对偶形式方法解决时会要计算映射值的转置与自身的内积,导致高开销。这个问题可以通过使用内核技巧来解决。

核函数是一个二元函数,可以表示为某些函数的内积:
k(x,x′)=ϕ(x)Tϕ(x′)k(\bold x,\bold x')=\phi(\bold x)^T\phi(\bold x') k(x,x′)=ϕ(x)Tϕ(x′)
Mercer定理:如果函数k(x,x′)k(\bold x,\bold x')k(x,x′)是对称正定的,即:
∫∫g(x)k(x,y)g(y)dxdy≥0∀g(⋅)∈L2\int\int g(\bold x)k(\bold x,\bold y)g(\bold y)d\bold xd\bold y\geq0\forall g(\cdot)\in L^2 ∫∫g(x)k(x,y)g(y)dxdy≥0∀g(⋅)∈L2
就存在函数ϕ(⋅)\phi(\cdot)ϕ(⋅)使得k(x,x′)=ϕ(x)Tϕ(x′)k(\bold x,\bold x')=\phi(\bold x)^T\phi(\bold x')k(x,x′)=ϕ(x)Tϕ(x′)。一个函数如果满足正定条件就必然是核函数。

最常用的核函数之一是高斯核,有着无限维:
k(x,x′)=exp⁡{−12σ2∣∣x−x′∣∣2}k(\bold x,\bold x')=\exp\{-\frac1{2\sigma^2}||\bold x-\bold x'||^2\} k(x,x′)=exp{−2σ21​∣∣x−x′∣∣2}
利用核函数,可以将对偶最大边距分类器重写为:
max⁡ag(a)s.t.:an≥0,an≤C,∑n=1Nany(n)=0\max_{\bold a}g(\bold a)s.t.:a_n\geq0,a_n\leq C,\sum^N_{n=1}a_ny^{(n)}=0 amax​g(a)s.t.:an​≥0,an​≤C,n=1∑N​an​y(n)=0
其中,
g(a)=∑n=1Nan−12∑n=1N∑m=1Nanamy(n)y(m)k(x(n),x(m))g(\bold a)=\sum^N_{n=1}a_n-\frac12\sum^N_{n=1}\sum^N_{m=1}a_na_my^{(n)}y^{(m)}k(\bold x^{(n)},\bold x^{(m)}) g(a)=n=1∑N​an​−21​n=1∑N​m=1∑N​an​am​y(n)y(m)k(x(n),x(m))
从而得到诱导分类器:
y^(x)=sign(∑n=1Nan∗y(n))k(x(n),x(m))+b∗)\hat y(\bold x)=sign(\sum^N_{n=1}a_n^*y^{(n)})k(\bold x^{(n)},\bold x^{(m)})+b^*) y^​(x)=sign(n=1∑N​an∗​y(n))k(x(n),x(m))+b∗)
核技巧:将函数k代入。如果ϕ\phiϕ不改变x\bold xx则为线性最大边际分类器,否则为基于基函数的有限维非线性最大边际分类器,如果为高斯核,则为无限维非线性最大边际分类器。

1.2 hinge loss线性分类和SVM模型之间的关系

活页损失hinge loss定义如下:
L(w)=max⁡(0,1−yh)L(\bold w)=\max(0,1-yh) L(w)=max(0,1−yh)
其中h=wTxh=\bold w^T\bold xh=wTx,y=±1y=\pm1y=±1。从公式可以直观地看出,当实际标签为1时,预测结果小于1则会产生梯度,否则梯度为0。而实际标签为-1时,预测结果大于-1时会产生梯度,否则梯度为0。也就是说,在活页损失下,该模型会自动舍弃一些预测结果正确的样本。即便这些样本也会产生误差,但它们都不是误差的主要来源。该模型下只会考虑带来更大误差的样本,这和最大边际分类器的思想是一致的。最大边际分类器的要求是找到两个类中距离超平面最近的两点,将这个距离和作为边际,并将其最大化。也就是说,只考虑离超平面最近的点来修正超平面的位置,而不考虑离超平面更远的样本。

一般的线性最大边际分类器的条件为:
min⁡w,b,ξ12∣∣w∣∣2s.t.:y(n)⋅(wTx(n)+b)≥1\min_{\bold w,b,\bold \xi}\frac12||\bold w||^2s.t.:y^{(n)}\cdot(\bold w^T\bold x^{(n)}+b)\geq1 w,b,ξmin​21​∣∣w∣∣2s.t.:y(n)⋅(wTx(n)+b)≥1
使用hinge loss的模型还可以加入松弛变量,从而弱化条件,使得非线性可分的数据具有一定的容忍性:
min⁡w,b,ξ12∣∣w∣∣2+C∑n=1Nξns.t.:y(n)⋅(wTx(n)+b)≥1−ξn\min_{\bold w,b,\bold \xi}\frac12||\bold w||^2+C\sum^N_{n=1}\xi_ns.t.:y^{(n)}\cdot(\bold w^T\bold x^{(n)}+b)\geq1-\xi_n w,b,ξmin​21​∣∣w∣∣2+Cn=1∑N​ξn​s.t.:y(n)⋅(wTx(n)+b)≥1−ξn​
但是即便如此,使用hinge loss的线性分类模型仍然是线性的。SVM模型在此基础上,将x\bold xx通过一个基函数ϕ\phiϕ变换到另一个空间,从而真正实现了非线性成分的引入。问题变为了:
min⁡w,b,ξ12∣∣w∣∣2+C∑n=1Nξns.t.:y(n)⋅(wTϕ(x(n))+b)≥1−ξn\min_{\bold w,b,\bold \xi}\frac12||\bold w||^2+C\sum^N_{n=1}\xi_n\\ s.t.:y^{(n)}\cdot(\bold w^T\bold \phi(\bold x^{(n)})+b)\geq1-\xi_n w,b,ξmin​21​∣∣w∣∣2+Cn=1∑N​ξn​s.t.:y(n)⋅(wTϕ(x(n))+b)≥1−ξn​
可以看到,使用hinge loss的线性分类模型和SVM模型的区别在于,原来hinge loss线性分类模型中的x\bold xx的项全部被换成了ϕ(x)\phi(\bold x)ϕ(x),从而实现了x\bold xx到高维空间的映射,使得模型获得了非线性成分。


2. 训练过程

2.1 线性和高斯核函数的SVM的实现

首先读入数据集:

train_images = np.load("train-images.npy")
train_labels = np.load("train-labels.npy")
test_images = np.load("test-images.npy")
test_labels = np.load("test-labels.npy")
print("验证集大小:",test_images.shape[0])

我使用了python的sklearn库来实现SVM:

from sklearn import svm

以线性核函数的SVM模型为例,首先创建模型:

model = svm.SVC(kernel='linear')

然后进行训练:

model.fit(train_images, train_labels)

训练时可以调整一定的超参数。对于线性分类器来说,需要确定模型训练结束的标志,可以设置模型精度和最大迭代次数。这里我选用库里的默认参数,即不设置最大迭代次数,模型精度为tol = 1e-3。模型会自适应地决定每个类所占据的权重,不同的类设置不同的惩罚参数。

训练后进行预测与结果统计:

predicted= model.predict(test_images)# 预测
# 统计
right = 0
for i in range(test_images.shape[0]):if predicted[i] == test_labels[i]:right += 1
print("分类正确个数(线性核):",right)
print("准确率(线性核):",right/test_images.shape[0])

高斯核的代码也基本一致,在创建模型时将核函数的参数改为高斯核rbf(径向基函数)即可:

model = svm.SVC(kernel='rbf')

高斯核中需要确定σ\sigmaσ的参数值,在sklearn的参数中为gammagammagamma,其中gamma=12σ2gamma=\frac1{2\sigma^2}gamma=2σ21​。我的实现中仍采用默认值,即gammagammagamma值为特征值个数的倒数。

2.2 hinge loss 和 cross-entropy loss 的线性分类模型实现

2.2.1 hinge loss线性分类模型

在读入数据后需要注意,使用合页损失hinge loss的话,要求二分类的标签为1或-1,而不是之前的1或0。所以需要更改标签:

train_labels[train_labels==0] = -1
test_labels[test_labels==0] = -1

对每个特征向量,还要增加一维1作为偏移项。在本次的实验中,处理的图像都属于图片,像素值范围为0~255,因此可以考虑将所有原有的特征值除以255进行归一化处理。即:
∀i=1,2,...,Nxi=xi−xminxmax−xmin\forall i=1,2,...,N\quad x_i=\frac{x_i-x_{min}}{x_{max}-x_{min}} ∀i=1,2,...,Nxi​=xmax​−xmin​xi​−xmin​​
从而使得所有特征值的范围为[0,1][0,1][0,1],便于之后的梯度计算:

tmp = np.zeros((train_images.shape[0],train_images.shape[1]+1))
for i in range(train_images.shape[0]):tmp[i] = np.append(train_images[i]/255, 1)
train_images = tmp[:][:]
tmp = np.zeros((test_images.shape[0],test_images.shape[1]+1))
for i in range(test_images.shape[0]):tmp[i] = np.append(test_images[i]/255, 1)
test_images = tmp[:][:]

然后设置基本的变量和超参数:

train_num = train_images.shape[0]   # 训练集大小
var_num = train_images.shape[1]     # 训练样本特征数
w = np.random.rand(var_num)         # 随机初始化权值向量
learn_rate = 0.001                  # 学习率
train_times = 1000                  # 训练次数

在训练过程中,需要对损失函数求梯度。hinge loss为:
L(w)=max⁡(0,1−yh)L(\bold w)=\max(0,1-yh) L(w)=max(0,1−yh)
其中h=wTxh=\bold w^T\bold xh=wTx,y=±1y=\pm1y=±1。

也就是说,当1−y(wTx)1-y(\bold w^T\bold x)1−y(wTx)小于等于0时,梯度就为0。换言之,当前的样本对修正梯度没有任何影响,直接忽略了。否则,对合页损失函数求导得到−hx-hx−hx,即:
KaTeX parse error: Undefined control sequence: \part at position 8: \frac{\̲p̲a̲r̲t̲ ̲L(\bold w)}{\pa…
从而可以使用梯度下降法对模型进行训练:

# 训练
for t in range(train_times):# 初始化梯度为0 grad = np.zeros((var_num))# 计算梯度for i in range(train_num):if 1 - np.dot(train_images[i],w) * train_labels[i] < 0:          continuegrad = grad - (np.dot(train_images[i],w) * train_labels[i]) * train_images[i]grad /= train_num# 梯度下降w = w - learn_rate * grad

验证时判断wTx\bold w^T\bold xwTx的符号即可。若为正则预测结果为1,否则为-1:

 total = test_images.shape[0]right = 0for i in range(total):if np.dot(test_images[i],w.T) >= 0:ans = 1else:ans = -1if ans == test_labels[i]:right += 1  print('验证集大小:',total)print('预测正确个数(hinge loss):',right)

2.2.2 cross-entropy loss 线性分类模型

使用交叉熵损失训练二分类模型需要用到sigmoid函数。该函数为:
σ(x)=11+e−x\sigma(x)=\frac1{1+e^{-x}} σ(x)=1+e−x1​
e−xe^{-x}e−x将xxx映射到(0,+∞)(0,+\infin)(0,+∞),在实际运算过程中,xxx数值过小时可能会造成溢出。因此考虑将xxx分符号讨论,保证−x-x−x为负,使得映射的结果为(0,1](0,1](0,1]:
Cannot read property 'type' of undefined
在数值角度来说没有任何变化,但实际运算过程中能有效避免数值溢出:

def sigmoid(x):x = float(x)if x<0:ans = np.exp(x)/(1+np.exp(x))ans = 1/(1+np.exp(-x))return ans

但是需要注意的是,sigmoid函数仍然对数值要很强的要求。当上述函数的输入参数x​到达-10及以下时,结果会非常接近0,导致计算梯度时造成梯度消失。为了避免绝对值较大的数值的出现,我希望初始化时,w\bold ww和x\bold xx的点乘结果的绝对值不会大于1,所以进行了以下操作:

  • 将所有x\bold xx的值除以最大特征值255。同时,增加偏移项时,将加在x\bold xx内的1也除以255,这样一来就能保证所有的特征值都在[0,1][0,1][0,1]中:
tmp = np.zeros((train_images.shape[0],train_images.shape[1]+1))
for i in range(train_images.shape[0]):tmp[i] = np.append(train_images[i], 1)/255
train_images = tmp[:][:]
tmp = np.zeros((test_images.shape[0],test_images.shape[1]+1))
for i in range(test_images.shape[0]):tmp[i] = np.append(test_images[i], 1)/255
test_images = tmp[:][:]

另外,初始化权重向量时也要注意。如果所有的w\bold ww中的值都在[−1,1][-1,1][−1,1]中,的确可以保证w\bold ww和x\bold xx两个向量的对应元素一一相乘时绝对值都不会大于1,但是将这些元素加和起来后绝对值又可能会大于1。特征向量维度越大越可能。因此,初始化w\bold ww,使得所有值都在[−1,1][-1,1][−1,1]中后,还要进一步除以特征向量的维度数,从而保证点乘结果的绝对值不会大于1。

同时,也要设置较小的学习率,防止w\bold ww变化太大,又产生之前提及过的sigmoid函数产生梯度消失的结果:

# 设置基本变量和超参数
train_num = train_images.shape[0]   # 训练集大小
var_num = train_images.shape[1]     # 训练样本特征数
w = np.random.rand(var_num)/var_num # 随机初始化权值向量
learn_rate = 0.001                  # 学习率
train_times = 500                   # 训练次数

之后的训练和验证过程与上面的hinge loss线性分类器基本一致,不再赘述。只不过交叉熵损失下,计算梯度的公式要改为:
KaTeX parse error: Undefined control sequence: \part at position 8: \frac{\̲p̲a̲r̲t̲ ̲L(\bold w)}{\pa…
其中σ\sigmaσ表示sigmoid函数。即:

grad = grad + (sigmoid(np.dot(train_images[i],w)) - train_labels[i]) * train_images[i]

3. 实验结果与分析

3.1 线性和高斯核函数的SVM

此处结果来自打包程序的SVM.py

验证集大小为2115,而使用线性核分类正确的个数为2113,使用高斯核分类正确的个数为2114。可以看出,使用SVM的效果较好,绝大部分的数据都能进行有效地分类。因为高斯核将特征向量映射到无穷花维,使用高斯核理论上可以使用超平面将全部的数据完全地分离,从而实现完全准确的预测。但因为精度限制较为宽松,所以没有完全分开。本次实验中高斯核的效果也优于使用线性核的SVM。因为本次实验给出的数据集基本上线性可分,因此结果差别不大,但因为高斯核引入了非线性成分,理论上性能可以优于线性核。

3.2 hinge loss 和 cross-entropy loss 线性分类模型的比较

此处结果来自打包程序的hinge.pycross-entropy

使用hinge loss的结果如下,其中,没有标注的数字表示已经训练的次数。

使用cross-entropy loss的结果如下:

二者的学习率相同,都为0.001,而使用交叉熵损失的模型的偏移量b比使用合页损失的模型更小(上面的实现部分已经具体讨论过),但是可以发现,使用交叉熵损失的模型的收敛速率明显快于使用合页损失的模型。这是因为在合页损失下,模型会筛除很多分类正确但仍存在误差的样本。这些样本也存在误差,但在合页损失下不提供任何梯度。只有那些误分类和离分类边界很近的样本才会产生梯度用于模型的训练;而交叉熵损失函数会考虑每一个样本的误差。如此下来,交叉熵损失的线性模型的收敛速度就明显快于使用合损失的模型。

本次实验中的数据高度线性可分,因此使用交叉熵损失的模型的结果也较好,在2115个验证集样本中,预测正确了2111个。这是上面提及的考虑每个样本的误差来计算梯度造成的。但是,使用合页损失的线性模型也有自己的优势。正因为合页损失不会考虑到很多正确分类的样本而是考虑误分类的样本和正确分类但离分类边界很近的样本,这为合页损失的线性分类模型提供了一定的泛化能力。在面对较难用线性分类器区分的数据、分类边界较为模糊时,使用合页损失往往能有更好的性能表现。

基于Python实现的向量机SVM模型相关推荐

  1. 【项目实战】Python实现RVM相关向量机回归模型(RVR算法)项目实战

    说明:这是一个机器学习实战项目(附带数据+代码+文档+视频讲解),如需数据+代码+文档+视频讲解可以直接到文章最后获取. 1.项目背景 相关向量机(Relevance Vector Machine,简 ...

  2. python应用(3)svm模型预测股票涨跌

    最近接了一个私活,指导学妹完成毕业设计.核心思想就是利用SVM模型来预测股票涨跌,并完成策略构建,自动化选择最优秀的股票进行资产配置. 在做这个项目的过程中,我体会到想成为一个合格的数据分析或者数据挖 ...

  3. matlab向量机保存模型,【2017年整理】基于Matlab的支持向量机工具箱.pdf

    [2017年整理]基于Matlab的支持向量机工具箱 第24卷第12期 计算机应用与软件 V01.24No.12 2007牟12月 and5dtware Dec.2007 ComputerApplic ...

  4. python文本分类算法_Python-基于向量机SVM的文本分类

    1.算法介绍 2.代码所用数据 文件结构 ├─doc_classification.py ├─stopwords.txt ├─vocabulary.txt ├─train.data ├─train.l ...

  5. [机器学习笔记]Note10--支持向量机(SVM)

    继续是机器学习课程的笔记,这节课的内容是介绍支持向量机(SVM)的内容.SVM是一个非常强大且流行的算法,在一些情况下,面对一些复杂的非线性问题可以提供比逻辑回归或神经网络更加简洁更加有效的结果. 优 ...

  6. 向量机SVM原理详解

    转自:http://www.blogjava.net/zhenandaci/category/31868.html (一)SVM的简介 支持向量机(Support Vector Machine)是Co ...

  7. 第11章 支撑向量机SVM

    支持向量机(support vector machines)是一种二分类模型,它的目的是寻找一个超平面来对样本进行分割,分割的原则是间隔最大化,最终转化为一个凸二次规划问题来求解.由简至繁的模型包括: ...

  8. 基于python的搜索引擎论文_技术分享 - 基于python构建搜索引擎系列——(四)检索模型...

    构建好倒排索引之后,就可以开始检索了. 检索模型有很多,比如向量空间模型.概率模型.语言模型等.其中最有名的.检索效果最好的是基于概率的BM25模型. 给定一个查询Q和一篇文档d,d对Q的BM25得分 ...

  9. 百面机器学习 #3 经典算法:01-3 核函数支撑向量机SVM

    文章目录 1.3 非线性SVM与核技巧 1.3.1 核函数 1.3.2 核技巧在支持向量机中的应用 1.3.3 常用核函数 1.4 其他问题 1.4.1 是否存在一组参数使SVM训练误差为0:是 1. ...

最新文章

  1. 台式电脑可以练计算机二级嘛6,练习六-计算机二级考试OFFICE高级应用试卷与试题.pdf...
  2. 查找(三)——基于平衡二叉树的查找(对排序二叉树的改进)
  3. 全国“最高”的视觉竞赛,华为Atlas打通遥感图像智能分析任督二脉
  4. Android Bitmap开发之旅--基本操作
  5. arcgis超级工具安装教程_【软件安装管家】Autodesk卸载官方工具安装教程
  6. 随机值获取—random
  7. ROS2学习(三).ROS2环境配置
  8. IE请求json数据时出现下载文件的现象。
  9. [LeetCode] 547. Friend Circles Java
  10. Java 并发框架Disruptor(七)
  11. [AlwaysOn Availability Groups]健康模型 Part 1——概述
  12. faster rcnn论文_论文导读-从Faster-RCNN/Mask RCNN/Cascade-RCNN到HTC
  13. 支持各硬件平台的机器学习模型 AWS发表新开源项目
  14. 金蝶K3老单据下推老单据并修改字段名(以销售订单下推销售出库单为例,后台操作部分)
  15. 黑龙江大学计算机科学技术学院软件学院青年志愿,计算机科学技术学院、软件学院青年志愿者协会2017年暑期三下乡宣誓大会...
  16. 关于微信各名词的英文翻译
  17. Tomcat环境配置 以及报错500的问题
  18. 基于DFA算法的敏感词过滤的go语言实现
  19. 四年前,我设计了一款纹胸小样儿……如图……
  20. jBPM4的运行期环境

热门文章

  1. JZOJ 4740 【雅礼联考GDOI2017模拟9.2】Zjr506的捕猫计划
  2. 小伙用C/C++编程自制纸牌游戏引擎,拿到月薪30k!
  3. 如何将多个Excel表合并成一个Excel表
  4. 无线上网 Wi-Fi
  5. 个人淘客推广App — 支持淘宝、京东、唯品会、拼多多、美团推广
  6. 关于Linux服务器上部署tomcat项目,输入数据乱码解决
  7. 设置规范日期格式:汪琪玩Excel第二十招
  8. vue学习DAY02
  9. Futter 屏幕适配框架flutter_ScreenUtil 用法
  10. 那些电影中黑化的人工智能,你了解多少?