python机器学习——SVM支持向量机

背景与原理:

支持向量机是一种用来解决分类问题的算法,其原理大致可理解为:对于所有n维的数据点,我们希望能够找到一个n维的直线(平面,超平面),使得在这个超平面一侧的点属于同一类,另一侧的点属于另一类。而我们在寻找这个超平面的时候,我们只需要找到最接近划分超平面的点,而一个n维空间中的点等同于一个n维向量,所以这些点就可以被称为支持向量。

在一个n维空间中,一个超平面可以用0=wTx+b表示,比如一条二维空间中的直线可以表示成ax+by+c=0,而一个三维空间中的平面可以表示成ax+by+cz+d=0,以此类推。而n维空间的一个超平面会把n维空间中的点分成三部分:对一个n维空间中的点x0和超平面wTx+b=0,若wTx0+b<0,说明x0在超平面的一侧,而若wTx0+b=0,说明x0在超平面上,若xTx0+b>0,说明x0在超平面的另一侧,也就是说,当我们给定了一个超平面之后,空间中的所有点就自然被这个超平面分成了两类(如果忽略超平面上的点)

线性可分支持向量机:

首先我们考虑线性可分的点,即客观存在一个超平面,使得两类数据点分别分布在超平面的两侧,那么不难想见,这样的超平面有无穷多个,但为了模型的泛化性能,我们希望选取的超平面应该是位于两组数据集中间的(个人理解:考虑一种极端情况:这个超平面是贴着某一类的数据点经过的,那么在实际情况中,属于这类的数据点很可能有些稍微散布到了这条直线外,这样这个分类器的性能就不够好了,而如果位于中间,即使有些数据点相较于训练数据有些往外散布,也能较为正确地分类。)

那么由计算几何中的已知公式,我们可以看到:n维空间中任意一个点x0到一个给定超平面wTx+b=0的距离为|wTx0+b||w|,其中|w|=i=1nwi2。而假设对于第i组数据,其类别为yi,由于我们要根据数据点在超平面的哪一侧确定类别,因此我们实际是要求数据点的位置与数据点的类别相匹配,那么我们的类别也非常自然地选取1/-1,而是否匹配则可以由yi(wTxi+b)的正负性来度量,如果为正我们认为是相匹配的。

那么这个问题就变成了:对于一组数据(X1,y1),...,(Xm,ym),其中Xin维向量,我们要求一个n维超平面wTx+b=0,而如果令r=mini=1m|wTXi+b||w|,那么我们要最大化r,而约束条件则是yiwTXi+b|w|r(这个约束条件有点唬人:y的取值只有1/-1,所以y不影响左边这个东西的绝对值,而r一定小于等于左边这个东西的绝对值,因此这个约束条件实际只约束了符号)

那么令w=w|w|r,b=b|w|r(这并不影响超平面),这样就得到了约束条件:yi(wTXi+b)1(对超平面的所有参数除以同一个值并不改变这个超平面,只是让约束条件更好看了)

而在这个约束条件下,我们可以看到|wTXi+b|1,于是r1|w|(由r的定义立得),于是我们实际上要最大化的是1|w|,那么也就是最小化|w|,而这不好求解,所以我们要求最小化12|w|2(这些变化都是为了计算简便)

这样我们的问题就转化为了:在yi(wTXi+b)1的条件下,最小化12|w|2

那么这是个条件最值,我们应用拉格朗日乘子法,构造拉格朗日函数:

L=|w|22i=1mαi(yi(wTXi+b)1)

这个问题并不好求解,因为参数个数等于数据集大小了,因此我们考虑改进:设θ(w)=maxαi0L

那么可以看到,在所有的约束条件都成立时,后面要减掉的那一项一定非负,这样的话对于所有的yi(wTXi+b)1>0,应该取对应的αi=0才能取得最大值,而最后能取得的最大值就是前面那项|w|22,于是此时的θ(w)=|w|22

而如果有某个约束条件不成立,后面要减掉的那一项是个负值,这样的话对应的αi越大,整个函数值越大,这样θ(w)=+

这样的话我们如果我们想在所有约束条件成立的情况下最小化|w|22,我们实际上就是在最小化θ(w),即我们要进行的是这样的操作:

minw,bmaxαi0L

而可以证明,这个问题与其对偶问题:

maxαi0minw,bL

是等价的,因此我们来解决这个对偶问题:首先我们对wb求偏导:

Lw=0w=i=1mαiyiXi

Lb=0i=1mαiyi=0

这样我们直接代入,可得我们要进行的任务是:

maxαi0|i=1mαiyiXi|22i=1mαi(yi((j=1mαjyjXj)Xi+b)1)

这样实际就是

maxαi0i=1mj=1mαiαjyiyj(XiXj)2+i=1mαi

取个符号变成等价的问题:

minαi0i=1mj=1mαiαjyiyj(XiXj)2i=1mαi

