查看全课大纲(第 48 / 82 节)
  1. 1.函数与数据泛化
  2. 2.向量:从几何直觉到高维空间
  3. 3.矩阵与张量
  4. 4.特征值与特征向量:矩阵变换的不动方向
  5. 5.凸函数:从向量求导的矩阵记号到好优化的函数形状
  6. 6.概率与统计:机器学习的数学基础
  7. 7.独立性与随机变量:贝叶斯公式与二项分布
  8. 8.期望、方差与协方差:随机变量的数字画像
  9. 9.联合概率:当一条轴不够用的时候
  10. 10.概率分布(一):从伯努利到伽马函数
  11. 11.概率分布(二):贝塔、泊松、正态与两个贝叶斯故事
  12. 12.机器学习的模型:找输入与输出的关系
  13. 13.机器学习的应用
  14. 14.过拟合
  15. 15.机器学习的分类
  16. 16.有监督学习:从房价预测说起
  17. 17.损失函数
  18. 18.梯度下降(上):更新规则与学习速率
  19. 19.梯度下降(2)
  20. 20.牛顿方法
  21. 21.KNN简介
  22. 22.距离测算
  23. 23.朴素贝叶斯
  24. 24.朴素贝叶斯的实际应用
  25. 25.信息熵:决策树构建的度量基础
  26. 26.信息增益与决策树的构建
  27. 27.ID3算法
  28. 28.决策树节点的划分
  29. 29.CART树
  30. 30.Bagging
  31. 31.Boosting:自适应提升算法
  32. 32.Gradient Boosting
  33. 33.线性模型:回归与分类的同一副骨架
  34. 34.最小二乘:把最优参数直接解出来
  35. 35.最小二乘法的几何与概率视角
  36. 36.极大似然:给最小二乘一个概率出身
  37. 37.逻辑回归(1):把线性模型接到分类上
  38. 38.逻辑回归(2):损失函数与多分类
  39. 39.---- 手造数据: 5 个样本, 2 个特征; x2 几乎 = 2*x1 (偏差手工指定, 无随机) ----
  40. 40.讲师例: 向量 "23014" -- 三把范数尺子量同一个向量
  41. 41.优化方法(1):坐标下降与前向选择
  42. 42.优化方法(2):前向策略与最小角回归
  43. 43.拉格朗日乘子法(1):从无约束到等式约束
  44. 44.拉格朗日乘子法(2):等式约束下的平行条件与拉格朗日函数
  45. 45.拉格朗日乘子法(3):不等式约束与 KKT 条件
  46. 46.最大间隔:把分类问题变成优化问题
  47. 47.拉格朗日对偶问题:把 w 和 b 全部换成 α
  48. 48.支持向量:边界上的少数派
  49. 49.SVM 计算过程:一笔到底算完,再走进核方法
  50. 50.贝叶斯公式:概率即信念
  51. 51.隐含变量和概率模型:给看不见的原因建模
  52. 52.EM 算法:期望与最大化的迭代
  53. 53.琴生不等式:EM 的数学钥匙
  54. 54.三个硬币问题:EM 的复盘与衔接
  55. 55.高斯混合模型:用隐变量解释双峰数据
  56. 56.隐马尔可夫模型
  57. 57.隐马尔可夫模型(2):前向算法与动态混合模型
  58. 58.隐马尔可夫模型在 NLP 中的运用
  59. 59.EM 算法:把「为什么收敛」推到底
  60. 60.HMM 在 NLP 中的运用:从分词到机器翻译
  61. 61.HMM 参数估计:Baum-Welch 的三块拉格朗日
  62. 62.高维下的高斯模型
  63. 63.主题模型:从词袋到隐含主题
  64. 64.Dirichlet 分布(1)
  65. 65.Dirichlet 分布(2)
  66. 66.统计学习内容回顾
  67. 67.K-means 算法
  68. 68.PCA 降维
  69. 69.SVD 降维
  70. 70.从聚类到流形
  71. 71.局部线性嵌入
  72. 72.随机邻域嵌入:SNE 与 t-SNE
  73. 73.无参模型:让数据自己说话
  74. 74.高斯过程:函数空间里的正态分布
  75. 75.高斯过程(2):噪声、正则化与核的线性组合
  76. 76.神经网络简介:从感知器到自编码器
  77. 77.反向传播:把误差一层层传回去
  78. 78.卷积神经网络(CNN):让特征自己学出来
  79. 79.循环神经网络(RNN):给网络装上记忆
  80. 80.深度学习回顾:静态一类,动态一类
  81. 81.增强学习(1):蒙特卡洛、树搜索与 Q-learning
  82. 82.强化学习(2):从乌鸦吃核桃到井字棋的 value 回传

