主要内容:
● 简单介绍支持向量机
● 利用SMO进行优化
● 利用核函数进行空间转换
● 将SVM和其他分类器进行比对
支持向量机(support vector machines,SVM),SVM有很多实现,我们现在来讲讲最流行的一种实现,即序列最小化(sequential minimal optimization,SMO)算法。
6.1 基于最大间隔分隔数据
● 优点:泛化错误率低,计算开销不大,结果易解释。
● 缺点:对参数调节和核函数的选择敏感,原始分类器不加修改仅适用于处理二类问题。
● 使用数据类型:数值型和标称型数据。
能将下列第一个图中的圆形点和方形点分开,这个分隔开的直线就是分隔超平面。
所给的数据点在二维平面上,分隔超平面就是一条直线。如果数据集是三维的,那么分隔数据的就是一个平面。如果数据集是N维的,那么N-1维的某某对象来对数据进行分隔,该对象被称之为 超平面,也就是分类的决策边界。
我们使用这种方式来构建分类器,如果数据点离决策边界越远,最后的预测结果就越可信。
点到分隔面的距离被称之为间隔。
支持向量(support vector)就是离分隔超平面最近的那些点。寻找最大的支持向量到分隔面的距离,找到这个问题的最优化求解方法。
6.2 寻找最大间隔
如何求解数据集的最佳分隔直线呢?
分隔超平面的形式可以写成: W^t *X + b
要计算点A到分隔超平面的距离,就必须给出点到分隔面的发现或垂线的长度,该值为,
| W^t *X + b | / || W ||
常数W和常数b给出了数据的分割线和超平面。
6.2.1 分类器求解的优化问题
输入数据个分类器输出一个类别标签,这相当于一个类似于是sigmoid的函数在起作用。
我们将使用类似海维塞德阶跃函数(即单位阶跃函数)的函数对W^t + b 作用得到
f (W^t + b) 。
当u<0 ,f(u) < -1
当u>0 ,f (u) > +1
我们采用 -1 和 1 ,而不是0 ,-1 ,是因为 -1 和 +1 仅仅相差一个符号,方便数学上的处理。
当计算数据点到分隔面的距离并确定分隔面的放置位置的时候,间隔通过 label * (W^t + b ) 来计算。
我们需要找到分类器定义的w和b,我们要找到具有最小间隔的数据点,而这些数据点也就是前面提到的支持向量。找到之后,我们需要对该间隔进行最大化:
直接求解上述公式将十分困难。
所以令 label * (W^t + b ) = 1 ,来求 || w || ^-1 的最大值来求解。
然而 label * (W^t + b ) 不全等于1, 存在距离分隔超平面最近的点的值为1 。
给出约束条件: label * (W^t + b ) >= 1
那么由于约束条件都是基于数据点的,
因此将超平面写成数据点的形式。目标函数写成:
这里有个假设:数据必须100%线性可分。约束条件为:
实际上,数据都几乎有那么多“不干净”,我们引入“松弛变量”,允许有些数据点可以处于分隔面错误的一侧。此时新的约束条件变为:
常熟C用于控制 ” 最大化间隔 ” 和 “ 保证大部分点的函数间隔小于1.0 ”这两个目标的权重。
6.2.2 SVM应用的一般框架
SVM的一般流程
1. 收集数据:可以使用任意方法
2. 准备数据:需要数值型数据。
3. 分析数据:有助于可视化的=分隔超平面
4. 训练算法:SVM的大部分时间都源自训练,该过程主要实现两个参数的调优
5. 测试算法:十分简单的计算过程就可以实现
6. 使用算法:几乎所有分类问题都可以使用SVM,值得一提的是,SVM本身是一个二类的分类器,对多类问题应用SVM需要对代码来做一些修改
6.3 SMO高效优化算法
6.3.1 Palatt的SMO算法
根据上面提到的,优化之后一个是最小化的目标函数,一个是优化过程需要遵循的约束条件。
算法SMO来训练SVM算法,SMO表示序列最小优化(sequential minimal optimization)。
SMO算法的目标是求出一系列的alpha 和 b ,从而计算出权重的向量w并得到分隔超平面。
SMO算法工作原理:每次循环中选择两个alpha 进行优化处理。
当(1)两个alpha在间隔的边界之外;(2)两个alpha未经过区间化处理或不在边界上 。此时 就算是找到了一对合适的alpha ,那么久增大其中一个同时减小另一个。
6.3.2 应用简化版SMO算法处理小规模数据集
SMO算法的完整实现需要大量的代码。我们先给出简化处理,以便于去了解算法的基本思路。
# SMO算法中的辅助函数
def loadDataSet(fileName):
fileName = r"E:\ML\ML_source_code\mlia\Ch06\testSet.txt"
dataMat = [];labelMat = []
fr = open(fileName)
for line in fr.readlines():
lineArr = line.strip().split('\t')
dataMat.append([float(lineArr[0]),float(lineArr[1])])
labelMat.append(float(lineArr[2]))
return dataMat,labelMat
def selectJrand(i,m):
"""
i 是第一个alpha下标,m是所有alpha的数目
"""
j = i
while (j == i):
j = int(np.random.uniform(0,m))
return j
def clipAlpha(aj,H,L):
if aj > H:
aj = H
if L > aj:
aj = L
return aj
"""
#SMO函数的伪代码
创建一个alpha向量并将其初始化为0向量
当迭代次数小于最大迭代次数时(外循环):
对数据集中的每个数据向量(内循环):
如果该数据向量可以被优化:
随机选择另外一个数据向量
同时优化这两个向量
如果两个向量都不能被优化,退出内循环
如果所有的向量都没有被优化,增加迭代项目,继续下一次循环
"""
● selectJrand()函数的两个参数,i 是第一个alpha下标,m是所有alpha的数目。只要函数不等
于输入值 i,函数就会进行随机选择。
● clipAlpha()函数用于调整大于H或者小于L的alpha值
测试一下代码,得到的标签是-1和1 ,而不是0和1
In [4]: import svm
In [5]: reload(svm)
Out[5]: <module 'svm' from 'svm.pyc'>
In [7]: fileName = r"E:\ML\ML_source_code\mlia\Ch06\testSet.txt"
...:
In [8]: dataArr,labelArr = svm.loadDataSet(fileName)
In [9]: labelArr
Out[9]:
[-1.0,
-1.0,
1.0,
-1.0,
....
-1.0,
-1.0,
-1.0]
6.4 利用完整的platt SMO算法加速优化
之所以要引进完整版的SMO算法,是因为简化版的算法运行速度太慢。
# 简化版SMO算法
def smoSimple(dataMatIn,classLabels,C,toler,maxIter):
"""
函数(数据集,类别标签,常数C,容错率,退出前最大的循环次数)
"""
dataMatrix = np.mat(dataMatIn); labelMat = np.mat(classLabels).transpose()
b = 0; m,n = np.shape(dataMatrix)
alphas = np.mat(np.zeros((m,1)))
iter = 0
while (iter < maxIter):
aphaPairsChanged = 0
for i in range(m):
fXi = float(np.multiply(alphas,labelMat).T * (dataMatrix*dataMatrix[i,:].T)) + b
Ei = fXi - float(labelMat[i])
#如果alpha可以更改进入优化过程
if ((labelMat[i]*Ei<-toler) and (alphas[i]<C)) or ((labelMat[i]*Ei>toler) and (alphas[i]>0)):
#随机选择第二个alpha
j = selectJrand(i,m)
fXj = float(np.multiply(alphas,labelMat).T * (dataMatrix*dataMatrix[j,:].T)) + b
Ej = fXj - float(labelMat[j])
alphaIold = alphas[i].copy();
alphaJold = alphas[i].copy();
#保证alpha在0与C之间
if (labelMat[i] != labelMat[j]):
L = max(0, alphas[j] - alphas[i])
H = min(C, C + alphas[j] - alphas[i])
else:
L = max(0, alphas[j] + alphas[i] - C)
H = min(C ,alphas[j] + alphas[i])
if L==H:
print "L==H";continue
eta = 2.0*dataMatrix[i,:]*dataMatrix[j,:].T-dataMatrix[i,:]*dataMatrix[i,:].T-dataMatrix[j,:]*dataMatrix[j,:].T
if eta > 0:
print "eta>0";continue
alphas[j] -= labelMat[j]*(Ei - Ej)/eta
alphas[j] = clipAlpha(alphas[j],H,L)
if (abs(alphas[j] - alphaJold) < 0.