🤖 第4章 带有约束条件的最小二乘法
📄 一句话概括

同一份数据加 ℓ2 罚项,λ 从 1e-6 到 1e3: 测试误差先降到 0.1832(λ 等于 1),再升到 11.4672(λ 等于 1000)。 同时条件数从 170 掉到 1.5,系数范数从 2.06 压到 0.56。 岭回归不做选择,它只做收缩——所有系数都变小,没有一个是零。

📝 本节和前面几节的关系

4.1 的约束是硬的:某些方向直接禁止。这节换成软的:所有方向都允许,但用罚项让大的变小。 好处是解变成连续的:λ 从 0 到无穷,解平滑地从最小二乘滑到全零。 代价是它永远不能让某个系数精确等于零——想要稀疏,得等第 5 章的 ℓ1。

一、从「不许去」到「少去一点」

ℓ2 约束的写法是:在最小二乘的目标后面加一项 λ 乘系数平方和。 λ 越大,系数越被压向零。 它有一个等价的几何说法:在「系数平方和不超过 R」这个球里找误差最小的点。 球的边界是光滑的,所以最优点一般落在球的内部或者切点上,坐标不会恰为零。 这就是它做不了稀疏的原因——第 5 章会把球换成菱形(ℓ1),棱角才会让坐标归零。 求导之后得到闭式解:θ 等于 (A 转置 A 加 λ 乘单位阵) 的逆乘 A 转置 y。 只比无约束版本多了一个 λI,代码里就是一行改动。 但它带来的性质变化很大:加了对角项,矩阵一定可逆,条件数一定下降。 「一定可逆」这一点常被忽略。 无约束解在 A 转置 A 奇异时有无穷多解,岭回归永远有唯一解。 所以它同时是「正则化」和「数值稳定化」两种工具。

二、λ 到底在做什么

把 A 做奇异值分解,岭回归的解可以写成:对每个方向,系数等于「无约束系数」乘一个收缩因子。 这个因子是 s 的平方除以 (s 的平方加 λ),s 是那个方向的奇异值。 看这个因子:奇异值远大于 λ 时,因子接近 1,几乎不收缩; 奇异值远小于 λ 时,因子接近 0,几乎压没。 所以 λ 事实上在按「数据在那个方向的把握程度」分级收缩。 这解释了 4.1 和 4.2 的关系:子空间约束是「前 k 个因子 1、其余 0」; 岭回归是「因子从 1 平滑降到 0」。 前者是阶梯,后者是斜坡。 也解释了为什么共线时岭回归特别有效:共线意味着某个方向的奇异值很小, 而那个方向的无约束系数往往极大。 收缩因子一乘,正好把它按下去。

三、实测:U 形与条件数

λ 训练 MSE 测试 MSE 系数范数 条件数
1e-06 0.1341 0.1931 2.06 1.7e+02
1e-01 0.1341 0.1918 2.05 1.7e+02
1 0.1361 0.1832 1.99 1.3e+02
10 0.2105 0.1981 1.72 4.2e+01
100 0.9802 0.9797 1.34 6.2e+00
1000 9.9899 11.4672 0.56 1.5e+00
最优在 λ 等于 1,测试 0.1832,比无约束的 0.1931 好 5 个百分点。
提升不大,因为这份数据还不算病态:60 个点对 10 维,比例是 6 比 1。
λ 到 100 时训练误差从 0.1341 涨到 0.9802,翻了七倍。
这就是偏差开始咬人的地方:系数被压得太小,模型连真值的形状都描不出来。
系数范数那一列最直观:2.06、1.99、1.72、1.34、0.56。
它一路降,没有跳变——这就是「收缩」这个词的来历。
注意它永远不等于零。
λ 到 1000 时范数还有 0.56,每个系数都还在,只是都很小。
如果你的目的是「挑出几个重要变量」,岭回归帮不了你。

四、什么时候必须用,什么时候没用

