把 SVM 写成「hinge 之和 加 lambda 乘 ||w|| 平方」,用次梯度法直接最小化, 和 C 等于 1 除以 2 lambda 的 SMO 解对比: 夹角 0.064 度与 0.042 度,目标值 19.676817 对 19.687858。 四种损失并排:margin 等于负 1 时,hinge 是 2.00、ramp 封顶在 1.00、平方损失是 4.000。 hinge 之和对 C 单调下降(18.738、18.047、16.855),可训练错数是 7、8、7——上界降了, 错数在它下面来回晃。
前四节一直在讲几何:最大间隔、支持向量、核映射。这一节把镜头翻过来,只看损失函数。 结果会让人有点意外:那套几何推导最后落到的东西, 就是「hinge 损失 加上 L2 正则」——和第 4 章的岭回归是同一套语言。 两种说法各有各的用处。 几何说法告诉你「为什么解只由边界附近的点决定」,损失说法告诉你「为什么它对异常点不鲁棒、 该怎么改」。
一、四种损失并排
margin 是标签乘输出。四种损失在几个关键位置上的取值:
| margin | 0/1 损失 | hinge | ramp | 平方损失 |
|---|---|---|---|---|
| 负 1.00 | 1.00 | 2.00 | 1.00 | 4.000 |
| 负 0.25 | 1.00 | 1.25 | 1.00 | 1.562 |
| 0.00 | 0.00 | 1.00 | 1.00 | 1.000 |
| 0.50 | 0.00 | 0.50 | 0.50 | 0.250 |
| 1.00 | 0.00 | 0.00 | 0.00 | 0.000 |
| 2.00 | 0.00 | 0.00 | 0.00 | 1.000 |
| 第一件要确认的事:hinge 那一列每一行都不小于 0/1 那一列。 | ||||
| 逐行比过去,没有例外。这就是「hinge 是 0/1 的上界」。 | ||||
| 上界这个性质给了优化的合法性:把上界压小,真正的错误数不可能比它更大。 | ||||
| 第二件:平方损失在 margin 大于 1 之后反而涨起来了(1.5 处 0.250,2.0 处 1.000)。 | ||||
| 因为它要的是「输出接近正负 1」,不是「分对就行」。 | ||||
| 第 7 章那个错标点被它放大,根子就在这儿——一个输出到 3 的点,残差平方是 4。 | ||||
| 第三件:ramp 在 margin 小于 0 之后一直平在 1.00。 | ||||
| 这个「封顶」是它抗噪的全部秘密,8.6 专门讲。 | ||||
| 这里先记住一点:hinge 在负方向上没有上限,一个 margin 等于负 6 的点罚 7, | ||||
| 它的梯度还一直是满的——它有无限的话语权。 |
二、实测:上界降了,错数不一定降
把 C 从 0.5 扫到 10,看 hinge 之和和训练错数:
| C | hinge 之和 | 训练错数 | ||w|| | 支持向量 | |---:|---:|---:|---:|---:| | 0.5 | 18.738 | 7 | 1.5423 | 25 | | 1 | 18.047 | 8 | 1.8056 | 23 | | 10 | 16.855 | 7 | 3.1030 | 19 | hinge 之和是三行单调下降的:18.738、18.047、16.855。 可训练错数那一列是 7、8、7——不是单调的,C 从 0.5 加到 1 反而多错了一个。 这就是「上界」的代价:上界一直在降,真正的错数可以在它下面来回晃。 上界降了不等于错数会降,只是保证错数不会超过它。 这个现象值得停一下。 如果 hinge 是「完美的代理」,压小它就应该压小错数。 可它不是完美的,它是一个上界,上界松的地方可以容纳错数的波动。 这也解释了为什么调 C 的时候准确率经常「不动」——8.2 那张表里五个 C 全在 95.0% 到 95.6% 之间。 另一条线:||w|| 从 1.5423 涨到 3.1030。 C 大就是「hinge 权重高」,模型宁可把间隔挤窄也要少违反; C 小就是「正则权重高」,宁可放过几个点也要保住间隔。 这个旋钮和第 4 章岭回归里那个惩罚系数是同一个东西,只是换了损失。
三、实测:两种说法是同一件事
把 C 语言翻译成 lambda 语言:最小化「||w|| 平方的一半 加 C 乘 hinge 之和」,两边同除以 2C, 就变成「hinge 之和 加 lambda 乘 ||w|| 平方」,其中 lambda 等于 1 除以 2C。 这两个问题的最优点完全一样,只是把同一个函数乘了个正数。 验证办法:用次梯度法直接最小化第二个式子(它没有约束,可以直接做梯度下降), 再和 C 等于 1 除以 2 lambda 的 SMO 解比。
| C | lambda | SMO 的 w | SMO 目标 | 次梯度的 w | 次梯度目标 | 夹角 |
|---|---|---|---|---|---|---|
| 1 | 0.5000 | (1.1463, 1.3950) | 19.676817 | (1.1524, 1.3993) | 19.687858 | 0.064 度 |
| 10 | 0.0500 | (1.9382, 2.4232) | 17.335962 | (1.9368, 2.4179) | 17.338723 | 0.042 度 |
| 两列 w 的前几位对得上(1.1463 对 1.1524,1.9382 对 1.9368),夹角 0.06 度和 0.04 度, | ||||||
| 目标值只差千分之几。 | ||||||
| 这就把两种说法焊在一起了:最大间隔的那个解,就是「hinge 加 L2」那个目标的最小点。 | ||||||
| 差的那千分之几不是理论问题,是次梯度法只跑到这个精度。 | ||||||
| 它的收敛速度是「迭代次数的倒数」,四万轮能到这个程度已经不错了; | ||||||
| 要更准得换更好的解法,但方向已经证明清楚。 | ||||||
| 顺带说一句:这也解释了为什么实际实现都用 SMO 而不是梯度下降——hinge 不可导, | ||||||
| 梯度法只能靠次梯度慢慢磨。 |
四、这个视角换来什么
第一个好处:能看清 SVM 和别的方法的关系。 把 hinge 换成交叉熵,就是 10.1 的 Logistic 回归;把 L2 换成 L1,就是第 5 章的稀疏学习; 把 hinge 换成平方损失,就退回第 7 章的最小二乘分类。 四个东西的区别只在「损失用哪个」和「正则用哪个」,框架是同一个。 第二个好处:能看清它对什么不鲁棒。 hinge 在负方向上没有上限,所以一个错得离谱的点会产生巨大的梯度。 这正是第 6 章讲过的老问题——平方损失被异常点拖走,hinge 也一样会。 下一节的 ramp 就是给 hinge 封顶。 第三个好处:能看清正则项在干什么。 ||w|| 平方这个惩罚,等价于「在间隔和违反之间做权衡」。 C 趋于无穷时惩罚消失,退回硬间隔; C 趋于零时惩罚压倒一切,退回「什么都不管、只求 ||w|| 小」。 所以 C 不是「学习率」那种辅助参数,它是目标函数的一部分。 最后提醒一句:这一节的等价只对「线性 + L2」成立。 换成核之后,正则项变成「乘子加权的核矩阵二次型」,形式变了但意思一样: 还是在惩罚模型的复杂度。 理解成「复杂度惩罚」比记具体公式更有用。
五、动手 Demo:Hinge 解释:与 C 等于 1 除以 2 lambda 的 SVM 同解,夹角 0.064 度
用 8.2、8.3 同一份数据(160 个点、有重叠)。 先把四种损失在几个关键 margin 上并排打出来,确认 hinge 处处不小于 0/1。 然后用次梯度法直接最小化「hinge 之和 加 lambda 乘 ||w|| 平方」(这是无约束问题, 可以直接做梯度下降),把解和 C 等于 1 除以 2 lambda 的 SMO 解对比。 最后扫三个 C,看 hinge 之和与训练错数是不是一起降。
def sgd_reg(X, y, lam, iters=40000): # 无约束,直接次梯度
w, b = np.zeros(X.shape[1]), 0.0
for t in range(1, iters + 1):
act = y * (X @ w + b) < 1.0 # hinge 的活跃集
gw = 2.0 * lam * w - (y[act, None] * X[act]).sum(0)
gb = -y[act].sum()
lr = 0.6 / (1.0 + t / 800.0)
w, b = w - lr * gw, b - lr * gb
return w, b
print(sgd_reg(X, y, 1.0 / (2.0 * C))) # 与 SMO 的 C 对应
实跑输出:
--------------------------------------------------------------------
Hinge 解释:SVM 的正则视角,与 C 等于 1/(2 lambda) 的 SVM 应该同解
--------------------------------------------------------------------
数据 和 D026、D027 同一份,160 个点、C 取 1 与 10
说法一 最小化 ||w|| 平方的一半,加上 C 乘 hinge 之和
说法二 最小化 hinge 之和,加上 lambda 乘 ||w|| 平方,其中 lambda 等于 1 除以 2C
为什么要讲两种 说法一是几何(最大间隔),说法二是统计(损失加正则)
先把四种损失并排看一下(margin 是标签乘输出):
margin 0/1 损失 hinge ramp 平方损失
------------------------------------------
-1.00 1.00 2.00 1.00 4.000
-0.25 1.00 1.25 1.00 1.562
+0.00 0.00 1.00 1.00 1.000
+0.50 0.00 0.50 0.50 0.250
+0.90 0.00 0.10 0.10 0.010
+1.00 0.00 0.00 0.00 0.000
+1.50 0.00 0.00 0.00 0.250
+2.00 0.00 0.00 0.00 1.000
**看两列的关系:hinge 那一列每一行都不小于 0/1 那一列,一个一个比过去,没有例外。**
**所以「最小化 hinge 之和」是在压一个上界;上界压到 0,真正的错误数自然也压到 0。**
**平方损失那一列在 margin 大于 1 之后反而涨起来了(1.5 处 0.250,2.0 处 1.000),因为它要的是「输出接近正负 1」,不是「分对就行」。第 7 章那个错标点被它放大,根子在这儿。**
**ramp 那一列在 margin 小于 0 之后一直平在 1.00,这是它抗噪的来源,下一节(D030)专门讲。**
现在验等价性:用次梯度法直接最小化「hinge 之和 加 lambda 乘 ||w|| 平方」,再和 C 等于 1/(2 lambda) 的 SMO 解比。
C lambda SMO 的 w SMO 的目标 次梯度的 w 次梯度的目标 夹角
------------------------------------------------------------------------------------
1 0.5000 (1.1463, 1.3950) 19.676817 (1.1524, 1.3993) 19.687858 0.064
10 0.0500 (1.9382, 2.4232) 17.335962 (1.9368, 2.4179) 17.338723 0.042
**两列 w 的前几位对得上(如 1.1463 对 1.1524),夹角 0.064 度和 0.042 度,目标值只差千分之几。**
**这就把两种说法焊在一起了:最大间隔的那个解,就是「hinge 加 L2」那个目标的最小点。**
**差的那千分之几不是理论问题,是我这个次梯度法只跑到这个精度——它的收敛速度是「迭代次数的倒数」,四万轮能到这个程度已经不错了。要更准得换更好的解法,但方向已经证明清楚了。**
再看 C 一变动,hinge 之和和错数怎么跟着动:
C hinge 之和 训练错数 ||w|| 支持向量
----------------------------------------------
0.5 18.738 7 1.5423 25
1 18.047 8 1.8056 23
10 16.855 7 3.1030 19
**hinge 之和是三行单调下降的:18.738、18.047、16.855。**
**可训练错数那一列是 7、8、7——不是单调的,C 从 0.5 加到 1 反而多错了一个。**
**这正是「hinge 是上界」的代价:上界一直在降,真正的错数可以在它下面来回晃。上界降了不等于错数会降,只是保证错数不会超过它。**
**同时 ||w|| 从 1.5423 涨到 3.1030。**
**这两件事是同一个旋钮的两头:C 大就是「hinge 权重高」,模型宁可把间隔挤窄也要少违反;C 小就是「正则权重高」,宁可放过几个点也要保住间隔。**
**所以调 C 的时候,你其实是在调「拟合训练集」和「留出余量」的比例,这和第 4 章岭回归里调那个惩罚系数是同一件事,只是换了损失。**
导数里那个 act 掩码就是 hinge 的全部脾气。
margin 小于 1 的点贡献梯度,大于等于 1 的点梯度是零——完全不管。
这就是稀疏性的来源:不活跃的点对 w 没有任何拉力。
把 < 1.0 改成 < 0.0,你就得到了 0/1 损失的一个「能算梯度」的版本,但那个版本不凸、
也不收敛——这也说明 hinge 那个「1」不是随便取的。
学习率用 0.6 / (1 + t / 800) 这种衰减,是因为次梯度法在最优附近不会停。
hinge 不可导,梯度在折点处跳变,固定步长会在最优点附近一直抖。
衰减能压住抖动,代价是收敛慢——这就是为什么它只能对到千分之几。
w, b = w - lr * gw, b - lr * gb 这一行同时更新两个量,用的是同一个学习率。
b 的梯度是「活跃点标签之和的相反数」,量级和 w 的梯度差很多。
真要用的话应该分开调,这里为了讲清原理就共用一个。
注意这个 Demo 里我没给 b 加正则。
标准做法通常也不罚 b——因为罚 b 会让边界被迫过原点附近,而原点位置没有特殊含义。
这是一个「数学上不优美、实践上必须」的细节。
要说明:次梯度法在这里只是用来验证等价性,不是推荐解法。
它的收敛速度是 1 除以迭代次数,四万轮才对到千分之几;SMO 71 轮就到残差 1e-05。
这个对比本身就是个值得记的教训:同一个问题,算法的选择能差三个数量级。
30 秒实验
- 把次梯度法的迭代次数从 4 万加到 40 万,重跑 C 等于 1 那一组。 夹角会缩到多少?耗时增加了几倍?这个交换划算吗?
- 把 hinge 的阈值从 1 改成 2(要求更大间隔),重跑 SMO,再用对应的正则形式验证。 等价关系还成立吗?lambda 要改成多少?
- 把 L2 换成 L1(惩罚项改成 lambda 乘 ||w|| 的绝对值之和),重跑。 w 会变稀疏吗?这和 5.1 的结论一致吗?
常见误区
- 「hinge 是最优的代理损失」。 它是「0/1 损失最紧的凸上界」之一,但不是唯一,也不是永远最好。 交叉熵也是凸上界,而且光滑、能给概率。 选哪个取决于你要不要概率输出、要不要稀疏解。
- 「hinge 之和小了,准确率就一定高」。 实测里 hinge 之和单调降(18.738 到 16.855),训练错数却是 7、8、7。 上界降了不等于错数降。 要评估分类效果,还是得看混淆矩阵和召回率。
- 「SVM 和岭回归是两个完全不同的东西」。 框架是同一个:损失加正则。 岭回归是「平方损失加 L2」,SVM 是「hinge 损失加 L2」。 把 hinge 换成平方损失,SVM 就退化成第 7 章那个最小二乘分类。
- 「C 是学习率」。 C 是目标函数里的权重,不是优化步长。 它决定「违反约束罚多重」,和「走多快」没关系。 把它当学习率调,会得到完全错误的行为——C 调大不会让收敛更快,反而更慢。
自测三问(点开对答案)
1. 为什么说 hinge 是 0/1 损失的「上界」这件事很重要? —— **因为上界给了优化的合法性。** **你把上界压小,真损失不可能比它更大——所以「最小化代理损失」是有意义的行为。** **没有这个性质,你可能把代理压得很小,而真正的错误率却很大。** **注意反过来不成立:上界降了,真损失可以在它下面随便晃。** 2. 为什么「最小化 hinge 之和 加 lambda 乘 ||w|| 平方」和「C 等于 1 除以 2 lambda 的 SVM」同解? —— **因为后者是「最小化 ||w|| 平方的一半 加 C 乘 hinge 之和」。** **把整个式子除以 2C,就变成「hinge 之和 加 (1 除以 2C) 乘 ||w|| 平方」。** **乘一个正数不改变最小点的位置,所以两个问题的最优点完全一样。** **对应关系是 lambda 等于 1 除以 2C。** 3. 为什么 C 变大的时候,支持向量个数不一定是单调增加的? —— **因为支持向量是「乘子大于零」的点,而乘子的分布会随边界移动整体改变。** **C 大确实让更多点能顶到上界,但边界同时也变得更贴合数据,** **有些原来在边界附近的点可能被推到间隔外侧,乘子归零。** **两个效应方向相反,净效果取决于数据。** **实测里 C 从 0.1 到 100,支持向量个数是 36、25、23、19、18,大体在减少。**损失这个视角一打开,SVM 的弱点就藏不住了。 hinge 在负方向上没有上限,一个错得离谱的点会一直拉着边界往自己那边走。 下一节给它封个顶——损失涨到 1 就不再涨了,导数直接变成零。 代价是目标函数不再凸,得用一套叫 CCCP 的迭代办法。
- ☐ (本节完,下一节 8.6 8.6)