凌云时刻 · 技术
导读:这一篇笔记主要讲机器学习算法中一个重要的分类算法,支撑向量机(Support Vector Machine)。它背后有严格的数学理论和统计学理论支撑的,这里我们只对它的原理和应用做以介绍,更深层次的数学理论有兴趣可以查阅其他资料。
作者 | 计缘
来源 | 凌云时刻(微信号:linuxpk)
Soft Margin SVM
在上一节介绍了Hard Margin SVM和Soft Margin SVM,并且在诠释SVM背后最优化问题的数学原理时也是基于Hard Margin SVM前提的。这一节我们来看看Soft Margin SVM。
Soft Margin SVM概念
如上图所示,点A是一个蓝色分类的点,但是它离红色分类的点非常近,那么如果按Hard Margin SVM的思路,上图情况的决策边界很有可能是下图所示这样:
这条决策边界直线看似很好的将蓝色和红色点完全区分开了,但是它的泛化能力是值得怀疑的,因为这条决策边界极大的受到了点A的影响,而点A可能是蓝色点中极为特殊的一个点,也有可能它根本就是一个错误的点。所以根据SVM的思想,比较合理的决策边界应该下图绿色的直线所示:
虽然绿色直线的决策边界没有完全将红蓝点分开,但是如果将它放在生产数据中,可能预测准确度更高,也就是泛化能力更强。
再如上图中的情况,已经根本不可能有一条线性决策边界能将红蓝点分开了,所以我们希望决策边界具有一定的包容性或容错性,已降低分类准确度的代价换来更高的泛化能力。那么这种SVM就称为Soft Margin SVM。
Soft Margin SVM原理
在Hard Margin SVM最优化问题的两个函数中,限定条件 表示在Margin区域内不会有任何点出现,但是在Soft Margin SVM中为了容错性,是允许在Margin区域内出现点的,也就是将Hard Margin SVM的限定条件加以宽松量,并且这个宽松量必须是正数:
上图中有四条直线,其中橘黄色的三条是在讲Hard Margin SVM中得出的,如果我们将Hard Margin SVM中的限定条件加以宽松量的话,其实Margin的区域就会变小,也就是图中绿色虚线和决策边界构成的区域。而绿色虚线的方程为:
那么Soft Margin SVM的限定条件我们就知道了。
但是现在问题来了,如果当 无穷大时会发生什么情况呢?那就意味着容错性无穷大,也就是可以将所有点都认为是同一类了,故而分不出类别。解决这个问题的思路我们之前已经了解过了,那就是模型正则化。
我们知道Hard Margin SVM的优化目标函数为 ,Soft Margin SVM也是基于Hard Margin SVM的思想演变的,所以我们将这个目标函数加一个正则模型,而这个正则模型又恰是Soft Margin SVM的宽松量,这样就达到了在Hard Margin SVM的思路下,增加宽松量从而实现Soft Margin SVM,所以 Soft Margin SVM的目标函数为:
公式里的 是模型正则化中的一个超参数,取值范围在0到1之间。用来权衡Hard Margin SVM目标函数和Soft Margin SVM宽松量两者之间的比例。如此一来也就限制了 无穷大的问题,因为Soft Margin SVM的目标函数最优化要同时估计两部分,相互制约。
我们在之前的笔记中学习过了 范数及 正则模型。这里的 就是 正则模型,而 正则模型是 。
C∑i=1mζi。
模型正则化的内容请参见 机器学习笔记(十七)、(十八)交叉验证、模型正则化 。
Scikit Learn中的SVM
前面两小节介绍了SVM背后的数学原理,这一节来看看如何使用Scikit Learn中封装的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 = 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()
|
接下来我们使用Scikit Learn封装的SVM方法对样本数据进行分类:
from sklearn.svm import LinearSVC
# 这里的参数C就是在讲Soft Margin SVM中的那个超参数,这里将其取一个很大的值
svc = LinearSVC(C=1e9)
svc.fit(X_sd, y)
# 仍然使用之前绘制决策边界的方法
def plot_decision_boundary(model, axis):
# meshgrid函数用两个坐标轴上的点在平面上画格,返回坐标矩阵
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),
)
# ravel()方法将高维数组降为一维数组,c_[]将两个数组以列的形式拼接起来,形成矩阵
X_grid_matrix = np.c_[X0.ravel(), X1.ravel()]
# 通过训练好的逻辑回归模型,预测平面上这些点的分类
y_predict = model.predict(X_grid_matrix)
y_predict_matrix = y_predict.reshape(X0.shape)
# 设置色彩表
from matplotlib.colors import ListedColormap
my_colormap = ListedColormap(['#0000CD', '#40E0D0', '#FFFF00'])
# 绘制等高线,并且填充等高区域的颜色
plt.contourf(X0, X1, y_predict_matrix, linewidth=5, cmap=my_colormap)
plot_decision_boundary(svc, axis=[-3, 3, -3, 3])
plt.scatter(X_sd[y == 0, 0], X_sd[y == 0, 1], color='red')
plt.scatter(X_sd[y == 1, 0], X_sd[y == 1, 1], color='blue')
plt.show()
|
在上面的示例代码中,我将超参数 取了一个非常大的数,那么为了平衡 整个函数, 就得等于0才能让最大限度的平衡正则模型和整个目标函数。所以宽松量为零,就成了Hard Margin SVM。并且从上图可以看出,离决策边界最近的点,无论是红色点还是蓝色点,也就是红蓝支撑向量到决策边界的距离都差不多。
如果我们将超参数C的值取的小一些,那么整个问题就变成了Soft Margin SVM,来对比看看决策边界会有什么不同:
svc2 = LinearSVC(C=0.01)
svc2.fit(X_sd, y)
plot_decision_boundary(svc2, axis=[-3, 3, -3, 3])
plt.scatter(X_sd[y == 0, 0], X_sd[y == 0, 1], color='red')
plt.scatter(X_sd[y == 1, 0], X_sd[y == 1, 1], color='blue')
plt.show()
|
此时从上图可以看到,红蓝支撑向量到决策边界的距离已经不一样了,并且还有一个红色的点被划到蓝色点的范围内,那么我们知道这都是因为Soft Margin SVM中加了宽松量的缘故。
绘制支撑向量直线
在上一小节,我们知道决策边界以及上下支撑向量构成的直线的公式分别是:
那么在鸢尾花的示例中,如何来求这三条直线呢?以决策边界直线为例,ww和dd其实我们已经知道了,就是特征系数和截距:
vc.coef_
# 结果
array([[ 4.03243252, -2.49295032]])
svc.intercept_
# 结果
array([ 0.95364645])
|
因为我们只用了鸢尾花的两个类型和两个特征,所以决策边界直线的公式可以展开为:
将上面的公式转换为 的形式:
同理我们也可以将支撑向量直线的公式作以转换:
此时我们在给定的坐标系内,构建一组,那么就可以求出一组x1x1,然后将这些点连起来,就绘制出了决策边界的直线:
# 构造一个绘制决策边界和上下支撑向量直线的方法
def plot_svc_decision_boundary(model, axis):
# 因为SVM可以解决多分类问题,所以特征系数和截距都是数组,在二分类问题下取第0个元素
w = model.coef_[0]
d = model.intercept_[0]
# 构建200个在axis范围内的,有线性关系的点,既构建x0
plot_x = np.linspace(axis[0], axis[1], 200)
# 根据上面转换的公式,求出x1
y = - (w[0] * plot_x)/w[1] - d / w[1]
up_y = - (w[0] * plot_x)/w[1] - d / w[1] + 1 / w[1]
down_y = - (w[0] * plot_x)/w[1] - d / w[1] - 1 / w[1]
# 对y轴的上下边界作以限制
y_index = (y >= axis[2]) & (y <= axis[3])
up_y_index = (up_y >= axis[2]) & (up_y <= axis[3])
down_y_index = (down_y >= axis[2]) & (down_y <= axis[3])
plt.plot(plot_x[y_index], y[y_index], color='black')
plt.plot(plot_x[up_y_index], up_y[up_y_index], color='black')
plt.plot(plot_x[down_y_index], down_y[down_y_index], color='black')
plot_decision_boundary(svc, axis=[-3, 3, -3, 3])
plot_svc_decision_boundary(svc, axis=[-3, 3, -3, 3])
plt.scatter(X_sd[y == 0, 0], X_sd[y == 0, 1], color='red')
plt.scatter(X_sd[y == 1, 0], X_sd[y == 1, 1], color='blue')
plt.show()
|
从上图可以看到,是一个标准的Hard Margin SVM,Margin区域没有任何一个点,支撑向量一共有五个,红色点三个,蓝色点两个。
我们再来看看Soft Margin SVM的情况:
plot_decision_boundary(svc2, axis=[-3, 3, -3, 3])
plot_svc_decision_boundary(svc2, axis=[-3, 3, -3, 3])
plt.scatter(X_sd[y == 0, 0], X_sd[y == 0, 1], color='red')
plt.scatter(X_sd[y == 1, 0], X_sd[y == 1, 1], color='blue')
plt.show()
|
从上图中可以看出,Margin区域内有很多点,说明相比Hard Margin SVM,Soft Margin SVM增加了不少宽松量。
END
往期精彩文章回顾
机器学习笔记(二十五):支撑向量机(SVM)
机器学习笔记(二十四):召回率、混淆矩阵
机器学习笔记(二十三):算法精准率、召回率
机器学习笔记(二十二):逻辑回归中使用模型正则化
机器学习笔记(二十一):决策边界
机器学习笔记(二十):逻辑回归(2)
机器学习笔记(十九):逻辑回归
机器学习笔记(十八):模型正则化
机器学习笔记(十七):交叉验证
机器学习笔记(十六):多项式回归、拟合程度、模型泛化
长按扫描二维码关注凌云时刻
每日收获前沿技术与科技洞见