机器学习基础-9.支持向量机SVM

一、支持向量机svm

1.概念

svm即support vector machine 支持向量机,它既可解决分类问题,也可解决回归问题。在说明svm前,先看由逻辑回归得到的决策边界,如下左图所示。虽然这个决策边界将训练的样本区分开,但是这个决策边界的泛化能力并不好,靠近红色的那个蓝点明显地影响了最终决策边界的位置,而实际上这可能只是个噪声点。更合理的划分方式应该是如下右图所示。svm的思想,直观地来说,就是使所有的样本点离决策边界都尽可能远,这样得到的决策边界的泛化能力应该是比较强的,这可以通过数学理论进行验证,这里不做展开。

如下图所示,svm就是试图找到一个最优的决策边界,距离两个类别的最近的样本最远。距离决策边界最近的样本点叫做支撑向量(如图2个红色点和1个蓝色点),它们距离决策边界的距离都是一样的。这些点又可以确定和决策边界平行的2条直线,直线到决策边界的距离都为d,2条线之间不再有任何的样本点。为了增大模型的泛化能力,就需要使d尽可能地大。如图所示,我们定义margin为2条直线之间的距离(margin=2d)。现在的目标就是最大化margin。

 对于svm,分为hard margin svm 和 soft margin svm。hard margin svm需要对训练样本严格区分,不存在错分的样本,这个带来的问题除了开篇讲的例子之外,还有一种情况:如果在红色样本点中混入了1个蓝色样本点,就可能无法得出决策边界,因为样本是线性不可分的。因此引入soft margin svm,使模型可以承受一定的错误。

先用svm解决二分类问题,再扩展到多分类问题。

2.hard margin svm

要最大化margin,也就是最大化d,为此要先得出d的表达式。回忆几何中点到直线的距离公式如下。

对于n维空间,如果设决策边界的方程设为w.T*x+b =0,将样本xi代入公式,就有如下点到直线的距离公式。现在要做的就是最大化这个距离。但是需要注意的是,这个目标函数有约束条件。

我们将样本分为-1和1两个类别,这样做的目的是使之后的计算更加简单方便。根据点到直线的距离公式,对于所有样本点可以得到如下的结论。

将d移动到不等式的左边。

将w.T和b除以底下的分母,得到如下的形式。因为决策向量距离决策边界最近,可以使下面的不等式等号成立,也就是w.T*x+b=1或者w.T*x+b=-1,除了决策向量,其他的样本点肯定是大于1或者小于-1,因此得到穿过决策向量的2根直线方程。如图所示。

为了接下来的书写方便,统一将下角标d给去掉,得到如下的形式,这个和上面是完全等价的。

将不等式写成一个,得到如下的形式,这就是开始将样本分为-1和1的因为。现在就得到了目标函数的约束条件。

回到最初的目标函数,如下所示,需要注意的是,目标函数中的(w,b)和上面约束条件中的(w,b)不是等价的,但是如果分子父母同时除以d【并不改变值的大小】,这个时候目标函数中的(w,b)和约束条件中的(w,b)就是等价的。这里省略了这一过程,直接写出了下面的式子【也将下脚标d给去掉】。对于目标函数,这里的x代表的是决策向量,将穿过决策向量的直线方程代入,容易得到分子就为1,因此有如下右边的式子。

这个函数等价于求min【(1/2)||w||^2】,之所以化成这个形式是为了在之后的计算中求导方便【1、】。

 

因此hard margin svm的目标函数如下所示。这是有条件的最优化问题。背后的求解比较复杂,在这里不做展开(需要引入拉格朗日算子λ)。

3.soft margin svm

hard margin svm决策边界受噪音点的影响比较大,且对于线性不可分的样本无法得出决策边界。soft margin svm引入松弛变量ξ,允许某些样本点不满足约束条件中大于等于1的条件,对hard margin svm进行改进。

这里对松弛变量做下解释,假设有1个蓝点【w】混在红色样本点中,如下图所示。最简单有效的做法就是把蓝点直接舍弃,因为它可能只是个噪音。不幸的是,机器无法知道样本中哪个点是噪音点,在这个例子中也就是机器无法知道那个蓝色点其实就是导致最终无法得出决策边界的点。它将所有样本代入目标函数进行求解计算,发现无论w怎么取值,都不能使所有的样本同时满足hard margin svm的约束条件。所以,为了得到决策边界,机器只能对所有的样本点都放宽约束条件,这时候约束条件就变成了yi(w.T*xi+b)≥1-ξi,ξ值为非负数,如果为负相当于条件更加严格了,这是矛盾的。对于不同样本ξ值可能不同。

