支持向量机是啥
有一次公司项目上的同事一起吃饭(面前是一锅炒土鸡),提到了支持向量机,学文的同事就问支持向量机是什么,另一个数学物理大牛想了一下,然后说,一种鸡。。。
确实很难一句话解释清楚这只鸡。。。support vector machine从字面意思来说应该是依靠support vector来划分数据(其实也能回归啦。。)的机器学习模型。它是一个凸优化问题。
SVM的核心将数据的特征投射到高维,然后找到超平面,分割不同类别的数据点,而且要使分离的程度越大越好,至于为什么叫支持向量机,是因为每个类别都会有一些数据点作为支撑向量,这些支撑向量决定了最后分割的超平面。
![@线性可分示意图|300x200]
(https://img-blog.youkuaiyun.com/20171116151440572?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvU3VtbWVyU3RvbmVT/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
这个图里的圈圈就是两边的支撑向量。
线性可分支持向量机
我们先讲最简单的情况,也就是线性可分的平面二分类问题。
支持向量机的目标是,找到一条分界线,把两类数据分开。但是这样的线可能有很多。
像上图。并不是每条线都是一个很好的分割线,如果紧贴着红点,那么线的另一头会立马被判为蓝色,这样对红点就很不合适了。所以SVM的目标不光是要找到这样一条分割线,而是要找到一条能最公平合理的分割线,让两类数据中最边缘的数据点距离分割线越远越好,这样这个分类器的泛化能力才会更强。于是乎支撑向量就被引入了。
分割线,分类表达式
直线的一般表达式:ax+by+c=0
对于多维,可以直接表达成:
w
T
x
+
b
=
0
w^{T}x+b=0
wTx+b=0,在多维情况下,它是个超平面。
对于直线上方的类别有
w
T
x
+
b
>
0
w^{T}x+b>0
wTx+b>0
对于直线下方的类别有
w
T
x
+
b
<
0
w^{T}x+b<0
wTx+b<0
距离表达式
有了直线,我们需要定义数据点到分割平面的距离,这里用的是点到直线的距离公式。
最大分割距离max margin
然后我们再来看一开始的那张图:
回顾一下支持向量机是要干啥来着,是要找到一条把两类数据分得很开的割平面对不对,这需要我们找到两类数据最边缘的数据点(支撑向量),然后画出来两条平行的切面,两边支撑向量距离的垂直平分线就是最终的切割平面。
支持向量机的目标是让最边缘的点之间的距离越大越好。即 ρ \rho ρ最大
这是我们的终极目标,敲黑板!
目标函数表达式推导
接下来,我们来分析怎么把这个目标写成数学表达式~
很简单,只需要表示support vector到分割线的距离,并且让该距离最大不就行了。
直线上方的点 y i = 1 w T x i + b ≥ ρ 2 \quad y_{i}=1 \quad w^{T}x_{i}+b \ge \frac{\rho}{2} yi=1wTxi+b≥2ρ
直线下方的点 y i = − 1 w T x i + b ≤ − ρ 2 \quad y_{i}=-1 \quad w^{T}x_{i}+b \le -\frac{\rho}{2} yi=−1wTxi+b≤−2ρ
等价于:$ y_{i}(w^{T}x_{i}+b)\ge \frac{\rho}{2}$
那么新的问题来了,我们如何知道哪个点是support vector?
答案就是离分割线最近的点对不对。
所以,目标函数是一个极小极大问题。。。
\begin{equation}
\mathop{\arg\max}{w,b} \left{\frac{2}{||w||} \mathop{\min}{i} y_{i}(w^{T}x_{i}+b)\right}.
\end{equation}
之前我们有 $ y_{i}(w^{T}x_{i}+b)\ge \frac{\rho}{2}$,我们总可以通过缩放w和b,使该不等式右边划归到1。如此,就可以简化目标函数。
\begin{equation}
\mathop{\arg\max}{w,b}\frac{2}{||w||}
\ {s.t. \quad y{i}(w^{T}x_{i}+b)\ge 1 for i=1,2,3…n}
\end{equation}
优化问题求解
我们把问题改为求它的对偶问题
\begin{equation}
\mathop{\arg\min}{w,b}\frac{||w||^2}{2}
\ {s.t. \quad y{i}(w^{T}x_{i}+b)\ge 1 for i=1,2,3…n}
\end{equation}
因为目标函数是二次的,而约束在参数w和b上是线性的,因此这个问题是一个凸优化问题,可以通过标准的拉格朗日乘子来求解。
用拉格朗日乘子法:
L
(
w
,
b
,
α
)
=
∣
∣
w
∣
∣
2
2
−
∑
i
=
0
n
α
i
(
y
i
(
w
T
x
i
+
b
)
−
1
)
(
1
)
L(w,b,\alpha)=\frac{||w||^2}{2}-\sum_{i=0}^{n} \alpha_{i}(y_{i}(w^{T}x_{i}+b)-1)\quad(1)
L(w,b,α)=2∣∣w∣∣2−i=0∑nαi(yi(wTxi+b)−1)(1) 其中,
α
i
≥
0
\alpha_{i} \ge 0
αi≥0
解这个优化问题的方法是先求关于
α
\alpha
α的极大,再求关于w和b的极小。
m
i
n
w
,
b
m
a
x
α
L
(
w
,
b
,
α
)
\mathop{min}_{w,b}\mathop{max}_{\alpha}L(w,b,\alpha)
minw,bmaxαL(w,b,α)
(因为
α
i
≥
0
\alpha_{i} \ge 0
αi≥0,
−
(
y
i
(
w
T
x
i
+
b
)
−
1
)
≤
0
-(y_{i}(w^{T}x_{i}+b)-1)\le 0
−(yi(wTxi+b)−1)≤0, 求这个式子关于
α
\alpha
α的极大,就是0,于是等价于原来的对
∣
∣
w
∣
∣
2
\frac{||w||}{2}
2∣∣w∣∣求极小)
我们转而求它的对偶问题:
m
a
x
α
m
i
n
w
,
b
L
(
w
,
b
,
α
)
\mathop{max}_{\alpha}\mathop{min}_{w,b}L(w,b,\alpha)
maxαminw,bL(w,b,α)
(1)先求极小问题
另L对w,b求偏导并等于0, 有
∂
L
∂
w
=
w
−
∑
i
=
1
n
α
i
y
i
x
i
=
0
\frac{\partial L}{\partial w}=w-\sum_{i=1}^{n} \alpha_{i}y_{i}x_{i}=0
∂w∂L=w−i=1∑nαiyixi=0
∂
L
∂
b
=
∑
i
=
1
n
α
i
y
i
=
0
\frac{\partial L}{\partial b} = \sum_{i=1}^{n} \alpha_{i}y_{i}=0
∂b∂L=i=1∑nαiyi=0
然后我们把这两个式子带入到拉格朗日函数(1)中,最终化简可以得到:
∑
i
=
1
n
α
i
−
1
2
∑
i
,
j
=
1
n
α
i
α
j
y
i
y
j
x
i
T
x
j
\sum_{i=1}^{n}\alpha_{i}-\frac{1}{2}\sum_{i,j=1}^{n}\alpha_{i}\alpha_{j}y_{i}y_{j}x_{i}^{T}x_{j}
i=1∑nαi−21i,j=1∑nαiαjyiyjxiTxj
(2)现在求极大问题
m
a
x
α
{
∑
i
=
1
n
α
i
−
1
2
∑
i
,
j
=
1
n
α
i
α
j
y
i
y
j
x
i
T
x
j
}
s
.
t
.
∑
i
=
1
n
α
i
y
i
=
0
α
i
≥
0
\mathop{max}_{\alpha}\left\{\sum_{i=1}^{n}\alpha_{i}-\frac{1}{2}\sum_{i,j=1}^{n}\alpha_{i}\alpha_{j}y_{i}y_{j}x_{i}^{T}x_{j}\right\}\\s.t. \sum_{i=1}^{n} \alpha_{i}y_{i}=0\\ \alpha_{i} \ge 0
maxα{i=1∑nαi−21i,j=1∑nαiαjyiyjxiTxj}s.t.i=1∑nαiyi=0αi≥0
还是将问题转成对偶问题,求解这个问题用到了SMO,sequential minimal optimization,即每次只挑两个拉格朗日乘子,其他乘子认为是常数,然后每次都化为两变量凸优化问题。
最终通过求得 α \alpha α的值,找到最优的w和b。注意,最后的结果里,只有支撑向量的 α > 0 \alpha \gt 0 α>0, 对于非支撑向量,其 α = 0 \alpha = 0 α=0
松弛变量
有时候数据点不会分隔的这么好,会有一些点,总是站在对方阵营。或者及时我们能找到一条线,把两个类别的数据点刚好完美分开,我们可能也不希望是这根线。。。因为我们希望模型能有较好的泛化能力,希望中间的过渡带 ρ \rho ρ越大越好,所以我们会对某些不好的点选择性忽略。
一般用 ξ \xi ξ来表示松弛变量。加入松弛变量后的模型优化目标和约束变成啥样了呢。
m i n w , b , ξ ∣ ∣ w ∣ ∣ 2 2 + C ∑ i = 1 n ξ i s . t . y i ( w T x i + b ) ≥ 1 − ξ i ξ ≥ 0 \mathop{min}_{w,b,\xi}\frac{||w||^2}{2}+C\sum_{i=1}^{n}\xi_{i}\\s.t. y_{i}(w^{T}x_{i}+b) \ge 1-\xi_{i}\\ \xi \ge 0 minw,b,ξ2∣∣w∣∣2+Ci=1∑nξis.t.yi(wTxi+b)≥1−ξiξ≥0
然后跟刚才的过程差不多,先对w,b, ξ \xi ξ求极小,在对 α \alpha α求极大。
这里需要强调的是,C越大,我们越倾向于没有松弛变量,即模型会尽可能分对每一个点,反之,C越小,模型的泛化能力越强。
核函数
终于讲到核函数了。。。我最喜欢的部分。。。
之前的问题中我们都是用的x本尊,现实是,这堆数据点可能一条线不可分,比如说圆环图,斑马图。。。于是我们可以对x做高维变换,以期望在高维空间中,数据点能分开。此处盗用一张非常著名的图。
之前所有
x
i
x_{i}
xi出现的地方,都可以改成
Φ
(
x
i
)
\Phi(x_{i})
Φ(xi)
比如最终的分割超平面:
w
∗
T
Φ
(
x
i
)
+
b
w
∗
=
∑
i
=
1
n
α
i
∗
y
i
Φ
(
x
i
)
b
∗
=
y
i
−
∑
i
=
1
n
α
i
∗
y
i
(
Φ
(
x
i
)
T
Φ
(
x
j
)
)
w^{*T}\Phi(x_{i})+b\\ w^*=\sum_{i=1}^{n} \alpha_{i}^*y_{i}\Phi(x_{i})\\b^*=y_{i}-\sum_{i=1}^{n} \alpha_{i}^*y_{i}(\Phi(x_{i})^{T}\Phi(x_{j}))
w∗TΦ(xi)+bw∗=i=1∑nαi∗yiΦ(xi)b∗=yi−i=1∑nαi∗yi(Φ(xi)TΦ(xj))
再比如求解中的目标函数:
∑
i
=
1
n
α
i
−
1
2
∑
i
,
j
=
1
n
α
i
α
j
y
i
y
j
Φ
(
x
i
)
T
Φ
(
x
j
)
\sum_{i=1}^{n}\alpha_{i}-\frac{1}{2}\sum_{i,j=1}^{n}\alpha_{i}\alpha_{j}y_{i}y_{j}\Phi(x_{i})^{T}\Phi(x_{j})
i=1∑nαi−21i,j=1∑nαiαjyiyjΦ(xi)TΦ(xj)
因为从始至终,我们都只用到了内积,所以我们并不需要定义核函数的具体形式,而只需给出核函数的内积形式。
三种常见核函数:
多项式核函数:
κ
(
x
i
,
x
j
)
=
(
α
∣
∣
x
i
−
x
j
∣
∣
a
+
r
)
b
\kappa(x_{i},x_{j})=(\alpha||x_{i}-x_{j}||^a+r)^b
κ(xi,xj)=(α∣∣xi−xj∣∣a+r)b
α
,
a
,
b
,
r
是常数
\alpha,a,b,r是常数
α,a,b,r是常数
RBF径向基核函数:
κ
(
x
i
,
x
j
)
=
e
x
p
(
−
∣
∣
x
i
−
x
j
∣
∣
2
2
σ
2
)
\kappa(x_{i},x_{j})=exp(-\frac{||x_{i}-x_{j}||^2}{2\sigma^2})
κ(xi,xj)=exp(−2σ2∣∣xi−xj∣∣2) 也叫高斯核函数,因为长得像
sigmoid核函数:
κ
(
x
i
,
x
j
)
=
t
a
n
h
(
γ
∣
∣
x
i
−
x
j
∣
∣
a
+
r
)
\kappa(x_{i},x_{j})=tanh(\gamma||x_{i}-x_{j}||^a+r)
κ(xi,xj)=tanh(γ∣∣xi−xj∣∣a+r)
一般来讲RBF核已经很好用了,它可以投射到无穷维(回顾一下
e
x
e^x
ex的tylor展开式。。。)
你可能还会有疑问,都什么函数能当核函数。答案是,所有半正定矩阵。
好了,现在我们终于可以来两行代码了。。。
from sklearn.datasets import load_iris
data = load_iris()
用sklearn自带的鸢尾花数据集,好嘛,我知道老掉牙了。。。复习一下
dir(data)返回的结果是[‘DESCR’, ‘data’, ‘feature_names’, ‘target’, ‘target_names’],比较直观,我就不解释了。
from sklearn.model_selection import train_test_split
X_train,X_test,y_train,y_test = train_test_split(data.data, data.target,test_size=0.3,random_state=30,stratify=data.target)
这一步是划分训练集和测试集,永远不要忘了这一步。
from sklearn import svm
model=svm.SVC(C=0.5, kernel='rbf',gamma=5 )
sklearn.svm.SVC(C=1.0, kernel=‘rbf’, degree=3, gamma=‘auto’, coef0=0.0, shrinking=True, probability=False, tol=0.001, cache_size=200, class_weight=None, verbose=False, max_iter=-1, decision_function_shape= ovr’, random_state=None)
C是松弛变量的系数
kernel是要定义核函数的名字,跟后面的系数也息息相关
gamma是这三个核函数的乘系数
degree是对多项式核函数来的。。。
coef0是另外两个核函数里的r
probability是可以允许多分类的情况
class_weight是可以实现给不同类别设不同的权重
from sklearn.metrics import accuracy
model.fit(X_train,y_train)
model.score(X_test,y_test)
输出是91%
虽然如此,我们还是象征性调个参
import numpy as np
from sklearn.model_selection import GridSearchCV
parameters={'kernel':['linear','rbf','sigmoid','poly'],'C':np.linspace(0.1,20,50),'gamma':np.linspace(0.1,20,20)}
svc = svm.SVC()
model = GridSearchCV(svc,parameters,cv=5,scoring='accuracy')
model.fit(X_train,y_train)
model.best_params_
model.score(X_test,y_test)
{‘C’: 1.7244897959183674, ‘gamma’: 0.10000000000000001, ‘kernel’: ‘rbf’}
accuracy=95%