别让模型“背答案“:正则化实战笔记

目录
- 1. 什么是正则化
- 2. 为什么需要正则化
- 3. 偏差-方差权衡
- 4. L1 正则化(Lasso)
- 5. L2 正则化(Ridge)
- 6. L1 与 L2 的对比
- 7. Elastic Net(弹性网络)
- 8. Dropout
- 9. 早停法(Early Stopping)
- 10. 数据增强(Data Augmentation)
- 11. 归一化方法详解
- 12. 权重衰减(Weight Decay)
- 13. 标签平滑(Label Smoothing)
- 14. 随机深度(Stochastic Depth)
- 15. R-Drop 一致性正则化
- 16. 知识蒸馏作为正则化
- 17. 梯度裁剪(Gradient Clipping)
- 18. 贝叶斯视角下的正则化
- 19. 正则化方法总览对比
- 20. 大模型时代的正则化特点
- 21. 正则化策略选择指南
- 22. 代码实践
- 23. 正则化效果评估
- 24. 总结
1. 什么是正则化
实际训练模型时常见这种情况:训练集上准确率刷得很高,一到测试集就露馅了。本质原因是模型把训练数据里的噪声和偶然规律也当成了"知识点",这种情况就是过拟合。正则化的思路很简单——给损失函数加个约束项,别让模型学过头。
数学上,正则化目标函数可以统一写成:
J reg ( θ ) = J ( θ ) + λ ⋅ Ω ( θ ) J_{\text{reg}}(\theta) = J(\theta) + \lambda \cdot \Omega(\theta) Jreg(θ)=J(θ)+λ⋅Ω(θ)
其中 J ( θ ) J(\theta) J(θ) 是原始损失(交叉熵、均方误差之类), Ω ( θ ) \Omega(\theta) Ω(θ) 是惩罚项, λ \lambda λ 控制惩罚力度, θ \theta θ 是所有可训练参数。
λ \lambda λ 是个关键旋钮。太小了等于没加正则化,太大了又会让模型学不到东西。具体怎么调,后面第 3 章会详细聊。
2. 为什么需要正则化
训练数据不够多、模型参数又太多时,模型就会把噪声当规律记住,这就是过拟合。
过拟合有三个典型信号:
- 训练集表现很好,测试集表现很差
- 模型参数的绝对值很大
- 输入稍微变一点,输出就剧烈波动
对付过拟合,正则化的思路很直接:要么限制参数别太大,要么给参数加个分布假设,要么让模型在多个子网络上取平均。
3. 偏差-方差权衡
要理解正则化,绕不开偏差-方差权衡。泛化误差可以拆成三部分:
泛化误差 = 偏差 2 + 方差 + 噪声 \text{泛化误差} = \text{偏差}^2 + \text{方差} + \text{噪声} 泛化误差=偏差2+方差+噪声
| 概念 | 含义 | 过拟合时的表现 |
|---|---|---|
| 偏差(Bias) | 模型预测与真实值的差距 | 低偏差 |
| 方差(Variance) | 模型对数据波动的敏感度 | 高方差 |
| 噪声(Noise) | 数据本身的随机性,不可约 | 不变 |
λ \lambda λ 的选择本质上就是在偏差和方差之间取舍。太小 → 过拟合(低偏差、高方差);太大 → 欠拟合(高偏差、低方差);刚好 → 泛化误差最小。
实际效果可以用一条 U 形曲线来表示:
误差
↑
| \
| \ ← 训练误差(随 λ 增大而增大)
| \
| \________________________________________
| \.
| \. ← 验证误差(先降后升,呈 U 形)
| \.
| \.
| \_____________
| \ ← 欠拟合区域
|____________________________\_________→ 正则化强度 λ
↑ ↑ ↑
λ 太小 λ 最优 λ 太大
过拟合 欠拟合
验证误差最低点对应的 λ \lambda λ 就是最优选择。实际操作中一般用网格搜索加交叉验证来找。
4. L1 正则化(Lasso)
4.1 数学定义
L1 正则化给损失函数加了参数绝对值之和作为惩罚:
J L1 ( θ ) = J ( θ ) + λ ∑ i = 1 n ∣ w i ∣ J_{\text{L1}}(\theta) = J(\theta) + \lambda \sum_{i=1}^{n} |w_i| JL1(θ)=J(θ)+λi=1∑n∣wi∣
4.2 稀疏性特性
L1 最出名的本事就是能产生稀疏解——把一部分权重精确地压到零,相当于自动做了特征选择。
为什么会产生稀疏解?从几何角度看,L1 的约束区域是高维空间中的菱形(二维下就是正方形),L2 的是圆形。损失函数的等高线和约束区域相切时,L1 的菱形更容易让切点落在坐标轴上,对应的维度权重就是零。
w₂
↑
| /\ ← L1 约束(菱形)
| / \
| / \
|/______\______→ w₁
|\ /|
| \ / |
| \ / |
| \/ | ← L2 约束(圆形)
| |
损失函数等高线(椭圆)与约束区域相切:
- L1 菱形:切点容易落在坐标轴上 → 稀疏解
- L2 圆形:切点一般不在坐标轴上 → 非稀疏解
4.3 求解方法
∣ w i ∣ |w_i| ∣wi∣ 在零点处不可导,所以不能直接套梯度下降。常用的解法有三种:
- 坐标下降法:每次只更新一个坐标方向,把多维问题拆成一维
- 近端梯度法:梯度下降一步之后再做个软阈值操作
- LARS:专门为线性模型设计的高效算法
软阈值操作的公式:
w i = sign ( w ~ i ) ⋅ max ( ∣ w ~ i ∣ − λ , 0 ) w_i = \text{sign}(\tilde{w}_i) \cdot \max(|\tilde{w}_i| - \lambda, 0) wi=sign(w~i)⋅max(∣w~i∣−λ,0)
其中 w ~ i \tilde{w}_i w~i 是去掉正则化项后梯度下降得到的中间值。直觉上就是先正常更新,然后把小于 λ \lambda λ 的值直接归零。
4.4 应用场景
- 高维数据,预期只有少数特征有用
- 需要自动特征选择的场景
- 模型可解释性要求高的场景(稀疏模型更容易解释)
5. L2 正则化(Ridge)
5.1 数学定义
L2 正则化加的是参数平方和:
J L2 ( θ ) = J ( θ ) + λ ∑ i = 1 n w i 2 = J ( θ ) + λ ∥ θ ∥ 2 2 J_{\text{L2}}(\theta) = J(\theta) + \lambda \sum_{i=1}^{n} w_i^2 = J(\theta) + \lambda \|\theta\|_2^2 JL2(θ)=J(θ)+λi=1∑nwi2=J(θ)+λ∥θ∥22
5.2 闭式解
对于线性回归,L2 正则化有解析解:
θ ^ = ( X T X + λ I ) − 1 X T y \hat{\theta} = (X^T X + \lambda I)^{-1} X^T y θ^=(XTX+λI)−1XTy
加入 λ I \lambda I λI 后,即使 X T X X^T X XTX 不可逆(比如特征数比样本数还多), X T X + λ I X^T X + \lambda I XTX+λI 也一定是可逆的正定矩阵。这是 L2 的一大数值优势。
5.3 梯度更新
L2 的梯度为:
∂ J L2 ∂ w i = ∂ J ∂ w i + 2 λ w i \frac{\partial J_{\text{L2}}}{\partial w_i} = \frac{\partial J}{\partial w_i} + 2\lambda w_i ∂wi∂JL2=∂wi∂J+2λwi
更新公式:
w i ← ( 1 − 2 η λ ) w i − η ∂ J ∂ w i w_i \leftarrow (1 - 2\eta\lambda) w_i - \eta \frac{\partial J}{\partial w_i} wi←(1−2ηλ)wi−η∂wi∂J
每次更新时权重先乘以一个小于 1 的因子 ( 1 − 2 η λ ) (1 - 2\eta\lambda) (1−2ηλ),这就是"权重衰减"的由来。
顺便提一句,有些文献和框架把正则项写成 λ 2 ∥ θ ∥ 2 2 \frac{\lambda}{2}\|\theta\|_2^2 2λ∥θ∥22,这时梯度中的系数变成 λ \lambda λ,衰减因子变成 ( 1 − η λ ) (1 - \eta\lambda) (1−ηλ),闭式解中的矩阵变成 ( X T X + λ 2 I ) − 1 (X^T X + \frac{\lambda}{2}I)^{-1} (XTX+2λI)−1。本文统一采用 λ ∥ θ ∥ 2 2 \lambda\|\theta\|_2^2 λ∥θ∥22 的写法。
5.4 实战案例:房价预测中的 L2 正则化
举个具体例子来看 L2 正则化的实际效果。假设要预测房价,数据集有 100 个特征(面积、卧室数、学区评分、距地铁站距离等),1000 个样本。
问题:特征之间存在相关性(比如面积和卧室数正相关),模型容易对训练数据过拟合。
不使用 L2 正则化:
训练集 R² = 0.95 测试集 R² = 0.72 泛化差距 = 0.23
权重分布:
w₁(面积) = 3.21 ← 权重很大
w₂(卧室数) = 2.87 ← 也很大
w₃(朝向) = -0.03 ← 几乎没用
w₄(楼层) = 0.15
...
w₉₅(噪音) = 2.45 ← 异常大,可能拟合了噪声
w₉₆(周边绿化率) = -1.98
问题:部分权重异常大,模型对输入的微小变化敏感
使用 L2 正则化(λ = 0.1):
训练集 R² = 0.88 测试集 R² = 0.85 泛化差距 = 0.03
权重分布:
w₁(面积) = 1.56 ← 缩小了约一半
w₂(卧室数) = 1.23
w₃(朝向) = -0.02
w₄(楼层) = 0.09
...
w₉₅(噪音) = 0.31 ← 异常大权重被有效抑制
w₉₆(周边绿化率) = -0.47
改善:权重分布更均匀,异常大权重被压缩,泛化差距从 0.23 降到 0.03
不同 λ 值的效果对比:
λ 训练 R² 测试 R² 泛化差距 最大权重
0 0.95 0.72 0.23 3.21
0.001 0.93 0.80 0.13 2.15
0.01 0.90 0.84 0.06 1.72
0.1 0.88 0.85 0.03 1.56 ← 最优
1.0 0.80 0.79 0.01 0.83
10.0 0.60 0.58 0.02 0.21 ← 欠拟合
从表中可以看到,λ = 0.1 时测试集表现最好。λ 太小(0.001)正则化不够,模型仍然过拟合;λ 太大(10.0)模型被压得太狠,连正常规律都学不到了。
几个值得注意的点:
- L2 对所有权重做了均匀压缩,把异常大的权重拉回到合理范围
- 相关特征(面积、卧室数)的权重被分摊,不再集中于某一个
- 最终的权重分布更平滑,模型对新数据的预测更稳定
5.5 特性
- 不会产生稀疏解,所有权重都缩小但不精确为零
- 对异常值比 L1 更敏感(平方会放大离群值的影响)
- 处处可导,计算上更友好
- 适合所有特征都有一定贡献的场景
6. L1 与 L2 的对比
| 特性 | L1(Lasso) | L2(Ridge) |
|---|---|---|
| 惩罚项 | ∑ ∣ w i ∣ \sum |w_i| ∑∣wi∣ | ∑ w i 2 \sum w_i^2 ∑wi2 |
| 稀疏性 | 能产生稀疏解 | 不能 |
| 特征选择 | 自动进行 | 不进行 |
| 可导性 | 零点不可导 | 处处可导 |
| 闭式解 | 一般没有 | 线性回归有 |
| 异常值敏感度 | 较低 | 较高 |
| 参数多于样本 | 最多选 n 个特征 | 始终可解 |
| 相关特征处理 | 倾向于选其中一个 | 倾向于平均分配权重 |
简单说就是需要做特征选择用 L1,不需要用 L2,拿不准就试 Elastic Net。
7. Elastic Net(弹性网络)
L1 和 L2 各有短板。L1 遇到相关特征群组时会随机挑一个,不太稳定;L2 不会做特征选择。Elastic Net 把两者结合起来:
J EN ( θ ) = J ( θ ) + λ ( α ∥ θ ∥ 1 + ( 1 − α ) ∥ θ ∥ 2 2 ) J_{\text{EN}}(\theta) = J(\theta) + \lambda \left( \alpha \|\theta\|_1 + (1-\alpha) \|\theta\|_2^2 \right) JEN(θ)=J(θ)+λ(α∥θ∥1+(1−α)∥θ∥22)
α ∈ [ 0 , 1 ] \alpha \in [0, 1] α∈[0,1] 控制 L1 和 L2 的混合比例。它继承了 L1 的稀疏性和 L2 的稳定性,还能把相关特征作为群组一起选中或一起剔除(群组效应)。
典型应用场景包括基因组学(成千上万个基因特征,只有少量有效)、特征高度共线性的回归问题等。
8. Dropout
8.1 基本思路
Dropout 的做法很粗暴:训练时随机把一部分神经元的输出置为零,每次前向传播,网络结构都不一样。
8.2 数学描述
训练时第 l l l 层的前向传播变为:
h ~ ( l ) = m ( l ) ⊙ h ( l ) , m i ( l ) ∼ Bernoulli ( p ) \tilde{h}^{(l)} = m^{(l)} \odot h^{(l)}, \quad m^{(l)}_i \sim \text{Bernoulli}(p) h~(l)=m(l)⊙h(l),mi(l)∼Bernoulli(p)
m ( l ) m^{(l)} m(l) 是掩码向量,每个元素以概率 p p p 保留、以概率 1 − p 1-p 1−p 丢弃。
8.3 为什么有效
可以从三个角度理解 Dropout 为什么有效:
- 模型平均:每次前向传播相当于采样一个不同的子网络。n 个神经元最多产生 2 n 2^n 2n 个子网络,训练过程等价于对这些子网络做隐式集成。
原始网络 子网络 A 子网络 B 子网络 C
+-----------+ +-----------+ +-----------+ +-----------+
| o1 o2 | | o1 o2 | | o1 o2 | | -- o2 |
| o3 o4 | | -- -- | | -- o3 | | o3 o4 |
+-----------+ +-----------+ +-----------+ +-----------+
4 个神经元 丢弃 o3, o4 丢弃 o2, o4 丢弃 o1, o3
n 个神经元 → 最多 2^n 个子网络 → 隐式集成(-- 表示被丢弃)
-
打破共适应:神经元不能总是指望特定的搭档存在,被迫独立学习有用的特征。
-
噪声注入:乘性噪声让模型对输入的扰动更鲁棒。
8.4 实践要点
- 丢弃率一般取 0.2 ~ 0.5
- 输入层用低丢弃率(0.1 ~ 0.2),全连接层用高丢弃率(0.5)
- 卷积层通常用 Spatial Dropout(丢弃整个特征图而非单个激活值)
- Inverted Dropout 把缩放挪到训练阶段,测试时什么都不用做
9. 早停法(Early Stopping)
9.1 基本思路
训练过程中,验证集损失通常会先降后升。早停法就是在验证损失开始上升之前喊停,回滚到最好的那个模型。这个思路最早由 Bishop 在 1995 年提出,至今仍然是最常用的正则化手段之一。
9.2 损失曲线
训练过程中,训练损失通常持续下降,而验证损失会先降后升。两者的分歧点就是过拟合开始的信号。
损失
^
| 训练损失 验证损失(先降后升)
| \ .
| \ .'
| \ .'
| \ .'
| \ .'
| \ .'
| \ .'
| \ .'
| \ .' ← 早停点(验证损失开始上升)
| \ .'
|________________________________________> 训练轮次 (Epoch)
9.3 实践要点
- 耐心值一般设 5 ~ 20 个 Epoch
- 可以监控验证损失或验证准确率
- 早停后一定要回滚到验证损失最低时的参数
- 可以和其他正则化方法组合使用
10. 数据增强(Data Augmentation)
10.1 基本思路
数据不够怎么办?自己造。通过对训练数据做各种变换来扩充数据集,让模型见过更多变体。
10.2 常见增强策略
图像数据增强:
| 变换类型 | 具体方法 | 效果 |
|---|---|---|
| 几何变换 | 随机翻转、旋转、裁剪、缩放 | 增强空间不变性 |
| 色彩变换 | 亮度、对比度、饱和度调整 | 增强色彩鲁棒性 |
| 噪声注入 | 高斯噪声、椒盐噪声 | 增强抗噪能力 |
| 混合策略 | Mixup、CutMix、CutOut | 正则化效果更强 |
文本数据增强:
| 变换类型 | 具体方法 | 效果 |
|---|---|---|
| 同义词替换 | 用同义词替换部分词语 | 增强语义鲁棒性 |
| 随机插入、删除、交换 | 对文本序列做随机修改 | 增强序列鲁棒性 |
| 回译 | 翻译成其他语言再翻译回来 | 生成语义等价的新样本 |
音频数据增强:
| 变换类型 | 具体方法 | 效果 |
|---|---|---|
| 时域变换 | 时间拉伸、音高偏移 | 增强时域鲁棒性 |
| 频域变换 | 频谱掩蔽(SpecAugment) | 增强频域鲁棒性 |
| 环境模拟 | 添加背景噪声、混响 | 增强环境鲁棒性 |
10.3 Mixup 与 CutMix
Mixup 和 CutMix 在样本对之间做混合,而不是对单个样本做变换。
Mixup:把两张图和它们的标签按一定比例混合:
x ~ = λ x i + ( 1 − λ ) x j , y ~ = λ y i + ( 1 − λ ) y j \tilde{x} = \lambda x_i + (1 - \lambda) x_j, \quad \tilde{y} = \lambda y_i + (1 - \lambda) y_j x~=λxi+(1−λ)xj,y~=λyi+(1−λ)yj
λ \lambda λ 从 Beta ( α , α ) \text{Beta}(\alpha, \alpha) Beta(α,α) 分布中采样。
CutMix:把一张图的局部区域替换成另一张图的对应区域,标签按区域面积比例混合。
11. 归一化方法详解
归一化技术通过调整中间层输入的分布来稳定训练。虽然主要目的是加速收敛,但也附带一定的正则化效果——mini-batch 统计量本身就是随机噪声。
11.1 批量归一化(Batch Normalization)
BN 由 Ioffe 和 Szegedy 在 2015 年提出。对每个 mini-batch,在特征维度上做归一化:
μ d = 1 m ∑ i = 1 m x i , d , σ d 2 = 1 m ∑ i = 1 m ( x i , d − μ d ) 2 \mu_d = \frac{1}{m} \sum_{i=1}^{m} x_{i,d}, \quad \sigma_d^2 = \frac{1}{m} \sum_{i=1}^{m} (x_{i,d} - \mu_d)^2 μd=m1i=1∑mxi,d,σd2=m1i=1∑m(xi,d−μd)2
x ^ i , d = x i , d − μ d σ d 2 + ϵ , y i , d = γ d x ^ i , d + β d \hat{x}_{i,d} = \frac{x_{i,d} - \mu_d}{\sqrt{\sigma_d^2 + \epsilon}}, \quad y_{i,d} = \gamma_d \hat{x}_{i,d} + \beta_d x^i,d=σd2+ϵxi,d−μd,yi,d=γdx^i,d+βd
γ d \gamma_d γd 和 β d \beta_d βd 是可学习的参数, ϵ \epsilon ϵ 是个很小的常数( 10 − 5 10^{-5} 10−5 左右)防止除零。
测试阶段用训练时累积的指数滑动平均统计量。
BN 的正则化效果来自:减少内部协变量偏移、平滑损失曲面、mini-batch 统计量引入的隐式噪声。
11.2 层归一化(Layer Normalization)
LN 由 Ba 等人在 2016 年提出。对单个样本沿特征维度做归一化,不依赖批量维度:
μ = 1 D ∑ d = 1 D x d , σ 2 = 1 D ∑ d = 1 D ( x d − μ ) 2 \mu = \frac{1}{D} \sum_{d=1}^{D} x_d, \quad \sigma^2 = \frac{1}{D} \sum_{d=1}^{D} (x_d - \mu)^2 μ=D1d=1∑Dxd,σ2=D1d=1∑D(xd−μ)2
y d = γ d ⋅ x d − μ σ 2 + ϵ + β d y_d = \gamma_d \cdot \frac{x_d - \mu}{\sqrt{\sigma^2 + \epsilon}} + \beta_d yd=γd⋅σ2+ϵxd−μ+βd
| 特性 | Batch Norm | Layer Norm |
|---|---|---|
| 归一化维度 | 跨样本,单特征 | 单样本,跨特征 |
| 依赖批量大小 | 是(批量小时不稳定) | 否 |
| 训练/测试行为不同 | 是(用 running stats) | 否(始终用当前统计量) |
| 适用架构 | CNN | RNN、Transformer |
11.3 实例归一化(Instance Normalization)
IN 对每个样本的每个通道单独归一化,最初用于风格迁移。对于形状为 ( N , C , H , W ) (N, C, H, W) (N,C,H,W) 的特征图:
μ n , c = 1 H × W ∑ h , w x n , c , h , w , σ n , c 2 = 1 H × W ∑ h , w ( x n , c , h , w − μ n , c ) 2 \mu_{n,c} = \frac{1}{H \times W} \sum_{h,w} x_{n,c,h,w}, \quad \sigma_{n,c}^2 = \frac{1}{H \times W} \sum_{h,w} (x_{n,c,h,w} - \mu_{n,c})^2 μn,c=H×W1h,w∑xn,c,h,w,σn,c2=H×W1h,w∑(xn,c,h,w−μn,c)2
IN 去除了通道间的对比度信息,保留每个通道的独立统计特性,这对风格迁移很合适——去掉内容相关的对比度,保留风格信息。
11.4 组归一化(Group Normalization)
GN 由 He 等人在 2017 年提出,将通道分成 G G G 组,在每组内归一化。是 BN 和 IN 的折中:
μ n , g = 1 ( C / G ) × H × W ∑ c ∈ g ∑ h , w x n , c , h , w \mu_{n,g} = \frac{1}{(C/G) \times H \times W} \sum_{c \in g} \sum_{h,w} x_{n,c,h,w} μn,g=(C/G)×H×W1c∈g∑h,w∑xn,c,h,w
G = 1 G = 1 G=1 时退化为 LN, G = C G = C G=C 时退化为 IN。优势是不依赖批量大小,批量小时性能比 BN 好很多。
11.5 RMSNorm
RMSNorm 是现代大语言模型(LLaMA、Gemma、Qwen 等)的标配。它简化了 Layer Norm,去掉了均值中心化:
$$
\text{RMS}(x) = \sqrt{\frac{1}{D} \sum_{d=1}^{D} x_d^2 + \epsilon}, \quad y_d = \gamma_d \cdot \frac{x_d}{\text{RMS}(x)}
为什么取代了 Layer Norm?计算效率更高(少了均值计算和减法),内核融合更容易实现,效果相当甚至更优,在大规模训练中数值稳定性也更好。
```mermaid
flowchart LR
subgraph layer_norm_flow
LN1[输入 x] --> LN2["计算 μ 和 σ2"]
LN2 --> LN3["(x - μ) / σ"]
LN3 --> LN4["γ·x̂ + β"]
end
subgraph rmsnorm_flow
RN1[输入 x] --> RN2["计算 RMS = sqrt(mean(x2))"]
RN2 --> RN3["x / RMS"]
RN3 --> RN4["γ·x̃"]
end
LN4 --> OUT[输出]
RN4 --> OUT
style LN1 fill:transparent
style RN1 fill:transparent
style RN4 fill:transparent
各归一化方法综合对比:
| 方法 | 归一化维度 | 可学习参数 | 依赖批量 | 典型应用 |
|---|---|---|---|---|
| Batch Norm | ( N , H , W ) (N, H, W) (N,H,W) | γ , β \gamma, \beta γ,β 各 C C C 个 | 是 | 图像分类 CNN |
| Layer Norm | ( C , H , W ) (C, H, W) (C,H,W) 或 ( C ) (C) (C) | γ , β \gamma, \beta γ,β 各 D D D 个 | 否 | Transformer、RNN |
| Instance Norm | ( H , W ) (H, W) (H,W) | γ , β \gamma, \beta γ,β 各 C C C 个 | 否 | 风格迁移 |
| Group Norm | ( C / G , H , W ) (C/G, H, W) (C/G,H,W) | γ , β \gamma, \beta γ,β 各 C C C 个 | 否 | 小批量图像任务 |
| RMSNorm | ( C ) (C) (C) 或最后一维 | γ \gamma γ 共 D D D 个 | 否 | 大语言模型 |
12. 权重衰减(Weight Decay)
12.1 基本思路
权重衰减就是在每次参数更新时把权重乘以一个略小于 1 的因子,让权重不断向零收缩:
w ← ( 1 − λ ) w − η ∂ J ∂ w w \leftarrow (1 - \lambda) w - \eta \frac{\partial J}{\partial w} w←(1−λ)w−η∂w∂J
12.2 与 L2 正则化的关系
在 SGD 中,权重衰减和 L2 正则化是等价的,可以相互转化:
λ decay = λ L2 η \lambda_{\text{decay}} = \frac{\lambda_{\text{L2}}}{\eta} λdecay=ηλL2
但在自适应优化器(如 Adam)中两者不等价。L2 正则化的梯度会被自适应学习率缩放,导致不同参数的正则化力度不均匀。权重衰减直接作用于权重本身,不受学习率影响,正则化效果更一致。
12.3 AdamW
AdamW 将权重衰减与梯度更新解耦:
w ← ( 1 − λ ) w − η ⋅ m ^ t v ^ t + ϵ w \leftarrow (1 - \lambda) w - \eta \cdot \frac{\hat{m}_t}{\sqrt{\hat{v}_t} + \epsilon} w←(1−λ)w−η⋅v^t+ϵm^t
m ^ t \hat{m}_t m^t 和 v ^ t \hat{v}_t v^t 是 Adam 中偏差修正后的一阶和二阶矩估计。
现在主流深度学习框架中,weight_decay 参数默认就是 AdamW 式的解耦衰减,不再需要手动在损失函数里加 L2 惩罚项。
13. 标签平滑(Label Smoothing)
分类任务里,训练标签通常是 one-hot 向量:[0, 0, 1, 0, 0]。这种硬标签容易让模型过度自信——把全部概率质量压在正确类别上,logit 值越来越大,模型的校准度变差。标签平滑的做法是把硬标签换成软标签,平滑因子 ϵ \epsilon ϵ 通常取 0.1:
y smooth = [ ϵ K − 1 , ϵ K − 1 , 1 − ϵ , ϵ K − 1 , ϵ K − 1 ] y_{\text{smooth}} = \left[\frac{\epsilon}{K-1},\ \frac{\epsilon}{K-1},\ 1 - \epsilon,\ \frac{\epsilon}{K-1},\ \frac{\epsilon}{K-1}\right] ysmooth=[K−1ϵ, K−1ϵ, 1−ϵ, K−1ϵ, K−1ϵ]
正确类别的概率质量是 1 − ϵ 1 - \epsilon 1−ϵ,剩余的 ϵ \epsilon ϵ 均匀分给其他 K − 1 K-1 K−1 个类别。
标签平滑有效的理由主要有四点:
- 防止过度自信:不再让模型把概率全部集中在正确类别上
- 隐式正则化:等价于在损失中加了对均匀分布的 KL 散度惩罚
- 改善梯度信号:非目标类别的梯度不再为零,所有类别的参数都能得到有效更新
- 提升校准度:预测概率和实际准确率更一致
实际使用中,PyTorch 的 CrossEntropyLoss 直接支持 label_smoothing 参数,一行代码搞定。
14. 随机深度(Stochastic Depth)
Huang 等人在 2016 年提出,用于训练极深的残差网络(ResNet-1000 这种)。训练时随机跳过某些残差块,测试时用完整的网络。这个想法受 Dropout 启发,但作用粒度从单个神经元提升到了整个网络层。
对于第 l l l 个残差块:
h ( l + 1 ) = { f ( l ) ( h ( l ) ) + h ( l ) , 以概率 p l 保留 h ( l ) , 以概率 1 − p l 跳过 h^{(l+1)} = \begin{cases} f^{(l)}(h^{(l)}) + h^{(l)}, & \text{以概率 } p_l \text{ 保留} \\ h^{(l)}, & \text{以概率 } 1 - p_l \text{ 跳过} \end{cases} h(l+1)={f(l)(h(l))+h(l),h(l),以概率 pl 保留以概率 1−pl 跳过
存活概率通常线性衰减,浅层高、深层低:
p l = 1 − l L ( 1 − p L ) p_l = 1 - \frac{l}{L}(1 - p_L) pl=1−Ll(1−pL)
L L L 是总层数, p L p_L pL 一般取 0.5。
为什么有效?训练时网络的有效深度随机变化,相当于在多个不同深度的子网络上做隐式集成。跳层连接给梯度提供了更短的传播路径,还能减少训练时的计算量。
| 特性 | Dropout | 随机深度 |
|---|---|---|
| 丢弃粒度 | 单个神经元 | 整个残差块 |
| 适用架构 | 通用 | 残差网络 |
| 训练加速 | 否 | 是(跳层减少计算) |
| 正则化层级 | 细粒度 | 粗粒度 |
15. R-Drop 一致性正则化
Wu 等人在 2021 年提出。同一个输入过两次带 Dropout 的网络(两次的掩码不同),输出的分布应该尽量接近,在两次输出之间加对称 KL 散度惩罚:
J R-Drop = J CE + α ⋅ 1 2 ( D KL ( p 1 ∥ p 2 ) + D KL ( p 2 ∥ p 1 ) ) J_{\text{R-Drop}} = J_{\text{CE}} + \alpha \cdot \frac{1}{2} \left( D_{\text{KL}}(p_1 \| p_2) + D_{\text{KL}}(p_2 \| p_1) \right) JR-Drop=JCE+α⋅21(DKL(p1∥p2)+DKL(p2∥p1))
R-Drop 能平滑输出分布、增强损失曲面局部平坦性,而且不需要额外数据。 α \alpha α 一般取 1.0 ~ 5.0,额外开销就是多一次前向传播。在低数据场景下效果尤其好。
16. 知识蒸馏作为正则化
Hinton 等人在 2015 年提出。小模型(学生)学习大模型(教师)的软输出,而不是直接学硬标签。这个思路的核心在于:教师模型输出里包含的"暗知识"(类别间的相似性信息)比单纯的硬标签丰富得多。
J KD = ( 1 − α ) ⋅ J CE ( y , σ ( z s ) ) + α ⋅ T 2 ⋅ J KL ( σ ( z t / T ) ∥ σ ( z s / T ) ) J_{\text{KD}} = (1 - \alpha) \cdot J_{\text{CE}}(y, \sigma(z_s)) + \alpha \cdot T^2 \cdot J_{\text{KL}}(\sigma(z_t / T) \| \sigma(z_s / T)) JKD=(1−α)⋅JCE(y,σ(zs))+α⋅T2⋅JKL(σ(zt/T)∥σ(zs/T))
z s , z t z_s, z_t zs,zt 是学生和教师的 logit, T T T 是温度参数控制软度, α \alpha α 是蒸馏损失权重。
温度 T T T 的作用: T = 1 T = 1 T=1 退化为标准 Softmax; T > 1 T > 1 T>1 让输出更平滑,暴露出类别间的"暗知识"(比如"这张猫图看起来也有点像狗"); T → ∞ T \to \infty T→∞ 趋向均匀分布,信息就丢了。
蒸馏作为正则化的原理:软标签提供了比硬标签更丰富的监督信号;目标函数更平滑;教师模型本身可以看作集成模型的近似。
自蒸馏是特殊情况——教师和学生是同一个架构,训练时用 EMA 版本做教师。
17. 梯度裁剪(Gradient Clipping)
RNN 和深层 Transformer 里,梯度可能因为连乘效应指数增长导致爆炸。梯度裁剪通过限制梯度的最大范数来防止这个问题。虽然主要目的是稳定训练,但也间接限制了参数更新的最大步长,起到了正则化作用。Pascanu 等人在 2013 年系统地分析了梯度爆炸问题并提出了按范数裁剪的方法。
两种裁剪方式:
按值裁剪:每个梯度元素限制在 [ − c , c ] [-c, c] [−c,c]
g i = clip ( g i , − c , c ) g_i = \text{clip}(g_i, -c, c) gi=clip(gi,−c,c)
按范数裁剪(推荐):梯度 L2 范数超过 c c c 时按比例缩放,保持方向不变
g = { g , ∥ g ∥ 2 ≤ c c ⋅ g ∥ g ∥ 2 , ∥ g ∥ 2 > c g = \begin{cases} g, & \|g\|_2 \leq c \\ c \cdot \frac{g}{\|g\|_2}, & \|g\|_2 > c \end{cases} g={g,c⋅∥g∥2g,∥g∥2≤c∥g∥2>c
实践要点:阈值 c c c 一般取 0.5 ~ 5.0(PyTorch 默认 1.0),推荐用 clip_grad_norm_。RNN、Transformer、GAN 训练中几乎是标配。
18. 贝叶斯视角下的正则化
18.1 MAP 估计
贝叶斯定理给出参数后验分布:
p ( θ ∣ D ) ∝ p ( D ∣ θ ) ⋅ p ( θ ) p(\theta | \mathcal{D}) \propto p(\mathcal{D} | \theta) \cdot p(\theta) p(θ∣D)∝p(D∣θ)⋅p(θ)
最大后验估计(MAP):
θ ^ MAP = arg min θ [ − log p ( D ∣ θ ) − log p ( θ ) ] \hat{\theta}_{\text{MAP}} = \arg\min_\theta \left[ -\log p(\mathcal{D} | \theta) - \log p(\theta) \right] θ^MAP=argθmin[−logp(D∣θ)−logp(θ)]
对比正则化目标函数 J reg ( θ ) = J ( θ ) + λ ⋅ Ω ( θ ) J_{\text{reg}}(\theta) = J(\theta) + \lambda \cdot \Omega(\theta) Jreg(θ)=J(θ)+λ⋅Ω(θ),可以发现: J ( θ ) J(\theta) J(θ) 对应负对数似然, λ ⋅ Ω ( θ ) \lambda \cdot \Omega(\theta) λ⋅Ω(θ) 对应负对数先验。
18.2 先验分布
L1 ↔ 拉普拉斯先验:
p ( w i ) = λ 2 exp ( − λ ∣ w i ∣ ) , − log p ( θ ) = λ ∑ i ∣ w i ∣ + const p(w_i) = \frac{\lambda}{2} \exp(-\lambda |w_i|), \quad -\log p(\theta) = \lambda \sum_i |w_i| + \text{const} p(wi)=2λexp(−λ∣wi∣),−logp(θ)=λi∑∣wi∣+const
L2 ↔ 高斯先验:
$$
p(w_i) = \frac{1}{\sqrt{2\pi\sigma^2}} \exp\left(-\frac{w_i2}{2\sigma2}\right), \quad -\log p(\theta) = \frac{1}{2\sigma^2} \sum_i w_i^2 + \text{const}
```mermaid
flowchart LR
subgraph prior_dist
P1[拉普拉斯分布<br/>p(w) ∝ exp(-λ|w|)]
P2[高斯分布<br/>p(w) ∝ exp(-w2/2σ2)]
end
P1 --> N1[负对数先验]
P2 --> N2[负对数先验]
N1 --> F1["λ·Σ|w_i|"]
N2 --> F2["Σw_i2 / 2σ2"]
F1 --> R1[L1 正则化<br/>稀疏解]
F2 --> R2[L2 正则化<br/>权重衰减]
style P1 fill:transparent
style P2 fill:transparent
style R1 fill:transparent
style R2 fill:transparent
概率密度
↑
| ╱╲
| ╱ ╲
|╱ ╲
| ╲
| ╲
| ╲
| ╲
| ╲
| ╲
|____________╲___________→ w
-3 -2 -1 0 1 2 3
拉普拉斯(L1):尖峰在零点,重尾 → 鼓励稀疏
高斯(L2):平滑钟形,快速衰减 → 鼓励小值
18.3 更广泛的解释
| 正则化方法 | 对应的先验/约束 | 贝叶斯解释 |
|---|---|---|
| L1 正则化 | 拉普拉斯先验 | 参数服从重尾分布,鼓励稀疏 |
| L2 正则化 | 高斯先验 | 参数服从正态分布,鼓励小值 |
| Dropout | 伯努利掩码 | 参数的变分近似,模型不确定性 |
| 早停法 | 优化步数限制 | 限制后验探索范围 |
| 数据增强 | 不变性先验 | 对变换具有不变性的隐式先验 |
正则化本质上是对参数施加先验知识,把 MLE 变成 MAP。
19. 正则化方法总览对比
| 方法 | 作用层面 | 计算开销 | 可微分 | 适用场景 | 核心超参数 |
|---|---|---|---|---|---|
| L1 正则化 | 参数 | 低 | 否(零点) | 特征选择、高维稀疏数据 | λ \lambda λ |
| L2 正则化 | 参数 | 低 | 是 | 通用场景、数值稳定 | λ \lambda λ |
| Elastic Net | 参数 | 低 | 否(零点) | 特征相关的高维数据 | λ , α \lambda, \alpha λ,α |
| Dropout | 神经元 | 低 | 是 | 全连接层、RNN | 丢弃率 p p p |
| 早停法 | 训练过程 | 无 | N/A | 所有场景 | 耐心值 |
| 数据增强 | 数据 | 中 | 是 | 图像、文本、音频 | 增强策略 |
| 批量归一化 | 激活值 | 中 | 是 | CNN、大批量训练 | 动量 |
| RMSNorm | 激活值 | 低 | 是 | 大语言模型 | 无 |
| 权重衰减 | 参数 | 低 | 是 | AdamW 优化器 | λ \lambda λ |
| 标签平滑 | 损失函数 | 低 | 是 | 分类任务 | ϵ \epsilon ϵ |
| 随机深度 | 网络层 | 低 | 是 | 极深残差网络 | 存活率 p l p_l pl |
| R-Drop | 损失函数 | 中(2x前向) | 是 | 低数据场景 | α \alpha α |
| 知识蒸馏 | 损失函数 | 中 | 有教师模型时 | 模型压缩、提升泛化 | T , α T, \alpha T,α |
| 梯度裁剪 | 梯度 | 低 | 是 | RNN、Transformer、GAN | 阈值 c c c |
20. 大模型时代的正则化特点
GPT-4、Claude、LLaMA 这些大模型的正则化策略和传统深度学习有明显区别,这种差异主要源于数据量、模型规模和训练方式的根本性变化。
20.1 策略变化
| 技术 | 传统深度学习 | 大语言模型 |
|---|---|---|
| 归一化 | Batch Norm | RMSNorm / Layer Norm |
| 权重衰减 | L2 正则化 | AdamW 解耦权重衰减 |
| Dropout | 广泛使用(0.1~0.5) | 几乎不用或极低(0.0~0.1) |
| 数据增强 | 图像翻转、裁剪等 | 文本去重、数据混合、课程学习 |
| 早停法 | 常用 | 不常用(训练固定步数) |
| 标签平滑 | 分类任务常用 | 不常用 |
| 梯度裁剪 | 常用 | 几乎标配(阈值 1.0) |
| 随机深度 | 极深 CNN | 部分模型使用 |
20.2 为什么大模型不用 Dropout
- 数据量太大(万亿 token),过拟合风险本身就低
- 权重衰减 + 梯度裁剪已经足够
- Transformer 的注意力机制天然有软选择效果,类似自适应 Dropout
- Dropout 的随机性会加剧大模型训练的不稳定性
20.3 推荐配置
大语言模型标准正则化配置:
├── 优化器:AdamW(weight_decay = 0.1)
├── 归一化:RMSNorm
├── 梯度裁剪:clip_grad_norm_(max_norm = 1.0)
├── 学习率调度:Warmup + Cosine Decay
├── Dropout:0.0 ~ 0.1(可选)
└── 数据层面:数据去重、质量过滤、课程学习
21. 正则化策略选择指南
21.1 按模型类型选择
21.2 按数据特征选择
| 数据特征 | 推荐正则化策略 |
|---|---|
| 高维稀疏数据 | L1 正则化、Elastic Net |
| 特征高度相关 | Elastic Net、L2 正则化 |
| 数据量小 | 数据增强、早停法、Dropout |
| 数据量大 | L2 正则化、早停法 |
| 图像数据 | 数据增强、Dropout、BN |
| 文本数据 | Dropout、权重衰减、标签平滑 |
21.3 组合使用
实际项目中通常组合使用多种方法。一个典型的组合策略大致是这样的:
通用组合策略:
├── 权重正则化(L2 或 AdamW 权重衰减)
├── Dropout(0.1 ~ 0.5)
├── 早停法(patience = 10 ~ 20)
├── 数据增强(根据数据类型选择)
└── 批量归一化 / 层归一化
22. 代码实践
22.1 PyTorch 实现
import torch
import torch.nn as nn
import torch.optim as optim
# L2 正则化 / 权重衰减
model = nn.Sequential(nn.Linear(784, 256), nn.ReLU(), nn.Linear(256, 10))
optimizer = optim.AdamW(model.parameters(), lr=1e-3, weight_decay=1e-2) # 推荐
# optimizer = optim.SGD(model.parameters(), lr=1e-1, weight_decay=1e-3, momentum=0.9) # 传统方式
# L1 正则化需要手动加
def l1_regularization(model, lambda_l1=1e-5):
return lambda_l1 * sum(p.abs().sum() for p in model.parameters())
# Dropout
class ClassifierWithDropout(nn.Module):
def __init__(self, input_dim=784, hidden_dim=256, num_classes=10, drop_rate=0.5):
super().__init__()
self.network = nn.Sequential(
nn.Linear(input_dim, hidden_dim), nn.ReLU(),
nn.Dropout(p=drop_rate),
nn.Linear(hidden_dim, hidden_dim), nn.ReLU(),
nn.Dropout(p=drop_rate),
nn.Linear(hidden_dim, num_classes)
)
def forward(self, x):
return self.network(x)
model.train() # 训练模式:启用 Dropout
model.eval() # 评估模式:关闭 Dropout
# 标签平滑
criterion = nn.CrossEntropyLoss(label_smoothing=0.1)
# 早停法
class EarlyStopping:
def __init__(self, patience=10, min_delta=1e-4):
self.patience = patience
self.min_delta = min_delta
self.counter = 0
self.best_loss = None
self.best_model_state = None
def __call__(self, val_loss, model):
if self.best_loss is None:
self.best_loss = val_loss
self.best_model_state = {k: v.clone() for k, v in model.state_dict().items()}
return False
if val_loss < self.best_loss - self.min_delta:
self.best_loss = val_loss
self.best_model_state = {k: v.clone() for k, v in model.state_dict().items()}
self.counter = 0
return False
else:
self.counter += 1
if self.counter >= self.patience:
model.load_state_dict(self.best_model_state)
return True
return False
# 数据增强
import torchvision.transforms as transforms
train_transform = transforms.Compose([
transforms.RandomHorizontalFlip(p=0.5),
transforms.RandomRotation(degrees=15),
transforms.RandomCrop(32, padding=4),
transforms.ColorJitter(brightness=0.2, contrast=0.2, saturation=0.2),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])
# 随机深度
class StochasticDepthBlock(nn.Module):
def __init__(self, in_channels, out_channels, stride=1, survival_prob=0.8):
super().__init__()
self.survival_prob = survival_prob
self.residual = nn.Sequential(
nn.Conv2d(in_channels, out_channels, 3, stride, 1, bias=False),
nn.BatchNorm2d(out_channels), nn.ReLU(inplace=True),
nn.Conv2d(out_channels, out_channels, 3, 1, 1, bias=False),
nn.BatchNorm2d(out_channels)
)
self.shortcut = nn.Identity() if (stride == 1 and in_channels == out_channels) \
else nn.Sequential(nn.Conv2d(in_channels, out_channels, 1, stride, bias=False),
nn.BatchNorm2d(out_channels))
self.relu = nn.ReLU(inplace=True)
def forward(self, x):
if self.training and torch.rand(1).item() > self.survival_prob:
return self.relu(self.shortcut(x))
return self.relu(self.residual(x) + self.shortcut(x))
# RMSNorm
class RMSNorm(nn.Module):
def __init__(self, dim, eps=1e-6):
super().__init__()
self.eps = eps
self.weight = nn.Parameter(torch.ones(dim))
def forward(self, x):
rms = torch.sqrt(torch.mean(x ** 2, dim=-1, keepdim=True) + self.eps)
return x / rms * self.weight
22.2 TensorFlow / Keras 实现
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers, regularizers
model = keras.Sequential([
layers.Dense(256, activation='relu',
kernel_regularizer=regularizers.l2(1e-4),
bias_regularizer=regularizers.l2(1e-4)),
layers.Dropout(0.5),
layers.Dense(128, activation='relu',
kernel_regularizer=regularizers.l2(1e-4)),
layers.Dropout(0.3),
layers.Dense(10, activation='softmax')
])
# Elastic Net
model.add(layers.Dense(256, activation='relu',
kernel_regularizer=regularizers.l1_l2(l1=1e-5, l2=1e-4)))
model.compile(
optimizer=keras.optimizers.AdamW(learning_rate=1e-3, weight_decay=1e-2),
loss=keras.losses.CategoricalCrossentropy(label_smoothing=0.1),
metrics=['accuracy']
)
early_stopping = keras.callbacks.EarlyStopping(
monitor='val_loss', patience=10, restore_best_weights=True
)
lr_scheduler = keras.callbacks.ReduceLROnPlateau(
monitor='val_loss', factor=0.5, patience=5, min_lr=1e-6
)
model.fit(train_dataset, validation_data=val_dataset, epochs=200,
callbacks=[early_stopping, lr_scheduler])
23. 正则化效果评估
23.1 学习曲线
最直观的评估方式。训练和验证曲线同步下降说明正则化有效,验证开始恶化说明过拟合。
指标
↑
| ─────────────────────── ← 训练指标持续提升
| ····· ← 验证指标停滞或下降 → 过拟合
| ····
| ····
| ····
|···· ← 训练和验证同步提升 → 正则化有效
|________________________________→ 训练轮次 (Epoch)
23.2 泛化差距
Gap = E test − E train \text{Gap} = E_{\text{test}} - E_{\text{train}} Gap=Etest−Etrain
Gap 越小泛化能力越强。理想情况下趋近于 0。
23.3 权重分布
无 L2 正则化 有 L2 正则化
频数 频数
↑ ↑
| ╱╲ | ╱╲
| ╱ ╲ | ╱ ╲
| ╱ ╲ | ╱ ╲
| ╱ ╲ |╱ ╲
|╱ ╲ | ╲___
|__________╲___→ w |______________→ w
-5 -3 0 3 5 -2 -1 0 1 2
权重分布分散 权重集中在零附近
23.4 交叉验证
23.5 评估检查清单
| 检查项 | 方法 | 目标 |
|---|---|---|
| 过拟合程度 | 训练/验证曲线 | 泛化差距小 |
| 模型复杂度 | 权重直方图 | 权重分布合理 |
| 正则化强度 | 网格搜索 + 交叉验证 | 验证集表现最优 |
| 模型校准度 | 可靠性图 | 预测概率与准确率一致 |
| 鲁棒性 | 对抗样本测试 | 对扰动不敏感 |
24. 总结
正则化的本质就是在模型拟合能力和泛化能力之间找平衡。下面把全文的核心要点梳理一下:
- L1 产生稀疏解,适合特征选择;L2 使权重均匀缩小,数值更稳定
- Dropout 通过随机丢弃神经元实现隐式模型集成
- 早停法 最简单有效,监控验证损失防止过拟合
- 数据增强 从数据层面扩充训练集,计算机视觉几乎必备
- 归一化(BN/LN/RMSNorm 等)加速训练,附带正则化效果
- 标签平滑 防止过度自信,提升校准度
- 随机深度 在层级别引入随机性,适合极深残差网络
- R-Drop 通过一致性约束增强鲁棒性
- 知识蒸馏 通过软标签传递暗知识
- 梯度裁剪 防止梯度爆炸,限制参数更新步长
- 从贝叶斯视角看,正则化本质是将 MLE 转化为 MAP 估计
没有万能的正则化公式。最佳策略取决于具体任务、数据特征和模型架构,需要根据实际情况灵活选择、组合和调整。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)