<audio id="audio" title="22丨SVM（上）：如何用一根棍子将蓝红两色球分开？" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/9d/49/9dedf904e39f47c1865d15ab91b18d49.mp3"></audio>

今天我来带你进行SVM的学习，SVM的英文叫Support Vector Machine，中文名为支持向量机。它是常见的一种分类方法，在机器学习中，SVM是有监督的学习模型。

什么是有监督的学习模型呢？它指的是我们需要事先对数据打上分类标签，这样机器就知道这个数据属于哪个分类。同样无监督学习，就是数据没有被打上分类标签，这可能是因为我们不具备先验的知识，或者打标签的成本很高。所以我们需要机器代我们部分完成这个工作，比如将数据进行聚类，方便后续人工对每个类进行分析。SVM作为有监督的学习模型，通常可以帮我们模式识别、分类以及回归分析。

听起来，是不是很高大上。我先带你做个小练习。

练习1：桌子上我放了红色和蓝色两种球，请你用一根棍子将这两种颜色的球分开。

<img src="https://static001.geekbang.org/resource/image/21/88/210bc20b5963d474d425ad1ca9ac6888.jpg" alt=""><br>
你可以很快想到解决方案，在红色和蓝色球之间画条直线就好了，如下图所示：

<img src="https://static001.geekbang.org/resource/image/ec/bb/ec657a6d274b5afb4d8169df9c248abb.jpg" alt=""><br>
练习2：这次难度升级，桌子上依然放着红色、蓝色两种球，但是它们的摆放不规律，如下图所示。如何用一根棍子把这两种颜色分开呢？

<img src="https://static001.geekbang.org/resource/image/b4/a3/b4b9793cdec47d0ea1528ff1922973a3.jpg" alt=""><br>
你可能想了想，认为一根棍子是分不开的。除非把棍子弯曲，像下面这样：

<img src="https://static001.geekbang.org/resource/image/14/eb/144d72013a808e955e78718f6df3d2eb.jpg" alt=""><br>
所以这里直线变成了曲线。如果在同一个平面上来看，红蓝两种颜色的球是很难分开的。那么有没有一种方式，可以让它们自然地分开呢？

这里你可能会灵机一动，猛拍一下桌子，这些小球瞬间腾空而起，如下图所示。在腾起的那一刹那，出现了一个水平切面，恰好把红、蓝两种颜色的球分开。

<img src="https://static001.geekbang.org/resource/image/9e/dc/9e07b78932456cd8a6f46d7ea65bdadc.png" alt="">

<img src="https://static001.geekbang.org/resource/image/f3/34/f3497cd97c8bb06e952efcdae6059434.jpg" alt=""><br>
在这里，二维平面变成了三维空间。原来的曲线变成了一个平面。这个平面，我们就叫做超平面。

## SVM的工作原理

用SVM计算的过程就是帮我们找到那个超平面的过程，这个超平面就是我们的SVM分类器。

我们再过头来看最简单的练习1，其实我们可以有多种直线的划分，比如下图所示的直线A、直线B和直线C，究竟哪种才是更好的划分呢？

<img src="https://static001.geekbang.org/resource/image/74/e7/7459de7fbe99af85cbfdacf2333f84e7.jpg" alt=""><br>
很明显图中的直线B更靠近蓝色球，但是在真实环境下，球再多一些的话，蓝色球可能就被划分到了直线B的右侧，被认为是红色球。同样直线A更靠近红色球，在真实环境下，如果红色球再多一些，也可能会被误认为是蓝色球。所以相比于直线A和直线B，直线C的划分更优，因为它的鲁棒性更强。

那怎样才能寻找到直线C这个更优的答案呢？这里，我们引入一个SVM特有的概念：**分类间隔**。

实际上，我们的分类环境不是在二维平面中的，而是在多维空间中，这样直线C就变成了决策面C。