支持向量:边界上的少数派

约 13 分钟

📺 正在播放小象官方高清录播(支持倍速与清晰度调节)

小象实战讲义 · 机器学习算法精讲

上一节把 SVM 变成了只关于 α\alpha 的对偶问题,结尾留了一个问题:这组 α\alpha 怎么解出来?这一节先看求解的思路(算法本身不展开),然后揭晓 α\alpha 的取值规律——边界上的点,α\alpha 大于零;远离边界、被正确分类的点,α\alpha 恰好是零。正是这条规律,让”支持向量机”这个名字有了着落:真正”支撑”起分类边界的,只是少数几个点,其余大部分数据对最终的模型没有贡献。

💡 核心导读

  • 求解思路:坐标轴式的迭代(SMO)——固定其余的 α\alpha、一次调一个,不断循环逼近最优。
  • 参数恢复:ww 由 w=∑iαiyixiw = \sum_i \alpha_i y_i x_i 直接算出;bb 由边界上的点满足 yi(wTxi+b)=1y_i(w^Tx_i + b) = 1 反解。
  • 核心规律:判对且远离边界的点,1−yi(wTxi+b)<01 - y_i(w^Tx_i+b) < 0 是负数,乘上 αi\alpha_i 只会拉低 LL——要 LL 最大,最优动作是把 αi\alpha_i 置零。
  • 命名由来:边界上的点叫 Support Vector;SVM = Support Vector Machines。
  • 两个引申:数据的稀疏性(大部分数据没用、关键在少数);形式上与第 9 章的正则化”就是一回事”。

对偶问题怎么解:坐标轴式的迭代

上一节的对偶问题:max⁡α ∑iαi−12∑i∑jαiαjyiyj(xiTxj)\max_\alpha\ \sum_i \alpha_i - \frac{1}{2}\sum_i\sum_j \alpha_i\alpha_j y_i y_j (x_i^Tx_j),s.t. ∑iαiyi=0\sum_i \alpha_i y_i = 0、αi≥0\alpha_i \ge 0。想求 LL 的最大值,讲师说可以”通过一些普通的优化算法”——并且提醒大家回想第 9 章讲 regularization 的时候提过的坐标轴优化:

先给定一个轴,比如说想优化 xx 和 yy 两个方向——先固定 x0x_0,在 x0x_0 里找 yy 的最好值;找到之后,再把 yy 固定住,在这条线上找 xx 方向的最好值。不断地循环,就能一步步逼近。对偶问题同理:α\alpha 有 nn 个——不断地调整不同的 α\alpha,先固定一个、调整另外一个,循环往复使 LL 的值最大。这种解法有一个专门的名字,叫 SMO 算法。

(讲师的诚实框定:这属于 quadratic 二次规划的一类方法,“因为时间有限,我们只是讲拉格朗日乘子的优化,没办法给大家再推更多的公式”——算法细节本课不展开。)

假设用这类算法解出了最优的一组乘子 α1∗,α2∗,…,αN∗\alpha_1^*, \alpha_2^*, \dots, \alpha_N^*,接下来所有的参数都水到渠成:

  • ww 直接恢复(上一节第一步的关系式):w∗=∑iαi∗yixiw^* = \sum_i \alpha_i^* y_i x_i,全是已知量,加出来就行;
  • bb 用边界上的点反解:我们要求的是 yi(wTxi+b)≥1y_i(w^Tx_i + b) \ge 1,而如果存在一个点恰好落在这条边界上,它满足 yi(wTxi+b)=1y_i(w^Tx_i + b) = 1,于是 b∗=yi−w∗Txib^* = y_i - w^{*T}x_i——一个边界点就够。