在下图,使蓝点【w】满足yi(w.T*xi+b)≥1-ξi,就可以得到决策边界【黑线g】。这个决策边界并不像hard margin svm中严格地将样本分为2类,而是允许某些样本被错分。这就是soft margin svm中松弛变量的作用和意义。

现在问题来了,ξ该取什么样的值才能够使得最终能够得到决策边界?如果ξ很小,甚至接近0【退化到hard margin svm】,这可能还是无法得到一个决策边界,那么ξ必然存在一个最小值。让我们再回到上面的图,试着从几何上理解ξ。可以发现,这个ξ其实就等价于将蓝点【w】移动到蓝色阵营中所用的距离,可以得出结论:ξ要大于等于将蓝点【w】移动到直线h所需的最短距离ε。用反证法来证明这个结论:假设已经得到了决策边界g,但是蓝点【w】所用的ξ值小于ε,这时蓝点必然处于h和f两条线之间,该点不能满足yi(w.T*xi+b)≥1的条件,因此决策边界不可能被求出,这和假设是互相矛盾的,所以可以得到上面的结论。

那是否ξ值越大越好?其实过大的ξ将会导致模型的泛化能力很差。试想一下,我们引入ξ的初衷是允许决策边界将部分样本错分。如果很多样本都引入了非零的ξ,得出来的决策边界必然是将这些样本进行错分;另一方面如果引入的ξ值越大,代表模型对错分样本的容忍度越高。如果一个模型,进行训练时候就将很多训练样本分错了,并且还对错分的容忍度很高,那么它在测试样本中的表现必然会很糟糕。

综上,我们可以得到结论:ξ在满足约束条件的同时,还必须尽可能地小。那么为了满足这样的约束,最终的目标函数和约束条件如下左图所示,ξ对应的项也叫L1正则项。引入参数C是为了平衡目标函数的比重,如果C越大表示容错空间越小,C越小表示容错空间越大。C也叫做惩罚因子。进一步ξ也可以取平方,这时候就叫做L2正则,如下右图所示。

求解svm目前有个比较好的方法:序列最小最优化算法(SMO),这里不再展开。由于水平有限,上面仅仅对svm做了简单的介绍,给出的解释也比较直观,还缺乏数学推导支持,如果想进一步了解svm的数学求解,可以参考如下文档。

关于SVM一篇比较全介绍的博文关于SVM一篇比较全介绍的博文 - wangduo - 博客园

接下来直接对svm进行应用。

二、svm应用

现在采用sklearn中封装好的svm对数据进行分类。

import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
iris = datasets.load_iris()
x=iris.data
y=iris.target
x=iris.data[y<2,:2]
y=iris.target[y<2]
plt.scatter(x[y==0,0],x[y==0,1])
plt.scatter(x[y==1,0],x[y==1,1])
plt.show()

def plot_decision_boundary(model,axis):#绘图模块
    x0,x1 = np.meshgrid(  
        np.linspace(axis[0],axis[1],int((axis[1]-axis[0])*100)).reshape(-1,1),
        np.linspace(axis[2],axis[3],int((axis[3]-axis[2])*100)).reshape(-1,1)
    )
    x_new = np.c_[x0.ravel(),x1.ravel()]
    y_predict = model.predict(x_new)
    zz = y_predict.reshape(x0.shape)
    from matplotlib.colors import ListedColormap
    custom_cmap = ListedColormap(['#EF9A9A','#FFF59D','#90CAF9'])
    plt.contourf(x0,x1,zz,linewidth =5,cmap=custom_cmap)

from sklearn.preprocessing import StandardScaler #数据标准化
standarscaler = StandardScaler()
standarscaler.fit(x)
x_standar = standarscaler.transform(x)
from sklearn.svm import LinearSVC #SVC指的是support vector classifier
svc = LinearSVC(C=1e9)#这里先传入比较大的C,也就是约束ξ值
svc.fit(x_standar,y)
plot_decision_boundary(svc,axis=[-3,3,-3,3])
plt.scatter(x_standar[y==0,0],x_standar[y==0,1])  
plt.scatter(x_standar[y==1,0],x_standar[y==1,1])
plt.show()#如下左图,将C设为0.01,这时允许ξ比较大,如下右图所示,将会产生错分样本

svc.coef_

查看系数发现,这是个二维的数组,因为sklearn已经封装了多分类的方法,如果是多分类将会有多条的直线,这里是二分类,因此只对应的一个元素,并且由于只取了2个特征,所以系数也就只有2个。接下来,利用系数将边界线也绘制出来。

