支持向量机(Sport Vector Machine,SVM)是经典的机器学习方法之一,其核心思想主要是找到一个超平面,使得它能够尽可能多地将两类数据点正确分开,同时使分开地两类数据点距离分类平面最远。
如图
下面将介绍支持向量机最简单地形式,线性可分支持向量机,即样本只有正负样本的情况且正负样本是能够通过一个超平面能够完全区分的。
但是,如图所示,其实存在多个个超平面都能够将正负样本区分开来,那我们应该选择能够使得正负样本边界间隔最大的那个超平面,作为我们的最优解,因为这样能够使得模型拥有较好的泛化能力,达到较好的效果。
为了最优超平面,在样本空间中,我们能够将划分样本的超平面定义如下
其中w = (w1;w2;…wn)为法向量,决定了超平面的方向,b为位移项,决定了超平面与原点之间的距离,因此只要解出w和b我们便能够确定那个最优的超平面了。
其实最优超平面就是由个别正负样本的边界点来决定的。由于需要满足正确区分正负样本的条件,因此给出约束条件6.3
其中r(距离公式求得)代表每个样本点到最优超平面(目前未知)的距离,xi代表样本值,yi代表样本类别(这里取1或-1),由于最优超平面距离正负样本的距离是一样的,因此间隔为2/(||w||)(同样也由距离公式可得的)
为了能够解出最优超平面,就是需要找到“最大间隔”,间隔中间的超平面即为我们需要求解的超平面,因此可以给出以下约束:
将此约束条件等价变换为如下(求最大即求倒数的最小):
满足此条件解出w和b即可求的我们需要的超平面。
为了更高效地求解此凸二次规划问题。我们需要引入拉格朗日函数,利用拉格朗日的对偶性,通过求解对偶问题得到原始问题的最优解,这就是线性可分支持支持向量机的对偶算法。
下面引入广义拉格朗日函数
以我目前理解来看(套公式的角度),当需要求目标函数f(x)最小时,当有条件限制为hi(x)=0(i=1,2,…m)且gj(x)<=0(j=1,2,…n)时,能够引入拉格朗日乘子使得有约束条件问题,转化为无约束条件问题,从而达到优化求解的目的。且需要满足KKT条件,才能够使得出的对偶问题拥有强对偶性,即使得对偶问题求出的解相等,使得对偶问题得以求解,从而使得主问题(原始问题)得以求解。
也可以看看b站视频对SVM中对偶问题的讲解:
https://www.bilibili.com/video/BV1Hs411w7ci?p=2&spm_id_from=pageDriver
下面我们通过广义拉格朗日函数重写原问题。
下面为支持向量机的基本型
能够将约束条件全移到右侧将新函数记为gi(x)=1-yi(wTxi + b),i=1,2,3…m,且gi(x)<=0。目标函数f(x)=1/2 * ||w||^2。
因此将其转换为拉格朗日函数之后如下所示:
由于原问题中没有h(x)=0的项,因此只需要引入一个拉格朗日乘子即可。
为了得到对偶问题的解,需要先求L(w,b,α)对w,b的极小,再求对α的极大。
α可使用二次规划的方法进行求解。
KKT条件即表明,能够影响超平面的只有个边位于最大间隔边界上的样本点(支持向量)。
KKT条件是使得原问题转换为对偶问题后原问题解与对偶问题解相等的充要条件,其条件大致分为三个部分:可行条件、互补松弛条件、梯度为0条件,具体可以参考https://www.bilibili.com/video/BV1aE411o7qd?p=35白板推导,十分不错。
我的通俗理解:
可行条件:原问题给的限制条件即c(x)<=0,h(x)=0+进行拉格朗日变换的限制条件即α>=0
互补松弛条件:有限制的拉格朗日乘子(乘子>=0) * 对应的原问题的限制函数 = 0
梯度为0:原问题中的未知数在拉格朗日变换后的新函数里的偏导=0
下面展示一道例题,能够帮助更深刻理解以上过程。
此题代码实现
# coding:utf-8
import numpy as np
import pylab as pl
from sklearn import svm
X = np.array([[3, 3], [4, 3], [1, 1]])
Y = np.array([1, 1, -1])
clf = svm.SVC(kernel="linear") # 定义分类器,核函数为线性核符合题意
clf.fit(X, Y) # 即计算出划分超平面
w = clf.coef_[0] # w 是一个二维数据,coef 就是 w = [w0,w1]
k = -w[0] / w[1] # 斜率,即w
xx = np.linspace(-6, 6) # 用于画超平面
# clf.intercept[0] 即b
yy = k * xx - (clf.intercept_[0]) / w[1] # 带入 x 的值,获得直线方程
b = clf.support_vectors_[0]
yy_down = k * xx + (b[1] - k * b[0]) # 计算下界
b = clf.support_vectors_[-1]
yy_up = k * xx + (b[1] - k * b[0]) # 计算上界
print("支持向量,即最大间隔边界上的点", clf.support_vectors_)
print("属于支持向量的点在原数据集中的索引", clf.support_)
print("在每一个类中属于支持向量的个数:", clf.n_support_)
print("(4,4)的预测值为", clf.predict([[4, 4]]))
print("超平面的w", w)
print("超平面的b", clf.intercept_[0])
# 绘制划分超平面,边际平面和样本点
pl.plot(xx, yy, 'k-')
pl.plot(xx, yy_down, 'k--')
pl.plot(xx, yy_up, 'k--')
pl.scatter(clf.support_vectors_[:, 0], clf.support_vectors_[:, 1],
s=80, facecolors='none')
pl.scatter(X[:, 0], X[:, 1], c=Y)
pl.axis('tight')
pl.show()
但是对于上述的支持向量机模型,是必须要找到一个超平面,能够完全区分正负样本,我们称之为硬间隔的支持向量机,因为他不允许有一点点错误,但是实际上我们遇到的现实中的数据,或多或少会有一些特殊值出现,或者说样本并不是完全线性可分的,但是如果去掉一些偏差比较大的点,样本还是可以线性可分的。因此,面对这样的情况,我们需要引入软间隔支持向量机。
当样本正确分类时,满足6.28条件约束
当允许一些样本有一些偏差,即能够容忍其不满足条件约束时,优化目标可写为以下形式
损失函数6.31为合页函数(hinge),6.34即在原来的硬间隔基础上增加了损失函数,其含义是当样本正确分类时,带入约束6.28左边得到的函数值记作t,t是>=1那么1-t一定<=0,带入hinge中得出损失值也为0,当样本不正确分类时,t是<=0的,那么1-t是>=0的带入hinge其损失值为1-t。其中C为一个常数,当C->无穷大时,迫使所有样本都必须满足分类正确的要求,也就是硬间隔支持向量机,当C为有限值时,便允许一些样本存在偏差。
这里也可以去看看b站上白板推导过程,还是很不错的
https://www.bilibili.com/video/BV1Hs411w7ci?p=4
为了表达简便,我们可以引入“松弛变量”代替6.34中的1-yi(wTxi + b),重写6.34如下
因为已经规定松弛变量是>=0了,那么就可以将原来的max函数脱掉了,因为当松弛变量<0时,一定是样本正确分类的情况,其损失值必然为0。
因此6.35目标函数的约束条件如下:
接下来就是使用拉格朗日函数求解,值得注意的是,这里有两个约束条件,因此需要引入两个拉格朗日乘子,将其转换为无约束形式,然后对其进行求解。
因此,软间隔的支持向量机模型同样也是只由少部分的支持向量有关,不过与硬间隔的对比其支持向量的分布更加复杂。硬间隔的支持向量机都在最大间隔的边界处,而软间隔的支持向量有可能在最大间隔边界,有可能在间隔边界与分离的超平面之间,也有可能在分离超平面误分的一侧。
代码如下:
# coding:utf-8
import numpy as np
import pylab as pl
from sklearn import svm
X = np.array([[0, 4], [3, 3], [4, 3], [1, 1], [2, 2], [2, 4.5]])
Y = np.array([-1, 1, 1, -1, -1, -1])
clf = svm.SVC(kernel="linear", C=0.2) # 定义分类器,核函数为线性核符合题意,定义C软间隔,允许误差存在
clf.fit(X, Y) # 即计算出划分超平面
w = clf.coef_[0] # w 是一个二维数据,coef 就是 w = [w0,w1]
k = -w[0] / w[1] # 斜率,即w
xx = np.linspace(-6, 6) # 用于画超平面
# clf.intercept[0] 即b
yy = k * xx - (clf.intercept_[0]) / w[1] # 带入 x 的值,获得直线方程
b = clf.support_vectors_[0]
yy_down = k * xx + (b[1] - k * b[0]) # 计算下界
b = clf.support_vectors_[-1]
yy_up = k * xx + (b[1] - k * b[0]) # 计算上界
print("支持向量", clf.support_vectors_)
print("属于支持向量的点在原数据集中的索引", clf.support_)
print("在每一个类中属于支持向量的个数:", clf.n_support_)
print("(4,4)的预测值为", clf.predict([[4, 4]]))
print("超平面的w", w)
print("超平面的b", clf.intercept_[0])
# 绘制划分超平面,边际平面和样本点
pl.plot(xx, yy, 'k-')
pl.plot(xx, yy_down, 'k--')
pl.plot(xx, yy_up, 'k--')
pl.scatter(clf.support_vectors_[:, 0], clf.support_vectors_[:, 1],
s=80, facecolors='none')
pl.scatter(X[:, 0], X[:, 1], c=Y)
pl.axis('tight')
pl.show()
本文参考 周志华著作的《机器学习》
李航著作的《统计学习方法》
https://www.bilibili.com/video/BV1aE411o7qd?p=35