哪些点的 α 大于零:看 L 最大化往哪边走

现在到本节的核心问题:解出来的这组 α\alpha,取值有什么规律?

盯住拉格朗日函数里乘子所在的那部分(讲师:“看这个图、看这个公式”):

∑i=1Nαi[1−yi(wTxi+b)]\sum_{i=1}^{N}\alpha_i\left[1 - y_i(w^Tx_i + b)\right]

对每一个样本问一句:它分对了吗?离边界远吗?

如果判对了——yi(wTxi+b)≥1y_i(w^Tx_i+b) \ge 1——那么方括号里的 1−yi(wTxi+b)1 - y_i(w^Tx_i+b) 是小于等于零的数(远离边界时严格小于零)。一个负数(或者零),乘上一个大于零的 αi\alpha_i,还要连加进总账——而我们的目标是使 LL 最大。那 αi\alpha_i 取什么值最划算?讲师的推理一步到位:把它置成 0 就可以了——置零之后,这个点对 LL 的拖累彻底消失,LL 才能取到最大。所以:

远离边界、被正确分类的点,它所对应的 αi\alpha_i 就是 0。

如果这个点落在边界上——yi(wTxi+b)=1y_i(w^Tx_i+b) = 1,方括号等于零——事情就变了:约束 1−yi(wTxi+b)≤01 - y_i(w^Tx_i+b) \le 0 恰好取等号,不等式优化退化成了等式优化。还记得讲 KKT 条件时的两种情况吗(《拉格朗日乘子法(3)》):一种是最优解在约束范围内(乘子归零),一种是贴在边界上(约束激活)。贴边的情况,把它带进去求所有的解——用拉格朗日乘数去求。在边界上的点,αi\alpha_i 是大于 0 的数。

三条边界与 Support Vector 的命名

把画面画全。分类线是 wTx+b=0w^Tx + b = 0,两边各有一条边界:

yi(wTxi+b)=+1wTx+b=0yi(wTxi+b)=−1y_i(w^Tx_i + b) = +1 \qquad w^Tx + b = 0 \qquad y_i(w^Tx_i + b) = -1

讲师的黑板:两条边界上的这些点,α\alpha 都是大于零的——比如叫 α1\alpha_1、α2\alpha_2、α3\alpha_3,全都大于零。这些 α\alpha 的值对整个优化非常重要:边界的位置,就是被它们顶着定的。而边界外面(左边或右边)被正确分类的那些数据,“显得就不是那么重要”,所以 α\alpha 直接置成 0——比如第四个数据,α4=0\alpha_4 = 0。

于是,边界上的这几个点,得到了一个专门的名字:Support Vector——“首先,这些点 xx 都是一些向量”,所以叫 Support Vector。整个算法叫 SVM,Support Vector Machines——中文叫支撑向量机、或者支持向量机,“实际上想讲的就是这样的一件事情”。

讲师顺势确认了几件事:SVM 首先还是线性函数的优化——如何找到一个很好的线性函数,使它的边界最大;而做完之后发现,在边界上的这些点,对你最后定下的边界是最重要的——它们叫 support vector;其他的点,相对权重置为 0。“SVM(Support Vector Machines)这个名字,就是从如此由来。”

一个直观的类比,讲师的原话场景:想知道一个人是年轻还是中年——最难分的是大概四十几岁的时候,三十八九岁、四十一二岁,年轻和中年的边界很模糊。这些”模糊地带”的点只要确定了,两个类别的分类线就能很好地确定。反过来,十八岁的点、五十六近六十岁的点,很容易就能区分出年轻或中年——这些点对分类线的影响,就不是那么重要。跟机器学习里的很多事情都相关(下一节还会看到这个直觉的更多侧面)。

亲手验证:三点例的完整账本

一个一维的三点数据集:x1=+1x_1 = +1(y1=+1y_1 = +1)、x2=−1x_2 = -1(y2=−1y_2 = -1)、x3=+2x_3 = +2(y3=+1y_3 = +1)。前两个对称地贴着边界,第三个是”分得很对、离边界很远”的点。对偶问题的最优解是 α∗=(0.5, 0.5, 0)\alpha^* = (0.5,\ 0.5,\ 0)——把它代进公式,恢复参数、对每个点记账:

# 三点例: 谁在边界上, 谁的 alpha 大于零
X = [1.0, -1.0, 2.0]   # 一维特征: x=+1 正类, x=-1 负类, x=+2 正类(远离边界)
y = [1, -1, 1]

alpha = [0.5, 0.5, 0.0]  # 对偶最优解: 前两点是支持向量, 第三点 alpha=0
w = sum(alpha[i] * y[i] * X[i] for i in range(3))    # w = sum(a*y*x)
b = y[0] - w * X[0]                                    # 由边界点恢复 b: y1*(w*x1+b)=1

print(f"alpha* = {alpha}")
print(f"w* = sum(a*y*x) = {w:.4f} ; b* = y1 - w*x1 = {b:.4f}  (用边界上的点1恢复)")
print()
print(f"{'点':>2} {'x':>5} {'y':>3} {'y*(w*x+b)':>11} {'1-y*(w*x+b)':>12} {'alpha':>6}")
for i in range(3):
    f = y[i] * (w * X[i] + b)
    print(f"{i+1:2d} {X[i]:5.1f} {y[i]:+3d} {f:11.4f} {1-f:12.4f} {alpha[i]:6.2f}")
print()
print("点1、点2: 函数间隔恰为 1 (落在边界上) -> alpha > 0")
print("点3: 函数间隔 2 > 1 (远离边界、分得很对) -> alpha = 0")
print()

# 把权重挪给远离点(保持约束 sum(a*y)=0), 对偶目标一路下滑
def dual(a):
    D = sum(a)
    for i in range(3):
        for j in range(3):
            D -= 0.5 * a[i] * a[j] * y[i] * y[j] * (X[i] * X[j])
    return D

print("把权重挪给远离边界的点3 (保持 sum(a*y)=0), 对偶目标变化:")
for a in ([0.5, 0.5, 0.0], [0.45, 0.55, 0.1], [0.4, 0.6, 0.2], [0.35, 0.65, 0.3]):
    print(f"  alpha = {a} -> D = {dual(a):.4f}")
print("alpha3 越大, D 越小 —— 远离点的 1-y*(w*x+b) = -1 是负数, 乘上 alpha 只会拉低 L")
alpha* = [0.5, 0.5, 0.0]
w* = sum(a*y*x) = 1.0000 ; b* = y1 - w*x1 = 0.0000  (用边界上的点1恢复)

 点     x   y   y*(w*x+b)  1-y*(w*x+b)  alpha
 1   1.0  +1      1.0000       0.0000   0.50
 2  -1.0  -1      1.0000       0.0000   0.50
 3   2.0  +1      2.0000      -1.0000   0.00

点1、点2: 函数间隔恰为 1 (落在边界上) -> alpha > 0
点3: 函数间隔 2 > 1 (远离边界、分得很对) -> alpha = 0

把权重挪给远离边界的点3 (保持 sum(a*y)=0), 对偶目标变化:
  alpha = [0.5, 0.5, 0.0] -> D = 0.5000
  alpha = [0.45, 0.55, 0.1] -> D = 0.3800
  alpha = [0.4, 0.6, 0.2] -> D = 0.2200
  alpha = [0.35, 0.65, 0.3] -> D = 0.0200
alpha3 越大, D 越小 —— 远离点的 1-y*(w*x+b) = -1 是负数, 乘上 alpha 只会拉低 L

账本上三行三列,正是讲师推理的数值版:点 1、点 2 落在边界上(函数间隔恰为 1,方括号 = 0)——α>0\alpha > 0,它们是支持向量;点 3 远离边界(函数间隔 2 > 1,方括号 = −1 是负数)——α=0\alpha = 0。下半段更直接:强行把权重从支持向量挪给点 3(每组都满足等式约束 ∑αiyi=0\sum\alpha_iy_i = 0),对偶目标从 0.50 一路掉到 0.02——负数乘上 α\alpha 只会拉低 LL,最大化自然把它压回零。

删掉”没用”的点,边界纹丝不动

αi=0\alpha_i = 0 的点对 ww 的贡献是零——那就干脆把它从数据里删掉,重新解一遍,看边界会不会变:

# 删掉 alpha=0 的点, 边界纹丝不动; 挪动远离点也不影响
X = [1.0, -1.0, 2.0]   # 同一批数据: x=+1 正类, x=-1 负类, x=+2 正类(远离边界)
y = [1, -1, 1]

def dual(a):
    D = sum(a)
    for i in range(3):
        for j in range(3):
            D -= 0.5 * a[i] * a[j] * y[i] * y[j] * (X[i] * X[j])
    return D

alpha = [0.5, 0.5, 0.0]                                # 完整 3 点的解
w = sum(alpha[i] * y[i] * X[i] for i in range(3))
b = y[0] - w * X[0]

alpha_sv = [0.5, 0.5]                                   # 只用两个支持向量重新解
w_sv = sum(alpha_sv[i] * y[i] * X[i] for i in range(2))
b_sv = y[0] - w_sv * X[0]

print("完整数据 3 点 vs 只留支持向量 2 点:")
print(f"  3 点解: w = {w:.4f}, b = {b:.4f}, margin = {2/abs(w):.4f}, 对偶目标 = {dual(alpha):.4f}")
print(f"  2 点解: w = {w_sv:.4f}, b = {b_sv:.4f}, margin = {2/abs(w_sv):.4f}  (完全一致)")
print()
print("把远离点 x3 挪得更远 (x3=10):")
for x3 in (2.0, 5.0, 10.0):
    f3 = y[2] * (w_sv * x3 + b_sv)
    print(f"  x3 = {x3:4.1f}: y3*(w*x3+b) = {f3:6.1f} > 1, 仍在边界外 -> alpha3 仍为 0, 解不变")
完整数据 3 点 vs 只留支持向量 2 点:
  3 点解: w = 1.0000, b = 0.0000, margin = 2.0000, 对偶目标 = 0.5000
  2 点解: w = 1.0000, b = 0.0000, margin = 2.0000  (完全一致)

把远离点 x3 挪得更远 (x3=10):
  x3 =  2.0: y3*(w*x3+b) =    2.0 > 1, 仍在边界外 -> alpha3 仍为 0, 解不变
  x3 =  5.0: y3*(w*x3+b) =    5.0 > 1, 仍在边界外 -> alpha3 仍为 0, 解不变
  x3 = 10.0: y3*(w*x3+b) =   10.0 > 1, 仍在边界外 -> alpha3 仍为 0, 解不变

删掉 α=0\alpha = 0 的点 3,用剩下的两个支持向量重新求解:ww、bb、margin 分毫不差。甚至把点 3 从 +2+2 挪到 +10+10——它分得更”对”、离边界更远,α3\alpha_3 依然是 0,解纹丝不动。这就是”大部分数据对最终的模型没有贡献”的直观含义:边界是被支持向量撑起来的,别的点随便挪。

稀疏性:数据的贡献是不均等的

讲师把这件事上升到数据层面的观察:数据对”真正的定义”并不是完全都有用的,每个点的 contribution 不一样。侧面讲的正是数据的稀疏化问题——大部分的数据,对最后的模型是没有影响的;关键的部分,集中在少数数据身上。这就是数据的 sparsity。(呼应第 9 章 LASSO 一节讲过的稀疏性:那里是参数稀疏,这里是样本稀疏——关键角色总是少数派。)

与正则化的形式呼应

还有一层联系讲师当场点破。我们这个带条件的优化,跟以前学过的什么东西很像?跟线性函数后面加上一个正则项,特别像。 以前学的损失:一个关于 θTx\theta^Tx 的线性函数的 LL,加上对 θ\theta 的模——L2L_2 范数就是 ridge regression;加上一维的模(L1L_1),就是 LASSO。回头看 SVM 的拉格朗日函数:12∥w∥2\frac{1}{2}\Vert w\Vert^2 加上 αi\alpha_i 的加和形式——“是不是特别相近?就是一回事”。所以”我们做的这些事,跟正则化也是相关的”。(讲师说这个具体的关系”一会再讲”——本课后续视频没有再展开这个话题,形式上的呼应本身已经把亲缘关系摆清楚了。)

