svm需要一定的数学功底,我是亦步亦趋花了一个星期,才渐渐有所了解。出于学习交流目的,我将自己的学习思路整理出来。由于对svm的理解有所不足,如有错误,敬请指正。
支持向量机(support vertor machines,SVM)是一种二分类模型。它的基本模型是定义在特征空间上的最大间隔分类器,核技巧使它称为线性分类器。支持向量机的学习策略就是间隔最大化,可形式化为一个求解凸二次规划的问题,也等价于正则化的合页损失最小化问题。支持向量机的学习算法是求解凸二次规划的最优化算法。
ok,让我们从最简单的线性分类说起,如图:
在一个平面上有两类点,中间的实线表示一个分类器。我们知道,空间中的一个(超)平面(在二维是直线)可以表示为
对于给定训练集T和超平面(w,b),定义超平面关于样本点 (xi,yi) 的函数间隔为
γi^=yi(w∗xi+b)
定义超平面(w,b)关于训练集T的函数间隔为所有样本点函数间隔的最小值即
γ^=mini=1,2...Nγi^
函数间隔虽然可以表示分类,但是如果成比例的改变w和b,函数间隔就会成比例的变化,由此我们引入几何间隔。
γ=γ^||w||
几何距离就是点到超平面的距离
用几何距离来表示间隔显然要比函数间隔好。那么,我们的目的就是使几何距离最大化,即
什么是支持向量?
从图中可以看出,两个支撑着中间间隙的超平面,它们到中间分类超平面的距离相等,即几何间隔 γ ,而支撑这两个超平面的点便叫做支持向量。
之前我们要求解式(2),由于对 ||w|| 求最大等价于对 1||ω|| 求最小,则原问题等价于
于是接下来我们的任务就是:(1)固定 α 对 θ(ω) 求极小,得到关于 α 的表达式(2)对 α 求极大,(3)利用SMO求对偶因子。
第一步 先固定 α ,对 θ(ω) 求偏导。
在讨论SMO算法之前,我们先说说另一个问题————离群点的处理。我们起初的假设是数据都是线性可分的,假如我们的数据中存在离群点,原有的模型会受到很大的影响,如下图
用黑色圈出来的蓝色点就是一个离群点。如果没有这个点,我们的模型会得到红色实线所示的分类器。但是由于有了这个点,现在得到的分类器会是如虚线所示,显然这个分类器的间隔比之前分类器的间隔要小。要是该离群点还往上一点,我们甚至无法利用我们的模型来构造分类超平面。
为了解决离群点问题,svm在一定程度上可以支持离群点。如上图,将离群点移动到蓝色实线所示的支持平面上,超平面就不会改变了。为此,我们引入了松弛变量 ξ 。我们原来的约束条件是
到这一步,我们有了式(13)-(15),我们现在来看一看SMO是怎样求解的。
首先,更具KKT条件得出 αi 的取值意义:
记 ui=ωx+b
以下情况会出现不满足:
其中,H和L分别为
这样,一次迭代后我们就更新了 α 和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 alphaior 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(iter0;
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]elif((alpha[j]>0 and alpha[j]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的精髓,大家有兴趣可以去深入学习。
参考资料:支持向量机通俗导论