Adam与AdamW的权重衰减解耦:优化器升级核心解析
发布时间:2026/10/18 11:09:55 锦皓数字建站

前段时间调试一个图像模型遇到一个挺典型的情况同一套网络结构原先用Adam配合L2正则训练验证损失怎么压都压不到预期训练后期还偶尔出现 loss 跳变。换成 AdamW 之后几轮训练就见了起色收敛也更稳了。把这件事拆开看真正起作用的并不是什么玄学而是 Adam 和 AdamW 在权重衰减处理方式上的一个关键差别——这个差别就是很多人口中“优化器升级版”的核心。这篇文章想把 Adam 和 AdamW 的区别彻底讲透。我会先说明 Adam 里 L2 正则为什么“名不副实”再拆解 AdamW 的“解耦”到底做了什么顺带解释偏置校正、训练节奏和权重衰减的联动关系最后给出一套可以直接落地的选型、调参和避坑建议。适合已经会用优化器、但不太清楚两者底层差异的开发者也适合正在为模型收敛慢、泛化能力差而头疼的朋友。1. Adam的L2正则处理和权重衰减的“名不副实”同一项、两种作用路径很多人第一次看到 AdamW第一反应是“Adam 加了个 L2 正则不就有 weight decay 了吗为什么还要单独搞一个新的”。这个想法本身没问题问题在于L2 正则和权重衰减在 SGD 里等价但在 Adam 里一点也不等价。1.1 经典 L2 正则把惩罚项直接塞进梯度先看 SGD 场景。L2 正则的做法是在损失函数里加一个 \(\frac{1}{2}\mu |w|^2\)这样梯度里就会多出 \(\mu w\) 这一项。如果把这一项挪到参数更新公式里实际效果等价于\[ w_{t} \leftarrow w_{t-1} - \lambda \mu w_{t-1} - \lambda \nabla L(w_{t-1}) \]也就是每一步更新时权重先自己缩小一个固定的比例 \(\lambda \mu\)再沿着梯度方向移动。因为 \(\lambda\mu\) 是常数所以无论梯度大小、无论这个方向的梯度历史怎么样权重收缩的比例始终不变。这就是“权重衰减”四个字的本意。1.2 问题出在 Adam 的归一化上Adam 的更新公式里有一个很关键的操作需要估计梯度的一阶矩和二阶矩然后用二阶矩去归一化一阶矩形成自适应学习率。L2 项一旦跟着梯度一起进入这个流程就彻底变味了。假设网络权重是 \(w\)梯度是 \(g_t \nabla L \mu w\)。Adam 会计算\[ m_t \beta_1 m_{t-1} (1-\beta_1) g_t \] \[ v_t \beta_2 v_{t-1} (1-\beta_2) g_t^2 \]然后更新参数\[ w_t w_{t-1} - \alpha \frac{m_t}{\sqrt{v_t} \epsilon} \]注意\(\mu w\) 这一项被放进了 \(g_t\)最后还要除以 \(\sqrt{v_t}\epsilon\)。这意味着在实际更新时L2 惩罚项对每个参数产生的“有效收缩比例”不是常数而是一个随梯度历史动态变化的值。说得再直白一点在梯度振荡剧烈、\(v_t\) 很大的方向\(\mu w\) 被 \(\sqrt{v_t}\) 一除几乎起不到惩罚作用在梯度平缓、\(v_t\) 很小的方向惩罚反而被放大。结果就是Adam 的 L2 正则不是按你设想的“统一比例拉向零”而是根据每个方向的梯度尺度扭曲地施加惩罚。1.3 实际影响训练不稳定和泛化能力下降这种扭曲会带来两个实际后果。第一频繁更新、梯度幅度大的权重几乎没受到正则约束容易在训练后期持续累积异常值第二小梯度方向的权重被过度压缩模型表达能力受限。两个因素叠加就会表现为验证损失下不去、后期 loss 抖动甚至在迁移任务上泛化变差。我印象很深的一次实验是在某个跨平台系统的特征抽取模块里用 ViT 结构做微调。原本配置是 Adam 加 L2 正则weight_decay 设成 1e-4结果 fine-tune 十几轮后出现明显的训练集和验证集 loss 分叉。后来查了一下参数状态发现不少非核心参数的 \(v_t\) 极小L2 惩罚在这些方向上被无限放大参数几乎被压到零附近。这不是任务问题是优化器和正则机制的匹配出了问题。这个现象也解释了为什么很多实战项目从 Adam 切到 AdamW 后即使不调超参训练稳定性也能明显改善——因为权重衰减终于回归了“固定比例收缩”的本意。2. AdamW的“解耦”到底解耦了什么公式差异与梯度更新逻辑“解耦权重衰减”是 AdamW 的核心英文叫 Decoupled Weight Decay。所谓“解耦”就是把权重衰减从梯度计算过程中单独拿出来不让它参与一阶矩和二阶矩的估计。下面用最朴素的公式对比来说明。2.1 两张更新流程的区别传统 Adam 的每一轮这样走g_t ∇L(w_t) μ * w_t # L2 项混进梯度 m_t β1 * m_{t-1} (1-β1) * g_t v_t β2 * v_{t-1} (1-β2) * g_t² w_t w_{t-1} - α * m_t / (√v_t ε)AdamW 则把这个过程拆成两步梯度里不再包含 \(\mu w\)g_t ∇L(w_t) # 只取数据梯度 m_t β1 * m_{t-1} (1-β1) * g_t v_t β2 * v_{t-1} (1-β2) * g_t² w_t w_{t-1} - α * m_t / (√v_t ε) - α * μ * w_{t-1} # 衰减单独执行从这两组式子里能看到三个关键点。第一m_t 和 v_t 不再包含权重衰减项。这意味着 AdamW 的自适应归一化只针对数据梯度而不是让正则项也去干扰梯度尺度的估计。正则项从此不再被 \(\sqrt{v_t}\) 动态缩放。每一步的权重衰减比例都是 \(\alpha\mu\)和 SGD 时代的权重衰减语义完全一致。第二权重衰减项在更新时是乘在参数上的。每一步对参数施加的是一个固定比例的“缩小”。从这个角度看AdamW 的理念反而更像带 momentum 的 SGD数据梯度负责决定往哪里走而权重衰减负责一个基础的回缩两者互不干扰。第三如果原版 Adam 里已经把 L2 或者 weight_decay 在框架层面加进梯度那么切换到 AdamW 时需要显式去掉原来那个往梯度里加惩罚项的操作。否则就变成“既在梯度里加了 L2又额外做一次衰减”正则力度直接翻倍训练会明显变慢甚至崩掉。2.2 从梯度更新式看它和 SGD Momentum 的关联AdamW 还有一个值得注意的性质因为 m_t 和 v_t 都不含权重衰减项如果只看 m_t 和 v_t 的更新过程它其实等价于在“带 momentum 的 SGD 式衰减”基础上为每个方向单独配了一个自适应学习率。权重衰减不再参与二阶矩估算意味着权重对梯度历史的“缩放节奏”不会因为正则强度变化而改变。有人可能会问这不就是把正则从里面挪到外面吗为什么效果差这么多。这里要提醒一句AdamW 在实践上的优势不只体现在公式上更体现在超参数语义回归了直觉。以前的 weight_decay 设 0.01你没法确切知道每一轮权重会被缩小多少现在 AdamW 里设 0.01在无 momentum 干扰的纯更新里每一轮权重都会稳定缩小 1%。这让调参变得可持续、可推断也让提前判断一个超参是否合理成为可能。3. 偏置校正其实在控制“训练节奏”AdamW为什么能去掉它讨论 Adam 和 AdamW 时很多人会忽略偏置校正bias correction。实际上在从 Adam 换成 AdamW 的过程中偏置校正是一个非常容易被误解、也容易被配置错的点。偏置校正其实是在控制训练早期的更新步伐和方差估计节奏。3.1 为什么需要偏置校正Adam 在 t1 时m_0 和 v_0 都初始化为 0。如果直接用 \(m_t/\sqrt{v_t}\) 去更新参数早期的一阶矩和二阶矩估计都会有明显偏差。比如第一个 batch 的梯度为 \(g_1\)那么 \(m_1 (1-\beta_1)g_1\)比真实梯度小好几倍\(v_1 (1-\beta_2)g_1^2\)同样严重偏小而且由于分母带平方根偏小程度比一阶矩更严重结果早期更新步长会被过分放大。传统 Adam 通过偏置校正将 m_t、v_t 都除以 \((1-\beta_1^t)\)、\((1-\beta_2^t)\)把矩估计拉回无偏水平。这个机制的主要工作区间就在训练最初的十几步到几十步对快速稳定启动至关重要。3.2 解耦之后为什么偏置校正的“存在感”变了原版 AdamW 论文中有一个值得注意的细节既然解耦了权重衰减m_t 和 v_t 只由数据梯度驱动那么偏置校正的作用就变得非常纯粹——它只管训练早期的矩估计不再和正则项发生互动。这可能给人一种错觉既然偏置校正原本就是为 Adam 设计的那用 AdamW 时是不是可以关掉不建议这么做。主流深度学习框架开箱即用的 AdamW 实现里默认都会开启偏置校正例如 Python 生态里常用的 optimizer 实现内部参数 bias_correction 默认为 True。如果你手动把偏置校正关掉训练前几十步的步长会受到较大影响尤其是在随机初始化权重较大、第一个 batch 异常值较多时很容易导致 loss 起步阶段就偏高。有些人会说“我用 AdamW 时偏置校正关掉影响不大”。这在短任务、小模型、小学习率下确实可能不明显但一旦进入大规模预训练或十几轮的 Vision Transformer 微调训练初期的波动会被后续的余弦退火放大最终影响模型收敛质量。因此除非你明确在做极致性能调试否则保持默认开启。3.3 预热、偏置校正和后半程的稳定性AdamW 的另一个常见搭配是 learning rate warmup。这其实和偏置校正形成了一个互补关系。偏置校正负责让前几步的矩估计偏差尽快收敛而 warmup 则从另一个方向限制早期更新步长避免初始状态下模型参数和梯度方向还没有建立可信的统计关系时就产生过大的参数位移。在预处理、ReZero、LayerScale 这类对早期训练稳定性要求高的结构上尤其如此。我自己偏好 AdamW 配上线性预热加余弦退火前 5-10% 的 epoch 逐渐把学习率从 0 升到预设最大值训练尾段用余弦退火平滑收敛。这种情况下偏置校正和预热叠加以后早期波动会变得非常小几乎可以忽略这也是 AdamW 在 long-tail 数据上表现稳健的一个重要原因。4. 实操中的选型与调参学习率、weight_decay和参数转移理论讲的再多最后还是要落在配置上。从 Adam 切换 AdamW 不是一个“把类名改掉就完事”的过程。这里给出一份可落地的操作清单。4.1 权重衰减参数不能直接照搬这是切换时最容易踩的坑。假设你原来用 Adam 设了 weight_decay0.01等价于在梯度里加入 \(0.01 w\)。由于这个项在 Adam 里被 \(\sqrt{v_t}\) 归一化过实际对参数施加的收缩力度往往被严重削弱尤其是在梯度尺度大的方向。现在直接换成 AdamW再去掉梯度里的 \(\mu w\) 而仍然设 weight_decay0.01权重在每轮更新中都获得了固定 1% 的收缩实际正则力度可能比原来大得多。实际操作中我习惯从更小的 weight_decay 起步。以视觉任务为例如果是中小规模数据几万张图级别建议从 0.01 开始试如果发现验证集 loss 一直下不去或者参数范数下降太剧烈就往下调一两个量级比如 1e-3 或者 5e-4。如果是大规模预训练或者数据分布很杂可以适当加大到 0.05 甚至 0.1但要配合早停和验证集监控。为了直观理解不同参数下的正则力度可以做一个简单推导。原版 Adam 中对于梯度尺度 G 的参数方向权重衰减项 \(\mu w\) 经过归一化后相当于每步额外施加的收缩比例约为 \(\alpha \mu / (\sqrt{G} \epsilon)\)。换句话说如果 \(G\) 很大有效收缩比例很小而 AdamW 中每步收缩比例永远是 \(\alpha \mu\)。这意味着从 Adam 切到 AdamW想让有效收缩强度大致相当在梯度尺度较大的层上往往需要调小 weight_decay而不是保持不变。4.2 学习率的调整策略AdamW 中的权重衰减不再影响梯度矩估计所以自适应学习率的计算更“干净”。一般来说原 Adam 使用的学习率在 AdamW 中可以直接沿用但很多情况下可以稍微调大一点。因为之前被 v_t 扭曲的惩罚项消失后参数更新方向里少了一部分干扰同样的学习率下训练可以更稳。如果权重衰减参数变小了学习率又可以适当增大那么常规做法是先固定 weight_decay把学习率从原值的 0.7 倍扫到 1.5 倍用一个小规模验证集快速筛选。找到合适的组合后再在大数据集上跑完整实验。这比直接在大规模任务上一通盲调要省很多时间和算力。4.3 代码层面的迁移与参数分组配置现代深度学习框架中切换成本很低。以常见的 Python 环境为例一行代码就能搭起 AdamW 优化器import torch optimizer torch.optim.AdamW( model.parameters(), lr1e-3, weight_decay0.01 )不过不同框架对 weight_decay 的实现细节存在差异。有些框架的 AdamW 会把权重衰减乘到梯度上再执行更新有些则会在参数更新后统一施加。因此在跑大型实验前先在小任务上跑一遍验证参数更新行为和预期一致非常有必要。更精细的排序器配置是把不同层分成不同参数组。比如在微调场景中对于 BERT 或 ViT 这类结构通常只对 attention 输出层、MLP 层施加适量权重衰减而不对 bias、LayerNorm 的 scale 施加衰减。PyTorch 里可以通过分组实现decay_params [ p for n, p in model.named_parameters() if p.dim() 2 and p.requires_grad ] no_decay_params [ p for n, p in model.named_parameters() if p.dim() 2 and p.requires_grad ] optimizer torch.optim.AdamW( [ {params: decay_params, weight_decay: 0.01}, {params: no_decay_params, weight_decay: 0.0}, ], lr1e-4, )这种配置思路来自于一个基本直觉像 bias 这类一维参数本身维度低过强的衰减容易把偏移量压没而二维以上的权重矩阵才最适合做权重衰减。4.4 从已有 Adam 模型迁移到 AdamW 时的注意事项如果你的项目里已经有一个用 Adam 训练好的 checkpoint想要微调到 AdamW这里会有一个很容易踩的坑Adam 的一阶矩、二阶矩缓存 m 和 v 里保存的是包含权重衰减项的梯度统计。直接换优化器继续跑那些旧缓存会导致前几次更新方向出现偏差。解决办法是切换优化器后重置优化器状态。以 PyTorch 为例重新构建 optimizer 实例时状态会自然清空。或者你也可以手动调用 state 清空方法# 切换优化器类型后清空旧状态 for state in optimizer.state.values(): for key in list(state.keys()): del state[key] optimizer.state {}实际操作中重置状态后建议用小学习率多预热几个 epoch让新的 m、v 估计重新建立起来。不要指望切换后第一个 epoch 就有亮眼表现这需要一个“状态重建”的过渡期。5. 什么时候必须用AdamW什么时候用Adam也无妨最后一部分讲选型。AdamW 并非在所有场景下都能碾压 Adam这里要给出一张清晰的取舍对照表让大家根据自身任务做选择。5.1 我说的“必须用”的场景先从前面的推导和实验来看以下几类情况强烈建议用 AdamWVision Transformer、Swin Transformer 等注意力结构微调。这类模型通常有大量一维参数和 LayerNorm不做分组的 AdamW 能提供更稳定的衰减避免权重范数异常。大规模多模态预训练、大规模语言模型训练。长训练周期中权重衰减若被 v_t 扭曲很容易导致部分参数持续偏离和训练不稳定。使用 Feature-wise 学习率缩放、逐层学习率衰减、或大 batch size 训练时。AdamW 的权重衰减和自适应学习率解耦后梯度噪声相对更大时也更稳定。需要可解释调参、可复现实验的团队场景。因为 AdamW 的 weight_decay 语义回归了“固定比例收缩”方便跨项目迁移超参经验。在以上场景里AdamW 不仅提升了收敛稳定性常常还在最终精度上有肉眼可见的优势。5.2 使用 Adam 也没问题的场景Adam 仍然有它适用的地方。如果任务本身非常简单数据集只有几千样本训练轮次不超过几十轮L2 正则的扭曲效应还没来得及积累到明显程度Adam 和 AdamW 的差异会非常小。在这种情况下沿用已有的 Adam 配置完全合理不需要花时间去调 weight_decay 和学习率的匹配关系。另外如果项目里大量代码、实验记录都围绕 Adam 积累直接切换 AdamW 意味着所有历史对比结果都需要重跑。如果当前模型的训练曲线已经很稳定、泛化也没问题那么纯粹为了“升级”而换优化器不是明智选择。优化器迁移应该以解决具体问题为驱动而不是跟风。5.3 迁移成本和收益的权衡关于从 Adam 切 AdamW我个人经验是可以有取舍。若当前模型是用了很久的老 baseline且验证指标已经稳定没必要动优化器。如果换结构、换数据集、加正则之后训练曲线明显异常那优先检查原配置是否把 weight_decay 混进了 Adam 的梯度里然后果断切换到 AdamW并按前面的策略重调 weight_decay 和学习率。这里可以直接给出一个“切换清单”式的经验先将优化器切换为 AdamW设 weight_decay0.01保持学习率不变或者降到原来的 70%跑 10-15 个 epoch观察验证集 loss 是否比原来更平滑权重范数是否逐步收敛而无爆炸如果收敛不理想优先调 weight_decay其次是学习率和预热比例如果发现和原来差异不大再决定是否值得继续调参。在实际操作中我见过不少项目只是把 Adam 换成 AdamW就解决了一直困扰的中后期振荡问题。也有项目没做任何改动切换后反而因为 weight_decay 强度增大而训练变慢。关键永远在于权重衰减参数需要重新校准而不是机械地照搬。最后再分享一个小习惯。我现在配置优化器时已经默认把 AdamW 作为第一选择除非有非常明确的历史包袱需要保留 Adam否则不会主动使用 Adam。而且我会在每次训练前手动打印一下 optimizer 里的 weight_decay 参数确认它指向的是 0 而不是从前某个实验残留的值——这件小事曾经帮我避开过一次“值虽然改了但参数组配置优先级导致衰减其实没生效”的尴尬问题。希望这篇关于 Adam 与 AdamW 核心差异的梳理能帮你少走一些我走过的弯路。
锦
锦皓数字建站
深耕本土企业品牌数字化升级,专注原创端正雅致商务官网,从视觉设计到稳定运维全程保驾护航。