机器学习(一)——SVM

前言

这是机器学习的第一篇,之所以在研究深度学习的时候还选择学习机器学习,是因为8个字“不忘初心,方得始终”。尽管近几年机器学习已被深度学习的光环给笼罩,但我们还是要理智地思考,深度学习终究是数据驱动的,失去了数据,再精密的深度网络结构也是画饼充饥,无的放矢。在很多的实际问题中,我们很难得到海量且带有精确标注的数据,这时深度学习也就没有大显身手的余地,反而是许多传统方法可以灵活巧妙地处理。另外,在研究深度学习的过程中,我们可以看到一些深度学习的模型借鉴了机器学习的思想,因此研究好机器学习,对深度学习也有一定的启发性作用。

初识 SVM

SVM全名Support Vector Machine,生于上世纪60年代,出名于上世纪90年代,主要工作是解决监督学习中的分类问题,偶尔也可以解决回归问题。其特殊技巧是kernel trick,将其输入隐式地映射到高维特征空间。其为人正义,往往能以最大间隔把两个类分开。

动机

分类数据是机器学习中一项常见的任务。假设某些给定的数据点各自属于两个类之一,而目标是确定新数据点将在哪个类中。对于支持向量机来说,数据点被视为p维,则我们想知道是否可以用(p-1)维超平面来分开这些点。这就是我们常说的线性分类器。而SVM是要选择出所有线性分类器中最佳的超平面,这里定义最佳超平面是能以最大间隔把两个类分开的超平面(特别正义)。显然我们要做的就是让离超平面最近点到超平面的距离最大化(通过调整超平面)。这样得到的超平面称为最大间隔超平面,而其定义的线性分类器就被称为最大间隔分类器。
SVM_1
上面(a)图是我们的数据点的分布,(b)、(c)中间的实线都是一种线性分类器,显然(b)的间距更大更稳定。

线性SVM——SVM最初的模样。

我们考虑以下形式的n个数据点:$(\vec{x_1},y_1),….,(\vec{x_n},y_n)$
其中$y_i$是1或者-1,表明$\vec{x_i}$所属的类。$\vec{x_i}$中每个都是一个p维实向量。如上所述,我们现在要做的是找到那个p-1维能以最大间隔分类+1,-1两类的超平面。
任何一个超平面都可以表达成这种形式:$\vec{w}·\vec{x}-\vec{b}=0$,这里的$\vec{x}$可以是超平面上的所有点。$\vec{w}$是法向量。

硬间隔

如果这些训练数据是线性可分的,即可以没有误差地分开两个类。这是非常特殊的情况,既然它那么优秀(各自类别有明显的区分,如下图),那么我们SVM的态度就要强硬点。
SVM_2
我们需要分别在两类数据中找到两类数据最近的两个点。然后在这两个点上做两个平行的超平面,使得这两个超平面得距离最大。显然根据前面得关于SVM分类器的特征描述,我们所希望求得的最大间隔超平面,就在这两个平行的超平面的正中间。
上面我们描述我们的最大间隔超平面为:$\vec{w}·\vec{x}-\vec{b}=0$
这里由于这两个平行的超平面是与最大间隔超平面平行的我们可以表示为:

这里为什么是1和-1,因为无论是什么数我们都可以在等式两侧同除以该数,使其变为1或-1.
两个超平面的距离为$\frac{2}{||\vec{w}||}$,因此我们的目标是使这个距离值尽可能大。显然我们需要的是最小化$||\vec{w}||$。同时为了使得样本数据点都在超平面的间隔之外,我们需要保证对于所有的i满足其中的一个条件:

或是

这些约束表明每个数据点都必须位于间隔的正确一侧。
两个式子可以合起来为:

将上述最优化问题写成数学公式:

暂时我们先不解这个式子,跳到下一种情况。

软间隔

从上面的线性可分的图我们可以看出,这是一种很理想很特殊的情况。真实的情况是我们往往会存在着一点噪声,它们并不是线性可分的。但是此时我们的SVM很大度,它可以选择示弱,在不改变自身线性分类的前提下,软化自己前面强硬的要求(即数据点必须线性可分)。但它要做出一定的权衡:我们的初衷不能变即要找到最大间隔的超平面,但是又要考虑如果按硬间隔的方式处理会有一些点错分,我们也不能让太多的点错分。
这里我们引入hinge loss函数作为惩罚项

