我最早是在一本经典深度学习教材里看到这句话的:L2 正则化等价于 weight decay。之后很长一段时间,我在 PyTorch 里给优化器设 weight_decay 参数时,都默认它就是在做 L2 正则化。直到有一次,我在一个 Transformer 模型上同时调 Adam 和 weight decay,发现验证集准确率怎么调都上不去,换成 AdamW 后,同样的 weight_decay 值,效果立刻好了。那时我才意识到,这两个概念之间藏着一个大多数人忽略的坑。

在随机梯度下降(SGD)里,L2 正则化和 weight decay 确实是同一个东西。L2 正则化会给损失函数加上 λ/2·||θ||²,于是梯度里多出 λθ;而 weight decay 是在每次更新后让参数整体缩小一点,等价于更新前减掉一个 ηλθ。两者数学上完全一致。所以教材才会说“weight decay 就是 L2 正则化”——但这句话有一个隐藏前提:优化器必须像 SGD 那样直接使用梯度。
Adam 一插手,等价关系就碎了
Adam 的出现打破了这种等价。Adam 为了自适应学习率,会把梯度除以一个二阶矩估计的平方根。这个操作会把 L2 正则化中的 λθ 项也一起缩放。于是,同一个 λ 对不同的参数产生的衰减力度完全不一样:梯度波动大的参数,λθ 被分母削弱;梯度平稳的参数,λθ 被分母放大。这不再是“均匀的 weight decay”,而是一种参数相关的、扭曲的正则化。
这个结论本身没有错,但很多人忽略了它的适用范围。为什么教材敢这么写?因为深度学习最常用的优化器就是 SGD,SGD 下两者真的等价。但如果你去看 Adam 的原始论文,就会发现 Adam 的作者并没有声称 weight decay 和 L2 等价。这个误解是在 PyTorch 等框架的 API 设计中被放大的:它们统一用 weight_decay 这个参数名,对 SGD 和 Adam 都适用,但对 Adam 来说,它实际做的是 L2 正则化。
用公式说更直白。SGD 的更新是 θ ← θ − η(∇f + λθ)。Adam 的更新是 θ ← θ − η·m̂/√v̂。当你把 L2 项加进 Adam 时,这个 λθ 会先被算进梯度,然后被 m̂ 和 v̂ 处理。最终,它对参数的影响近似为 ηλθ/√v̂,而不是 ηλθ。多出来的这个分母,就是 Adam 的自适应缩放因子。它把“固定比例的衰减”变成了“随梯度历史变化的衰减”。
梯度为零时,扭曲最明显
考虑训练后期,某个参数的梯度几乎为 0,只剩下 L2 项在起作用。在 SGD 里,它的衰减速度是固定的,每一轮乘以 (1−ηλ)。在 Adam 里,由于历史梯度平方也很小,分母 √v̂ 会非常小,L2 项被放大,这个参数会被猛烈地推向零。反过来,如果另一个参数梯度很大,分母也大,L2 项的影响就被稀释。你设置的同一个 λ,对不同参数实际产生了完全不同的正则化强度。
更麻烦的是,Adam 的 v̂ 是历史梯度平方的指数滑动平均,所以 L2 项自己也会被算进 v̂ 里。这意味着 L2 惩罚的扭曲程度还随着训练时间变化,你很难提前预测某个参数到底会被衰减多少。这也是为什么用 Adam 调 L2 时,你会觉得 weight_decay 这个超参数特别“玄”。
AdamW:把衰减从梯度里剥出来
Ilya Loshchilov 和 Frank Hutter 在 2017 年的论文 Decoupled Weight Decay Regularization 里明确指出了这个问题。他们提出 AdamW,把 weight decay 从梯度计算中解耦出来:Adam 照常根据梯度计算自适应缩放,然后在更新时额外减去一个 λθ。这样,衰减项不再受 m̂ 和 v̂ 的扭曲,每个参数都被同一个比例向零拉动。
"L2 regularization and weight decay are not equivalent for adaptive gradient algorithms. We propose a simple modification to recover the original formulation of weight decay regularization by decoupling the weight decay from the optimization steps."
AdamW 的更新可以写成:θ ← θ − η( m̂/√v̂ + λθ )。注意这里的 λθ 不在分母里面。它不参与 m̂ 和 v̂ 的计算,只作为一个附加的衰减项。这就是“解耦”的含义。
| 对比 | Adam + L2 正则化 | AdamW |
|---|---|---|
| 衰减项位置 | 在损失函数中,进梯度 | 在更新公式中,独立于梯度 |
| 是否被二阶矩缩放 | 是,衰减强度随梯度历史变化 | 否,所有参数统一比例 |
| 超参数含义 | λ 的实际效果被扭曲 | λ 就是每一步的衰减比例 |
| 典型调参范围 | 需要很小的 weight_decay(如 1e-4) | 可以用更大的值(如 1e-2) |
这对你训练模型意味着什么
这个区别不是理论洁癖,它直接影响训练结果。论文在多个图像分类任务上对比了 Adam+L2 和 AdamW,后者泛化误差更低。后来 Transformer 大规模训练也基本默认使用 AdamW,比如 GPT、BERT、ViT 的预训练。原因很简单:解耦后的权重衰减让正则化行为更可预测,超参数搜索也更容易。
我自己在调参时发现一个规律:如果用 Adam+L2,通常需要把 weight_decay 设得很小,比如 1e-4,否则训练会不稳定。而换成 AdamW 后,weight_decay 可以放心地设到 1e-2 甚至更大,模型依然稳定,泛化也更好。这是因为在 Adam 里,L2 项被二阶矩缩放,实际衰减量被扭曲了,你很难通过观察梯度判断合适的 λ。而 AdamW 的 λ 就是字面意义上的“每一步对权重缩小多少”,物理意义清晰得多。
但也要注意,AdamW 的 weight decay 并不是完全独立于学习率。从公式看,衰减项是 ηλθ,所以学习率越大,实际衰减越强。所以调学习率和调 weight decay 仍然是耦合的,只是不像 Adam+L2 那样耦合得那么隐蔽。
在 PyTorch 里,使用 AdamW 很简单:
optimizer = torch.optim.AdamW(model.parameters(), lr=1e-4, weight_decay=0.01)
常见误区,一次说清
PyTorch 里给 Adam 设置 weight_decay 就是在做 L2 正则化?
对 SGD 是对的,对 Adam 不对。PyTorch 的 Adam 实现实际上是把 weight_decay 当作 L2 正则化加在梯度上,不是解耦的。要真正解耦,请使用 torch.optim.AdamW。
SGD 里我该用哪个?
SGD 里两者等价,选哪个都一样。PyTorch 的 weight_decay 就是 L2 正则化,没问题。
AdamW 一定比 Adam+L2 好吗?
在大多数现代任务上是,但也不是绝对。有些任务中 Adam 配合合适的 L2 也能达到类似效果,只是调参更困难。AdamW 的优势是更稳定、更可预测。
为什么叫 weight decay 不叫 L2?
因为它是直接衰减权重,不是通过损失函数惩罚权重。历史上 weight decay 先出现,后来人们发现 SGD 下它等价于 L2,才混用。AdamW 恢复了 weight decay 的原始含义。
我的建议
下次再有人告诉你“weight decay 就是 L2 正则化”,你可以回答:在 SGD 里是,在 Adam 里不是。AdamW 之所以成为预训练模型默认优化器,不是因为它的名字好听,而是因为它把“衰减”这件事从自适应优化器的扭曲中解放了出来。如果你正在用 Adam 调 L2 调得头大,不妨试试 AdamW,也许会有惊喜。
原创文章,作者:guanweilu,如若转载,请注明出处:https://guanweilu.cn/article/288.html