svc.intercept_#查看b值
def plot_svc_decision_boundary(model,axis):
    x0,x1 = np.meshgrid(  
        np.linspace(axis[0],axis[1],int((axis[1]-axis[0])*100)).reshape(-1,1),
        np.linspace(axis[2],axis[3],int((axis[3]-axis[2])*100)).reshape(-1,1)
    )
    x_new = np.c_[x0.ravel(),x1.ravel()]
    y_predict = model.predict(x_new)
    zz = y_predict.reshape(x0.shape)
    from matplotlib.colors import ListedColormap
    custom_cmap = ListedColormap(['#EF9A9A','#FFF59D','#90CAF9'])
    plt.contourf(x0,x1,zz,linewidth =5,cmap=custom_cmap)
    #这里取出系数,并对公式进行一定的变形,使之映射到二维的平面上
    w = model.coef_[0]
    b = model.intercept_[0]
    plot_x = np.linspace(axis[0],axis[1],200)
    up_y= -w[0]/w[1]*plot_x-b/w[1]+1/w[1]
    down_y = -w[0]/w[1]*plot_x-b/w[1]-1/w[1]
    
    up_index = (up_y >= axis[2])&(up_y<=axis[3])
    down_index = (down_y>= axis[2])&(down_y<=axis[3])
    plt.plot(plot_x[up_index],up_y[up_index],color='black')
    plt.plot(plot_x[down_index],down_y[down_index],color='black') 
plot_svc_decision_boundary(svc,axis=[-3,3,-3,3])
plt.scatter(x_standar[y==0,0],x_standar[y==0,1])  
plt.scatter(x_standar[y==1,0],x_standar[y==1,1])
plt.show()#C=1e9,如下右图是c=0.1时的图像

三、svm使用多项式

import numpy as np  
import matplotlib.pyplot as plt 
from sklearn import datasets

x,y = datasets.make_moons(noise=0.15,random_state=666)#自动生成非线性数据,并引入噪音
plt.scatter(x[y==0,0],x[y==0,1])
plt.scatter(x[y==1,0],x[y==1,1])
plt.show()

from sklearn.preprocessing import PolynomialFeatures,StandardScaler
from sklearn.svm import LinearSVC
from sklearn.pipeline import Pipeline

def PolynomialSVC(degree,C=1.0): #将过程封装
    return Pipeline([
        ("poly",PolynomialFeatures(degree=degree)),
        ("std_scaler",StandardScaler()),
        ("linearSVC",LinearSVC(C=C))
        
    ])
poly_svc = PolynomialSVC(degree=3)
poly_svc.fit(x,y)
def plot_decision_boundary(model,axis):
    x0,x1 = np.meshgrid(  
        np.linspace(axis[0],axis[1],int((axis[1]-axis[0])*100)).reshape(-1,1),
        np.linspace(axis[2],axis[3],int((axis[3]-axis[2])*100)).reshape(-1,1)
    )
    x_new = np.c_[x0.ravel(),x1.ravel()]
    y_predict = model.predict(x_new)
    zz = y_predict.reshape(x0.shape)
    from matplotlib.colors import ListedColormap
    custom_cmap = ListedColormap(['#EF9A9A','#FFF59D','#90CAF9'])
    plt.contourf(x0,x1,zz,linewidth =5,cmap=custom_cmap)
    
plot_decision_boundary(poly_svc,axis=[-1.5,2.5,-1,1.5])
plt.scatter(x[y==0,0],x[y==0,1])  
plt.scatter(x[y==1,0],x[y==1,1])
plt.show()#如下左图。右图为C=0.1时候的图像


其实也可以不使用PolynomialFeatures函数,而是将数据转换到更高维度的空间,这就是核函数的作用。

四、核函数

1.概念

在求解svm时候,将目标函数可以推导得到右边的形式。

在多项式中,xi*xj需要经过添加多项式后再进行相乘,核函数的思想是设计出一个函数,使得传入xi和xj直接得到他们添加多项式再相乘的结果,即如下所示。

于是目标函数进步写成如下形式。核函数是个数学技巧,它不仅仅用在svm中。

下面我们来看一个核函数。它经过展开后可以表示成x和y经过多项式拓展再相乘的形式,所以可以直接用(x*y+1)^2进行计算。

 这个还可以拓展到2次方以上的核函数。

2.高斯核函数

高斯核函数使用非常广泛。核函数表示x和y的点乘,对于高斯核函数有如下形式。

高斯核函数又叫做RBF核,Radial Basis Function Kernel,它的映射比较复杂,是将每一个样本点映射到一个无穷维的特征空间。之前采用多项式特征,其实是依靠升维将原本线性不可分的数据线性可分。如下左图所示,此时样本是线性不可分的,但是如果添加多项式特征,那么就可以将样本映射到更高维的空间,此时样本就是线性可分的,如下右图所示。高斯核其实也就是做这样的事情。

           