显然当数据点分类正确时,这一项为0,然而当数据点错分了,则要以$1-y_i(\vec{w}·\vec{x_i}-b)$作为惩罚项,我们也想最小化这个惩罚.

现在我们可以得到新的最优问题的公式:

求解最优化问题

我们会用到一些数学内容:1.拉格朗日乘子法2.KKT条件 3.拉格朗日对偶性

拉格朗日乘子法

由此可以定义拉格朗日函数:

此时求解变量的偏导数:

KKT条件

可以理解为更一般化的拉格朗日乘子法

由此可以得到拉格朗日函数为:

KTT条件是说最优值必须满足以下条件

  • 1.$L(x,\lambda,\mu)对x求导为0$
  • 2.$h(x)=0$
  • 3.$\mu_k g_k(x)=0 (\mu_k≥0)$

拉格朗日对偶性

1.原始问题:假设$f(x),c_i(x),h_j(x)$是定义在$R^n$上的连续可微函数

得到拉格朗日函数:

$其中α_i,β_j是拉格朗日乘子,α_i≥0$

考虑x的函数

上述下标p表示的是原始问题。

假设给定某个x,如果x违反原始问题的约束条件,则存在i使得$c_i(w)>0或者存在j使得某个j使h_j(x)≠0$
那么就有$\theta_p(x)=max_{α,β;α_i≥0}[f(x)+\sum_{i=1}^kα_ic_i(x)+\sum_{j=1}^lβ_jh_j(x)]=+∞$
上述公式的意思是给定x的情况下如果不满足前面的约束条件,则存在α和β会使上述公式为+∞

我们有下列等式:

所以如果考虑极小化问题:

等价于

定义原始问题的最优值为$p^* =min_{x}\theta_p(x)$

对偶问题
定义 $\theta_D(α,β)=min_x L(x,α,β)$
再考虑极大化$\theta_D(α,β)=min_x L(x,α,β)$

上述问题称为拉格朗日函数的极大极小问题。

可以将广义拉格朗日函数的极大极小化问题表示为约束最优化问题:

称为原始问题的对偶问题
我们可以定义对偶问题的最优值:

称为对偶问题的值
原始问题和对偶问题的关系
若原始问题和对偶问题都有最优解,则

推论1:
设$x^* $和$α^* $,$β^* $分别是原始问题和对偶问题的可行解,并且$d^* =p^* $则$x^* $和$α^* $,$β^* $,使得$x^* $是原始问题的解,$α^* ,β^* $是对偶问题的解,并且$p^* =d^* =L(x^* ,α^* ,β^* )$

求解SVM的最优化问题

硬间隔

我们先放下软硬间隔的问题,只考虑硬间隔。
如前所述,SVM硬间隔的最优化问题是:

引入拉格朗日乘子法

在这里我们得到的:

带入到$L_p$
(这里事实上$L_p=L_D$,区别在于x与α的选择顺序以及min和max的顺序)
这里得到

上面用到的是拉格朗日对偶性

求的最优解$α^* =(α_1^* ,α_2^* ,….,α_N^* )^T$

当我们有了$α^* $以后,我们就可以根据前面的求w和b,

由此可以得到我们日思夜想的超平面:

并且我们的分类决策函数为:
$f(x)=sign(w^* ·x+b^* )$

对于$w^* ·x_i+b^* =±1$
的数据点$x_i$称为支持向量。

以上就是硬间隔情况的SVM的求解。

软间隔

上述的硬间隔问题往往是理想化的。我们现在要解决的是线性不可分的软间隔问题。
原始问题为:

则有拉格朗日函数

其中$α_i≥0,\mu_i≥0$
求$L(w,b,\xi,α,\mu)$对$w,b,\xi$的极小,得到

将这些带入$L(w,b,\xi,α,β)$用$α_i$表示L

具体形式:

如前面所示,我们使用拉格朗日对偶性
来求解我们要的α

对偶化:

可以求解得到$α^* $(上述第二第三个约束条件可以写成一个:$0≤α_i≤C$)

