一.基本概念
svm是现成的很好的分类器,因为它能够不加修改的,直接应用于训练集,并且效果也不错。svm从直观上来时是找到分割两类数据的最佳的gap,即最大的间隔(margin)。
那既然svm是在找最大间隔,那什么是间隔呢?间隔指的就是支持向量和分割超平面之间距离。那支持向量又是什么呢?支持向量指的就是离分割超平面距离最近的那些点。
那要怎么寻找这个最大化间隔呢?从它的定义上,我们可以知道只要我们确定了分割平面就可以求出一个间隔,然后将这个间隔的最大化,这就是我们要求的最大化间隔。所以svm从本质上说是在分割超平面。把它转换成数学表达如下:
在上图中蓝线表示最佳的分类超平面,虚线之间的就是gap。那么svm问题就是要最大这个gap。目标函数为:
arg max{min lable * wTx*1/||w||}
s.t:
lable * wTx >=1
利用拉格朗日函数将其进行变换,就可以得到:
max{}
s.t:
当数据集中出现一些噪声点干扰时,需要加入松弛变量,松弛变量就是为了保证当数据点落在gap中时,依然能够被正确分类。要满足这个要求,只需要改变一下约束条件,目标函数不变,此时约束条件变为:
s.t:
可见加不加松弛变量,他们的区别在于a的取值范围。
优点:计算的开销比较小,并且泛化错误率低,结果也比较容易理解。未加修改的原始的svm分类器,可以直接应用于二分类问题。
缺点:对于参数和核函数的选择很敏感。
二.smo高效优化算法
smo算法是实现svm的一个最常用的算法。全称是序列最小化算法。
smo算法的工作原理是每次循环选择两个alpha进行处理。一旦找到合适的alpha就对其中的一个进行增加,另一个进行减小。合适的alpha,指的是它要满足一定的条件,一个是这两个alpha必须在边界之外,另一个是这两个alpha还没有进行过区间化处理或者不在边界上。
三.简化版的smo算法实现
首先要知道几个公式:
(1)
(2)
(3)
(4)
(5)
总的伪代码:
初始化alpha向量
初始化b
当迭代次数小于最大迭代次数:
记录alpha对变化的次数
对于每一数据向量:
计算出Ei的值
如果alphai可以进行更改:
随机选择除i外的数据向量j
计算出Ej的值
记录此时alphai,j的值
根据第一个公式计算出此时的H,L的值
如果H==L,那么直接跳出本次循环
根据第二公式计算出eta的值
如果eta的值大于0,跳出本次循环
根据公式二计算出此时的alphaj的值
限制alphaj的值不能大于等于C,不能小于等于0
如果alphaj的变化量太小(比如小于0.0001),跳出本次循环
根据第三个公式计算出alphai的值
根据第四个公式计算出b1,b2的值
根据第五个公式确定b的值
将alpha对变化的次数加1
判断alpha对变化的次数是不是等于0,是的话迭代次数加一,否则迭代次数置0
它的python实现
from random import random from numpy import * # load the special data def loaddata(filename): dataset = [] datalabel = [] fr = open(filename) for line in fr.readlines(): frlines = line.strip().split(' ') dataset.append([float(frlines[0]), float(frlines[1])]) datalabel.append(float(frlines[2])) return dataset, datalabel # select j where j != i and j in range(0,m) def selectJ(i, m): j = i while j == i: j = int(random.uniform(0, m)) return j # limit i smaller than H, and bigger than L def limiti(i, l, h): if i > l: i = l if i < h: i = h return i def smosimple(dataset, datalable, C, toler, timeinter): data = mat(dataset) classlable = mat(datalable).transpose() m, n = shape(data) alpha = mat(zeros((m,1))) b = 0 inter = 0 while inter<timeinter: alphapairchange = 0 for i in range(m): fxi = float(multiply(alpha, classlable).T*data*data[i,:].T) + b ei = fxi - classlable[i] if (classlable[i]*ei<-toler and alpha[i]<C) or (classlable[i]*ei > toler and alpha[i] >0): j = selectJ(i, m) fxj =float(multiply(alpha, classlable).T*data*data[j,:].T) + b ej = fxj - classlable[j] alphaiold = alpha[i].copy() alphajold = alpha[j].copy() if classlable[i] != classlable[j]: h = min(C, C+alphajold-alphaiold) l = max(0, alphajold-alphaiold) else: h = min(C, alphaiold+alphajold) l = max(0, alphaiold+alphajold-C) if h == l: continue eta = 2.0*data[i,:]*data[j,:].T - data[i,:]*data[i,:].T -data[j,:]*data[j,:].T if eta>=0: continue alpha[j] -= classlable[j]*(ei-ej)/eta alpha[j]=limiti(alpha[j], h, l) if (alpha[j]-alphajold)<0.00001: continue alpha[i] += classlable[i]*classlable[j]*(alphajold-alpha[j]) b1 = b - ei - classlable[i]*(alpha[i] - alphaiold)*data[i,:]*data[i,:].T - classlable[j]*(alpha[j]- alphajold)*data[i,:]*data[j,:].T b2 = b - ej - classlable[i]*(alpha[i] - alphaiold)*data[i,:]*data[j,:].T - classlable[j]*(alpha[j]- alphajold)*data[j,:]*data[j,:].T if (alpha[i] <= C) and (alpha[i] >=0): b = b1 elif (alpha[j] <= C) and (alpha[j] >= 0): b = b2 else: b = (b1+b2)/2 alphapairchange += 1 if alphapairchange == 0: inter += 1 else: inter = 0 return alpha, b
上述是简单的smo算法,还有一种加速的platt smo算法。它的改进在于不是遍历每一个alpha,而是目标在于调整值位于0到C之间的alpha分量。它遍历一个alpha,找到alpha的分量值在0到C之间的分量,然后调整这些分量,不断的重复以上三个小步骤,知道收敛。根据上述方法第一个alpha分量的值,我们可以随机选择,但是第二个alpha的值,我们需要选择误差最大的alpha分量,也就是使ei-ej最大对应的alpha分量。
具体的思路:
以下是详细的python代码
四.参考资料
推荐大家看看on2way的SVM系列:
http://blog.csdn.net/on2way/article/details/47729827