如果将样本x从一维映射到二维,应用高斯核函数对应的方程如下,特征值由1个变成2个,即扩展到2维。l值称为地标点(landmark),有几个地标点,数据就会被映射到多少维的空间。下面代码模拟高斯核函数的过程。

import numpy as np  
import matplotlib.pyplot as plt 
x = np.arange(-4,5,1)
y = np.array((x>=-2)&(x<=2),dtype='int')

plt.scatter(x[y==0],[0]*len(x[y==0]))
plt.scatter(x[y==1],[0]*len(x[y==1]))
plt.show()#这时候数据是线性不可分的,需要通过核函数映射到高维空间

def gaussian(x,l):  #编写高斯核函数
    gamma = 1.0     #gamma也是个超参数,这里先取1.0   
    return np.exp(-gamma*(x-l)**2)
l1,l2=-1,1 #固定l的值,这样样本就会被映射到2维空间
xnew = np.empty((len(x),2))
for i,data in enumerate(x):
    xnew[i,0] = gaussian(data,l1)
    xnew[i,1] = gaussian(data,l2)
plt.scatter(xnew[y==0,0],xnew[y==0,1])
plt.scatter(xnew[y==1,0],xnew[y==1,1])
plt.show() #容易发现,这时候很容易找到1条直线将样本进行划分

这里的L值为固定的2个值,实际上在高斯核函数中,是将每个样本点都作为地标点,也就是说m*n的数据将会被映射成m*m的数据。

3.sklearn中高斯核函数

import numpy as np  
import matplotlib.pyplot as plt 
from sklearn import datasets

x,y = datasets.make_moons(noise=0.15,random_state=666)#自动生成非线性数据,并引入噪音
plt.scatter(x[y==0,0],x[y==0,1])
plt.scatter(x[y==1,0],x[y==1,1])
plt.show()
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC
from sklearn.pipeline import Pipeline

def RPFKernelSVC(gamma=1.0):
    return Pipeline([
        ("std_scaler",StandardScaler()),
        ("SVC",SVC(kernel="rbf",gamma=gamma))        
    ])
svc = RPFKernelSVC(gamma=0.1)#gamma取值分别为0.1,1,100,图像绘制如下
svc.fit(x,y)
def plot_decision_boundary(model,axis):
    x0,x1 = np.meshgrid(  
        np.linspace(axis[0],axis[1],int((axis[1]-axis[0])*100)).reshape(-1,1),
        np.linspace(axis[2],axis[3],int((axis[3]-axis[2])*100)).reshape(-1,1)
    )
    x_new = np.c_[x0.ravel(),x1.ravel()]
    y_predict = model.predict(x_new)
    zz = y_predict.reshape(x0.shape)
    from matplotlib.colors import ListedColormap
    custom_cmap = ListedColormap(['#EF9A9A','#FFF59D','#90CAF9'])
    plt.contourf(x0,x1,zz,linewidth =5,cmap=custom_cmap)
    
plot_decision_boundary(svc,axis=[-1.5,2.5,-1,1.5])
plt.scatter(x[y==0,0],x[y==0,1])  
plt.scatter(x[y==1,0],x[y==1,1])
plt.show()

依次增大超参数gamma的值,可以发现gamma值越高,模型越复杂,过拟合现象越严重;gamma越低,模型越简单,欠拟合现象越严重,gamma的具体取值需要依据实际问题进行取值,

五、svm解决回归问题

1.思想

svm解决回归问题的思想和svm解决分类问题的思想正好相反,在分类问题中,margin对应2根直线中间要尽量避免有元素,对应hard margin svm甚至要求中间不能有任何的元素,而用svm解决回归的时候是要定义一个epsilon,使之中间有更多的元素。

2.sklearn中的svm解决回归问题

import numpy as np  
import matplotlib.pyplot as plt 
from sklearn import datasets

boston = datasets.load_boston()
x = boston.data
y = boston.target

from sklearn.model_selection import train_test_split

x_train,x_test,y_train,y_test = train_test_split(x,y,random_state=666)

from sklearn.svm import LinearSVR
from sklearn.svm import SVR
from sklearn.preprocessing import StandardScaler
from sklearn.pipeline import Pipeline

def StandarLinearSVR(epsilon=0.1):
    return Pipeline([
        ("std_scaler",StandardScaler()),
        ("linearsvr",LinearSVR(epsilon=epsilon))        
    ])
svr = StandarLinearSVR()
svr.fit(x_train,y_train)
svr.score(x_test,y_test)

  • 2
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值