同样得到$α^* $以后就可以得到$w^* $和$b^* $

分离超平面可以写成:

决策函数为:

此时我们求得的是线性不可分情况下软间隔的超平面

分离超平面我们往往用实现表示,间隔边界由虚线表示,$x_i$到间隔边界的距离为$\frac{\xi_i}{||w||}$

如前面软间隔处介绍的那样,我们用的$\xi$往往是hinge loss

非线性SVM与核函数

非线性SVM的核函数技巧是基于线性SVM进行的一定修改。

线性SVM中的优化问题:

我们知道核函数要做的是将数据点映射到高维空间。我们可以用$\phi(x)$将其映射到高维空间。

对于非线性问题引入核函数技巧

对于软间隔问题则有:

但由于从输入空间到特征空间的这种映射会使得维度发生爆炸式的增长,因此上述约束问题中内积$\phi(x_i)·\phi(x_j)$的运算会非常大以至于无法承受,因此通常我们会构造一个核函数

我们常用的核函数有:

  • 1.线性和函数:$K(x,x_i)=x·x_i$
  • 2.多项式核函数:$K(x,x_i)=((x·x_i)+1)^d$
  • 3.高斯核函数:$K(x,x_i)=exp(-\frac{||x-x_i||^2}{\sigma^2})$
  • sigmoid核函数:$K(x,x_i)=tanh(\eta+\theta)$

核函数定理:令X为输入空间,K(·,·)是定义在X×X上得对称函数,则K是核函数当且仅当对于任意数据$D={x_1,x_2,…,x_m}$,核矩阵K总是半正定的

只要一个对称函数所对应的核函数半正定,它就能作为核函数使用.事实上,对于一个半正定核矩阵,总能找到一个与之对应的映射$\phi$。换言之,任何一个核函数都隐式地定义了一个称为”再生核希尔伯特空间”(RKHS)的特征空间。

但我们并不知道什么样的核函数是合适的,而核函数也仅是隐式定义了这个特征空间。于是,”核函数选择”成为支持向量机的最大变数。

核函数的选取

吴恩达的见解
1.如果Feature的数量很大,跟样本数量差不多,这时候选用LR或者Linear Kernel的SVM。(数据维度已经很高了则不需要上升到更高维度)
2.如果Feature的数量比较小,样本数量一般,不算大也不算小,选用SVM+Gaussian Kernel
3.如果Feature的数量比较小,而样本数量很多,需要手工添加一些feature变成第一种情况。(不太能理解)

核函数的本质

这一步是我们用一个K函数替换数据映射到高维后的内积计算。直接设计K比设计$\phi$后得到K方便且计算量小。
而核函数的价值在于它是在低维上进行计算,而实质上的分类效果表现在高维上,避免了在高为空间中的复杂计算。

SVM实现多分类

SVM最初是为二值分类设计的,当处理多类问题时需要构造合适的多类分类器。
有下述几种方法:

  • 1.一对多(one-versus-rest)
  • 2.一对一(one-versus-one)
  • 3.层次支持向量机(SVM决策树):层次分类首先将所有类别分成两类,然后再将子类进一步分成两个次级子类,如此循环,知道得到一个单独的类别为止。

SVM优缺点

优点:

  • 用核函数代替了高维空间的非线性映射
  • 支持向量决定时间复杂,而不是数据集的大小
  • 结果易于理解
  • 泛化能力强(适用于小样本的学习)
  • 少数支持向量决定了最终结果,鲁棒性强(1.体现在增删非支持向量样本对模型无影响,2.支持向量样本集具有一定的鲁棒性3.有些成功的应用中,SVM方法对核的选取不敏感)
  • 它是一个凸优化问题,局部最优一定时全局最优
    缺点:
  • 对于参数以及核函数有依赖(将高维空间的复杂性的困难转为求核函数的困难)
  • SVM对大规模训练样本难以实施。
  • 解决多分类问题有点复杂

支持向量回归(SVR)

