本文主要参考浙江大学胡浩基老师的课程,加上作者自己的理解进行介绍,有理论有实践,超超详细!
目录
一、支持向量机(Support Vector Machine)
一、支持向量机(Support Vector Machine)
1. 基础知识
- 线性可分:假设特征空间是二维的,而各个训练样本在特征空间的分布如下图所示
可以看到,这是一个二分类问题。那么线性可分就指的是,有一条直线可以将图中的×和○分开。
敲黑板!!!用数学公式如何表示?如下:
我们给x和○分类,○用表示,x用表示,我们图中的这条直线可以将x和○分开,假设直线方程如下:
式中,是权重,表示偏置。
在○一侧,,另一侧(这个是人为规定的,颠倒也是可以滴)
敲黑板!!!用数学严格定义训练样本以及他们的标签。如下:
这些○和x用数学来表示,为。
其中,,,假如属于,那么就是+1,如果属于,那么就是-1。(也可以反过来!都是人为规定的!+1和-1也可以为其他数)
综上,线性可分的严格定义:一个训练样本集,在线性可分,是指存在,使得对,有如下式子!!!
用向量形式来定义线性可分:
假设,,上边的条件就简化为如下:
可以用一个式子来表示,即:
如果空间是三维的呢?图就是这样的,如下所示
- 线性不可分:指的是,不存在一条直线将图中的×和○分开,例如下图
三维的如下所示:
1.2 问题描述
上边我们了解了线性可分,我们支持向量机的作用就是① 解决线性可分问题;② 将线性可分问题中获得的结论推广到线性不可分情况。
1.2.1 如何解决线性可分问题
既然数据集是线性可分的,那就存在无数多个超平面,将各个类别分开,那么在这无数多个超平面中,哪个是最好的???如下所示,123直线都可以将○和x分开,一般人都会觉得2比较好,但是,觉得2号线好的,心里是会对数据集有一些预设的。那么2号线怎么画出来的?这就是一个最优化问题了。接着往下看!
假设,对于任意一条分开○和x的直线,把这条线朝一侧平行移动,直到碰到训练样本为止,另一侧也一样,如下图:
我们将这两条虚线接触到的训练样本,称为支持向量,两条平行线的距离叫做间隔如下图:
我们所要求的2号线就是使间隔最大的一条线!那看一下1和3号线的间隔,如下图,可以看出2号线间隔最大!支持向量机要找的就是间隔最大的那条线!
但是!!!间隔最大这个准则不能唯一确定一条直线,例如下面这幅图,任何一条平行于2号线,同时也能分开两类的直线,所产生的间隔是一样大的,为了让找到的线唯一,我们还需要定义这条线在两条平行线的正中间!
综上,支持向量机寻找的最优分类直线应该满足3个条件:① 该直线分开了两类;② 该直线最大化间隔;③ 该直线处于间隔的最中间,到所有支持向量的距离相等。
用数学该如何表示呢?
假设训练样本集是线性可分的,支持向量机需要寻找的是最大化间隔的超平面,已知训练样本集,求和b,也就是下式:
解释一下为什么是。
因为:是个向量,假设它有m个分量,分别为:
然后:
因此,支持向量机优化问题就是要最小化,限制条件是。
这里有两个事实!
第一个:与是同一个超平面。
第二个:一个点到超平面的距离
所以支持向量机优化问题推导如下:
根据注意的点1:用去缩放,最终使在支持向量上有,而在非支持向量上
根据注意的点2:支持向量到超平面的距离将会变为:
因此最大化支持向量到超平面的距离等价于最小化,之所以定为,是因为之后求导方便。
针对限制条件:的作用是协调超平面的左右,让一边,另一边,和线性可分里的的作用一样。注意:在限制条件中,1可以改为任意正数。例如,如果换成2,那么算出的比起原来的相差一个a,根据注意点1可知,他们代表的是一个平面。
科普一下:上边这个优化问题实际上是凸优化问题中的二次规划问题(目标函数是二次项,限制条件是一次项)!这样式儿的,要么无解!要么有唯一最小值的解!
1.2.2 线性不可分情况
如果线性不可分的话,那么上述的优化问题是没有解的,也就是说不存在和b 满足所有N个限制条件。怎么办!!!
我们需要适当放松限制条件,思路如下:
对每个训练样本及标签,设置一个松弛变量
那么,上述限制条件可写为:
在线性不可分的情况下,我们不可能保证所有的,因此引入作用在不等式右边,只要每个取足够大,那么上述限制条件就可以成立。
所以改造后的支持向量机优化版本如下!
以前的目标函数只需要最小化,现在增加了一项所有的和。意味着不仅要让越小越好,同时要让所有的和越小越好。C是比例因子,是人为设定的,用来平衡两项的作用,实际应用中,会不断变化C的值,选取最优的超参数C。
科普一下:人为事先设定的参数叫做算法的超参数。
那我们看这幅图!
很显然,这个平面远远不能让人满意,分错了将近一半的训练样本。问题出在哪里?
问题在于,我们的算法模型是线性的,即我们假设分开两类的函数是线性的,但是线性模型的表现力是不够的。如上图,能够分开上述样本的应该是一个曲面。因此我们只有扩大可选的函数范围,使它超越线性,才有可能应对各种复杂的线性不可分的情况。
1.2.3 扩大可选函数范围
支持向量机的做法是通过特征空间由低维映射到高维,然后在高维的特征空间当中仍然用线性超平面对数据进行分类。
举个栗子,如下图:
如上,
我们需要构造一个二维到五维的映射,如下:
按照这个映射,可以算出如下对应的分量:
当映射变成五维时,到就变成线性可分了,设,,可以得出如下:
由于x1和x2属于一类,x3和x4属于一类,上述例子,从2维到5维的映射后变成了线性可分的了。
用定理来说明:
假设在一个M维空间上随机取N个训练样本,随机对每个训练样本赋予标签+1或-1,同时,假设这些训练样本线性可分的概率为,则当M趋于无穷大时,
这个定理说明了:将训练样本由低维映射到高维,能够增大线性可分的概率。因此,如何构造由低维到高维的映射?
假设已经确定,将x映射为,那么优化问题的表示就变为了如下:
可以看到所有的都被替换,并且的维度是与相同的。
高维情况下,优化问题的解法和低维情况是完全类似的。
1.3 核函数定义
那么的形式是怎么样的呢?这一小节研究的形式。
对任意两个向量和,有,那么仍然能够通过一些技巧获得测试样本X的类别信息,从而完成对测试样本类别的预测。
定义为核函数,是实数。下面举例说明核函数K以及低维到高维的映射的关系。
例子1:已知求
假设是一个将二维向量映射到三维向量映射,如下所示:
我们来看下核函数的形式,假设有两个二维向量:,,根据前边得:,,那么有如下式子:
上述就是一个已知求的例子。
例子2:已知求
假设X是一个二维向量,,。
有如下式子:(为什么这么表示呢,这是一种常见的多项式核函数)
根据观察发现,假设,那么如下:
综上,核函数K和映射是一一对应的关系。核函数的形式不能随意取,需要满足一定的条件,才能分解为两个内积的形式。核函数必须满足的条件如下:
满足上边的条件,就一定能写成内积的形式。
1.4 原问题和对偶问题
1.4.1 定义
这里边涉及了一些最优化理论的东西,基础不好的理解起来可能有点吃力。
原问题的定义:
自变量,是一个多维向量,目标函数是
对偶问题的定义:首先定义一个函数
在定义了函数的基础上,对偶问题如下:
综合原问题和对偶问题的定义得到:
定理一:如果是原问题的解,是对偶问题的解,则有:
证明如下:
将定义为对偶差距,对偶差距。
还有强对偶定理:如果, , 为凸函数,则有:
,则对偶差距为0。
即:如果原问题的目标函数是凸函数,而限制条件是线性函数,则,对偶差距为0。
根据定理一推出的不等式:若,则定理一中必然能够推出,对于所有的,要么,要么。这个条件称为KKT条件。
1.4.2 支持向量机原问题转为对偶问题
利用1.4.1的原理,将支持向量机的原问题转化为对偶问题,进一步完成支持向量机优化问题的求解。
支持向量机的原问题满足强对偶定理。我们现在的优化问题如下所示:
我们对这个版本进行改造。首先,在原问题的描述中,而支持向量机的限制条件中,两个不等式都是大于等于0的,所以我们将限制条件①②都改为小于等于0的形式:
取相反数就可以变为小于等于0的形式!
第一步:将转换成,得到:
第二步:将限制条件②转换成小于等于0的形式,就变成了下式:
满足强对偶定理。
将原问题转化为对偶问题:
上一节中对偶问题的等于这里的,这里不存在,我们将对偶问题写成如下形式:
化为对偶问题:
由于是遍历所有的,求最小值,因此,可以对这三个变量求导,并令导数为0。得到如下:
化简得下边的对偶问题为:
由于,,根据前边的推导得:,所以,所以。这个对偶问题也是个二次规划问题。
1.5 算法整体流程
支持向量机的对偶问题如上所示,如何求解该对偶问题。
由于式中,这个K是上述讲的核函数。所以我们只需要知道核函数,就能够求解这个最优化的对偶问题了。
我们要推导,在不知道的显式表达时,也可以通过核函数K算出。
首先是如何求b?
由于,并且,则:
其次,根据KKT条件,对所有的,能够推出:
如果对于某个,且,则根据KKT条件,必有。所以只需要找个,就可以这样求出:
算出之后,考虑对于一个测试样本,如何预测其类别,这里需要计算
将代入得:
结论:即使不知道,只知道核函数K,也可以算出(这一结论被称为核函数戏法)
最终判别标准如下:
敲黑板!!!
总结支持向量机训练流程:
总结支持向量机测试流程:
以上就是原理部分,下边来做一个小小的案例实现。
二、鸢尾花案例实现
本案例使用支持向量机模型,将鸢尾花的数据进行分类。
2.1 数据展示
鸢尾花的数据如下:
数据集中包含150个样本,每行数据包含每个样本的4个特征和样本类别信息。每个样本包含了花萼长度(Sepal.Length)、花萼宽度(Sepal.Width)、花瓣宽度(Petal.Length)、花瓣宽度(Petal.Width)。我们要建立一个分类器,通过这4个特征判断样本是否属于山鸢尾(setosa)、变色鸢尾(versicolor)、维吉尼亚鸢尾(virginica)
2.2 案例实现(线性案例)
我们在jupyter里边运行,jupyter的安装见这篇文章:
【安装软件】手把手教你在Windows上安装anaconda和jupyter-CSDN博客
原始数据点分布,代码如下:
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.preprocessing import StandardScaler
from sklearn.svm import LinearSVC
iris = datasets.load_iris()
X = iris.data
Y = iris.target
X = X [Y<2,:2] # 只取y<2的类别,也就是0 1 并且只取前两个特征
Y = Y[Y<2] # 只取y<2的类别
# 分别画出类别 0 和 1 的点
plt.scatter(X[Y==0,0],X[Y==0,1],color='red')
plt.scatter(X[Y==1,0],X[Y==1,1],color='blue')
plt.show()
将数据标准化
# 标准化
standardScaler = StandardScaler()
standardScaler.fit(X)
# 计算训练数据的均值和方差
X_standard = standardScaler.transform(X) # 再用 scaler 中的均值和方差来转换 X ,使 X 标准化
svc = LinearSVC(C=1e9) # 线性 SVM 分类器
svc.fit(X_standard,Y) # 训练svm
绘制决策边界
def plot_decision_boundary(model, axis):
x0, x1 = np.meshgrid( np.linspace(axis[0], axis[1], int((axis[1]-axis[0])*100)).reshape(-1,1), np.linspace(axis[2], axis[3], int((axis[3]-axis[2])*100)).reshape(-1,1) )
X_new = np.c_[x0.ravel(), x1.ravel()]
y_predict = model.predict(X_new)
zz = y_predict.reshape(x0.shape)
from matplotlib.colors import ListedColormap
custom_cmap = ListedColormap(['#EF9A9A','#FFF59D','#90CAF9'])
plt.contourf(x0, x1, zz, cmap=custom_cmap) # 绘制决策边界
plot_decision_boundary(svc,axis=[-3,3,-3,3]) # x,y轴都在-3到3之间
# 绘制原始数据
plt.scatter(X_standard[Y==0,0],X_standard[Y==0,1],color='red')
plt.scatter(X_standard[Y==1,0],X_standard[Y==1,1],color='blue')
plt.show()
可视化如下图:
2.3 案例实现(非线性案例)
首先我们生成一些非线性的数据,然后生成一些噪声点,如下所示:
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
X,y = datasets.make_moons() #使用生成的数据
print(X.shape) # (100,2)
print(y.shape) # (100,)
#接下来绘制下生成的数据
plt.scatter(X[y==0,0],X[y==0,1])
plt.scatter(X[y==1,0],X[y==1,1])
#plt.show()
X, y = datasets.make_moons(noise=0.15,random_state=777) #随机生成噪声点,random_state是随机种子,noise是方差
plt.scatter(X[y==0,0],X[y==0,1])
plt.scatter(X[y==1,0],X[y==1,1])
plt.show()
可视化图如下:
通过多项式特征的SVM来对它进行分类:
from sklearn.preprocessing import PolynomialFeatures,StandardScaler
from sklearn.svm import LinearSVC
from sklearn.pipeline import Pipeline
def PolynomialSVC(degree,C=1.0):
return Pipeline([ ("poly",PolynomialFeatures(degree=degree)),#生成多项式
("std_scaler",StandardScaler()),#标准化
("linearSVC",LinearSVC(C=C))#最后生成svm
])
poly_svc = PolynomialSVC(degree=3)
poly_svc.fit(X,y)
plot_decision_boundary(poly_svc,axis=[-1.5,2.5,-1.0,1.5])
plt.scatter(X[y==0,0],X[y==0,1])
plt.scatter(X[y==1,0],X[y==1,1])
plt.show()
分类如下:
如上图所示,已经不是直线分割了,而是曲线。
案例代码参考: