在《入门支持向量机1:图文详解SVM原理与模型数学推导》这篇文章中,我们介绍了SVM的相关概念、算法思想以及推导过程。在本篇文章中,我们会继续深入,研究泛化性更好的“软间隔”以及相应的正则化思想。并且使用sklearn中的SVM模型进行Coding,增强对线性可分SVM的认识。
在线性可分问题中,对于样本点来说,存在一根直线可以将样本点划分,我们称之为Hard Margin SVM
;但是(同样线性不可分),有时候会出现不那么完美,样本点会有一些噪声或者异常点,并不能完全分开。即没有一条直线可以将样本分成两类。那么就提出了Soft Margin SVM
。
Soft Margin SVM
的思想也很朴素,就是在Hard Soft
的基础上,将原来的约束条件放宽一些。增加容错性。
在Hard Soft
中的约束条件为:
对于限制条件,形象地说,Margin区域里必须是任何数据点都没有,所有的数据点都必须在与两条直线的外侧。
如果有些数据点不能满足这个要求,就对条件加以宽松,在margin区域外给他一个宽松量(大于等于0):
但是我们很容易地想到:容错空间也不能无限制的放大。在最小化的同时加上所有点的容错空间的和,就可以在最小化的同时又可以容忍一点程度的错误。并且通过参数来平衡重要程度。
因此Soft Margin SVM
最优化问题对应的数学表达式为:
我们观察上面的表达式的形式,其实相当于在Soft Margin SVM
中加入了L1正则。可以将理解为正则化项,避免因为训练出的模型往极端的方式发展,让模型的拥有一定的容错能力,泛化性有所提升。
相应的,也有L2正则,即在的求和项上有所不同:
系数越大,相应的容错空间越小。如果取正无穷,意味着逼迫着容错空间趋近于零,也就变成了Hard Margin SVM
。
那么Soft Margin的目标函数长什么样呢?还是用拉格朗日的方法:
由于优化函数增加了惩罚项,且增加了一些约束条件,因此目标函数变得很复杂了。其实求解方法和之前是一样的,同样是分别对变量求偏导等于0:
带回到原表达式中得到:
SVM的美妙之处,就是加了Soft Margin这么一个复杂的东西,但是通过求导之后,发现解决问题的方法殊途同归,并没有很复杂。
在使用SVM算法时,需要先对数据进行标准化处理。
SVM算法寻找的是使得Margin区间最大的中间的决策边界(超平面),而衡量Margin使用的是数据点之间的距离。涉及距离的,就应当对量纲问题进行关注,即进行标准化处理。
对于SVM算法来说,如果特征在不同维度上,数据尺度不同的话,非常影响SVM算法的决策边界。
import matplotlib.pyplot as pltimport numpy as npfrom sklearn import datasetsiris = datasets.load_iris()X = iris.datay = iris.targetX = X[y<2,:2]y = y[y<2]plt.scatter(X[y==0,0],X[y==0,1],color='red')plt.scatter(X[y==1,0],X[y==1,1],color='blue')plt.show()
然后进行数据标准化处理:
from sklearn.preprocessing import StandardScalerstandardScaler = StandardScaler()standardScaler.fit(X)X_std = standardScaler.transform(X)
使用sklearn.svm
包中的类LinearSVC
,即使用支撑向量机做分类。
首先取一个非常大的C值进行观察,在这种情况下,算法近似Hard Margin
from sklearn.svm import LinearSVC svc = LinearSVC(C=1e9)svc.fit(X_std,y)
下面我们要观察决策边界,除了决策边界以外还想要观察整个Margin区域。要想画出Margin的两条边界,那么如何求出呢?我们知道决策边界为,相应地Margin边界为、。这就要求我们知道系数和截距。SVC中每个特征对应一个系数,现在是二维数据,可以得到和。下面求出系数和截距:
# 系数svc.coef_# 输出:array([[ 4.03243632, -2.50699663]])# 截距svc.intercept_# 输出:array([0.92733291])
然后绘制决策边界以及Margin的边界:
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) # 绘制Margin区域上下两根线 w = model.coef_[0] b = model.intercept_[0] # w0 * x0 + w1 * x1 + b = +1 # w0 * x0 + w1 * x1 + b = -1 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_std[y==0,0], X_std[y==0,1])plt.scatter(X_std[y==1,0], X_std[y==1,1])plt.show()
通过上面的图片,可以非常清晰地看到,有三个蓝色数据点落在上面的边界,有两个橙色的数据点落在下边界上,这就是支撑向量。因为近似于Hard Margin SVM 因此Margin之间是没有任何数据的。既保证了正确分类,又让里决策边界最近的点到决策边界的距离最远。
然后取一个非常小的C值,来看看Soft Margin
的表现:
svc2 = LinearSVC(C=0.01)svc2.fit(X_std,y)plot_svc_decision_boundary(svc2, axis=[-3, 3, -3, 3])plt.scatter(X_std[y==0,0], X_std[y==0,1])plt.scatter(X_std[y==1,0], X_std[y==1,1])plt.show()
可见有一个蓝色的点被错误地分类了,C越小,Margin越大。其中有很多数据点,给出了很大的容错空间。
至此,我们通过两篇文章详细地介绍了SVM算法的原理,以及如何将其转换为最优化问题。并且对于有时候包含少量的异常点而导致的数据集不能线性可分的情况,推出了Soft Margin这种形式。其实,可以看作是给Hard Margin加上一个正则化项,提高其容错性。
事实上,无论是Hard Margin还是Soft Margin其实都是对于线性支持向量机来说的。那么在实际使用中,除了线性可分的数据集以外,SVM如何对高度非线性的数据集如何进行分类呢?