跟我一起机器学习系列文章将首发于公众号:月来客栈,欢迎文末扫码关注!
在前面两篇文章中,笔者通过两个角度来介绍了什么是支持向量机。不过说一千道一万,还是不如动手来做做。在本篇文章中,笔者将首先介绍如何通过sklearn来搭建相应的SVM分类模型,然后将接着介绍如何处理SVM中的线性不可分问题。
1 SVM建模
1.1 API介绍
在sklearn中,我们通过from sklearn.svm import SVC
这句代码就能够导入SVM分类模型了。有人可能会觉得奇怪,为什么导入的是一个叫SVC的东西?这是因为其实SVM不仅可以用来分类,他同样也能用于回归问题,因此SVC其实就是支持向量分类的意思。
点击进入SVC定义的地方,我们可以发现里面有很多参数可以设置:
def __init__(self, C=1.0,
kernel='rbf',
degree=3,
gamma='scale',
coef0=0.0,
tol=1e-3,
decision_function_shape='ovr'):
但好在我们今天要用到的就一个参数kernel
,其它的放在后面的文章再介绍暂时保持模型即可。根据前面两篇文章的介绍,我们知道SVM是一个线性分类器,因此我们只需要将参数kernel
设置为kernel='linear'
即可。
1.2 线性SVM建模
在完成SVC的导入工作后,根据如下代码就可以使用线性SVM进行分类建模了:
def train(x_train, x_test, y_train, y_test):
clf = SVC(kernel='linear')
clf.fit(x_train, y_train)
y_pre = clf.predict(x_test)
print(classification_report(y_test,y_pre))
# 结果
precision recall f1-score
0 1.00 1.00 1.00
1 0.96 0.98 0.97
2 1.00 1.00 1.00
3 1.00 0.95 0.98
4 1.00 1.00 1.00
5 0.94 0.98 0.96
6 1.00 0.97 0.98
7 0.96 0.98 0.97
8 0.94 0.98 0.96
9 0.94 0.92 0.93
accuracy 0.98
可以看出,在sklearn中使用一个模型的步骤就是我们前面总结的三步走:建模、训练和预测。同时,由于这里我们的超参数kernel
就一个取值,因此也不需要进行模型选择。从最后在测试集上的结果来看,SVM分类器的表现在各类指标中均有不错的结果。
2 线性不可分
2.1 从线性不可分谈起
根据前面介绍SVM的思想来看,我们谈到的情况都是线性可分的,也就是说总能找到一个超平面将数据集分开。 可事实是现实总是那么不完美,大多情况都是线性不可分的。如下图所示[1]:

对于上面这种情况我们应该怎么才能将其分开呢?如果看过前面的一篇文章(特征映射)的朋友应该知道,将原来的输入特征映射到更高维度的空间即可完成上述的分类问题。