在保证决策面不变，且分类不产生错误的情况下，我们可以移动决策面C，直到产生两个极限的位置：如图中的决策面A和决策面B。极限的位置是指，如果越过了这个位置，就会产生分类错误。这样的话，两个极限位置A和B之间的分界线C就是最优决策面。极限位置到最优决策面C之间的距离，就是“分类间隔”，英文叫做margin。

如果我们转动这个最优决策面，你会发现可能存在多个最优决策面，它们都能把数据集正确分开，这些最优决策面的分类间隔可能是不同的，而那个拥有“最大间隔”（max margin）的决策面就是SVM要找的最优解。

<img src="https://static001.geekbang.org/resource/image/50/ea/506cc4b85a9206cca12048b29919a7ea.jpg" alt=""><br>
**点到超平面的距离公式**

在上面这个例子中，如果我们把红蓝两种颜色的球放到一个三维空间里，你发现决策面就变成了一个平面。这里我们可以用线性函数来表示，如果在一维空间里就表示一个点，在二维空间里表示一条直线，在三维空间中代表一个平面，当然空间维数还可以更多，这样我们给这个线性函数起个名称叫做“超平面”。超平面的数学表达可以写成：

<img src="https://static001.geekbang.org/resource/image/76/28/765e87a2d9d6358f1274478dacbbce28.png" alt=""><br>
在这个公式里，w、x是n维空间里的向量，其中x是函数变量；w是法向量。法向量这里指的是垂直于平面的直线所表示的向量，它决定了超平面的方向。

**SVM就是帮我们找到一个超平面**，这个超平面能将不同的样本划分开，同时使得样本集中的点到这个分类超平面的最小距离（即分类间隔）最大化。

在这个过程中，**支持向量**就是离**分类超平面**最近的样本点，实际上如果确定了支持向量也就确定了这个超平面。所以支持向量决定了分类间隔到底是多少，而在最大间隔以外的样本点，其实对分类都没有意义。

所以说， SVM就是求解最大分类间隔的过程，我们还需要对分类间隔的大小进行定义。

首先，我们定义某类样本集到超平面的距离是这个样本集合内的样本到超平面的最短距离。我们用di代表点xi到超平面wxi+b=0的欧氏距离。因此我们要求di的最小值，用它来代表这个样本到超平面的最短距离。di可以用公式计算得出：

<img src="https://static001.geekbang.org/resource/image/83/76/8342b5253cb4c294c72cef6802814176.png" alt=""><br>
其中||w||为超平面的范数，di的公式可以用解析几何知识进行推导，这里不做解释。

**最大间隔的优化模型**

我们的目标就是找出所有分类间隔中最大的那个值对应的超平面。在数学上，这是一个凸优化问题（凸优化就是关于求凸集中的凸函数最小化的问题，这里不具体展开）。通过凸优化问题，最后可以求出最优的w和b，也就是我们想要找的最优超平面。中间求解的过程会用到拉格朗日乘子，和KKT（Karush-Kuhn-Tucker）条件。数学公式比较多，这里不进行展开。

## 硬间隔、软间隔和非线性SVM

假如数据是完全的线性可分的，那么学习到的模型可以称为硬间隔支持向量机。**换个说法，硬间隔指的就是完全分类准确，不能存在分类错误的情况。软间隔，就是允许一定量的样本分类错误**。

我们知道，实际工作中的数据没有那么“干净”，或多或少都会存在一些噪点。所以线性可分是个理想情况。这时，我们需要使用到软间隔SVM（近似线性可分），比如下面这种情况：

<img src="https://static001.geekbang.org/resource/image/7c/a6/7c913ee92cdcf4d461f0ebc1314123a6.jpg" alt=""><br>
另外还存在一种情况，就是非线性支持向量机。

比如下面的样本集就是个非线性的数据。图中的两类数据，分别分布为两个圆圈的形状。那么这种情况下，不论是多高级的分类器，只要映射函数是线性的，就没法处理，SVM 也处理不了。这时，我们需要引入一个新的概念：**核函数。它可以将样本从原始空间映射到一个更高维的特质空间中，使得样本在新的空间中线性可分**。这样我们就可以使用原来的推导来进行计算，只是所有的推导是在新的空间，而不是在原来的空间中进行。

