一、SVM原理与模型数学推导
支持向量机,SVM(Support Vector Machine),其实就是一个线性分类器。在最初接到这个算法时,我们可能会一头雾水:这个名词好奇怪[问号脸],怎么“支持”?什么“向量”,哪来的“机”?
(一)由决策边界开始
1 分类中的“不适定问题”
首先,我们看一个简单的二分类问题。在二维的特征平面中,所有的数据点分为了两类:蓝色圆形和黄色三角。我们的目标是找到了一条决策边界,将数据分类。但实际上我们可以找到多条决策边界。
这就所谓的“不适定问题”。“不适定问题”会影响模型的泛化性。比如在下面的模型中,被黄色箭头标出的点被决策边界划为蓝色圆点,但实际上它和黄色三角更近一些。也就说决策边界的选择,不仅要考虑已经存在的数据上的是否分类正确,还要考虑是否能够更好地划分未出现的测试数据:
逻辑回归算法如何解决“不适定问题”问题呢?首先定义一个概率函数sigmoid函数:
σ
(
t
)
=
1
1
+
e
−
t
σ(t)=\frac {1}{1+e^{-t}}
σ(t)=1+e−t1
然后根据概率函数进行建模:
P
(
Y
=
1
)
=
1
1
+
e
−
θ
T
⋅
X
b
P(Y=1)=\frac{1}{1+e^{-θ^T\cdot X_b}}
P(Y=1)=1+e−θT⋅Xb1
建立损失函数:
J
(
θ
)
=
−
1
m
∑
i
=
1
m
y
(
i
)
l
o
g
(
σ
(
θ
T
⋅
X
b
(
i
)
)
)
+
(
1
−
y
(
i
)
)
l
o
g
(
1
−
σ
(
θ
T
⋅
X
b
(
i
)
)
)
J(θ)=-\frac{1}{m}\sum_{i=1}^my^{(i)}log(σ(θ^T\cdot X_b^{(i)}))+(1-y^{(i)})log(1-σ(θ^T\cdot X_b^{(i)}))
J(θ)=−m1i=1∑my(i)log(σ(θT⋅Xb(i)))+(1−y(i))log(1−σ(θT⋅Xb(i)))
最小化损失函数,从而求出一条决策边界(线性):
x
2
=
−
θ
0
−
θ
1
x
1
θ
2
x_2=\frac{-θ_0-θ_1x_1}{θ_2}
x2=θ2−θ0−θ1x1
(以上内容逻辑回归算法)
也就说,逻辑回归的损失函数完全是由训练数据集确定的。
2 SVM中的决策边界
支撑向量机如何解决“不适定问题呢”?SVM要找到一条泛化性比较好的决策边界,就是这条直线要离两个分类都尽可能的远,我们认为这样的决策边界就是好的:
如上图所示:在上面的两个类别中,离决策边界最近的点到决策边界的距离尽可能地远。
那也就是说,我们可以忽略其他大部分的数据点,只关注这几个特殊的点即可。
(二)Support Vector & Margin
1 定义及思想
将最优决策边界向上&下平移,在遇到第一个点时停下来,这个点被称为支撑向量Support Vector;支撑向量到决策边界的距离是d;这两条平移后的直线的间隔(2d)被称为最大间隔Margin。
支撑向量就是支撑着两条平移边界的点,我们只需要重点研究这几个支撑向量即可,这也是SVM名称的由来;Margin就是分界面可以移动的范围,范围越大表示容错能力越强。
所以我们可以看到,所谓的支撑向量机,最初就是一个线性分类器,只不过这个线性分类器不仅能把样本分对,可以最大化Margin。
到目前为止,我们就将SVM转换为了一个最优化问题,下面的工作就是求出Margin的数学表达式,即将支撑向量机思想转化为数学问题。
2 转化为最优化问题(核心)
回忆解析几何的知识,点(x ,y)到直线
A
x
+
B
y
+
C
=
0
Ax+By+C=0
Ax+By+C=0的距离:
d
=
∣
A
x
+
B
y
+
C
∣
A
2
+
B
2
d=\frac {|Ax+By+C|}{\sqrt{A^2+B^2}}
d=A2+B2∣Ax+By+C∣
扩展到n维空间:x点
ω
T
x
+
b
=
0
ω^Tx+b=0
ωTx+b=0到(其中ω是n维向量,b是截距)的距离为:
d
=
ω
T
x
+
b
∣
∣
ω
∣
∣
,
∣
∣
ω
∣
∣
=
ω
1
2
+
ω
2
2
+
.
.
.
+
ω
n
2
+
d=\frac{ω^Tx+b}{||ω||},||ω||=\sqrt{ω_1^2+ω_2^2+...+ω_n^2+}
d=∣∣ω∣∣ωTx+b,∣∣ω∣∣=ω12+ω22+...+ωn2+
然后我们去找决策边界的表达式:
求出在满足下面的条件下,ω的值是多少。
{
ω
T
x
(
i
)
+
b
≥
1
∀
y
(
i
)
=
1
ω
T
x
(
i
)
+
b
≤
−
1
∀
y
(
i
)
=
−
1
\left\{\begin{matrix} ω^Tx^{(i)}+b\geq 1 & \forall y^{(i)}=1\\ ω^Tx^{(i)}+b\leq -1 & \forall y^{(i)}=-1\end{matrix}\right.
{ωTx(i)+b≥1ωTx(i)+b≤−1∀y(i)=1∀y(i)=−1
我们将上面的两个式子进行合并,即可以将
y
(
i
)
y^{(i)}
y(i)提到前面去。这样,支撑向量机的所有数据点都要满足下面的式子:
y
(
i
)
(
ω
T
x
(
i
)
+
b
)
≥
1
y^{(i)}(ω^Tx^{(i)}+b)\geq 1
y(i)(ωTx(i)+b)≥1
对于任意支撑向量点x到决策边界的距离为d,我们要最大化Margin,将前面的式子带入后得到
m
a
x
2
∣
ω
T
x
(
i
)
+
b
∣
∣
∣
ω
∣
∣
max\frac{2|ω^Tx^{(i)}+b|}{||ω||}
max∣∣ω∣∣2∣ωTx(i)+b∣,也就是
m
a
x
2
∣
∣
ω
∣
∣
max\frac{2}{||ω||}
max∣∣ω∣∣2。即相当于最小化
m
i
n
1
2
ω
T
ω
min\frac{1}{2}ω^Tω
min21ωTω。
OK,现在我们已经得到了SVM的最优化问题:
Φ
(
x
)
=
1
2
ω
T
ω
Φ(x)=\frac{1}{2}ω^Tω
Φ(x)=21ωTω s.t.
y
(
i
)
(
ω
T
x
(
i
)
+
b
)
≥
1
y^{(i)}(ω^Tx^{(i)}+b)\geq 1
y(i)(ωTx(i)+b)≥1
即最优化的目标函数为
m
i
n
1
2
ω
T
ω
min\frac{1}{2}ω^Tω
min21ωTω,还要有限定条件(
s
.
t
.
s
u
b
j
e
c
t
t
t
o
,
s.t. subjecttto,
s.t.subjecttto,受
限制于):所有数据满足
y
(
i
)
(
ω
T
x
(
i
)
+
b
)
≥
1
y^{(i)}(ω^Tx^{(i)}+b)\geq 1
y(i)(ωTx(i)+b)≥1
我们发现,SVM的最优化问题是有限制条件的,与之前接触的没有限制条件的全局最优化问题的计算方法很不同。
(三)求解有条件限制的最优化问题
1 数学推导
通过六步数学推导,求解有条件限制的最优化问题。如果觉得吃力,大家可以仅仅了解推导过程,记住结果即可。
第一步:给出表达式
对于有约束条件的最优化问题,用拉格朗日乘数法来解决,得到(
a
i
a_i
ai是拉格朗日系数):
L
p
=
1
2
∣
∣
ω
∣
∣
2
−
∑
i
=
1
l
a
i
y
i
(
ω
T
⋅
x
i
+
b
)
+
∑
i
=
1
l
a
i
L_p=\frac{1}{2}||ω||^2-\sum_{i=1}^la_iy_i(ω^T\cdot x_{i}+b)+\sum_{i=1}^la_i
Lp=21∣∣ω∣∣2−i=1∑laiyi(ωT⋅xi+b)+i=1∑lai
此时,我们要求
L
p
L_p
Lp基于ω,b的极小值。
第二步:求导
我们对ω,b进行求导,可以得到两个式子:
∂
L
p
∂
ω
=
0
⇒
ω
=
∑
i
=
1
l
a
i
y
i
x
i
\frac{\partial L_p}{\partial ω}=0\Rightarrow ω=\sum_{i=1}^l{a_iy_ix_i}
∂ω∂Lp=0⇒ω=i=1∑laiyixi
∂
L
p
∂
b
=
0
⇒
∑
i
=
1
l
a
i
y
i
=
0
\frac{\partial L_p}{\partial b}=0\Rightarrow \sum_{i=1}^la_iy_i=0
∂b∂Lp=0⇒i=1∑laiyi=0
从上两式子可以看出,我们已经求得了ω和a的关系,只要后面接着能够求出优化函数极大化对应的ω,就可以求出ω了,至于b,由于上两式已经没有b,所以最后的b可以有多个。
第三步:转换对偶问题
将ω,b的求导结果带回到原式
L
p
L_p
Lp中,得到新的目标函数
L
D
L_D
LD:
L
D
≡
∑
i
−
1
2
∑
i
,
j
a
i
a
j
y
i
y
j
x
i
⋅
x
j
L_D\equiv \sum_i-\frac{1}{2}\sum_{i,j}a_ia_jy_iy_jx_i\cdot x_j
LD≡i∑−21i,j∑aiajyiyjxi⋅xj
≡
∑
i
−
1
2
a
T
H
a
\equiv \sum_i-\frac{1}{2}a^TH_a
≡i∑−21aTHa
w
h
e
r
e
H
i
,
j
=
y
i
y
j
x
i
⋅
x
j
where H_{i,j}=y_iy_jx_i\cdot x_j
whereHi,j=yiyjxi⋅xj
s
.
t
∑
i
a
i
y
i
=
0
s.t \sum_ia_iy_i=0
s.t∑iaiyi=0 &
a
i
≥
0
a_i\geq 0
ai≥0
其实
L
D
L_D
LD和
L
P
L_P
LP是对偶问题。我们可以通过拉格朗日对偶将优化问题转化为等价的对偶问题来求解,原问题和对偶问题在一般情况下是不等价的,但是在SVM中是等价的。
第四步:求a
把 L D L_D LD方程解出来,会得到很多a,大部分都为0,少部分非0,就是支撑向量。
第五步:求ω
我们将所有非零的支撑向量相乘并累加起来,最终得到 ω = ∑ i = 1 l a i y i x i ⋅ x ω=\sum_{i=1}^la_iy_ix_i\cdot x ω=∑i=1laiyixi⋅x。
这里要注意,两个向量做内积,是SVM运算的精华所在。
第六步:求b
已知
y
s
(
x
s
⋅
ω
+
b
)
=
1
y_s(x_s\cdot ω +b)=1
ys(xs⋅ω+b)=1,将ω 的结果带入,并且在两侧同时乘以
y
s
y_s
ys后得到:
y
s
2
(
∑
m
∈
S
a
m
y
m
x
m
⋅
x
s
+
b
)
y_s^2(\sum_{m \in S}a_my_mx_m\cdot x_s + b)
ys2(∑m∈Samymxm⋅xs+b),则得到b:
b
=
1
N
∑
m
∈
S
(
y
s
−
∑
m
∈
S
a
m
y
m
x
m
⋅
x
s
)
b=\frac{1}{N}\sum_{m \in S}(y_s-\sum_{m \in S}a_my_mx_m\cdot x_s)
b=N1m∈S∑(ys−m∈S∑amymxm⋅xs)
2 SVM如何求解示例
通过一个简单的例子,展示SVM如何求解。有两个样本:
通过约束条件:
∑
i
=
1
l
a
i
y
i
=
0
\sum_{i=1}^la_iy_i=0
∑i=1laiyi=0,可以得到:
∑
i
=
1
2
a
i
y
i
=
0
⇒
a
1
−
a
2
=
0
⇒
a
1
=
a
2
\sum_{i=1}^2a_iy_i=0 \Rightarrow a_1-a_2=0 \Rightarrow a_1=a_2
i=1∑2aiyi=0⇒a1−a2=0⇒a1=a2
我们要优化的表达式为: L D = ∑ i a i − 1 2 a T H a L_D=\sum_ia_i-\frac{1}{2}a^TH_a LD=∑iai−21aTHa。需要先求出H.
将a带入到矩阵H中,已知:
H
i
,
j
=
y
i
y
j
x
i
⋅
x
j
H_{i,j}=y_iy_jx_i\cdot x_j
Hi,j=yiyjxi⋅xj;则有:
H
=
[
H
11
H
12
H
21
H
22
]
=
[
2
0
0
0
]
H=\begin{bmatrix} H_{11} & H_{12}\\ H_{21} & H_{22} \end{bmatrix}=\begin{bmatrix} 2 & 0\\ 0 & 0 \end{bmatrix}
H=[H11H21H12H22]=[2000]
带入到表达式中,得到:
L
D
≡
∑
i
=
1
2
a
i
−
1
2
[
a
1
,
a
2
]
H
[
a
1
a
2
]
=
2
a
i
−
a
1
2
L_D\equiv \sum_{i=1}^2a_i-\frac{1}{2}[a_1,a_2]H\begin{bmatrix} a_1\\ a_2 \end{bmatrix}=2a_i-a_1^2
LD≡i=1∑2ai−21[a1,a2]H[a1a2]=2ai−a12
则表达式 L D L_D LD的最大值为: a 1 = 1 , a 2 = 1 a_1=1,a_2=1 a1=1,a2=1。
带回到ω中则可以得到: ω = ∑ i = 1 2 a i y i x i = [ 1 , 1 ] ω=\sum_{i=1}^2a_iy_ix_i=[1,1] ω=∑i=12aiyixi=[1,1];计算 b : b = − ω x 1 + 1 = − 2 + 1 = − 1 b:b=-ωx_1+1=-2+1=-1 b:b=−ωx1+1=−2+1=−1。
最终得到判决平面为: g ( x ) = ω x + b = x 1 + x 2 − 1 g(x)=ωx+b=x_1+x_2-1 g(x)=ωx+b=x1+x2−1;Margin为 M = 2 ∣ ∣ ω ∣ ∣ = 2 M=\frac{2}{||ω||}=\sqrt 2 M=∣∣ω∣∣2=2
(四)小结
SVM算法其最核心的思想就是从Input Space向更高维的Feature Space的映射,进行有Margin的线性分类。
二、软间隔与sklearn中的SVM
(一)Soft Margin
1. 为何需要Soft Margin
在线性可分问题中,对于样本点来说,存在一根直线可以将样本点划分,我们称之为Hard Margin SVM;但是(同样线性不可分),有时候会出现不那么完美,样本点会有一些噪声或者异常点,并不能完全分开。即没有一条直线可以将样本分成两类。那么就提出了Soft Margin SVM。
2. Soft Margin的思想
Soft Margin SVM的思想也很朴素,就是在Hard Soft的基础上,将原来的约束条件放宽一些。增加容错性。
在Hard Soft中的约束条件为:
Φ
(
x
)
=
1
2
ω
T
ω
s
.
t
.
y
(
i
)
(
ω
T
x
(
i
)
+
b
)
≥
1
Φ(x)=\frac{1}{2}ω^Tω \; \; s.t. \; \; y^{(i)}(ω^Tx^{(i)}+b)\geq 1
Φ(x)=21ωTωs.t.y(i)(ωTx(i)+b)≥1
对于限制条件
y
(
i
)
(
ω
T
x
(
i
)
+
b
)
≥
1
y^{(i)}(ω^Tx^{(i)}+b)\geq 1
y(i)(ωTx(i)+b)≥1,形象地说,Margin区域里必须是任何数据点都没有,所有的数据点都必须在
y
(
i
)
(
ω
T
x
(
i
)
+
b
)
=
1
y^{(i)}(ω^Tx^{(i)}+b)=1
y(i)(ωTx(i)+b)=1与
y
(
i
)
(
ω
T
x
(
i
)
+
b
)
=
−
1
y^{(i)}(ω^Tx^{(i)}+b)=- 1
y(i)(ωTx(i)+b)=−1两条直线的外侧。
如果有些数据点不能满足这个要求,就对条件加以宽松,在margin区域外给他一个宽松量(大于等于0):
Φ
(
x
)
=
1
2
ω
T
ω
s
.
t
.
y
(
i
)
(
ω
T
x
(
i
)
+
b
)
≥
1
−
ζ
i
Φ(x)=\frac{1}{2}ω^Tω \; \; s.t. \; \; y^{(i)}(ω^Tx^{(i)}+b)\geq 1-ζ_i
Φ(x)=21ωTωs.t.y(i)(ωTx(i)+b)≥1−ζi
但是我们很容易地想到:容错空间 ζ i \zeta_i ζi也不能无限制的放大。在最小化的同时加上所有点的容错空间的和,就可以在最小化的同时又可以容忍一点程度的错误。并且通过参数 C C C来平衡重要程度。
因此Soft Margin SVM最优化问题对应的数学表达式为:
Φ ( x ) = 1 2 ω T ω + C ∑ i = 1 m ζ i s . t . y ( i ) ( ω T x ( i ) + b ) ≥ 1 − ζ i ζ i ≥ 0 Φ(x)=\frac{1}{2}ω^Tω+C\sum_{i=1}^mζ_i \; \; s.t. \; \; y^{(i)}(ω^Tx^{(i)}+b)\geq 1-ζ_i \;ζ_i\geq 0 Φ(x)=21ωTω+Ci=1∑mζis.t.y(i)(ωTx(i)+b)≥1−ζiζi≥0
3. 正则化思想
我们观察上面的表达式的形式,其实相当于在Soft Margin SVM中加入了L1正则。可以将理解为正则化项,避免因为训练出的模型往极端的方式发展,让模型的拥有一定的容错能力,泛化性有所提升。
相应的,也有L2正则,即在的求和项上有所不同:
Φ
(
x
)
=
1
2
ω
T
ω
+
C
∑
i
=
1
m
ζ
i
2
s
.
t
.
y
(
i
)
(
ω
T
x
(
i
)
+
b
)
≥
1
−
ζ
i
ζ
i
≥
0
Φ(x)=\frac{1}{2}ω^Tω+C\sum_{i=1}^mζ_i^2 \; \; s.t. \; \; y^{(i)}(ω^Tx^{(i)}+b)\geq 1-ζ_i \;ζ_i\geq 0
Φ(x)=21ωTω+Ci=1∑mζi2s.t.y(i)(ωTx(i)+b)≥1−ζiζi≥0
系数 C C C越大,相应的容错空间越小。如果取正无穷,意味着逼迫着容错空间趋近于零,也就变成了Hard Margin SVM。
(二)Soft Margin的目标函数
那么Soft Margin的目标函数长什么样呢?还是用拉格朗日的方法:
L
p
=
1
2
∣
∣
ω
∣
∣
2
+
C
∑
i
=
1
l
ζ
i
−
∑
i
=
1
l
a
i
[
y
i
(
ω
⋅
x
i
+
b
)
−
1
+
ζ
i
]
−
∑
i
=
1
l
μ
i
ζ
i
L_p=\frac{1}{2}||ω||^2+C\sum_{i=1}^lζ_i-\sum_{i=1}^la_i[y_i(ω\cdot x_{i}+b)-1+ζ_i]-\sum_{i=1}^lμ_iζ_i
Lp=21∣∣ω∣∣2+Ci=1∑lζi−i=1∑lai[yi(ω⋅xi+b)−1+ζi]−i=1∑lμiζi
由于优化函数增加了惩罚项,且增加了一些约束条件,因此目标函数变得很复杂了。其实求解方法和之前是一样的,同样是分别对变量求偏导等于0:
∂
L
p
∂
ω
=
0
⇒
ω
=
∑
i
=
1
l
a
i
y
i
x
i
\frac{\partial L_p}{\partial ω}=0 \Rightarrow ω=\sum_{i=1}^la_iy_ix_i
∂ω∂Lp=0⇒ω=i=1∑laiyixi
∂
L
p
∂
b
=
0
⇒
∑
i
=
1
l
a
i
y
i
=
0
\frac{\partial L_p}{\partial b}=0 \Rightarrow \sum_{i=1}^la_iy_i=0
∂b∂Lp=0⇒i=1∑laiyi=0
∂
L
p
∂
ζ
i
=
0
⇒
C
=
a
i
+
μ
i
\frac{\partial L_p}{\partial ζ_i}=0 \Rightarrow C=a_i+ μ_i
∂ζi∂Lp=0⇒C=ai+μi
带回到原表达式中得到:
L
D
=
∑
i
a
i
−
1
2
a
T
H
a
s
.
t
.
0
≤
a
i
≤
C
a
n
d
∑
i
a
i
y
i
=
0
L_D=\sum_ia_i-\frac{1}{2}a^THa \: \: s.t.\: \: 0\leq a_i\leq C \: and \sum_i a_iy_i=0
LD=i∑ai−21aTHas.t.0≤ai≤Candi∑aiyi=0
SVM的美妙之处,就是加了Soft Margin这么一个复杂的东西,但是通过求导之后,发现解决问题的方法殊途同归,并没有很复杂。
(三)使用sklearn中的SVM
1. SVM需要标准化处理
在使用SVM算法时,需要先对数据进行标准化处理。
SVM算法寻找的是使得Margin区间最大的中间的决策边界(超平面),而衡量Margin使用的是数据点之间的距离。涉及距离的,就应当对量纲问题进行关注,即进行标准化处理。
对于SVM算法来说,如果特征在不同维度上,数据尺度不同的话,非常影响SVM算法的决策边界。
2 数据准备及处理
import matplotlib.pyplot as plt
import numpy as np
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()
然后进行数据标准化处理:
from sklearn.preprocessing import StandardScaler
standardScaler = StandardScaler()
standardScaler.fit(X)
X_std = standardScaler.transform(X)
3 SVM使用及C的选择
使用sklearn.svm包中的类LinearSVC,即使用支撑向量机做分类。
首先取一个非常大的C值进行观察,在这种情况下,算法近似Hard Margin
from sklearn.svm import LinearSVC
svc = LinearSVC(C=1e9)
svc.fit(X_std,y)
LinearSVC(C=1000000000.0, class_weight=None, dual=True,
fit_intercept=True,
intercept_scaling=1, loss=‘squared_hinge’, max_iter=1000,
multi_class=‘ovr’, penalty=‘l2’, random_state=None, tol=0.0001,
verbose=0)
下面我们要观察决策边界,除了决策边界以外还想要观察整个Margin区域。要想画出Margin的两条边界,那么如何求出呢?我们知道决策边界为 w x + b = 0 wx+b=0 wx+b=0,相应地Margin边界为 w x + b = + 1 wx+b=+1 wx+b=+1、 w x + b = − 1 wx+b=-1 wx+b=−1。这就要求我们知道系数 w w w和截距 b b b。SVC中每个特征对应一个系数,现在是二维数据,可以得到 w 0 w_0 w0和 w 1 w_1 w1。下面求出系数和截距:
# 系数
svc.coef_
#array([[ 4.03244351, -2.50699244]])
# 截距
svc.intercept_
#array([0.92735089])
然后绘制决策边界以及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越大。其中有很多数据点,给出了很大的容错空间。
三、巧妙的Kernel Trick
SVM,本质上就是个线性分类器,保证Margin最大。实际上都是线性SVM,包括Soft Margin解决的也不是线性不可分问题,而是解决在线性可分问题下存在噪点、异常点等问题。那么如何解决一个线性不可分问题呢?
(一)使用多项式处理非线性数据
处理非线性数据最典型的思路就是使用多项式的方式:扩充原本数据,制造新的多项式特征。
准备数据:
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
X, y = datasets.make_moons(noise=0.20,random_state=123)
plt.scatter(X[y==0,0], X[y==0,1])
plt.scatter(X[y==1,0], X[y==1,1])
plt.show()
下面使用多项式特征,用SVM算法进行分类:
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)
然后我们使用之前用到的plot_decision_boundary方法绘制决策边界:
通过多项式的方式,让决策边界不再是一条直线了,而是一条曲线。
对于SVM算法来说,可以不使用多项式PolynomialFeatures的方式。而是使用一种巧妙的方法。
(二)SVM解决非线性问题
SVM算法如何解决线性不可分问题?下面通过两个例子进行展示。
1. 例子1
上面的图片有两种示例情况。对于最上面的情形来说,是线性可分的一维数据,在红蓝之间找到一个阈值,即可将二者分开(外面套着圆圈的点是支持向量)。
第二种情况就有所不同了,红-蓝-红,不管如何选择阈值,都不能把它分开。
SVM使用了一种非常巧妙的思路:一维空间不能解决,投影到二维空间。空间中的分布变成了如下的样子:
横坐标是x,纵坐标是
x
2
x^2
x2,相当于引入了一个新的维度。此时,就可以用一条线将其分开。
在原始空间中无法使用线性分类器,就将其映射到另一空间(Feature Space)当中,使其容易分。
2. 例子2
在下面的样本数据集中,红色点在中心,蓝色点在四周,所以一根线是肯定分不开的。
如果我们一定要在当前空间将其分开的话,可以找到一个圆圈作为决策边界,将其分离:
那么如何通过坐标变化,将其映射到新的空间中,得到线性可分的形式呢?将横坐标变为
x
1
2
x_1^2
x12纵坐标变为
x
2
2
x_2^2
x22,这样数据在空间分布中如下图所示:
我们可以想象,有一个内功深厚的武林高手,面对桌子上的一堆混在一起的绿豆和红豆(数据点),一掌拍在桌子上,用内力将其逼到空中(从低维空间映射到高维空间),再它们在空中分散的时候(在高维空间上线性可分),然后再一刀将其分开(决策平面)。如下图:
(三)认识核函数
现在我们已经知道,可以将数据从原始空间映射到Feature Space中去,就可以解决原本的线性不可分问题。那么如何做映射呢?
其实我们并不用特意地去设计映射(实际上我们也很难在高维空间去设计映射),而是使用固定的几种映射方法即可。这些映射方法就是核函数。
核函数本身不是SVM特有的一种技巧,事实上,只要我们在求解最优化问题中,存在 x i ⋅ x j x_i\cdot x_j xi⋅xj,都可以将其转换为 K ( x i ⋅ x j ) K(x_i\cdot x_j) K(xi⋅xj),下面让我们具体学习。
1. 多项式核函数
下面我们介绍一种映射方法:
Φ
(
x
)
Φ(x)
Φ(x):
Φ
(
x
)
Φ(x)
Φ(x)这种映射方法实际上是将维度升上去,映射到一个非常高的维度(大约有
m
2
2
\frac{m^2}{2}
2m2维)。
我们知道,高维数据的运算是十分复杂的,那将数据升维的好处是是什么呢?又该如何计算呢?其实这其中就有SVM算法的精妙之处。
在《图文详解SVM原理与模型数学推导》中3.1小节,求解有条件限制的最优化问题的过程中最后求解 w = ∑ i = 1 l a i y i x i ⋅ x w=\sum_{i=1}^la_iy_ix_i\cdot x w=∑i=1laiyixi⋅x时介绍过:两个向量做内积是SVM运算的精髓。
在原始空间中,两个向量做内积映射到高维空间中则变成
x
i
⋅
x
j
=
Φ
(
x
i
)
⋅
Φ
(
x
j
)
x_i\cdot x_j=Φ(x_i)\cdot Φ(x_j)
xi⋅xj=Φ(xi)⋅Φ(xj)。展开后相乘得到:
合并之后得到很复杂的表达式:
Φ
(
a
)
⋅
Φ
(
b
)
=
1
+
2
∑
i
=
1
m
a
i
b
i
+
∑
i
=
1
m
a
i
2
b
i
2
+
∑
i
=
1
m
−
1
∑
j
=
i
+
1
m
2
a
i
a
j
b
i
b
j
Φ(a)\cdot Φ(b)=1+2\sum_{i=1}^ma_ib_i+\sum_{i=1}^ma_i^2b_i^2+\sum_{i=1}^{m-1}\sum_{j=i+1}^{m}2a_ia_jb_ib_j
Φ(a)⋅Φ(b)=1+2i=1∑maibi+i=1∑mai2bi2+i=1∑m−1j=i+1∑m2aiajbibj
但是,数学家们神奇地发现(具体怎么发现的,我们不用知道…),存在这样一个表达式:
(
a
⋅
b
+
1
)
2
(a\cdot b+1)^2
(a⋅b+1)2,它的展开式居然也是上面的表达式,这就是SVM中最重要的一个特性:
K
(
a
,
b
)
=
(
a
⋅
b
+
1
)
2
=
Φ
(
a
)
⋅
Φ
(
b
)
K(a,b)=(a\cdot b+1)^2=Φ(a)\cdot Φ(b)
K(a,b)=(a⋅b+1)2=Φ(a)⋅Φ(b)
这就是SVM算法的精妙之处:数学家设计出一种巧妙的数学变换,使高维空间的操作等价于低维空间的操作,解决了高维空间中的计算量问题,大大减少了运算量。
数据从低维映射到高维以后,问题的可分析性会增加,会变得更容易;但是高维空间的运算量太大,引入了Kernel Trick。
上面就是所谓的多项式核函数。可以拓展为一般形式:
K
(
x
,
y
)
=
(
x
⋅
y
+
c
)
d
K(x,y)=(x\cdot y +c)^d
K(x,y)=(x⋅y+c)d
其中d为多项式的阶数,在sklearn中,degree的默认参数为3.
2 高斯核函数
高斯核函数是SVM算法中使用最多的核函数,也被称作是RBF核(Radial Basis Function Kernel):
K
(
x
,
y
)
=
e
−
γ
∣
∣
x
−
y
∣
∣
2
K(x,y)=e^{-γ||x-y||^2}
K(x,y)=e−γ∣∣x−y∣∣2
其中与高斯函数的
γ
γ
γ成反比,即
γ
γ
γ越大,高斯分布越窄;
γ
γ
γ越小,高斯分布越宽。
∣
∣
x
−
y
∣
∣
2
||x-y||^2
∣∣x−y∣∣2表示向量的范数,可以理解为向量的模。
高斯核函数的数据映射是非常复杂的,其本质是将一个样本点映射到一个无穷维的特征空间。我们只需要关注映射后的结果即可。
下面看看高斯核函数本质上是在做什么事?现在先对核函数进行一个改变,将原本的
∣
∣
x
−
y
∣
∣
2
||x-y||^2
∣∣x−y∣∣2中的y值替换成两个固定点
l
1
,
l
2
l_1,l_2
l1,l2。我们称这两个特殊的点为地标landmark。
高斯核函数升维的过程就是对于原本的每个x值,如果有两个landmark的话,就将其维为二维样本点,每个样本的取值是高斯核函数的值:
x
⇒
(
e
−
γ
∣
∣
x
−
l
1
∣
∣
2
,
e
−
γ
∣
∣
x
−
l
2
∣
∣
2
)
x\Rightarrow (e^{-γ||x-l_1||^2},e^{-γ||x-l_2||^2})
x⇒(e−γ∣∣x−l1∣∣2,e−γ∣∣x−l2∣∣2)
通过这样的映射,就可以将线性不可分数据变得线性可分。
我们再返过来看高斯核函数:
K
(
x
,
y
)
=
e
−
γ
∣
∣
x
−
y
∣
∣
2
K(x,y)=e^{-γ||x-y||^2}
K(x,y)=e−γ∣∣x−y∣∣2
我们认为
y
y
y就是地标点landmark的话,那么实际上在高斯核函数中,对于每一个数据点都是landmark,有多少样本,就有多少地标点。将mn的数据映射成mm的数据。
(四) SVM最终优化函数
SVM本质就是求解下面最优化问题:
1
2
∣
∣
ω
∣
∣
2
+
C
∑
i
=
1
m
ζ
i
s
.
t
.
y
(
i
)
(
ω
T
x
(
i
)
+
b
)
≥
1
−
ζ
i
ζ
i
≥
0
\frac{1}{2}||ω||^2+C\sum_{i=1}^mζ_i \; \; s.t. \; \; y^{(i)}(ω^Tx^{(i)}+b)\geq 1-ζ_i \;ζ_i\geq 0
21∣∣ω∣∣2+Ci=1∑mζis.t.y(i)(ωTx(i)+b)≥1−ζiζi≥0
为了求解有条件的最优化问题,转换为下面的形式:
m
a
x
∑
i
a
i
−
1
2
∑
i
,
j
a
i
a
j
y
i
y
j
x
i
⋅
x
j
s
.
t
.
∑
i
a
i
y
i
0
0
≤
a
i
≤
C
max\sum_ia_i-\frac{1}{2}\sum_{i,j}a_ia_jy_iy_jx_i\cdot x_j \; s.t. \sum_ia_iy_i0 \;0\leq a_i\leq C
maxi∑ai−21i,j∑aiajyiyjxi⋅xjs.t.i∑aiyi00≤ai≤C
要注意,上面的形式是求最大值max,当然,如果不习惯,也可以转换成最小值min。
m
i
n
1
2
∑
i
,
j
a
i
a
j
y
i
y
j
x
i
⋅
x
j
−
∑
i
a
i
s
.
t
.
∑
i
a
i
y
i
0
0
≤
a
i
≤
C
min\frac{1}{2}\sum_{i,j}a_ia_jy_iy_jx_i\cdot x_j-\sum_ia_i \; s.t. \sum_ia_iy_i0 \;0\leq a_i\leq C
min21i,j∑aiajyiyjxi⋅xj−i∑ais.t.i∑aiyi00≤ai≤C
然后引入核函数进行替换:
m
i
n
1
2
∑
i
,
j
a
i
a
j
y
i
y
j
K
(
x
i
,
x
j
)
−
∑
i
a
i
s
.
t
.
∑
i
a
i
y
i
0
0
≤
a
i
≤
C
min\frac{1}{2}\sum_{i,j}a_ia_jy_iy_jK(x_i,x_j)-\sum_ia_i \; s.t. \sum_ia_iy_i0 \;0\leq a_i\leq C
min21i,j∑aiajyiyjK(xi,xj)−i∑ais.t.i∑aiyi00≤ai≤C
其中
K
(
x
i
,
x
j
)
=
Φ
(
x
i
)
⋅
Φ
(
x
j
)
K(x_i,x_j)=Φ(x_i)\cdot Φ(x_j)
K(xi,xj)=Φ(xi)⋅Φ(xj)。但是实际上我们从来不用计算
Φ
(
x
i
)
,
Φ
(
x
j
)
Φ(x_i), Φ(x_j)
Φ(xi),Φ(xj),直接用不同的核函数代入,如:
K
(
a
,
b
)
=
(
x
⋅
y
+
c
)
d
K
(
a
,
b
)
=
e
−
γ
∣
∣
a
−
b
∣
∣
2
K(a,b)=(x\cdot y +c)^d \; K(a,b)=e^{-γ||a-b||^2}
K(a,b)=(x⋅y+c)dK(a,b)=e−γ∣∣a−b∣∣2
(五)小结
SVM算法作为数据挖掘领域十大经典算法之一,其中设计了很多精妙的思想。
在低维空间中线性不可能的数据,可以转化成高维度数据,使得数据更具有分析性;然后使用核函数,其在高维空间上的运算等价于低维空间,从而解决了运算量大的问题。
四、多项式核函数与RBF核函数代码实现及调参
(一)使用多项式核函数的SVM
1 数据准备及模型训练
首先准备数据:
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
X, y = datasets.make_moons(noise=0.20,random_state=123)
plt.scatter(X[y==0,0], X[y==0,1])
plt.scatter(X[y==1,0], X[y==1,1])
plt.show()
sklearn中关于多项式核函数SVM的使用。
首先也需要返回一个Pipeline对象,其有两步:第一步是对数据进行标准化;第二步实例化SVC并将kernel参数设置为poly。这样就自动对传递的数据进行多项式化了。
from sklearn.svm import SVC
def PolynomialKernelSVC(degree, C=1.0):
return Pipeline([
("std_scaler", StandardScaler()),
("kernelSVC", SVC(kernel='poly',degree=degree,C=C))
])
poly_kernel_svc = PolynomialKernelSVC(degree=3)
poly_kernel_svc.fit(X, y)
我们看看决策边界是什么样的?
plot_decision_boundary(poly_kernel_svc, axis=[-1.5, 2.5, -1.0, 1.5])
plt.scatter(X[y==0,0], X[y==0,1])
plt.scatter(X[y==1,0], X[y==1,1])
plt.show()
2 调参
我们再尝试着保持阶数degree不变,去调大参数C:
poly_kernel_svc = PolynomialKernelSVC(degree=3,C=200)
poly_kernel_svc.fit(X, y)
则得到如下的决策边界:
通过肉眼观察可见,参数C越大,中间的“凸起”越尖锐。我们知道,参数C是正则化项的系数,C越大,越接近于Hard Margin;C越小,容错性越大。
下面调试一个degree参数:
poly_kernel_svc = PolynomialKernelSVC(degree=4)
poly_kernel_svc.fit(X, y)
通过一些尝试发现,degree参数为偶数时,决策边界类似于双曲线的形式,而为奇数时,则是以直线为基础。大家可以使用网格搜索找到最佳参数。但是我们要注意,多项式阶数degree参数越大,则越容易出现过拟合。
(二)RBF核函数
下面使用sklearn中的高斯核函数,查看器决策边界,同时比较参数对于决策边界的影响。
使用上节的数据,使用sklearn.svm只需要将SVC类的参数kernel传入rbf,并且传入参数gamma即可。
from sklearn.preprocessing import StandardScaler
from sklearn.svm import SVC
from sklearn.pipeline import Pipeline
def RBFKernelSVC(gamma=1.0):
return Pipeline([
('std_scaler', StandardScaler()),
('svc', SVC(kernel='rbf', gamma=gamma))
])
svc = RBFKernelSVC(gamma=1.0)
svc.fit(X,y)
下面我们查看其决策边界:
plot_decision_boundary(svc, axis=[-1.5, 2.5, -1.0, 1.5])
plt.scatter(X[y==0,0], X[y==0,1])
plt.scatter(X[y==1,0], X[y==1,1])
plt.show()
这就是使用RBF核函数,在
γ
=
1.0
γ=1.0
γ=1.0时的决策边界。看上去比多项式核函数稍好一些。
2 RBF核函数几何意义以及调参
下面我们调整gamma参数,可以更加直观地看到RBF核函数的特点。先调大gamma参数:
svc = RBFKernelSVC(gamma=100)
svc.fit(X,y)
可见分类效果极其不好,看上去甚至有点恶心。
我们知道, γ γ γ参数取值越大,意味着高斯函数的“山峰”越窄。那对于决策边界有何影响呢?针对其中的某一类,在每个样本点的周围都形成了一个“山峰”(想象,这是一个俯视图,蓝色点是“山顶”),以“山峰”比较窄,所以看起来就在每个蓝色点的周围圆环区域就比较。
由此可见,RBF核函数,实际上只找到一类,然后在以该类中的每个点为“山尖”,用gamma参数表示“山峰的粗细”,所有的数据点连成“山脉”,这一区域是一类范围,其他范围都属于另一类。
当然了,上面的结果太过拟合了。我们减少gamma值再来看看:
svc = RBFKernelSVC(gamma=10)
svc.fit(X,y)
由此可见,“山峰”变得更比刚才“粗”了,从而连成了“山脉”。下面将gamma变得更小,极端一些,变成0.1,其决策边界如下:
我们看到,这个决策边界变得和线性边界差不多了,有点欠拟合了。由此我们得到:
γ
γ
γ越小,模型复杂度就越小,倾向于欠拟合;
γ
γ
γ越大,模型复杂度就越高,倾向于过拟合。这就要结合具体业务进行调参了。
(三)小结
SVM算法中的多项式核函数与RBF核函数的使用。在使用多项式核函数时,多项式阶数degree决定了决策边界的形状以及模型的复杂程度,参数C是决定了正则化项的权重,即模型的容错性。在使用RBF核函数时,决策边界对应的几何意义是什么样子,并且看到了在调节参数 γ γ γ时对决策边界的影响。
五、回归问题及系列回顾总结
(一) 解决回归问题
1 解决回归问题的思想
SVM思想也可以解决回归问题。回归问题的本质就是找到一根能够很好滴拟合数据点的线(直线、曲线)。**不同回归算法的关键是怎么定义拟合。**比如我们之前学习的线性回归算法,定义拟合的方式就是数据点到拟合直线的MSE最小。
而对于SVM算法来说,如何定义“拟合”呢?
指定一个Margin值,在Margin区域的范围内,包含的数据点越多越好。这就表示这个Margin范围能够比较好地表达样本数据点,在这种情况下取中间的直线作为真正回归结果,用它来预测其他点的y值。
这和SVM算法解决分类问题的思路是相反的,在解决分类问题时,我们期望Margin中的范围越少越好。但是解决回归问题恰恰相反,我们希望所定义的Margin范围能够拟合更多的数据。
在具体训练SVM解决回归问题时,需要指定Margin范围,这里就引入了一个超参数 ε ε ε,即Margin的边界到中间直线的距离。
那么这种思想如何转换成具体的最优化问题表达式极其推导,就不介绍了。我们直接看看如何在sklearn中使用它。
2 sklearn算法中的使用
import numpy as np
import matplotlib.pyplot as plt
from sklearn import datasets
from sklearn.model_selection import train_test_split
boston = datasets.load_boston()
X = boston.data
y = boston.target
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 StandardLinearSVR(epsilon = 0.1):
return Pipeline([
('std_scaler', StandardScaler()),
('linearSVR', LinearSVR(epsilon=epsilon))
])
svr = StandardLinearSVR()
svr.fit(X_train,y_train)
svr.score(X_test, y_test)
# 输出:0.6358704741572658
虽然得到的分数不高,但实际上我们还有很多参数可以调节。除了epsilon以外,我们还可以调节C。并且如果不使用线性分类器LinearSVR,我们还可以使用SVR包,可以添加kernel,这样就又有很多参数可以调节了。
(二)算法回顾 Road Map
上个世纪六十年代,就提出了SVM的概念,到九十年代才逐渐完善。
最初就是一个线性分类器,对其进行改进,使得Margin最大化,这就导出了Linear SVM,定义了Margin,将其数学表达式求出来,去优化这个函数。
Linear SVM也有一些问题,即在实际情况下不能完美地将各个数据点分开,在不满足限制条件的下就求不到数学解。为了解决线性可分的情况下存在异常点的问题,就提出了Soft Margin的思想,增加正则化项,提高容错性。
那么如何解决线性不可能问题呢?把原始空间的问题映射到高维空间,将线性不可分问题转换成线性可分问题,即将 a ⋅ b ⇒ Φ ( a ) ⋅ Φ ( b ) a\cdot b\Rightarrow Φ(a)\cdot Φ(b) a⋅b⇒Φ(a)⋅Φ(b)。这种想法非常好,但是高维数据做内积,计算量太大了。
为了解决这个问题,数学家提出了Kernel Trick核函数: Φ ( a ) ⋅ Φ ( b ) = K ( a , b ) Φ(a)\cdot Φ(b)=K(a,b) Φ(a)⋅Φ(b)=K(a,b)。实际上我们从来不会去真正去求高维映射,而是直接用核函数来代替了,既相当于在高维空间进行操作,又解决了计算量大的问题。
(三)总结
SVM算法是一个很优秀的算法,在集成学习和神经网络之类的算法没有表现出优越性能前,SVM基本占据了分类模型的统治地位。目前则是在大数据时代的大样本背景下,SVM由于其在大样本时超级大的计算量,热度有所下降,但是仍然是一个常用的机器学习算法。
SVM算法的主要优点有:
- 解决高维特征的分类问题和回归问题很有效,在特征维度大于样本数时依然有很好的效果。
- 仅仅使用一部分支持向量来做超平面的决策,无需依赖全部数据。
- 有大量的核函数可以使用,从而可以很灵活的来解决各种非线性的分类回归问题。
- 样本量不是海量数据的时候,分类准确率高,泛化能力强。
SVM算法的主要缺点有:
- 如果特征维度远远大于样本数,则SVM表现一般。
- SVM在样本量非常大,核函数映射维度非常高时,计算量过大,不太适合使用。
- 非线性问题的核函数的选择没有通用标准,难以选择一个合适的核函数。
- SVM对缺失数据敏感。