如图所示,现在我们已经用一个平面完美的将其分开了。不过此时可能传来这么一个声音“你逗我?,这是刚刚的数据集么?明明刚刚在二维平面,现在却跑到三维平面去了,靠谱点好不?” 对,数据集确确实实已经不是同一个数据集了,但是:每个数据样本所对应的类别却依旧和原来的一样,只不过现在我们给它穿上了一件“马甲”。也就是说,假如 x ( i ) x^{(i)} x(i)是正样本,那么它穿上马甲变成 x ^ ( i ) \hat{x}{^{(i)}} x^(i)之后仍然属于正样本。正如那句:你大妈 (数据集)已经不是你原来的(数据集)大妈了,但(每个样本点所对应的类别)你大爷仍旧(和先前一样)是你大爷。
说到这里是不是意味着我们就解决了SVM中线性不可分的问题呢?解决倒是解决了,可这种方式的效率太低了。
跟我一起机器学习系列文章将首发于公众号:月来客栈,欢迎文末扫码关注!
2.2 将低维特征映射到高维空间
所谓将低维特征映射到高维空间指的是用一定的映射关系,将原始特征映射到更高维度的空间。比如通过一个函数 ϕ ( x ) \phi(x) ϕ(x)将一维特征 x x x映射到三维特征 x , x 2 , x 3 x,x^2,x^3 x,x2,x3。
在这里,笔者将首先直接给出SVM中权重
w
w
w的计算你解析式,其具体由来可参见后续文章。
w
=
∑
i
=
1
m
α
i
y
(
i
)
x
(
i
)
(1)
w=\sum_{i=1}^m\alpha_iy^{(i)}x^{(i)}\tag{1}
w=i=1∑mαiy(i)x(i)(1)
假如此时
α
i
\alpha_i
αi和
b
b
b已知,那么对一个新的样本点进行预测,则其预测结果为:
y
=
w
T
x
+
b
=
∑
i
=
1
m
α
i
y
(
i
)
x
(
i
)
x
+
b
=
∑
i
=
1
m
α
i
y
(
i
)
⟨
x
(
i
)
,
x
⟩
+
b
(2)
\begin{aligned} y&=w^Tx+b\\[1ex] &=\sum_{i=1}^m\alpha_iy^{(i)}x^{(i)}x+b\\[1ex] &=\sum_{i=1}^m\alpha_iy^{(i)}\langle x^{(i)},x\rangle+b \end{aligned}\tag{2}
y=wTx+b=i=1∑mαiy(i)x(i)x+b=i=1∑mαiy(i)⟨x(i),x⟩+b(2)
其中
x
(
i
)
x^{(i)}
x(i)表示训练集中的样本点(其实只是支持向量),
x
x
x新的样本点;
⟨
a
,
b
⟩
\langle a,b\rangle
⟨a,b⟩表示
a
,
b
a,b
a,b之间的内积(inner products)。
按照我们上面提到的通过函数
ϕ
(
x
)
\phi(x)
ϕ(x)将低维映射到高维的思想,我们在预测时只需要将之前的
x
x
x,全部替换成
ϕ
(
x
)
\phi(x)
ϕ(x)即可:
y
=
∑
i
=
1
m
α
i
y
(
i
)
⟨
x
(
i
)
,
x
⟩
+
b
=
∑
i
=
1
m
α
i
y
(
i
)
⟨
ϕ
(
x
)
,
ϕ
(
z
)
⟩
+
b
(3)
\begin{aligned} y&=\sum_{i=1}^m\alpha_iy^{(i)}\langle x^{(i)},x\rangle+b\\[1ex] &=\sum_{i=1}^m\alpha_iy^{(i)}\langle \phi(x),\phi(z)\rangle+b \end{aligned}\tag{3}
y=i=1∑mαiy(i)⟨x(i),x⟩+b=i=1∑mαiy(i)⟨ϕ(x),ϕ(z)⟩+b(3)
其中
ϕ
(
x
)
\phi(x)
ϕ(x)表示训练集映射之后的结果,
ϕ
(
z
)
\phi(z)
ϕ(z)表示新样本映射后的结果,用不同字母只是为了区分。
虽然这样一来解决了SVM中线性不可分的难题,但是又出现了一个新的问题——“维度爆炸”,如下所示:
现有数据集
X
\mathcal{X}
X,其样本点
x
(
i
)
x^{(i)}
x(i)有3个维度,分别为
x
1
(
i
)
,
x
2
(
i
)
,
x
3
(
i
)
x^{(i)}_1,x^{(i)}_2,x^{(i)}_3
x1(i),x2(i),x3(i)(下面简写为
x
1
,
x
2
,
x
3
x_1,x_2,x_3
x1,x2,x3)。现通过函数
ϕ
(
x
)
\phi(x)
ϕ(x)将其映射到某个9维空间中,假设其分别为(
x
1
x
1
,
x
1
x
2
,
x
1
x
3
,
x
2
x
1
,
x
2
x
2
x_1x_1,x_1x_2,x_1x_3,x_2x_1,x_2x_2
x1x1,x1x2,x1x3,x2x1,x2x2
,
x
2
x
3
,
x
3
x
1
,
x
3
x
2
,
x
3
x
3
,x_2x_3,x_3x_1,x_3x_2,x_3x_3
,x2x3,x3x1,x3x2,x3x3)。如果此时要对新样本进行预测,则先要进行计算
⟨
ϕ
(
x
)
,
ϕ
(
z
)
⟩
\langle \phi(x),\phi(z)\rangle
⟨ϕ(x),ϕ(z)⟩:
ϕ
(
x
)
=
(
x
1
x
1
,
x
1
x
2
,
x
1
x
3
,
x
2
x
1
,
x
2
x
2
,
x
2
x
3
,
x
3
x
1
,
x
3
x
2
,
x
3
x
3
)
T
ϕ
(
z
)
=
(
z
1
z
1
,
z
1
z
2
,
z
1
z
3
,
z
2
z
1
,
z
2
z
2
,
z
2
z
3
,
z
3
z
1
,
z
3
z
2
,
z
3
z
3
)
T
⟨
ϕ
(
x
)
,
ϕ
(
z
)
⟩
=
(
x
1
x
1
z
1
z
1
+
x
1
x
2
z
1
z
2
+
⋯
+
x
3
x
3
z
3
z
3
)
T
(4)
\begin{aligned} \phi(x)&=(x_1x_1,x_1x_2,x_1x_3,x_2x_1,x_2x_2,x_2x_3,x_3x_1,x_3x_2,x_3x_3)^T\\[1ex] \phi(z)&=(z_1z_1,z_1z_2,z_1z_3,z_2z_1,z_2z_2,z_2z_3,z_3z_1,z_3z_2,z_3z_3)^T\\[1ex] \langle \phi(x),\phi(z)\rangle&=(x_1x_1z_1z_1+x_1x_2z_1z_2+\cdots+x_3x_3z_3z_3)^T \end{aligned}\tag{4}
ϕ(x)ϕ(z)⟨ϕ(x),ϕ(z)⟩=(x1x1,x1x2,x1x3,x2x1,x2x2,x2x3,x3x1,x3x2,x3x3)T=(z1z1,z1z2,z1z3,z2z1,z2z2,z2z3,z3z1,z3z2,z3z3)T=(x1x1z1z1+x1x2z1z2+⋯+x3x3z3z3)T(4)
此时你应该会发现这个过程的计算量太大了,整体复杂度为
o
(
n
2
)
o(n^2)
o(n2)(分别为
o
(
n
2
)
,
o
(
n
2
)
,
o
(
n
)
o(n^2),o(n^2),o(n)
o(n2),o(n2),o(n))。因此,若要进行更为复杂的映射那么其复杂度将不可想象的,而这就是“维度爆炸”。但是此时我们仔细想一想,“映射”和“预测”之间到底是什么关系了。“映射”是作为一种思想,将低维映射到高维,从而解决线性不可分到可分的问题;而“预测”时所计算的则是
⟨
ϕ
(
x
)
,
ϕ
(
z
)
⟩
\langle \phi(x),\phi(z)\rangle
⟨ϕ(x),ϕ(z)⟩,但说穿了它就是一个值,不管你采取何种的映射规则,在预测时都要计算这么一个值。因此,假如我们能通过某种“黑箱”直接计算出这么一个值,岂不乐哉?有没有呢?当然有,这一“黑箱”操作就称为核函技巧(Kernel Trick)。
2.3 核技巧
设 X \mathcal{X} X是输入空间(欧式空间 R n R^n Rn的子集或离散集合),又设 H \mathcal{H} H为特征空间(希尔伯特空间),如果存在一个从 X \mathcal{X} X到 H \mathcal{H} H的映射 ϕ ( x ) : X ⟶ H \phi(x):\mathcal{X}\longrightarrow \mathcal{H} ϕ(x):X⟶H使得对所有 x , z ∈ X x,z\in\mathcal{X} x,z∈X,函数 K ( x , z ) K(x,z) K(x,z)满足条件 K ( x , z ) = ϕ ( x ) ⋅ ϕ ( z ) K(x,z)=\phi(x)\cdot\phi(z) K(x,z)=ϕ(x)⋅ϕ(z),则称 K ( x , z ) K(x,z) K(x,z)为核函数, ϕ ( x ) \phi(x) ϕ(x)称为映射函数。
直白点说就是:所有的映射都能找到一个与之对应的核函数 K ( x , z ) K(x,z) K(x,z)用来计算 ⟨ ϕ ( x ) , ϕ ( z ) ⟩ \langle \phi(x),\phi(z)\rangle ⟨ϕ(x),ϕ(z)⟩,从而避免了上面出现的“维度爆炸”的问题。因此,核函数可以看做是实现“黑箱”操作(核技巧)的工具。
假设
(
4
)
(4)
(4)中,有两个样本点,
x
x
x为训练集中的样本点,
z
z
z为新的样本点,其分别为
x
=
(
1
,
2
,
3
)
,
z
=
(
2
,
3
,
4
)
x=(1,2,3),z=(2,3,4)
x=(1,2,3),z=(2,3,4),则:
ϕ
(
x
)
=
(
x
1
x
1
,
x
1
x
2
,
x
1
x
3
,
x
2
x
1
,
x
2
x
2
,
x
2
x
3
,
x
3
x
1
,
x
3
x
2
,
x
3
x
3
)
T
=
(
1
×
1
,
1
×
2
,
1
×
3
,
2
×
1
,
2
×
2
,
2
×
3
,
3
×
1
,
3
×
2
,
3
×
3
)
T
ϕ
(
z
)
=
(
z
1
z
1
,
z
1
z
2
,
z
1
z
3
,
z
2
z
1
,
z
2
z
2
,
z
2
z
3
,
z
3
z
1
,
z
3
z
2
,
z
3
z
3
)
T
=
(
2
×
2
,
2
×
3
,
2
×
4
,
3
×
2
,
3
×
3
,
3
×
4
,
4
×
2
,
4
×
3
,
4
×
4
)
T
⟨
ϕ
(
x
)
,
ϕ
(
z
)
⟩
=
(
x
1
x
1
z
1
z
1
+
x
1
x
2
z
1
z
2
+
⋯
+
x
3
x
3
z
3
z
3
)
T
=
4
+
12
+
24
+
12
+
36
+
72
+
24
+
72
+
144
=
400
K
(
x
,
z
)
=
(
x
T
z
)
2
=
(
2
+
6
+
12
)
2
=
400
\begin{aligned} \phi(x)&=(x_1x_1,x_1x_2,x_1x_3,x_2x_1,x_2x_2,x_2x_3,x_3x_1,x_3x_2,x_3x_3)^T\\[1ex] &=(1\times1,1\times2,1\times3,2\times1,2\times2,2\times3,3\times1,3\times2,3\times3)^T\\[1ex] \phi(z)&=(z_1z_1,z_1z_2,z_1z_3,z_2z_1,z_2z_2,z_2z_3,z_3z_1,z_3z_2,z_3z_3)^T\\[1ex] &=(2\times2,2\times3,2\times4,3\times2,3\times3,3\times4,4\times2,4\times3,4\times4)^T\\[1ex] \langle \phi(x),\phi(z)\rangle&=(x_1x_1z_1z_1+x_1x_2z_1z_2+\cdots+x_3x_3z_3z_3)^T\\[1ex] &=4+12+24+12+36+72+24+72+144=400\\[3ex] K(x,z)&=(x^Tz)^2=(2+6+12)^2=400 \end{aligned}
ϕ(x)ϕ(z)⟨ϕ(x),ϕ(z)⟩K(x,z)=(x1x1,x1x2,x1x3,x2x1,x2x2,x2x3,x3x1,x3x2,x3x3)T=(1×1,1×2,1×3,2×1,2×2,2×3,3×1,3×2,3×3)T=(z1z1,z1z2,z1z3,z2z1,z2z2,z2z3,z3z1,z3z2,z3z3)T=(2×2,2×3,2×4,3×2,3×3,3×4,4×2,4×3,4×4)T=(x1x1z1z1+x1x2z1z2+⋯+x3x3z3z3)T=4+12+24+12+36+72+24+72+144=400=(xTz)2=(2+6+12)2=400
有没有发现,两者的结果一样但是在计算上却相差甚远?前者需要
O
(
n
2
)
O(n^2)
O(n2)的时间复杂度,而后者只要
O
(
n
)
O(n)
O(n)即可。那有人就会问,你是怎么知道
(
x
T
z
)
2
(x^Tz)^2
(xTz)2等于
ϕ
(
x
)
⋅
ϕ
(
z
)
\phi(x)\cdot\phi(z)
ϕ(x)⋅ϕ(z)的?那下面就是推导:
(
x
T
z
)
2
=
(
∑
i
=
1
n
x
i
z
i
)
(
∑
j
=
1
n
x
j
z
j
)
=
∑
i
=
1
n
∑
j
=
1
n
x
i
x
j
z
i
z
j
=
∑
i
=
1
n
∑
j
=
1
n
x
i
x
j
z
i
z
j
=
x
1
x
1
z
1
z
1
+
x
1
x
2
z
1
z
2
+
x
1
x
3
z
1
z
3
+
⋯
+
x
n
x
n
z
n
z
n
=
ϕ
(
x
)
⋅
ϕ
(
z
)
(5)
\begin{aligned} (x^Tz)^2&=\large(\sum_{i=1}^nx_iz_i)\large(\sum_{j=1}^nx_jz_j)\\[2ex] &=\sum_{i=1}^n\sum_{j=1}^nx_ix_jz_iz_j\\[2ex] &=\sum_{i=1}^n\sum_{j=1}^n\color{red}{x_ix_j}\color{blue}{z_iz_j}\\[2ex] &=x_1x_1z_1z_1+x_1x_2z_1z_2+x_1x_3z_1z_3+\cdots+x_nx_nz_nz_n\\[2ex] &=\phi(x)\cdot\phi(z) \end{aligned}\tag{5}
(xTz)2=(i=1∑nxizi)(j=1∑nxjzj)=i=1∑nj=1∑nxixjzizj=i=1∑nj=1∑nxixjzizj=x1x1z1z1+x1x2z1z2+x1x3z1z3+⋯+xnxnznzn=ϕ(x)⋅ϕ(z)(5)
其实也就是说,我先进行了这么一个推导知道
(
x
T
z
)
2
(x^Tz)^2
(xTz)2等于
ϕ
(
x
)
⋅
ϕ
(
z
)
\phi(x)\cdot\phi(z)
ϕ(x)⋅ϕ(z),然后在举例过程中才列出了
ϕ
(
x
)
\phi(x)
ϕ(x)这么一中映射规则。但是话又说回来,你这么关系映射规则干什么呢?你需要的是映射规则吗?你需要的不就是这个内积吗?假如我现在换成
K
(
x
,
z
)
=
(
x
T
z
)
5
K(x,z)=(x^Tz)^5
K(x,z)=(xTz)5,那么你也只需要计算
(
2
+
6
+
12
)
5
(2+6+12)^5
(2+6+12)5的值即可,而根本不用关系原始特征被映射到了一个什么样的高维空间,并且从一定程度上来说映射到的空间越高越有利于找到分类决策面。我们所要担心的就是核
K
(
x
,
z
)
K(x,z)
K(x,z)背后所表示的空间是否存在,即这个核的有效性。
2.4 从高维到无穷维
上面我们说到,从一定程度上来说映射到的空间越高越有利于找到分类决策面,那要是能映射到 n n n维空间岂不是更好?说得倒是没错,但这该怎么实现呢?是令 K ( x , z ) = ( x T z ) n K(x,z)=(x^Tz)^n K(x,z)=(xTz)n?
要实现从低维到无穷维的映射的方法之一就是借助高斯核函数(Gaussian Kernel)或者称之为径向基函数(Radial Basis Function, RBF)。
KaTeX parse error: No such environment: align at position 8: \begin{̲a̲l̲i̲g̲n̲}̲ K(x, z) &= \ex…
为什么借助
(
6
)
(6)
(6)我们就能实现到无穷维的映射呢?想象一下泰勒展开,
(
6
)
(6)
(6)式中第二行第三项的泰勒展开为:
$$
\exp \left( \frac{\langle x, z \rangle}{\sigma^2} \right)
= 1 + \frac{\langle x, z \rangle}{\sigma^2}
- \frac{\langle x, z \rangle^2}{2 \sigma^4}
- \frac{\langle x, z \rangle^3}{6 \sigma^6} + \cdots,\tag{7}
$$
也就是说,由于泰勒展开的存在,RBF自然就隐含的实现了从低维到无穷维的映射。
2.5 常见核函数
我们在实际解决问题的时候,甚至都不用关心它到底是如何映射的,只需要正确选用核函数,实现分类的目的即可。下面是一些常见的核函数,虽然大家都常见但用得最多的依旧是高斯核函数。
-
线性核(Linear Kernel)
k ( x , z ) = x T z + C (8) k(x,z) = x^Tz + C\tag{8} k(x,z)=xTz+C(8)
其中 C C C为常数 -
多项式核(Polynolial Kernel)
k ( x , z ) = ( x T z ) d + C (9) k(x,z) = (x^Tz)^d + C\tag{9} k(x,z)=(xTz)d+C(9)
其中 C C C为常数 -
高斯核(Gaussian Kernel)
k ( x , z ) = exp ( − ∣ ∣ x − z ∣ ∣ 2 2 σ 2 ) (10) k(x,z) = \exp\large({-\frac{||x-z||^2}{2\sigma^2}})\tag{10} k(x,z)=exp(−2σ2∣∣x−z∣∣2)(10)
其中, σ > 0 \sigma>0 σ>0为高斯核的带宽(width) -
拉普拉斯核(Laplacian Kernel)
k ( x i , x j ) = e x p ( − ∣ ∣ x i − x j ∣ ∣ σ ) , σ > 0 (11) k(x_i,x_j) = exp\large({-\frac{||x_i-x_j||}{\sigma}}),\sigma>0\tag{11} k(xi,xj)=exp(−σ∣∣xi−xj∣∣),σ>0(11) -
Sigmoid核
k ( x , z ) = t a n h ( β x T z + θ ) (12) k(x,z) = tanh(\beta x^Tz+\theta)\tag{12} k(x,z)=tanh(βxTz+θ)(12)其中, t h a n h thanh thanh 为双曲正切函数, β > 0 , θ < 0 \beta>0,\theta<0 β>0,θ<0
通过前面的讨论可知,我们希望样本点在特征空间内线性可分,因此特征空间的好坏对支持向量机的性能至关重要。需要注意的是,在不知道特征映射的形式时,我们并不知道什么样的核函数的合适的,而核函数也仅是隐式地定义了这个特征空间。于是,核函数的选择称为支持向量机最大的变数。
最后,对于核函数的使用只需要在定义模型的时候通过参数kernel='rbf'
指定即可,默认为高斯核函数。
3 总结
在这篇文章中,笔者首先介绍了如何在sklearn中搭建一个SVM分类模型;然后进一步解释了SVM中的线性不可分问题;接着介绍了如何通过核函数来解决线性不可分的问题,同时还进一步的说明了为什么使用核函数能够将低维特征映射到无穷维的原理。尽管如此,但是仍旧一个问题没有谈到——核函数的有效性问题,即怎么判断一个核函数背后确确实实存在这么一个高维空间。对于这个问题感兴趣的朋友可以参考[3] [4]资料中的讲解。本次内容就到此结束,感谢阅读!
若有任何疑问与见解,请发邮件至moon-hotel@hotmail.com并附上文章链接,青山不改,绿水长流,月来客栈见!
引用
[1]https://medium.com/analytics-vidhya/how-to-classify-non-linear-data-to-linear-data-bb2df1a6b781
[2] The kernel trick https://nealjean.com/ml/kernel-trick/#fnref:credits
[3]《统计机器学习(第二版)》李航,公众号回复“统计学习方法”即可获得电子版与讲义
[4] Andrew Ng. CS229. Note3 http://cs229.stanford.edu/notes/cs229-notes3.pdf
[5]示例代码:关注公众号回复“示例代码”即可直接获取!
近期文章
