权重为何会越学越小?从过拟合到 L2 正则化与岭回归
从高次多项式追逐噪声出发,推导 L2 惩罚、手算参数收缩,解释特征尺度与 alpha,并用 sklearn 和 PyTorch 实现可调试训练。
上一篇用学习曲线识别出一种典型症状:训练误差很低,验证误差却明显更高。减少模型容量或增加数据都可能有效,但如果我们仍希望保留高次特征,又不允许模型用极端系数追逐每一个噪声点,该怎样改变训练目标?
一个直接办法是告诉优化器:拟合数据很重要,但使用过大的参数也要付出代价。 这就是正则化(Regularization)的基本思想。
本文只聚焦 L2 正则化及其在线性回归中的形式——岭回归(Ridge Regression),回答三个紧密问题:惩罚项怎样改变最优参数、为什么必须关注特征尺度,以及正则化强度如何选择。
01 旧目标为何会偏爱极端参数?#
设多项式回归把一个输入 展开成:
模型为:
一批 个样本的数据流是:
X [N,1]
│ 多项式展开 φ
▼
Φ [N,p] ──► Φw+b ──► y_hat [N]
│
y [N] ──────────────────┘
▼
数据损失text普通最小二乘只最小化残差平方和:
- :展开后的设计矩阵。
- :每个多项式特征的系数。
- :截距。
- :真实值与预测值。
- :向量元素平方和。
当 很大、样本很少或特征高度相关时,许多系数组合都能把训练残差压得很低。有些组合依赖巨大的正负系数相互抵消:在训练点上恰好准确,输入稍有变化便剧烈摆动。
y
▲ 高次模型:穿过噪声点但剧烈弯折
│ ● _/\__●
│ ● / \___/ \_/\
│ _/ ●
│ ●╱ 平滑趋势
└──────────────────────────► xtext普通训练目标只关心最终残差,不关心取得这个残差用了多大的系数。因此需要给“极端解”增加成本。
02 L2 惩罚怎样写进目标?#
在数据损失后加入参数平方和:
- :正则化强度(Regularization Strength)。
- :L2 惩罚。
- 截距 通常不惩罚,因为它只移动整体基线,不控制输入方向的敏感度。
优化器现在必须在两件事之间权衡:减小预测残差,或减小权重范数。
| 数据拟合压力 | 权重收缩压力 | 常见风险 | |
|---|---|---|---|
| 最大 | 无 | 高方差、追逐噪声 | |
| 适中 | 平衡 | 适中 | 可能改善验证表现 |
| 很大 | 较弱 | 很强 | 系数接近 0、欠拟合 |
03 一个参数也能手算“收缩”#
只看一个样本 ,模型 ,目标为:
求导并令其为 0:
所以:
当 时,,训练残差为 0;当 时:
此时数据损失为 ,惩罚为 ,总目标为 6。若仍取 ,数据损失虽为 0,惩罚却为 。正则化目标因此选择了较小的 。
交互手算:当 α 从 2 增加到 9,最优权重如何变化?
。正则化越强,权重越接近 0;但预测也从 3 退到 0.3,说明过强惩罚会制造欠拟合。
这个单样本例子只展示机制,不证明泛化改善。真实项目必须在未参与拟合的验证数据上比较不同 。
04 梯度下降中发生了什么?#
若数据损失对第 个权重的梯度为 ,则:
学习率为 时:
重新整理:
即使当前 batch 的数据梯度 ,权重也会乘上一个小于 1 的因子,向 0 收缩。这种更新视角常被称为权重衰减(Weight Decay)。
当前权重 w
│
├── 数据梯度 g_data ──────────┐
│ │ 相加
└── L2 梯度 2αw ──────────────┤
▼
总梯度 g_total
│ -ηg_total
▼
新权重text不过不同库可能把目标写成 、 或按样本数取平均,所以梯度里是否出现 2、参数名叫 alpha 还是 weight_decay,不能脱离接口定义直接比较。
05 为什么特征尺度会改变惩罚含义?#
假设同一个房屋面积既可用平方米,也可用平方千米表示:
若模型预测不变,使用平方千米时对应权重必须比平方米时大 倍。L2 惩罚直接作用在数值权重上,于是仅仅换单位,就会受到完全不同的惩罚。
因此,不同量纲特征进入岭回归前通常要标准化:
- :第 个样本的第 个特征。
- :仅从训练集估计的均值和标准差。
- :标准化后的特征。
标准化后,一个单位权重更接近“特征变化一个标准差时的影响”,L2 对各方向的约束才更可比。
这里再次出现数据泄漏边界:均值和标准差必须在每个训练折内拟合,验证折只能复用。因此标准化、特征展开与 Ridge 都应放入同一 Pipeline。
06 用当前 scikit-learn API 完成岭回归#
继续使用一维非线性数据,并把它展开成 8 个多项式特征:
import numpy as np
from sklearn.linear_model import Ridge
from sklearn.model_selection import GridSearchCV, KFold, train_test_split
from sklearn.pipeline import Pipeline
from sklearn.preprocessing import PolynomialFeatures, StandardScaler
rng = np.random.default_rng(42)
X = np.linspace(-3, 3, 120, dtype=np.float64).reshape(-1, 1) # [120, 1]
y = np.sin(X[:, 0]) + rng.normal(0.0, 0.20, size=120) # [120]
X_dev, X_test, y_dev, y_test = train_test_split(
X, y, test_size=0.20, random_state=42
)
pipeline = Pipeline([
('poly', PolynomialFeatures(degree=8, include_bias=False)),
('scale', StandardScaler()),
('ridge', Ridge()),
])
search = GridSearchCV(
estimator=pipeline,
param_grid={'ridge__alpha': np.logspace(-4, 4, 25)},
scoring='neg_root_mean_squared_error',
cv=KFold(n_splits=5, shuffle=True, random_state=42),
n_jobs=-1,
refit=True,
)
search.fit(X_dev, y_dev)
test_prediction = search.predict(X_test) # [24]
best_ridge = search.best_estimator_.named_steps['ridge']
print('best alpha:', search.best_params_['ridge__alpha'])
print('validation RMSE:', -search.best_score_)
print('coefficient shape:', best_ridge.coef_.shape) # (8,)
print('prediction shape:', test_prediction.shape) # (24,)python数据在 Pipeline 中的形状变化:
X_dev [96,1]
│ PolynomialFeatures(degree=8)
▼
Φ_dev [96,8]
│ StandardScaler:每列用训练折统计量变换
▼
Z_dev [96,8]
│ Ridge:拟合 w [8] 与 b []
▼
prediction [96]text当前 Ridge 官方目标是:
几个重要 API 细节:
Ridge(alpha=...):alpha必须非负;越大通常收缩越强。poly__...、ridge__alpha:Pipeline 使用步骤名__参数名暴露内部超参数。GridSearchCV(..., refit=True):用交叉验证选择最佳alpha后,在全部开发数据X_dev上重新拟合最佳 Pipeline。best_score_:这里仍是负 RMSE,报告误差时取负号。predict(X_test):只在全部选择冻结后调用一次;测试集从未进入网格搜索。
官方文档建议当 alpha=0 时直接使用 LinearRegression,而不是把 Ridge(alpha=0) 当普通最小二乘求解器。
07 不依赖黑盒:用 PyTorch 明确写出惩罚#
下面用线性层展示训练过程中的数据损失和 L2 项如何汇合。为了让公式完全对应代码,我们手动构造 l2_penalty,并且只惩罚 weight,不惩罚 bias:
import torch
torch.manual_seed(42)
X = torch.randn(32, 8) # [batch=32, features=8]
true_w = torch.tensor([[2.0], [-1.0], [0.5], [0.0],
[0.0], [0.0], [0.0], [0.0]]) # [8, 1]
y = X @ true_w + 0.1 * torch.randn(32, 1) # [32, 1]
model = torch.nn.Linear(8, 1) # weight [1,8], bias [1]
optimizer = torch.optim.SGD(model.parameters(), lr=0.05)
alpha = 0.01
for step in range(300):
prediction = model(X) # [32, 1]
data_loss = torch.mean((prediction - y) ** 2) # []
l2_penalty = model.weight.pow(2).sum() # []
objective = data_loss + alpha * l2_penalty # []
optimizer.zero_grad(set_to_none=True)
objective.backward()
optimizer.step()
with torch.no_grad():
test_X = torch.randn(4, 8) # [4, 8]
test_prediction = model(test_X) # [4, 1]
print('data loss:', data_loss.item())
print('weight norm:', model.weight.norm().item())python调试时要分别记录 data_loss、l2_penalty 和 objective。只看总目标下降,可能掩盖“数据损失已经变差很多,但惩罚项变小更多”的过强正则化。
若改用 torch.optim.SGD(..., weight_decay=...),必须先确认当前优化器文档的精确定义与系数约定;不要假设它与这段手写目标在任意优化器上都逐步一致。
08 alpha 应该怎样选择?#
alpha 是超参数,不由训练集残差直接决定。合理流程是:
候选 alpha(对数尺度)
│
▼
每个 alpha 在开发数据内做交叉验证
│
├── 太小:训练好,验证差
├── 适中:验证误差最低
└── 太大:训练与验证都差
│
▼
冻结 alpha 与全部流程
│
▼
只在最终测试集验收一次text通常用对数网格,例如 ,因为有效尺度可能跨多个数量级。找到较优区间后再细化,而不是一开始在线性刻度上试 0.1, 0.2, 0.3。
同时记录三组量:训练误差、验证误差、。随着 alpha 增大,权重范数应整体下降;如果完全不变,可能是参数名写错、Pipeline 网格未命中或代码没有使用预期估计器。
09 常见错误与调试方法#
- 不标准化就比较系数或统一惩罚。 不同单位让相同预测对应不同权重大小。
- 用测试集选择
alpha。 测试集一旦参与选择,就不再是最终无偏验收。 - 正则化了截距。 某些手写实现把所有参数一起平方;先明确是否真的希望惩罚全局基线。
- 混淆总和与均值。 数据损失从
sum改成mean后,正则项的相对强度会随样本数变化,alpha不能机械照搬。 - 只观察训练损失。 正则化本来就可能提高训练误差,关键证据来自验证误差。
- 把权重变小当成特征不重要。 高度相关特征会共享权重;缩小后的系数不等价于因果重要性。
- 多项式次数爆炸。 输入有 个特征时,高次组合数量迅速增长,内存和数值条件都可能先失控。
最小数值检查:
coef = search.best_estimator_.named_steps['ridge'].coef_
assert coef.shape == (8,)
assert np.isfinite(coef).all()
assert np.isfinite(search.best_score_)
assert search.best_params_['ridge__alpha'] >= 0python如果最佳值总落在搜索边界,应扩展网格;如果各折分数方差很大,应先检查数据划分和样本代表性,而不是把更多小数位当成稳定结论。
10 它与相近方法有什么不同?#
| 方法 | 惩罚或约束 | 典型效果 | 主要区别 |
|---|---|---|---|
| L2 / Ridge | 连续收缩,多数系数不为 0 | 对共线与高方差问题常很稳健 | |
| L1 / Lasso | 可产生精确的 0 | 可做稀疏选择,但相关特征中可能不稳定 | |
| Elastic Net | L1 与 L2 组合 | 稀疏且带平滑收缩 | 多一个混合比例超参数 |
| Early Stopping | 限制训练步数 | 阻止继续拟合噪声 | 通过优化路径约束,不显式惩罚参数 |
| 减小模型容量 | 删除特征、降阶、减小网络 | 缩小假设空间 | 直接移除表达能力,而非柔性收缩 |
L2 也有失败场景:真正关系需要少数极大系数时会被过度收缩;训练与部署分布改变时无法补救;标签泄漏时甚至可能让一个错误流程显得更稳定;非线性结构根本没有进入特征时,收缩线性权重也不能创造缺失规律。
11 今天真正需要记住什么?#
- L2 正则化在数据损失之外惩罚权重平方和,用一点训练拟合换取更稳定的未见数据预测。
- 梯度中增加与当前权重成比例的项,使参数在每步更新中向 0 收缩。
alpha越大不代表越好;过强正则化会从过拟合走向欠拟合,必须由验证集选择。- L2 对数值权重施加惩罚,因此特征尺度决定惩罚含义;标准化要放进防泄漏 Pipeline。
- 不同库对损失的求和、平均与系数约定可能不同,比较参数前先读目标函数定义。
12 思考题与小练习#
练习 1:手算两个维度的一步更新
设 ,数据梯度 ,,,目标使用 。总梯度是 ,更新后 。
练习 2:为什么单位会改变结果?
将米换成千米后,为保持预测相同,权重数值要放大 1,000 倍,平方惩罚放大 倍。若不标准化,同一个 alpha 实际施加了完全不同的约束。
练习 3:画一条验证曲线
在代码中记录每个 ridge__alpha 的训练 RMSE、验证 RMSE 与系数范数。找出验证误差最低点,并解释其左侧为何更像过拟合、右侧为何更像欠拟合。
相关工作#
- Hoerl & Kennard: Ridge Regression—Biased Estimation for Nonorthogonal Problems ↗:岭回归的奠基论文,讨论用有偏估计降低不稳定性。
- Tikhonov: Solution of Incorrectly Formulated Problems and the Regularization Method ↗:正则化逆问题的经典来源。
- Tibshirani: Regression Shrinkage and Selection via the Lasso ↗:以 L1 同时实现收缩与变量选择的代表性工作。
- Zou & Hastie: Regularization and Variable Selection via the Elastic Net ↗:结合 L1 与 L2 的经典方法。
- scikit-learn: Ridge ↗:当前官方目标函数、
alpha、输入形状和求解器说明。
13 下一篇预告#
岭回归展示了怎样在连续预测中控制线性模型的复杂度。下一篇将进入经典机器学习模型,完整推导逻辑回归:线性分数如何变成决策边界、概率如何产生,以及它与“线性回归后强行阈值化”究竟差在哪里。