这样最后的问题就确定了:我们要在αi非负的条件下最小化i=1mj=1mαiαjyiyj(XiXj)2i=1mαi,求出最小的α后我们就可以计算出:

w=i=1mαiyiXi

同时一定存在一个αj>0(否则w=0),那么对于这个j,有:

yjwTXj=b

代入上面求出的w,即得到:

b=yji=1mαiyi(XiXj)

这里有一个逻辑:我们上文已经提到了,对于所有的yi(wTXi+b)1>0,应该取对应的αi=0,也就是说只有yi(wTXi+b)=1才会产生非零的αi,而由于yi只能为1/-1,因此移项即得yjwTXj=b

而这同样也启示我们:训练完成后,大部分样本都不需要保留,支持向量机只与处于边界位置的支持向量有关。

当然了,这并不是线性问题的结束,因为真实生活中,很难存在绝对线性可分的数据,大多数的数据在分割位置上是有交叉的,这样的话上面的理论模型就不好用了。

因此我们退一步,允许有一定的偏移,同时在损失函数中增加一个惩罚项,也即我们要最小化

|w|22+Ci=1mξi

其中ξi=max(0,1yi(wTXi+b)表示一个分类错误的程度,而C是惩罚系数,C越大代表我们对分类错误的惩罚越大。

因此整个算法流程为:

(1)给定训练集(X1,y1),...,(Xm,ym),其中yi=1/1,选取惩罚参数C

(2)最小化i=1mj=1mαiαjyiyj(XiXj)2i=1mαi,约束条件是0αiCi=1mαiyi=0(这个求解过程与无惩罚项的时候类似,这里不展开了)

(3)w=i=1mαiyiXi,找到αj>0,对于这个jb=yji=1mαiyi(XiXj),得到超平面wTx+b=0

(4)分类函数即为f(x)=sgn(wTx+b)

非线性可分支持向量机:

在现实生活中,很多数据并不是线性可分的,比如二维空间按一个圆内和圆外对点进行分类,这显然不是一个线性可分的问题,那么对于这种问题我们的处理方法是将其映射到更高维度的空间中去,以期在更高维度的空间中其是线性可分的。

但是在上述讨论过程中我们可以看到,我们需要的其实并不是真正的高维空间中的点是什么,我们只需要计算出两个点之间的点积!

那么如果我们设函数ϕ(x)x投影到高维空间,那我们只需要一个函数K(x,z)=ϕ(x)ϕ(z),这样的话我们就只需最小化

i=1mj=1mαiαjyiyjK(Xi,Xj)2i=1mαi

而不需要得知某个X投影到高维空间中具体是什么了。

所以我们实际上要选取的是K,这个K又被称为核函数,常用的核函数有多项式核,高斯核K(x1,x2)=e|x1x2|22σ2和线性核K(x1,x2)=x1x2(线性核存在的意义是统一了所有的支持向量机的形式,这样无论是什么问题我们都要选取一个核然后操作,而不用根据是否是线性可分选取不同的形式了)

因此所有的支持向量机的算法步骤为:

(1)给定训练集(X1,y1,...,(Xm,ym),其中yi=1/1,选取惩罚参数C和核函数K

(2)最小化i=1mj=1mαiαjyiyjK(Xi,Xj)2i=1mαi,约束条件是0αiCi=1mαiyi=0

(3)找到αj>0,对于这个jb=yji=1mαiyiK(Xi,Xj),得到超平面wTx+b=0

(4)分类函数即为f(x)=sgn(i=1mαiyiK(X,Xi)+b)

代码实现:

 

复制代码
import numpy as np
import math
import matplotlib.pyplot as plt
from sklearn import svm


x=np.arange(0.,10.,0.02)
y=5-2*x/3+np.random.randn(500)
now=0
dataset=[]
for i in range(0,500):
    typ = -1
    if 2*x[i]+3*y[i] <= 15:
        if abs(np.random.randn(1)[0])<2:
            typ = 1
        else:
            typ = -1
    else:
        if abs(np.random.randn(1)[0]) < 2:
            typ = -1
        else:
            typ = 1

    dataset.append([x[i],y[i],typ])

X=(np.array(dataset)[:,0:2])
Y=(np.array(dataset)[:,2])
model=svm.SVC(C=1.0,kernel='linear').fit(X,Y)



for i in range(0,500):
    if Y[i]==1:
        plt.scatter(X[i,0],X[i,1],c='r')
    else:
        plt.scatter(X[i,0],X[i,1],c='b')

w=model.coef_
b=model.intercept_
plt.plot(X[:,0],-(w[0][0]/w[0][1])*X[:,0]-b[0]/w[0][1],c='g',linewidth=3)
plt.show()
复制代码

支持向量机的代码不好实现,这里直接调了库,要分类的数据和之前逻辑回归是的是一样的,分类效果如下:

可以看到分类效果还是很不错的

posted @   lleozhang  Views(392)  Comments(0Edit  收藏  举报
相关博文:
阅读排行:
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
levels of contents
点击右上角即可分享
微信分享提示