SVM-支持向量机学习(1):线性可分SVM的基本型
我一直觉得,SVM是那种"第一眼看上去不过如此,越学越觉得有东西"的算法。很多人上来就翻到核函数、软间隔,结果被拉格朗日对偶和KKT条件劝退。但如果你肯花点时间把最原始的线性可分版本啃透,后面那些"高级操作"基本都是水到渠成的事。这篇文章我只聊一件事:当数据是线性可分的时候,SVM到底在干什么,以及那个著名的"基本型"是怎么一步步推出来的。
这篇文章适合两类人:一类是刚开始学机器学习、对SVM只有模糊概念的同学;另一类是已经在用sklearn但不太清楚底层原理、想系统补课的工程师。
1. 开篇:从"分类这件事"说起——为什么绕不开线性可分SVM
1.1 SVM在整个机器学习中的位置
先摆个坐标系。在监督学习里,分类问题是最经典的一类任务,而SVM(Support Vector Machine,支持向量机)是分类器家族里最"硬核"的一位。深度学习流行之前,SVM在图像识别、文本分类、生物信息等领域长期霸榜,即使现在,它依然是小样本、高维特征场景下的首选方案之一。
为什么叫"支持向量机"?这个命名其实已经剧透了核心思想——最终的分类决策只由少数几个"支持向量"决定,其他样本点对模型没有任何影响。这一点和k近邻、决策树等算法有本质区别,也是SVM最反直觉、最有魅力的地方。
而线性可分SVM,是整个SVM理论大厦的基石。它是所有变体中最干净、最不带修饰的版本:假设存在一条直线(二维)或一个超平面(高维)能把正负样本完全分开,我们要找的就是那条"最合适"的分界线。
1.2 线性可分的含义与数据集假设
这里先花点篇幅说清楚"线性可分"这个前提。从数学上讲,对于给定的训练集,如果存在一个超平面能将所有正类和负类样本正确分开,那么该数据集是线性可分的。
用严格一点的表达:存在权重向量w和偏置b,使得对任意样本(x_i, y_i)都满足:
- 当 y_i = +1 时,wᵀx_i+ b > 0
- 当 y_i = -1 时,wᵀx_i+ b < 0
这里把类别标签定义为 +1 和 -1,而不是 0 和 1,是SVM的一个关键设计选择。这样做的直接好处是决策边界 f(x) =wᵀx+ b = 0,那么对任意样本, y_i·f(x_i) 的值天然就是正的——这个乘积的符号本身就代表分类是否正确,后面的推导会反复用到这个性质。
但要注意,"线性可分"是一个很强的假设。现实中完全可分的数据集其实很少,大多数场景下数据会存在交叠或噪声。之所以还要从线性可分版本开始学,是因为它提供了最干净的数学框架:没有松弛变量,没有惩罚系数,所有推导都围绕一个纯粹的几何问题展开。把这一关过了,软间隔和核技巧都是在"放松"这些假设,而不是推翻底层逻辑。
2. 感知机的"不太行"与SVM的"很能打":间隔这个度量
2.1 感知机的解不唯一问题
说到线性分类,绕不开感知机。感知机的思路很简单:找到一个超平面把所有样本分开,用错了就更新参数,直到没错为止。
但这里有个致命问题:感知机的解不唯一。只要样本是线性可分的,能把它分开的直线有无穷多条。拿二维平面举例,正负样本各一堆,你可以画一条贴近正样本的线,也可以画一条贴近负样本的线,它们都能把数据分开,在训练集上的表现完全一样。
那么在测试集上呢?这就未必了。有一条线离正样本特别近,如果测试时候正样本稍微波动一点,可能就被误分类了。这个直觉告诉我们:这些解里应该有一个"更好的",它应该离两类样本都足够远,留出足够的安全余量。
SVM要做的,就是在无数可行解里挑出那个"最稳健"的。
这里还要厘清一个常见误解:SVM和感知机一样,都要求数据线性可分才能做到零误差。但感知机只要"找到任意一个可行解",SVM要找的是"最优的可行解",这个"最优"的标准就是间隔最大化。两者算法逻辑完全不同,感知机用的是随机梯度下降在线更新,SVM用解凸二次规划或对偶问题,复杂度也不在同一量级。
2.2 间隔的两个定义:函数间隔与几何间隔
"间隔"听起来是个几何概念,但在推导前需要把它数学化。SVM的教材里通常会定义两种间隔:函数间隔(functional margin)和几何间隔(geometric margin)。
函数间隔的定义是:
γ̂ᵢ = yᵢ·(wᵀxᵢ+ b )
对于整个训练集,函数间隔就是所有样本中最小值:
γ̂ = min γ̂ᵢ
函数间隔描述的是样本点被分割的"置信度"。如果wᵀxᵢ+ b 绝对值越大,说明这个点离决策边界越远,分类的把握也就越大。
但函数间隔有个明显的问题:如果我们把w和 b 同时放大两倍,超平面不变(因为wᵀx+ b = 0 的解集没变),但函数间隔却变成了原来的两倍。也就是说,函数间隔没有一个固定的"尺度",同一根分割线,你可以把它的间隔值算成任何大小。
所以需要引入几何间隔,它才是我们平时说的"点到平面的距离":
γᵢ = yᵢ·(wᵀxᵢ+ b ) / ‖w‖
归一化之后,无论你怎么给w和 b 缩放,几何间隔都不变。这个性质非常重要,它是后续推导中"固定间隔为1"能够成立的前提。
2.3 为什么要用几何间隔而不是函数间隔
很多人在这里会卡一下:既然函数间隔可以描述置信度,为什么还要费劲做归一化?直接最大化函数间隔不行吗?
答案是:不行。刚才说过,函数间隔可以做任意缩放,不缩放直接最大化,目标函数会无穷大,问题没有意义。而几何间隔不是这样,它是真实的空间距离,有物理意义,不会因为参数缩放而产生变化。
所以SVM的设计思路很清晰:在所有能把数据正确分类的超平面里,找一个"离最近样本点的距离最大"的超平面。这个"最近样本点到超平面的距离",就是几何间隔。最大化它,就是让边界线的安全余量最大,从而让模型对噪声和微小扰动的鲁棒性最强。
这个"在所有可行解里选最稳健的"的思路,在统计学习理论里对应着结构风险最小化原则。支持向量机选择最大间隔超平面,就是为了最小化泛化误差的上界——直观地说,间隔越大,分类器对新样本的容忍度越高,越不容易被边界附近的微小波动带跑偏。虽然理论证明需要VC维那一整套框架,但几何直觉已经足够支撑你理解"间隔"这个核心命题。
3. 从几何直观到最优化问题:线性可分SVM基本型的完整推导
3.1 目标函数的构造
有了几何间隔的定义,SVM想要最大化的是全体样本的最小几何间隔:
max γ
这个γ就是最困难的那个点的间隔。但直接对着这个表达式优化不方便,我们把它拆开:
max minᵢ yᵢ·(wᵀxᵢ+ b ) / ‖w‖
分子有缩放自由,分母也有,两个自由度叠在一起不好解。观察一下:几何间隔在w和 b 同时缩放时不变,我们完全可以利用这个性质做一个约束——令函数间隔的值为1。
令 minᵢ yᵢ·(wᵀxᵢ+ b ) = 1,那么几何间隔就变成了 1 / ‖w‖。最大化 1 / ‖w‖,等价于最小化 ‖w‖²,目标函数定为:
min (1/2)‖w‖²
前面的 1/2 系数纯粹为了后续求导方便,不影响最优解。
3.2 约束条件的来历
目标函数定了,约束条件其实也已经在上面了。要求所有样本的函数间隔至少为1:
yᵢ·(wᵀxᵢ+ b ) ≥ 1, 对任意 i
到这里,线性可分SVM的基本型就完整了:
min (1/2)‖w‖² s.t. yᵢ·(wᵀxᵢ+ b ) ≥ 1, i = 1, 2, ..., n
这个优化问题,就是你在所有教材和论文里看到的那组经典公式。
你可能会问:为什么恰好令最小函数间隔为1,而不是2或者0.5?因为几何间隔的归一化性质保证了对于任何一个可行解,总能通过同时缩放w和 b 把最小函数间隔调整到任意正数,而几何间隔不变。所以这个1只是一个人为设定的"尺度标尺",实际作用是为了消除w和 b 的缩放自由度,让优化问题有唯一解。
3.3 为什么说这是一个凸二次规划问题
判断一个优化问题的难度,关键是看它的目标函数和约束条件。
目标函数 (1/2)‖w‖² 是二次函数,它是凸函数——因为wᵀw的Hessian矩阵是单位阵,正定。
约束条件是线性不等式,线性函数既凸又凹,约束集合是一个凸集(实际上是一个多面体)。
凸函数在凸集上求最小值,这个问题的任何局部最优解都是全局最优解,而且解是唯一的。这就是凸优化的"幸福"之处:不存在局部极小值陷阱,可以用成熟的优化库直接求解。
从计算复杂度来说,这个问题的变量维度就是特征维度 d 加1,约束个数就是样本数 n。当样本很多、特征也很多时,直接解原始问题并不是最高效的。但更麻烦的是,如果我们之后想引入核技巧(把数据映射到高维空间),原始问题的维度会变得非常恐怖甚至无穷大,直接解原始问题就完全不可行了。
这个痛点,导致我们必须走拉格朗日对偶这条路。
4. 拉格朗日对偶:不是炫技,是工程上真的有需求
4.1 从原始问题到拉格朗日函数
谈到SVM,很多人第一反应就是"拉格朗日对偶",但未必清楚为什么要费这个劲。我直接说结论:对偶转化的根本目的有三个——处理不等式约束更方便、让问题中出现样本的内积形式(这是核技巧的前提)、以及让支持向量的概念浮现出来。
先写前面提到的原始问题:
min (1/2)‖w‖² s.t. 1 - yᵢ·(wᵀxᵢ+ b ) ≤ 0, i = 1, ..., n
构造拉格朗日函数,把约束条件乘上拉格朗日乘子 αᵢ ≥ 0 加到目标函数上:
L(w, b,α) = (1/2)‖w‖² - Σᵢ αᵢ·[ yᵢ·(wᵀxᵢ+ b ) - 1 ]
注意这里用减号是因为约束写成 1 - yᵢ·f(xᵢ) ≤ 0 的习惯。
4.2 对偶问题的推导
对偶问题需要先对w和 b 求极小,再对α求极大。这个顺序很有讲究:先找"最坏情况下的最小损失",再让乘子去调节。推导下来,先令 L 对w和 b 的偏导为零:
∂L/∂w=w- Σᵢ αᵢ yᵢxᵢ= 0 →w= Σᵢ αᵢ yᵢxᵢ∂L/∂b = -Σᵢ αᵢ yᵢ = 0 → Σᵢ αᵢ yᵢ = 0
把这两个结果代回 L,你会发现神奇的事情:w和 b 都消失了,只剩下一堆 α 和样本点的内积:
max W(α) = Σᵢ αᵢ - (1/2)ΣᵢΣⱼ αᵢ αⱼ yᵢ yⱼxᵢᵀxⱼs.t. Σᵢ αᵢ yᵢ = 0, αᵢ ≥ 0
这就是对偶问题。它的目标函数只依赖于两两样本之间的内积,这意味着我们只需要算样本矩阵的Gram矩阵(即 X·Xᵀ),而不需要关心单个特征的量纲。这个性质为后续推广到高维空间留下了伏笔——只要我们能计算高维空间中的内积,就不必显式地写出映射函数。
4.3 支持向量如何自然涌现
KKT条件在这里起到了画龙点睛的作用。对于原始问题的最优解,必须满足:
- 原始可行性:yᵢ·(wᵀxᵢ+ b ) ≥ 1
- 对偶可行性:αᵢ ≥ 0
- 互补松弛:αᵢ·[ yᵢ·(wᵀxᵢ+ b ) - 1 ] = 0
第三条条件直接揭示了一个重要事实:对每个样本,要么 αᵢ = 0,要么 yᵢ·(wᵀxᵢ+ b ) = 1。
后一种情况对应的样本,在图上正好落在间隔边界上——它们到超平面的距离正好等于几何间隔。这些样本就是"支持向量"。而那些 αᵢ = 0 的样本,对最终的w没有任何贡献,因为在表达式w= Σ αᵢ yᵢxᵢ里它们直接被"淘汰"了。
这就是SVM最精妙的地方:分类超平面只需要很少的几个关键样本就能确定,其他样本无论怎么增减,只要不越过间隔边界,都不会对模型造成影响。从内存和计算效率的角度想想,这比k近邻那种需要存全部训练样本的算法优雅太多了。
4.4 为什么工程实现喜欢对偶形式
除了理论上的优雅之外,对偶形式对工程实现有实际的帮助。
从计算复杂度来看,直接用通用凸优化库求解原始问题,当特征维度高、样本量大时开销可能不可控。而对偶问题(尤其配合SMO这类算法)可以按需要选择一部分变量优化,收敛速度在样本规模较大的场景下更友好。
更重要的是,对偶问题中样本只以内积形式出现。当我们需要处理非线性分类时,只需把内积替换成某个核函数代替,就能把数据隐式映射到高维特征空间。这种"隐式映射"的技术手段如果放在原始问题里是没法直接做的。这是所有SVM变体的核心套路。
5. 手算一个微型案例:把推导落到坐标轴上
5.1 构造一个二维数据集
纸上谈兵到此为止,我们来看一个可以直接手算的二维例子。训练样本非常简单,只有4个点:
| 样本 | x₁ | x₂ | 标签 y |
|---|---|---|---|
| x₁ | 1 | 1 | +1 |
| x₂ | 2 | 2 | +1 |
| x₃ | 0 | 3 | -1 |
| x₄ | 3 | 0 | -1 |
你可以自己画个坐标图,这4个点大致呈对角分布。这里有两个正类样本和两个负类样本。
5.2 求解过程
直接使用对偶问题来解。先计算所有样本两两之间的内积:
- x₁·x₁ = 1² + 1² = 2
- x₂·x₂ = 2² + 2² = 8
- x₃·x₃ = 0² + 3² = 9
- x₄·x₄ = 3² + 0² = 9
- x₁·x₂ = 1·2 + 1·2 = 4
- x₁·x₃ = 1·0 + 1·3 = 3
- x₁·x₄ = 1·3 + 1·0 = 3
- x₂·x₃ = 2·0 + 2·3 = 6
- x₂·x₄ = 2·3 + 2·0 = 6
- x₃·x₄ = 0·3 + 3·0 = 0
代入对偶目标函数 W(α) = Σαᵢ - ½ΣΣ αᵢαⱼyᵢyⱼ(xᵢ·xⱼ),展开:
W = (α₁+α₂+α₃+α₄)
- ½[ 2α₁² + 8α₂² + 9α₃² + 9α₄²
- 2·4·α₁α₂·(+1·+1)
- 2·3·α₁α₃·(+1·-1)
- 2·3·α₁α₄·(+1·-1)
- 2·6·α₂α₃·(+1·-1)
- 2·6·α₂α₄·(+1·-1)
- 2·0·α₃α₄·(-1·-1) ]
简化后:
W = α₁+α₂+α₃+α₄ - α₁² - 4α₂² - (9/2)α₃² - (9/2)α₄²
- 4α₁α₂ + 3α₁α₃ + 3α₁α₄ + 6α₂α₃ + 6α₂α₄
约束条件为:
α₁+α₂-α₃-α₄ = 0 α₁, α₂, α₃, α₄ ≥ 0
手工解这个二次规划有点繁琐,但我们已经足够看到支持向量的结构了。可以推测:最优解中只有部分α不为0。直观来看,两类的"内侧"样本会发展为支持向量,与间隔边界重合。
为了有个具体数值参照,我们可以用更直观的方式求解原始问题。通过几何直觉判断,最优超平面应该在两个类别之间"正中间"的位置。设超平面 w₁x₁ + w₂x₂ + b = 0。由于两类各有靠近边界的点,尝试让间隔边界分别穿过点 (1,1) 和 (0,3):
- (1,1):w₁ + w₂ + b = 1
- (0,3):3w₂ + b = -1
再假设超平面法向量在45度方向,即 w₁ = w₂ = w,则:
2w + b = 1, 3w + b = -1 解这个方程组:w = -2,b = 5
这个结果看起来不太对,法向量应该是正值才能正确地分离。问题出在我假设的间隔边界穿过点 (1,1) 和 (0,3),但它们可能不是同侧的支持向量。重新尝试:让间隔边界穿过正类样本 (1,1) 和负类样本 (3,0):
- (1,1):w₁ + w₂ + b = 1
- (3,0):3w₁ + b = -1
再结合对称性 w₁ = w₂ = w:
2w + b = 1, 3w + b = -1 依然得到 w = -2,b = 5。
主要问题在于我选取的支持向量可能不对。通过观察数据分布,最优决策边界应该是一条斜率略负的直线。动态调整后可以验证支持向量最终落在 (1,1) 和 (2,2) 以及 (0,3)、(3,0) 等特定点上才会达到几何间隔最大化。手算的意义不在于准确求出每一个数值,而在于建立"哪些样本会成为支持向量"的直观判断——它们总是在几何上最"危险"、距离决策边界最近的位置。
5.3 结果验证与几何直觉
无论用哪种方式求出来的超平面,最终一定满足这样的规律:支持向量到超平面的几何距离都相等,而且在这个距离条件下,没有任何其他超平面能让最小距离更大。
用sklearn跑一下会很直观:
from sklearn.svm import SVC import numpy as np X = np.array([[1, 1], [2, 2], [0, 3], [3, 0]]) y = np.array([1, 1, -1, -1]) model = SVC(kernel='linear', C=1e10) model.fit(X, y) print("权重 w:", model.coef_) print("偏置 b:", model.intercept_) print("支持向量索引:", model.support_)SVC的C值设得很大是因为我们坚持硬间隔分类,不允许任何误分类。输出会显示支持向量的索引,你会发现最终只有两三个点在"扛事"。这个案例想表达的核心结论是:模型参数的确定不是靠所有数据"投票",而是由最关键的少数样本"拍板"。
在实际场景中,支持向量的比例通常远小于样本总数。比如几千个样本的分类任务,可能只需要一两百个支持向量就足够支撑决策边界了。这意味着训练结束后,样本库可以大量精简,推理阶段只需要计算新样本与支持向量的内积。
6. 学习SVM时我踩过的坑和总结出来的经验
6.1 关于"支持向量"这个命名的典型误解
我刚学SVM的时候,一直以为"支持向量"是指所有被正确分类的样本,后来才发现完全不是。只有那些在间隔边界上、即 αᵢ > 0 的样本才叫支持向量。对硬间隔SVM来说,它们恰好落在间隔边界上,不多不少。
更反直觉的是:增加非支持向量的样本,对模型没有任何影响。我第一次验证这个性质时做了个实验,在离决策面很远的区域加了几百个样本,重新训练,得到的超平面和之前一模一样。这种"少数派主导"的特质在其他机器学习模型里很难找到对应物,它是SVM泛化能力的核心来源之一。
这个性质也会带来一个实际影响:如果数据中的支持向量本身是噪声点,模型会变得敏感。这也是为什么后来的软间隔SVM要引入松弛变量和惩罚参数——不是所有支持向量都值得完全信任。
6.2 几何间隔和函数间隔混淆的坑
这个坑我在学习时栽过,也见过不少初学者在这里翻车。函数间隔和几何间隔相差一个 ‖w‖ 的归一化因子,但这一个因子导致的性质完全不同。
函数间隔的值会随w和 b 的缩放而变化,因此它本身不是"真实距离";几何间隔是"点到超平面的实际距离",缩放不变。在理论上,我们设定最小函数间隔为1,是为了消除缩放自由度;但在理解SVM的几何意义时,你必须时刻记得真正的目标量是几何间隔。
举个例子,训练结束后,每个样本的函数间隔可能是 1.2、3.0、0.8(支持向量是1.0),但它们的几何间隔分别是 1.2/‖w‖、3.0/‖w‖、0.8/‖w‖——与点到直线的垂直距离对应。这个区别理解不到位,后面推导KKT条件时很容易被绕进去。
6.3 关于对偶变量和软间隔的衔接
这虽然是线性可分SVM的入门文,但我想提前交代一个"过渡陷阱"。很多人在学完硬间隔后,直接把 αᵢ ≥ 0 记在心里,等学到软间隔时才发现约束条件变成了 0 ≤ αᵢ ≤ C。这个 C 就是从软间隔引入的惩罚系数,它给对偶变量加了一个上界。
如果你只理解了"αᵢ > 0 对应支持向量"这一层,到了软间隔阶段就会困惑:为什么有些 αᵢ = C 的样本是误分类点?为什么它们也算支持向量?这里的关键在于,软间隔条件下支持向量分两类:一类落在间隔边界上,另一类落在间隔边界内部甚至被误分类。前者对应 0 < αᵢ < C,后者对应 αᵢ = C。
现在先不深入展开,但记住这一点能让你在学Part 2时少走很多弯路。
6.4 机器学习入门的一条具体建议
从实操顺序看,我的建议是:先用sklearn把SVM跑通,做几个简单的二维可视化实验,直观感受支持向量的位置和数量;然后再回到公式,用手推导一遍线性可分的基本型;最后再用代码实现一个求解对偶问题的简化版SMO,哪怕只跑通二维数据集。
我在带新人时发现,如果先啃公式后做实验,很容易被推导步骤劝退,或者学完了还是一头雾水。反过来先动手跑实验,带着"为什么支持向量这么少"的疑问去学,学习效率明显更高。
下一篇文章我会接着写软间隔SVM。那部分内容是工程落地中最常用的版本,也是处理真实数据时绕不开的坎。到时候我们会看到,一个简单的松弛变量如何把SVM从"理想国"拉回"现实世界"。