支撑向量机(Support Vector Machine,简称svm) 是一种解决二分类问题的有监督的机器学习模型。核心思想就是寻找一个最优的决策边界(超平面)来区分两种不同类别的样本。
线性模型
对于一个线性可分的样本集合其决策边界可能并不唯一,我们要如何理解svm中最优的决策边界?什么是最优的决策边界?对于一个二维可分的样本空间最优决策边界是:
1)找到一个决策边界
2)该决策边界距离类别A的所有样本数据最远,距离类别B的所有样本数据最远。
如图我们说黑色决策边界是最优的,青色也是一个决策边界,但不是最好的。在未来处理新的样本数据的时候模型会比较敏感,黑色决策边界的模型泛化能力比较好。
在这里我们就想找到一个决策边界,不仅在训练数据数据集表现的很好,同时他也考虑将来新的数据过来之后,也能有较好的区分,也就是说模型的泛化能力比较好。
其实提高模型的泛化能力有两种正则化(L1正则化、L2正则化)svm是将模型的泛化能力直接放到了其算法内部,就是寻找一个决策边界尽可能的远离来两个分类样本。
我们来看一下如何将上面所说的最优决策边界转化为数学问题,进而求解该决策边界。
我们需要最大化 margin,斜线经过的点叫支撑向量。
假设决策边界(超平面)是
w
T
x
+
b
=
0
w^{T}x + b =0
wTx+b=0
对于决策边界上方的样本点我们把它的标签值记为1,下方的样本点标签值记为-1,所有的样本数据应该满足样本点到决策边界的距离大于等于d,即
{
w
T
x
(
i
)
+
b
∥
w
∥
≥
d
∀
y
(
i
)
=
1
w
T
x
(
i
)
+
b
∥
w
∥
≤
−
d
∀
y
(
i
)
=
−
1
\begin{aligned} & \left\{\begin{array}{cc} \dfrac{w^T x^{(i)}+b}{\|w\|} \geq d & \forall y^{(i)}=1 \\ \dfrac{w^T x^{(i)}+b}{\|w\|} \leq-d &\ \ \ \forall y^{(i)}=-1 \end{array}\right. \\ \end{aligned}
⎩
⎨
⎧∥w∥wTx(i)+b≥d∥w∥wTx(i)+b≤−d∀y(i)=1 ∀y(i)=−1
注意:正常二分类问题都是将样本标签值分为0和1,这里分为1和-1仅是为了后面的公式简化,无任何影响。
进而推导
{
w
T
x
(
i
)
+
b
∥
w
∥
d
≥
1
∀
y
(
i
)
=
1
w
T
x
(
i
)
+
b
∥
w
∥
d
≤
−
1
∀
y
(
i
)
=
−
1
\begin{aligned} & \left\{\begin{array}{cc} \dfrac{w^T x^{(i)}+b}{\|w\| d} \geq 1 & \forall y^{(i)}=1 \\ \dfrac{w^T x^{(i)}+b}{\|w\| d} \leq-1 &\ \ \ \forall y^{(i)}=-1 \end{array}\right. \end{aligned}
⎩
⎨
⎧∥w∥dwTx(i)+b≥1∥w∥dwTx(i)+b≤−1∀y(i)=1 ∀y(i)=−1
由于
∥
w
∥
d
\|w\| d
∥w∥d是一个实数,上式可以化简为,其中
w
d
T
=
w
T
∥
w
∥
d
,
b
d
=
b
∥
w
∥
d
w_{d}^T=\frac{w^T}{\|w\| d},b_{d}=\frac{b}{\|w\| d}
wdT=∥w∥dwT,bd=∥w∥db
{
w
d
T
x
(
i
)
+
b
d
≥
1
∀
y
(
i
)
=
1
w
d
T
x
(
i
)
+
b
d
≤
−
1
∀
y
(
i
)
=
−
1
\begin{aligned} & \left\{\begin{array}{cc} w_{d}^T x^{(i)}+b_{d} \geq 1 & \forall y^{(i)}=1 \\ w_{d}^T x^{(i)}+b_{d} \leq-1 & \ \ \ \forall y^{(i)}=-1 \end{array}\right. \end{aligned}
{wdTx(i)+bd≥1wdTx(i)+bd≤−1∀y(i)=1 ∀y(i)=−1
由于记号比较繁琐,我们将
w
d
T
w_{d}^T
wdT仍记为
w
T
w^T
wT,
b
d
b_{d}
bd记为
b
b
b,上式可以简化
{
w
T
x
(
i
)
+
b
≥
1
∀
y
(
i
)
=
1
w
T
x
(
i
)
+
b
≤
−
1
∀
y
(
i
)
=
−
1
\begin{aligned} & \left\{\begin{array}{cc} w^T x^{(i)}+b \geq 1 & \forall y^{(i)}=1 \\ w^T x^{(i)}+b \leq-1 & \ \ \ \forall y^{(i)}=-1 \end{array}\right. \end{aligned}
{wTx(i)+b≥1wTx(i)+b≤−1∀y(i)=1 ∀y(i)=−1
我们可以将这个分段函数写成一个表达式
y
(
i
)
(
w
T
x
(
i
)
+
b
)
≥
1
\begin{aligned} y^{(i)}(w^{T}x^{(i)}+b) \geq 1 \end{aligned}
y(i)(wTx(i)+b)≥1
因为margin=2d, 最大化margin就是最大化d,d就是任意数据点到决策边界的距离。
由于支撑向量是到决策边界最近的样本点,其他样本点到决策边界的距离都是大于1的,所以对于支撑向量来说满足
m
a
x
∣
w
T
x
+
b
∣
∥
w
∥
⟹
m
a
x
1
∥
w
∥
⟹
m
i
n
∥
w
∥
⟹
m
i
n
1
2
∥
w
∥
2
\begin{aligned} & max \quad \dfrac{|w^{T}x+b|}{\|w\|} \\ & \Longrightarrow max \quad \frac{1}{\|w\|} \\ & \Longrightarrow min \quad \|w\| \\ & \Longrightarrow min \quad \frac{1}{2}\|w\|^{2} \end{aligned}
max∥w∥∣wTx+b∣⟹max∥w∥1⟹min∥w∥⟹min21∥w∥2
最终svm就转化为一个有约束条件的优化问题
m
i
n
1
2
∥
w
∥
2
s
.
t
.
y
(
i
)
(
w
T
x
(
i
)
+
b
)
≥
1
\begin{aligned} & min \quad \frac{1}{2}\|w\|^{2} \\ & s.t. \quad y^{(i)}(w^{T}x^{(i)}+b) \geq 1 \end{aligned}
min21∥w∥2s.t.y(i)(wTx(i)+b)≥1
我们可以知道在线性可分空间中决策边界到两个类别的支撑向量之间是没有任何样本数据的,这样求解出来的决策边界我们称为hard margin svm。但是这样的决策边界可能会导致模型的泛化能力较差,甚至在一些特殊的场景中是无解的。
比如用hard margin svm求解的决策边界是红色,但是我们会觉得黑色决策边界将来在处理新数据的时候效果可能会更好。由于一个异常的数据导致模型的泛化能力变差了。
此时如果我们允许模型在训练的时候犯一点点错误的话,那么决策边界就可能是我们想要的那个黑色的了。此时就有了 soft margin svm。
非线性模型
对于一个线性可分的样本空间,我们可以利用hard margin svm找到一个最优的决策边界,在实际中可能会有一些异常的数据导致模型的泛化能力过于敏感,此时我们就要让模型可以犯一点错误,比如下图忽略异常的灰色类别。将红色直线向左下移动一点,让距离d适当的变小即黑色虚线方程。
soft margin svm
此时我们的优化目标和约束条件如下
m
i
n
(
1
2
∥
w
∥
2
+
C
∑
i
=
1
m
ζ
i
)
s
.
t
.
y
(
i
)
(
w
T
x
(
i
)
+
b
)
≥
1
−
ζ
i
ζ
i
≥
0
\begin{aligned} & min \quad \left( \frac{1}{2}\|w\|^{2} + C \sum^{m}_{i=1}\zeta_{i}\right) \\ & s.t. \quad y^{(i)}(w^{T}x^{(i)}+b) \geq 1-\zeta_{i} \\ & \qquad \quad \zeta_{i} \geq 0 \end{aligned}
min(21∥w∥2+Ci=1∑mζi)s.t.y(i)(wTx(i)+b)≥1−ζiζi≥0
C是超参数,C越大容错空间越小, 当C 趋向无穷大,表明
ζ
\zeta
ζ值越小求和趋向0此时就是hard margin svm。这里的
ζ
\zeta
ζ称为松弛变量。
sklearn中代码验证
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.preprocessing import StandardScaler
from sklearn.svm import LinearSVC
## 代码实现
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]
# w0*x0 + w1*x1 + b = 0
# => x1 = -w0/w1 * x0 - b/w1
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')
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()
# 数据归一化
standardScaler = StandardScaler()
standardScaler.fit(X)
X_standard = standardScaler.transform(X)
# C的值比较大,可以理解成是hard margin svm
svc = LinearSVC(C=1e9)
svc.fit(X_standard, y)
# 画图
plot_svc_decision_boundary(svc, axis=[-3, 3, -3, 3])
plt.scatter(X_standard[y == 0, 0], X_standard[y == 0, 1])
plt.scatter(X_standard[y == 1, 0], X_standard[y == 1, 1])
plt.show()
原始样本分布
当C=1e9时
当C=0.51时
参考
https://zhuanlan.zhihu.com/p/666178000
https://blog.csdn.net/czhendehaonan/article/details/130031744