正则化就是在损失函数上再加一道罚款,罚系数太大。 加系数平方和叫岭回归(L2),系数会一起变小但不为零; 加系数绝对值和叫 Lasso(L1),一部分系数会被压成恰好零。 实测 8 个特征里 3 个真有用、还故意造了共线,不罚款时系数冒到 −7.9453 与 +8.9080 一对; 岭回归把测试 MSE 从 0.2458 压到 0.1373;Lasso 在 λ = 4 时剩下 5 个非零系数。
2.1 节的线性回归在「特征都独立、样本足够」时没问题。 但真实数据常常是特征彼此相关(共线性)、特征比样本还多,这时最小二乘的解会变得极不稳定。 正则化就是给这种病开的药。 它也是全部十七个算法里唯一一个「不改变模型形式、只改损失函数」的算法—— 所以它可以挂到逻辑回归、神经网络上,成为通用技巧。
一、先看它治的是什么病
教材举的例子是「特征之间高度相关」。这话听着抽象,但有个非常具体的后果:系数的解不再唯一。 设想两个特征几乎一模一样(比如「身高厘米」和「身高毫米」)。 模型完全可以用 1.0 的系数配第一个、−9.0 的配第二个,合起来仍然正确。 数学上这无数个解都一样好,但算法只会挑一个给你,挑到哪个取决于数值细节——换个随机种子, 系数就换一套。 更糟的是噪声。共线性让矩阵接近奇异,求逆时把极小的数值误差放大成巨大的系数。 Demo 里就出现了这一对:−7.9453 与 +8.9080,一个正一个负、几乎抵消。 合起来看模型没问题,单个系数已经毫无意义。 正则化的思路很直接:既然「系数大」是病根,那就在损失里加一项惩罚系数大小。 系数想变大多解释一点数据,就得先交罚款——最后停在一个折中点。
二、岭回归:罚平方,把系数一起按小
岭回归(Ridge)在残差平方和后面加上 λ 乘系数平方和。
λ 是人工设的强度,等于 0 就退回普通最小二乘,越大罚得越狠。
这个改动让解从 w = (XᵀX)⁻¹Xᵀy 变成 w = (XᵀX + λI)⁻¹Xᵀy。
差别只在那个 +λI——
它在矩阵对角线上加了一个正数,让本来接近奇异的矩阵变得可逆。 这是岭回归在数值上稳的根本原因。
注意公式里的 I 是单位矩阵,但截距那一项通常不罚(Demo 里就是 A[0, 0] -= lam)。
因为截距只负责平移,罚它只会让模型整体偏移,没有正则的意义。
岭回归的一个关键性质:系数会一起变小,但不会变成 0。 因为罚的是平方,系数越小罚得越轻,
梯度越接近 0 越平缓,所以它只敢把系数往小里压,不敢直接删掉。
这也意味着岭回归不能用来选特征。8 个特征进去,8 个系数出来,只是都小了一圈。
要知道哪个特征没用,得看 Lasso。
三、Lasso:罚绝对值,把没用的直接删掉
Lasso 把罚项换成系数绝对值之和。
看起来只改了一个平方,性质却完全不同:它会把一部分系数压成恰好零。
为什么?教材的解释很形象:L1 的等值线是菱形,L2 的等值线是圆。
损失函数的等高线(椭圆)往外扩,第一次碰到约束区域的点,
在菱形上很容易正好落在尖角——而尖角就在坐标轴上,坐标轴上意味着某个系数正好是 0。
圆没有尖角,所以 L2 的解一般落不到坐标轴上。这一句是整节的核心:尖角产生稀疏,圆弧不产生稀疏。
稀疏的代价是求解变难。
L1 不可导(0 点是个尖),不能直接求导令零,常用的是坐标下降:每次只调一个系数,其他固定,
用「软阈值」这个操作一步到位。Demo 里的 soft() 就是软阈值:把值往 0 的方向推 λ,推过头就停在 0。
实测 λ = 4 时 8 个系数只剩 5 个非零。
真值只有 3 个——该删的删了,但没删干净。 λ 再往上加,噪声特征会继续掉,但真信号也会被削,
测试误差重新上升。这就是要调 λ 的原因。
四、λ 怎么选:只能在验证集上试
λ 是超参数——它不由训练过程决定,得人来定。 定的办法只有一个:换几个 λ 各训练一次,看谁在验证集上最好。 Demo 里岭回归的 λ 从 0 走到 100,测试 MSE 从 0.2458 一路降到 0.1373, 然后到 λ = 100 时反弹到 1.9035。这条 U 形曲线是正则化的标准形状:左边过拟合,右边欠拟合。 注意「反弹」那一段有多陡:λ 从 10 到 100,MSE 涨了 14 倍。 λ 的量级比它的精确值重要得多,所以实际调参时按 10 的倍数扫,而不是 0.1、0.2、0.3 这样细调。 教材还提到「弹性网」(Elastic Net),就是把 L1 和 L2 加权混起来。 它适合「特征是成组相关」的场景:一组相关的特征,Lasso 会随便留一个删其他, 弹性网倾向于把这一组一起留或一起删,更稳。
五、动手 Demo:给系数加一道罚款,压住它的脾气
数据是 60 个样本、8 个特征,只有前 3 个真有用(系数 1.5、−2.0、1.0),后 5 个是噪声。 另外故意造了两组共线:第 6 个特征是第 2 个的翻版,第 7 个是第 3 个的两倍。 这些设定都写死在代码里,读者可以自己改倍数看结果怎么变。
import numpy as np
Z = (X - X.mean(axis=0)) / X.std(axis=0) # 先标准化
Z = np.column_stack([np.ones(len(Z)), Z]) # 第一列放截距
def ridge(Z, y, lam):
p = Z.shape[1]
A = Z.T @ Z + lam * np.eye(p) # 对角线上加 lam
A[0, 0] -= lam # 截距不罚
return np.linalg.solve(A, Z.T @ y)
def soft(v, lam): # Lasso 的软阈值
if v > lam:
return v - lam
if v < -lam:
return v + lam
return 0.0
print("lam=0 岭回归系数", np.round(ridge(Z, y, 0.0), 3))
print("lam=10 岭回归系数", np.round(ridge(Z, y, 10.0), 3))
实跑输出:
--------------------------------------------------------------------
正则化:给系数加一道罚款,压住它的脾气
--------------------------------------------------------------------
造 60 个样本、8 个特征,但**只有前 3 个是真的有作用**
(系数 1.5、−2.0、1.0),后 5 个是噪声。另外故意做两件事:第 6 个特征是第 2 个的翻版,
第 7 个是第 3 个的两倍——**这叫共线性**。
【一】不罚款:系数会乱跳
八个系数(已经标准化过,所以能横向比大小):
1.1903 -1.7994 -7.9453 0.0676 0.0818 0.0024 8.9080 -0.0801
真值应该是 [1.5, −2.0, 1.0, 0, 0, 0, 0, 0] 附近。实际解出来,
系数里出现了 -7.9453 和 +8.9080 这样一对——一个正一个负、几乎抵消。这正是共线性的招牌症状:
**两个几乎一样的特征,模型只能用一正一负去凑,合起来看着没事,单个系数已经毫无意义。**
【二】岭回归:加一项 λ 乘系数平方和
λ 系数长度 测试 MSE 共线那位的系数
------------------------------------------------------
0.0 12.131 0.2458 0.002
0.1 2.130 0.2099 -0.578
1.0 1.887 0.1952 -0.837
10.0 1.744 0.1373 -0.743
100.0 1.413 1.9035 -0.296
λ 往大里走,系数长度一路缩,测试误差先降后升。**中间那个最少的位置就是该选的 λ**
——λ 太大模型被压扁(欠拟合),λ 太小共线性的噪声又回来了(过拟合)。
这就是「偏差和方差的权衡」。
注意岭回归的系数**会一起变小,但不会变成 0**。
这是 L2 罚款的特点:它罚的是平方,系数越小罚得越轻,所以它只敢把系数压小,不敢直接删掉。
【三】Lasso:加一项 λ 乘系数绝对值之和
λ 非零系数个数 系数长度 测试 MSE
--------------------------------------------------
0.0 8 2.454 0.1853
0.5 7 2.323 0.1741
1.0 7 2.300 0.1603
2.0 7 2.255 0.1420
4.0 5 2.177 0.1438
Lasso 的 λ 一上去,系数会被压成**恰好为 0**
——这叫稀疏。λ = 4.0 时还剩 5 个非零,而真值只有 3 个——
**该删的删了,但没删干净,λ 再大就轮到真信号被削。**
接着问:为什么 L1 能删、L2 不能删?因为 L1 的罚款在 0 附近是个尖角,
最优点容易正好落在尖角上;L2 在 0 附近是光滑的碗底,最优点只会滑到碗底附近,落不到正中间。
【四】一句话分工
叫法 罚什么 系数会怎样 什么时候用
----------------------------------------------------------------------
岭回归 系数平方和 一起变小,不为 0 特征都有点用、彼此相关
Lasso 系数绝对值和 一部分变成 0 特征很多,想顺手选特征
弹性网 两者加权混合 介于两者之间 特征是成组相关时更稳
**正则化不是「让模型更准」,是「让模型别太自信」。**
它的收益在测试集上,不在训练集上——训练误差一定会因为罚款而变大。
第一段先看「不罚款」有多糟。八个系数里出现了 −7.9453 和 +8.9080 这一对, 而它们对应的两个特征本来就是同一个东西的两种写法。这不是算法算错了,是这个问题本身没有唯一解。 岭回归那张表要看两列:系数长度和测试 MSE。λ 从 0 到 10,长度从 12.131 掉到 1.744, MSE 从 0.2458 降到 0.1373。λ = 100 时长度 1.413、MSE 反弹到 1.9035——压过头了,模型被压扁。 注意 λ = 0 那一行的 MSE 是 0.2458,而 2.1 节同样的最小二乘在干净数据上是 0.0727。 差了 3 倍多,就是因为这份数据有共线性。 这就是正则化存在的理由。 Lasso 那张表的「非零系数个数」是关键。λ = 0 时 8 个全在,λ = 0.5 掉到 7,λ = 4 掉到 5。 系数是被压成精确的 0,不是「很小」——这是 L1 和 L2 最本质的区别。 最后那张三行表是这一节的总结:岭回归留下所有特征但都变小,Lasso 删掉一部分,弹性网介于两者之间。 选哪个不取决于哪个更先进,取决于你要不要那份稀疏。
30 秒实验
- 把 Lasso 的 λ 改成 8 和 16,非零系数个数会怎么变?真信号还在吗?
- 把第 6 个特征改成「和第 2 个完全一样」(去掉那 0.05 的噪声),不罚款的解会变成什么?
- 岭回归的 λ 一直加到 10000,所有系数会趋近于什么?模型还在预测什么?
常见误区
- 把正则化当成「提升精度」的技巧。它的收益在测试集上,训练误差一定会因为罚款而变大。 如果你只看训练分数,会觉得它有害。
- 标准化之前就加正则。罚的是系数大小,而系数大小取决于特征尺度——一个以毫米为单位的特征, 系数天然比以米为单位的小一千倍,不标准化就罚,等于专挑小单位的特征罚。
- 以为 Lasso 选出来的特征一定「真有用」。共线的两个特征里它只会留一个,留哪个几乎是随机的。 所以 Lasso 的结果要结合业务判断,不能当成因果结论。
自测三问(点开对答案)
1. 为什么岭回归的系数不会变成 0,而 Lasso 会? —— 因为 L2 的约束区域是圆、L1 是菱形。 椭圆等高线外扩第一次碰到的点,在菱形上容易落在尖角(就在坐标轴上),在圆上一般落不到。 尖角对应某个系数为 0,这叫稀疏。 2. λ 从 10 加到 100,测试 MSE 从 0.1373 弹到 1.9035,这叫什么? —— 欠拟合。罚得太狠,系数被压得接近 0,模型几乎只剩下截距,预测值全挤在均值附近,误差自然大。 3. 为什么加正则项之前一定要标准化? —— 因为罚的是系数的绝对大小,而系数大小和特征尺度成反比。 不标准化的话,单位小的特征会被罚得更狠,罚款变成了一种偏见。线性回归输出的是连续值。 要输出「是/不是」这种类别,得在它外面套一个把实数压进 0 到 1 的函数——2.3 节讲逻辑回归。
- ☐ (本节完,下一节 2.3 算法 3:逻辑回归)