现在考虑回归问题.给定训练样本$D={(x_1,y_1),(x_2,y_2),….,(x_m,y_m)},y_i∈R$,希望学得一个回归模型,使得f(x)与y尽可能接近,w和b是待确定的模型参数。
假设我们能容忍f(x)与y之间最多有$\xi$的偏差,即当f(x)与y之间的差别绝对值大于$\xi$时才计算损失.
SVR的问题可形式化为

其中C是正则化常数,$l_c$是$\xi$不敏感损失函数

引入松弛变量$\theta_i和\hat{\theta}_i$可以将目标函数写成

可以类似求SVM的方式通过拉格朗日乘子法求得

scikit-learn里的SVM

分类

SVC、NuSVC和LinearSVC能在数据集中实现多元分类。
SVC和NuSVC是相似的方法。LinearSVC是另一个实现线性和函数的支持向量分类,记住LinearSVC不接受关键词kernel,因为它被假设为线性的。它也缺少一些SVC和NuSVC的成员比如support_.

SVC,NuSVC和LinearSVC将两个数组作为输入:[n_samples,n_featires]大小的数组X作为样本,[n_samples]大小的数组Y作为标签

1
2
3
4
5
6
7
8
9
>>> from sklearn import svm
>>> X = [[0, 0], [1, 1]]
>>> Y = [0, 1]
>>> clf = svm.SVC()
>>> clf.fit(X, Y)
SVC(C=1.0, cache_size=200, class_weight=None, coef0=0.0,
decision_function_shape='ovr', degree=3, gamma='auto', kernel='rbf',
max_iter=-1, probability=False, random_state=None, shrinking=True,
tol=0.001, verbose=False)

在拟合后,这个模型可以用来预测新值:

1
2
>>> clf.predict([[2., 2.]])
array([1])

SVMs决策函数取决于训练集的一些子集,称作支持向量。这些支持向量的部分特征可以在support_vectors_,support_和n_support找到

1
2
3
4
5
6
7
8
9
10
>>> # 获得支持向量
>>> clf.support_vectors_
array([[ 0., 0.],
[ 1., 1.]])
>>> # 获得支持向量的索引get indices of support vectors
>>> clf.support_
array([0, 1]...)
>>> # 为每一个类别获得支持向量的数量
>>> clf.n_support_
array([1, 1]...)

多分类

SVC和NuSVC为多分类实现了“one-versus-one”方法。如果n_class是类别的数量,那么n_class*(n_class-1)/2个分类器被重构,而且每一个从两个类别中训练数据。为了给其他分类器提供一致的交互,decision_function_shape允许聚合”one-against-one”分类器的结果成(n_samples,n_class)的大小到决策函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
>>> X = [[0], [1], [2], [3]]
>>> Y = [0, 1, 2, 3]
>>> clf = svm.SVC(decision_function_shape='ovo')
>>> clf.fit(X, Y)
SVC(C=1.0, cache_size=200, class_weight=None, coef0=0.0,
decision_function_shape='ovo', degree=3, gamma='auto', kernel='rbf',
max_iter=-1, probability=False, random_state=None, shrinking=True,
tol=0.001, verbose=False)
>>> dec = clf.decision_function([[1]])
>>> dec.shape[1] # 4 classes: 4*3/2 = 6
6
>>> clf.decision_function_shape = "ovr"
>>> dec = clf.decision_function([[1]])
>>> dec.shape[1] # 4 classes
4

另一方面,LinearSVC实现”one-vs-the rest”多类别策略,从而训练n类别的模型。如果只有两类,只训练一个模型:

1
2
3
4
5
6
7
8
9
>>> lin_clf = svm.LinearSVC()
>>> lin_clf.fit(X, Y)
LinearSVC(C=1.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)
>>> dec = lin_clf.decision_function([[1]])
>>> dec.shape[1]
4

记住LinearSVC也实现了可选择的多类别策略,通过使用选项multi_class=’crammer_singer’,所谓的多元SVM由Crammer和Singer明确表达,这个方法是一致的,对于one-vs-rest是不正确的。实际上,one-vs-rest分类通常受到青睐,因为结果大多数是相似的,但是运行时间却显著减少。

非均衡问题