📝 动手练一练

  1. 为什么远离边界的点 αi=0\alpha_i = 0:用”使 LL 最大”的逻辑,把推理链完整说一遍:从”这个点判对了”出发,走到”αi\alpha_i 置零”结束。
👉 点击查看参考答案

点 ii 判对且远离边界 ⟹ yi(wTxi+b)>1y_i(w^Tx_i+b) > 1 ⟹ 1−yi(wTxi+b)<01 - y_i(w^Tx_i+b) < 0,是负数。LL 中这个点贡献的是 αi×(负数)\alpha_i \times (\text{负数}),且 αi≥0\alpha_i \ge 0——只要 αi>0\alpha_i > 0,这一项就在往下拖 LL。目标是 LL 最大,而 αi\alpha_i 有”置零”的自由 ⟹ 最优动作 αi=0\alpha_i = 0,拖累归零。落在边界上则相反:1−yi(wTxi+b)=01 - y_i(w^Tx_i+b) = 0,这项怎么乘都不拖后腿,αi\alpha_i 可以大于 0——边界的撑力全部来自这些点。

  1. 挪动远离点:把三点例中的 x3x_3 从 +2+2 改成 +1.2+1.2,还是改成 +0.9+0.9,哪种情况会改变支持向量集与最终边界?各说明理由。
👉 点击查看参考答案

改成 +1.2+1.2:y3(wTx3+b)=1.2>1y_3(w^Tx_3+b) = 1.2 > 1,仍在边界外(α3=0\alpha_3 = 0、解不变)。挪到多远都一样——边界外的点对模型零贡献。

改成 +0.9+0.9:y3(wTx3+b)=0.9<1y_3(w^Tx_3+b) = 0.9 < 1,闯进了边界之内——原来的两个支持向量 (±1)(\pm 1) 已经”罩不住”它了,约束被违反。此时必须重新求解:x3x_3 会成为新的支持向量(挤进边界、α3>0\alpha_3 > 0),边界被迫收缩或移动。分水岭就在边界上:边界外随便挪,越界才改变。

本章小结

  • 求解思路:SMO——坐标轴式迭代,固定其余 α\alpha、一次调一个、循环逼近(算法细节本课不展开);解出 α∗\alpha^* 后,w=∑iαiyixiw = \sum_i \alpha_i y_i x_i 直接恢复,bb 由边界点 yi(wTxi+b)=1y_i(w^Tx_i+b) = 1 反解。
  • 取值规律:边界上的点 αi>0\alpha_i > 0(不等式约束取等、退化为等式优化);远离边界、正确分类的点 αi=0\alpha_i = 0(负数乘 α\alpha 拉低 LL,最大化压零)。
  • 命名:边界上的点都是向量 → Support Vector;SVM = Support Vector Machines——“找很好的线性函数使边界最大,结果发现边界上的点最重要,其余权重置零”,名字从如此由来。
  • 年龄类比:三十八九、四十一二岁的”模糊点”定住边界;十八岁、近六十岁的点随手就分、无足轻重。
  • 稀疏性:数据 contribution 不均等,大部分数据对模型没有影响,关键集中在少数——数据的 sparsity。
  • 正则化呼应:12∥w∥2+∑αi\frac{1}{2}\Vert w\Vert^2 + \sum\alpha_i 的形式,与 ridge(L2L_2)/LASSO(L1L_1)的”线性函数 + 模”形式”就是一回事”。

📋 行动清单

  • 把”判对 → 负数 → 拉低 LL → α\alpha 置零”的推理链不看讲义复述一遍
  • 跑代码块一,自己设计第四个点(比如 x4=−3x_4 = -3 负类),先预测它的 α\alpha,再看记账结果
  • 跑代码块二,把 x3x_3 改成 0.90.9(越界),观察支持向量集和边界怎么变
  • 用年龄边界的比喻,向一个没学过 SVM 的人解释”支持向量”
  • 预习下一节《SVM 计算过程》——一个从头到尾手算的完整 SVM 例子,讲师预告的”为什么求最大边界”的直觉也在那里展开

—— 小象教研组

配套资料 · 直接下载

课件、代码、数据集等配套资料全部免费,点击即可直接下载,无需分享或解锁。

  • 第11章笔记:最大间隔(Max Margin)
    下载