必须用的情况:特征数接近或超过样本数、特征之间高度相关。 这两条中任何一条成立,无约束解的方差都会大到不可用。 没用的情况:特征少、彼此正交、样本充足。 这时候 λ 的最优值会非常小,加了也没变化——Demo 里 1e-6 和 1e-1 的结果几乎一样。 所以「先看条件数,再决定要不要正则化」是个好习惯。 另一个常见误用:把 λ 当超参调到「训练误差最小」。 那会得到 λ 接近 0,等于没用。 λ 必须用交叉验证或者留出集选,这是下一节的内容。 还有一点:岭回归的系数不能直接拿来做解释。 它是有偏的,而且偏向「把所有系数按比例压小」,这个压缩比例依赖于特征的尺度。 所以用岭回归之前,必须先把特征标准化——否则量纲大的特征被压得更狠,这是纯粹的伪影。

五、动手 Demo:岭回归:λ 从 1e-6 到 1e3,测试误差先降到 0.183 再升到 11.467

同一份 60 点、10 维、高度相关的数据。 对每个 λ,解 (A 转置 A 加 λ 乘单位阵) 乘 θ 等于 A 转置 y,记录训练与测试均方误差、系数范数、 以及加了 λ 之后的条件数。

A = _XC.T @ _XC
b = _XC.T @ _Y

for lam in (1e-6, 1e-3, 1e-1, 1.0, 10.0, 100.0, 1000.0):
    w = np.linalg.solve(A + lam * np.eye(D), b)   # 只多了一个 λI
    print(lam,
          np.mean((_XC @ w - _Y) ** 2),
          np.mean((_XTC @ w - _YT) ** 2),
          np.linalg.norm(w),
          np.linalg.cond(A + lam * np.eye(D)))

实跑输出:

--------------------------------------------------------------------
岭回归:λ 从 1e-6 到 1e3,测试误差先降到 0.183 再升到 11.467
--------------------------------------------------------------------
  数据                     60 个训练点,10 维,各列共享一个大公共因子
  真值系数范数             1.94
  无约束解                 测试 0.1931,系数范数 2.06
  条件数                   不加 λ 时 1.7e+02,λ 等于 100 时 6.2e+00

      λ   训练 MSE   测试 MSE   系数范数    条件数
  -----------------------------------------------
  1e-06     0.1341     0.1931       2.06   1.7e+02
  1e-03     0.1341     0.1931       2.06   1.7e+02
  1e-01     0.1341     0.1918       2.05   1.7e+02
  1e+00     0.1361     0.1832       1.99   1.3e+02
  1e+01     0.2105     0.1981       1.72   4.2e+01
  1e+02     0.9802     0.9797       1.34   6.2e+00
  1e+03     9.9899    11.4672       0.56   1.5e+00

  **无约束解:测试 0.1931,系数范数 2.06,真值只有 1.94。**
  **范数只大了 6 个百分点,但请注意它是 10 个分量凑出来的:**
  **真值只有 2 个非零分量,解出来的 10 个分量全都不是零,靠互相抵消凑出同一个预测。**
  **所以每个系数单独拿出来都不可信,而预测值还算对——这正是共线的代价。**
  **加 λ 之后两件事同时发生:条件数从 1.7e+02 掉到 6.2e+00,系数范数从 2.06 压到 1.34。**
  **条件数掉下来意味着求解稳定,系数被压小意味着方差小。**
  **代价是偏差:λ 到 1e+03 时训练误差已经从 0.1341 升到 9.9899。**
  **最优在 λ 等于 1e+00,测试 0.1832——比无约束解的 0.1931 好一点点。**
  **只好了这么一点,是因为这份数据本来就不算病态:60 个点对 10 维。**
  **如果样本再少一半,无约束解会更糟,岭回归的优势会明显得多。**
  **第 4.2 节的全部内容就是这个 U 形:岭回归不做选择,它只做收缩。**
  要说明:**λ 是我在测试集上挑的,真实项目里不允许**,该用交叉验证(下一节 Demo 12 会演示这件事)。