<img src="https://static001.geekbang.org/resource/image/55/23/5530b0e61085a213ef1d0dfe02b70223.jpg" alt=""><br>
所以在非线性SVM中，核函数的选择就是影响SVM最大的变量。最常用的核函数有线性核、多项式核、高斯核、拉普拉斯核、sigmoid核，或者是这些核函数的组合。这些函数的区别在于映射方式的不同。通过这些核函数，我们就可以把样本空间投射到新的高维空间中。

当然软间隔和核函数的提出，都是为了方便我们对上面超平面公式中的w*和b*进行求解，从而得到最大分类间隔的超平面。

## 用SVM如何解决多分类问题

SVM本身是一个二值分类器，最初是为二分类问题设计的，也就是回答Yes或者是No。而实际上我们要解决的问题，可能是多分类的情况，比如对文本进行分类，或者对图像进行识别。

针对这种情况，我们可以将多个二分类器组合起来形成一个多分类器，常见的方法有“一对多法”和“一对一法”两种。

1.一对多法

假设我们要把物体分成A、B、C、D四种分类，那么我们可以先把其中的一类作为分类1，其他类统一归为分类2。这样我们可以构造4种SVM，分别为以下的情况：

（1）样本A作为正集，B，C，D作为负集；

（2）样本B作为正集，A，C，D作为负集；

（3）样本C作为正集，A，B，D作为负集；

（4）样本D作为正集，A，B，C作为负集。

这种方法，针对K个分类，需要训练K个分类器，分类速度较快，但训练速度较慢，因为每个分类器都需要对全部样本进行训练，而且负样本数量远大于正样本数量，会造成样本不对称的情况，而且当增加新的分类，比如第K+1类时，需要重新对分类器进行构造。

2.一对一法

一对一法的初衷是想在训练的时候更加灵活。我们可以在任意两类样本之间构造一个SVM，这样针对K类的样本，就会有C(k,2)类分类器。

比如我们想要划分A、B、C三个类，可以构造3个分类器：

（1）分类器1：A、B；

（2）分类器2：A、C；

（3）分类器3：B、C。

当对一个未知样本进行分类时，每一个分类器都会有一个分类结果，即为1票，最终得票最多的类别就是整个未知样本的类别。

这样做的好处是，如果新增一类，不需要重新训练所有的SVM，只需要训练和新增这一类样本的分类器。而且这种方式在训练单个SVM模型的时候，训练速度快。

但这种方法的不足在于，分类器的个数与K的平方成正比，所以当K较大时，训练和测试的时间会比较慢。

## 总结

今天我给你讲了SVM分类器，它在文本分类尤其是针对二分类任务性能卓越。同样，针对多分类的情况，我们可以采用一对多，或者一对一的方法，多个二值分类器组合成一个多分类器。

另外关于SVM分类器的概念，我希望你能掌握以下的三个程度：

<li>
完全线性可分情况下的线性分类器，也就是线性可分的情况，是最原始的SVM，它最核心的思想就是找到最大的分类间隔；
</li>
<li>
大部分线性可分情况下的线性分类器，引入了软间隔的概念。软间隔，就是允许一定量的样本分类错误；
</li>
<li>
线性不可分情况下的非线性分类器，引入了核函数。它让原有的样本空间通过核函数投射到了一个高维的空间中，从而变得线性可分。
</li>

在SVM的推导过程中，有大量的数学公式，这里不进行推导演绎，因为除了写论文，你大部分时候不会用到这些公式推导。

所以最重要的还是理解我上面讲的这些概念，能在实际工作中使用SVM才是最重要的。下一节我会和你讲如何用sklearn工具包进行SVM分类，带你做一个实际的案例。

最后，你能说一下你对有监督学习和无监督学习的理解吗？以及，SVM最主要的思想就是硬间隔、软间隔和核函数。你是如何理解它们的？

欢迎你在评论区进行留言，与我分享你的答案。也欢迎点击“请朋友读”，把这篇文章分享给你的朋友或者同事。


