前言
svm需要一定的数学功底,我是亦步亦趋花了一个星期,才渐渐有所了解。出于学习交流目的,我将自己的学习思路整理出来。由于对svm的理解有所不足,如有错误,敬请指正。
svm简介
支持向量机(support vertor machines,SVM)是一种二分类模型。它的基本模型是定义在特征空间上的最大间隔分类器,核技巧使它称为线性分类器。支持向量机的学习策略就是间隔最大化,可形式化为一个求解凸二次规划的问题,也等价于正则化的合页损失最小化问题。支持向量机的学习算法是求解凸二次规划的最优化算法。
最大间隔的分类器
ok,让我们从最简单的线性分类说起,如图:
在一个平面上有两类点,中间的实线表示一个分类器。我们知道,空间中的一个(超)平面(在二维是直线)可以表示为
我们不妨设所有满足f(x)>0的点其y等于1,而f(x)<0的点其y等于-1(至于为什么是1和-1,读者如果感兴趣,可自行查阅相关资料)。当超平面的方程给定后,我们就可以将需要预测的点带入方程,如果得到的值大于0,则分到正类(y=1),如果得到的值小于0,则分到负类(y=-1)。那么,现在剩下的问题就是,怎样确保我们的超平面是分类效果最好的?svm利用间隔最大化求最优超平面。
一般而言,一个点距离超平面的远近可以表示匪类的准确度。在超平面 w*x+b 确定的情况下|w*x+b|可以表示点x距离超平面距离的远近,而标号y可以表示正确。所以,我们用 y(w∗x+b) 表示分类的确信度。由此引出函数间隔的定义。
对于给定训练集T和超平面(w,b),定义超平面关于样本点 (xi,yi) 的函数间隔为
γi^=yi(w∗xi+b)
定义超平面(w,b)关于训练集T的函数间隔为所有样本点函数间隔的最小值即
γ^=mini=1,2...Nγi^
函数间隔虽然可以表示分类,但是如果成比例的改变w和b,函数间隔就会成比例的变化,由此我们引入几何间隔。
γ=γ^||w||
几何距离就是点到超平面的距离
用几何距离来表示间隔显然要比函数间隔好。那么,我们的目的就是使几何距离最大化,即
当然,还有个附加条件
其中 γ=fracγ^||w|| ,出于简化计算的考虑,令 γ^=1 (对目标函数的优化没有影响,证明略),此时,我们的目标由(1)转换为
通过求解上面这个问题,我们就可以得到图中超平面的方程。
什么是支持向量?
从图中可以看出,两个支撑着中间间隙的超平面,它们到中间分类超平面的距离相等,即几何间隔 γ ,而支撑这两个超平面的点便叫做支持向量。
从原本是问题到对偶问题
之前我们要求解式(2),由于对
||w||
求最大等价于对
1||ω||
求最小,则原问题等价于
到了这一步,可以看出来这是一个二次凸优化问题。由于其特殊结构我们可以通过Lagrange对偶变换来求解出问题。
下面对Lagrange对偶性的进行简单的解释
假定 f(x),ci(x),hj(x) 是定义在 Rn 上的连续可微函数,考虑约束最优化问题
引入广义拉格朗日函数
这里 αi≥0
然后代入在我们的问题中,有
然后令 θ(ω)=maxα>0L(ω,b,α)
则问题转化为
在满足KKT条件的情况下(KKT条件略,但是可证明,我们这个问题是满足KKT条件的),该问题转换为
求解对偶问题
于是接下来我们的任务就是:(1)固定
α
对
θ(ω)
求极小,得到关于
α
的表达式(2)对
α
求极大,(3)利用SMO求对偶因子。
第一步 先固定
α
,对
θ(ω)
求偏导。
将结果代入式(6)得
第二步 由于有了式(11),问题转化为
要对 α 求极大,最有效地方法是SMO算法。
用松弛变量处理离群点
在讨论SMO算法之前,我们先说说另一个问题————离群点的处理。我们起初的假设是数据都是线性可分的,假如我们的数据中存在离群点,原有的模型会受到很大的影响,如下图
用黑色圈出来的蓝色点就是一个离群点。如果没有这个点,我们的模型会得到红色实线所示的分类器。但是由于有了这个点,现在得到的分类器会是如虚线所示,显然这个分类器的间隔比之前分类器的间隔要小。要是该离群点还往上一点,我们甚至无法利用我们的模型来构造分类超平面。
为了解决离群点问题,svm在一定程度上可以支持离群点。如上图,将离群点移动到蓝色实线所示的支持平面上,超平面就不会改变了。为此,我们引入了松弛变量
ξ
。我们原来的约束条件是
现在有了离群点,约束条件变为
ξ 表示的是离群点偏离函数间隔的量,如果我们允许 ξi 任意大,那么任意超平面都是符合条件的。所以,我们还要子啊优化目标上加上一项,使得 ξi 的总和最小,新优化目标是
其中C是控制目标函数中2项的权重的参数。然后我们按照之前的步骤对式(12)求偏导,回代,会得到一个和原来一样的目标函数(巧合?)不过由于我们有 C−αi−ri=0 又有 ri≥0 (拉格朗日乘数法的条件),因此有 αi≤C ,所以最后的问题转化为
SMO算法
到这一步,我们有了式(13)-(15),我们现在来看一看SMO是怎样求解的。
首先,更具KKT条件得出
αi
的取值意义:
记
ui=ωx+b
- 式(16)表示分类正常,点在边界内部。
- 式(17)表示点在边界上。
- 式(18)表示点在边界之间。
以下情况会出现不满足:
在引入松弛变量后,上述不满足条件变为
记 Ei=ui−yi
我们要将所有不满足条件的 αi 调整为满足条件,然而,由于 ∑ni=1αiyi=0 ,所以我们需要通过某种方法同时调整两个 α 。SMO算法住主要步骤就是(1)选择接下来需要更新的一对 αi 和 αj :采用启发式的方式进行选择,从而使目标函数最大程度接近全局最优值。(2)将目标函数对 αi 和 αj 进行优化。然后重复直到收敛。更新 αi 和 αj 的步骤如下:
假定需要更新某两个 αi 和 αj ,有 αnewiyi+alphanewjyj=alphaiyi+αjxj
利用上式,我们可以得到
其中$$\eta = 2*
其中,H和L分别为
然后就有
而关于 αi 和 αj 的选择,包括两点:
1.扫描所有 α ,选择第一个违反KKT的作为 αj
2.在不违反KKT的乘子中,选取使得 |Ej−Ei| 最大的 αi
然后,可计算出b
其中,
这样,一次迭代后我们就更新了
α
和b,迭代多次后,我们就可以得到最优的
α
。然后利用
f(x)=ωx+b=∑ni=1αiyi<xi,x>+b
即可得到分类器。
最后代码
##SVM
import numpy as np;
def load(filename):
fr = open(filename);
data=[];label=[];
for line in fr.readlines():
lineArr = line.strip().split('\t');
data.append([float(lineArr[0]),float(lineArr[1])]);
label.append(float(lineArr[2]));
return data,label;
#简化版选择alpha_j
def Salphaj(i,m):
j = i;
while(j==i):
j = np.random.uniform(i,m);
return int(j);
#规范化alpha_j
def clip(aj,H,L):
if(aj>=H):
aj=H;
elif(aj<=L) :
aj=L;
return aj;
def antiKKT(alphai,ui,yi,toler,C):
if(yi*ui < -toler and alphai<C or yi*ui > toler and alphai>0 ):
return True;
return False;
def LH(alphai,alphaj,a,C):
if(a):
return max(0,alphai+alphaj-C),min(C,alphai+alphaj);
else:
return max(0,alphaj-alphai),min(C,C+alphaj-alphaj);
#X:数据 numpy数组格式
#Y:标签 numpy数组格式
#alpha 参数,numpy数组格式
#C:参数 实数
def SMO(X,Y,C,toler,max):
X = np.mat(X);
Y = np.mat(Y).T;
m,n = np.shape(X);
alpha = np.mat(np.zeros((m,1)));
b=0;
iter = 0;
while(iter<max):
change = 0;
for i in range(m):
xi=X[i,:]
yi=Y[i,:]
ui = np.multiply(alpha,Y).T*(X*xi.T)+b;
ei = float(ui)-float(yi);
alphai = alpha[i].copy();
#alphai满足KKT就跳过,选择下一个alphai。否则,选择alphaj进行优化
if(antiKKT(alphai,ei,yi,toler,C)):
j = Salphaj(i,m);
alphaj = alpha[j].copy();
xj = X[j,:];
yj= Y[j,:];
uj = np.multiply(alpha,Y).T*(X*xj.T)+b;
ej = float(uj)-float(yj);
L,H = LH(alphai,alphaj,yi==yj,C);
if L==H: continue;
eta = 2*(xi*xj.T)-(xi*xi.T)-(xj*xj.T);
if eta >= 0: continue;
alpha[j] = alphaj - yj*(ei-ej)/eta;
alpha[j] = clip(alpha[j],H,L);
if(abs(alpha[j]-alphaj)<0.00001):continue;
alpha[i] = alphai+yi*yj*(alphaj-alpha[j]);
b1=b-ei-yi*(alpha[i]-alphai)*(xi*xi.T)-yj*(alpha[j]-alphaj)*(xi*xj.T);
b2=b-ej-yi*(alpha[i]-alphai)*(xi*xi.T)-yj*(alpha[j]-alphaj)*(xj*xj.T);
if(alpha[i]>0 and alpha[i]<C): b = b1;
elif((alpha[j]>0 and alpha[j]<C)): b= b2;
else: b = (b1+b2)/2.0;
change = change+1;
if(change == 0) :iter += 1;
else:iter=0;
return alpha,b
##可视化
### x特征矩阵,numpy数组格式
### y类标签,numpy数组格式
def plotFit(x,y,alpha,b):
import matplotlib.pyplot as plt;
x1=[]; y1=[]; #类别为1的点
x2=[]; y2=[];
m = np.shape(x)[0];
for i in range(m):
if(y[i]==-1) :
x1.append(x[i][0]);
y1.append(x[i][1]);
else:
x2.append(x[i][0]);
y2.append(x[i][1]);
plt.scatter(x1,y1,c='red');
plt.scatter(x2,y2);
a=np.arange(2.0,8.0,0.1);
x=np.mat(x);
y=np.mat(y).T;
w= sum(np.multiply(np.multiply(alpha,y),x));
w0 =w[0,0];
w1 = w[0,1];
b0 = b;
y = (-w0*a - b[0,0])/w1;
plt.plot(a,y);
plt.show();
plt.figure(2);
data,label = load('testSet.txt');
alpha,b = SMO(data,label,0.6,0.001,40);
print(alpha[alpha>0]);
print(b)
plotFit(data,label,alpha,b)
下载在这:github地址
总结
可以看到,svm的思路非常清晰,首先建模利用间隔最大对点进行分类,然后 由此引出求最大间隔超平面的问题,进而将问题转换为求解凸二次规划的问题,引入拉格朗日乘子,在这里,为了处理离群点,引入了松弛变量,然后利用SMO求解该问题,即求得最优超平面。整个一气呵成,充分展现的数学之美。还要说的一点是,我只介绍了svm对线性可分的点进行分类,实际上,对于线性不可分的点,svm在引入核方法之后,也能有很好的性能。可以说核方法才是svm的精髓,大家有兴趣可以去深入学习。
参考资料:支持向量机通俗导论