这个问题期望给予某一类或者某个别样例能使用的关键词class_weight,它形如{class_label:value}的字典,value是浮点数大于0的值,把类class_label的参数C设置为C*value
SVC,NuSVC,SVR,NuSVR和OneClassSVM在fit方法中通过关键词sample_weight为单一样例实现权重weights。与class_weight相似,这些把第i个样例的参数C换成C×sample_weight[i].

回归

支持向量分类的方法可以被扩展用作解决回归问题,这个方法被称为支持向量回归。
支持向量分类生成的模型(前面介绍)只依赖于训练集的子集,因为构建模型的cost function不在乎边缘之外的训练点。类似的,支持向量回归生成的模型只依赖于训练集的子集,因为构建模型的cost function忽略任何接近于模型预测的训练数据

支持向量回归有三种形式:SVR,NuSVR和LinearSVR.在只考虑线性核的情况下,LinearSVR比SVR提供了更快的实现形式,然而比起SVR和LinearSVR,NuSVR实现一个稍微不同的构思。

分类的类别一样,fit方法会调用参数向量X,y,其中y是浮点数不是整数型。

1
2
3
4
5
6
7
8
9
>>> from sklearn import svm
>>> X = [[0, 0], [2, 2]]
>>> y = [0.5, 2.5]
>>> clf = svm.SVR()
>>> clf.fit(X, y)
SVR(C=1.0, cache_size=200, coef0=0.0, degree=3, epsilon=0.1, gamma='auto',
kernel='rbf', max_iter=-1, shrinking=True, tol=0.001, verbose=False)
>>> clf.predict([[1, 1]])
array([ 1.5])

核函数

初始化时,不同核由不同的函数名调用:

1
2
3
4
5
6
>>> linear_svc = svm.SVC(kernel='linear')
>>> linear_svc.kernel
'linear'
>>> rbf_svc = svm.SVC(kernel='rbf')
>>> rbf_svc.kernel
'rbf

自定义核

可以自定义核函数。
自定义核的分类器和别的分类器一样,除了下面几点:

  • 1.空间support_vectors_现在不是空的,只是支持向量的索引被存储在support_
  • 2.请把fit()模型中的第一个参数的引用(不是副本)存储为将来的引用。如果在fit()和predict()之间有数组发生改变,您将会碰到意外的结果。

用python函数作为核函数:

1
2
3
4
5
6
>>> import numpy as np
>>> from sklearn import svm
>>> def my_kernel(X, Y):
... return np.dot(X, Y.T)
...
>>> clf = svm.SVC(kernel=my_kernel)

常见的面试题整理:

  1. SVM的原理是什么?
    SVM是一种二分类模型.其基本模型是在特征空间中找到间隔最大的分离超平面的线性分类器(间隔最大是它有别于感知机)

  2. SVM为什么采用间隔最大化?
    当训练数据线性可分时,存在无穷个分离超平面可以将两类数据正确分开.而要求间隔最大化,不仅能使解唯一,而且更具鲁棒性。

  3. 为什么要将求解SVM的原问题转换为其对偶问题?
    (1)对偶问题往往更易于求解
    (2)自然引入核函数,进而推广到非线性分类问题
    (3) 对偶问题将原始问题中的约束转为了对偶问题中的等式约束

  4. 为什么要引入核函数?
    当样本在原始空间中线性不可分时,可以将样本从原始空间映射到一个更高维的特征空间,使得样本在这个特征空间内线性可分

  5. 为什么SVM对缺失数据敏感?(SVM对噪声并不鲁棒)
    这里说的缺失数据是指缺失某些特征数据,向量数据不完整.SVM没有处理缺失值的策略(决策树有)。SVM希望样本在特征空间中线性可分,所以特征空间的好坏对SVM的性能很重要.缺失特征数据会影响训练结果的好坏。

  6. SVM如何处理多分类问题?
    典型:一对一和一对多.

  7. SVM的优缺点
    见上面博客

参考文献

1.SVM实现多分类的三种方案
2.sklearn 支持向量机

-------------本文结束感谢您的阅读-------------

本文标题:机器学习(一)——SVM

文章作者:Yif Du

发布时间:2018年11月21日 - 00:11

最后更新:2019年05月24日 - 03:05

原始链接:http://yifdu.github.io/2018/11/21/机器机器学习(一)——SVM/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。