solve 而不是 inv 再乘。 虽然 A 加 λI 一定可逆,但显式求逆会把条件数平方,多丢一倍有效数字。 这类细节在 λ 很小时尤其要紧。 条件数那一列是本 Demo 最值得看的东西。 它从 1.7e+02 一路掉到 1.5,说明「加 λ」这个动作同时在做两件事:稳定求解、降低方差。 很多教材把它们当两件事讲,其实是一件事的两个说法。 系数范数从 2.06 压到 0.56,而真值是 1.94。 所以最优 λ 那里(范数 1.99)已经很接近真值,说明岭回归把「多出来的那部分抵消」压掉了。 偏差和方差的最优点,常常就落在「范数接近真值」的附近——这也是个实用经验。 注意测试误差的最低点很浅:0.1832 对 0.1931,只差 5 个百分点。 真实项目里这点差距常常在噪声范围内,所以「用了岭回归」不该被当成卖点。 它在病态数据上才是救命的,在健康数据上只是锦上添花。 要说明:λ 是我在测试集上挑的,真实项目里不允许。 正确做法是交叉验证,下一节 Demo 12 会把流程走一遍。

30 秒实验

  1. 把 λ 网格加密到 1e-6 到 10 之间 12 个点,最低点在哪?和 1.0 比差多少?这点差距值得调吗?
  2. 把数据改成「特征 60 维、样本 60 个」,再跑一遍。 无约束解会变成什么样?岭回归的优势会扩大多少倍?
  3. 不做标准化,把第 3 列的数值乘 100,再跑岭回归。 看系数的收缩比例有没有变——这就是「必须先标准化」的实证。

常见误区

  1. 「岭回归能让系数变稀疏」。 不能。ℓ2 的球边界是光滑的,最优解一般不在坐标轴上,所以系数不会恰为零。 要稀疏得用 ℓ1(第 5 章),它的球有棱角。
  2. 「λ 越大越安全」。 λ 大到一定程度,模型就退化成「全部预测平均值」,那是最安全的,也是最有偏的。 Demo 里 λ 等于 1000 时训练误差 9.99,比什么都不做还差。
  3. 「岭回归可解释性比最小二乘好」。 相反:它是有偏估计,系数被系统性地压小了,不能直接解释成「影响大小」。 它的好处是稳定,不是可解释。
  4. 「反正都要标准化,那标准化的方式随便」。 不行。中心化和缩放的先后、用标准差还是用极差,都会改变 λ 的最优值。 更麻烦的是:训练集上的标准化参数必须原样用到测试集上,不能重算。
自测三问(点开对答案) 1. 为什么加了 λI 之后矩阵一定可逆? —— **因为 A 转置 A 是半正定的,特征值非负;加 λI 之后所有特征值都至少是 λ,严格为正。** **正定矩阵一定可逆。** **所以岭回归在数学上不存在「解不唯一」这个问题。** 2. 岭回归的收缩因子 s 平方除以 (s 平方加 λ),这个式子说明了什么? —— **说明 λ 不是「一刀切地压」,而是按方向分别压:数据在那个方向越有把握(s 越大),** **压得越轻。** **所以它比「统一乘一个 0.5」聪明得多——它压的正是数据说不清的方向。** 3. 如果业务要「从 50 个特征里挑出 5 个最重要的」,岭回归合适吗? —— **不合适。** **岭回归会把 50 个系数都留着,只是都变小,挑不出来。** **该用 ℓ1(Lasso)或者「先岭回归稳定、再按阈值筛」。** **这是岭回归和 Lasso 最本质的分工:一个负责稳,一个负责选。**

复杂度旋钮有了:k、λ、多项式次数。 但怎么知道该拧到哪一格?下一节讲模型选择: 为什么训练误差不能用、留出集有什么毛病、交叉验证怎么把它救回来。

相关笔记