资讯动态

AdamW解耦权重衰减:从SGD到自适应优化器的正则化原理与实践

发布时间:2026/10/16 4:18:40 来源:尧图企业网站定制
训练那些动辄上亿参数的网络时我们几乎都会在优化器列表里看见AdamW。但很多人在第一次接触时都以为它只是“Adam顺手加了个权重衰减开关”。直到有一次我在某个图像分类Demo上反复调节weight_decay从1e-4一路调到1e-2验证集指标纹丝不动才发现自己根本没理解这个参数背后那篇经典论文——FastFlow。这个在优化器领域被反复引用的工作核心只讲清楚了一件事权重衰减和L2正则化在SGD里可以互换但在Adam里不是一回事。传统实现把两者搅在一起导致正则化在训练过程中被隐式扭曲解耦之后才有了我们今天常用的AdamW。这篇论文适合两类人细读一是反复调weight_decay但看不到收益的调参者二是想做训练工程优化、想弄懂优化器为什么这么设计的开发者。文章没有花哨的操作就是数学推导加大量实验对比但结论直接改变了后来几乎所有Transformer类模型的训练配置。可以说把AdamW的原理吃透你才算真正开始理解“优化器选择”这件事对模型泛化能力的直接影响。1. FastFlow解耦权重衰减才是AdamW的真相1.1 权重衰减本来在做什么先回到最基础的问题权重衰减为什么存在。训练神经网络时如果某个参数值本身对损失函数只有微弱的梯度压力它就可能一直保持较大的数值模型也会因为这些“虚高”的参数而记住训练样本的噪声。权重衰减的做法很简单每轮更新完参数后再让所有参数向零收缩一小步。如果用生活里的例子类比可以想象成仓库管理员每次进货后都会顺手清理一遍库存避免那些长期不动的东西占满货架。在标准SGD里这个操作和L2正则化在数学上几乎等价。假设目标函数是Loss加上一个λ/2乘以参数平方和项那么梯度里就多出λ乘以参数这一项。更新时参数不仅按原始梯度移动还会被λ所对应的“拉力”往零的方向拽。由于SGD的更新里没有其他缩放机制这两种视角——把L2加进损失或者在更新后直接缩小参数——在常用学习率下得到的结果基本一致。这也是很多教材直接说“weight decay就是L2正则化”的原因。但这句话只在SGD里成立。一旦优化器切换到Adam事情就变了。1.2 传统AdamL2的正则化陷阱Adam的核心机制是自适应学习率每个参数维度都会根据历史梯度的平方统计量进行缩放。梯度大的方向更新步长相对变小梯度小的方向更新步长相对变大这样能让训练在稀疏梯度和陡峭方向上都能保持稳定推进。问题恰恰出在这个缩放上。如果在Adam里按传统方式把L2正则化项加进梯度那么梯度里代表“惩罚”的那一项——λ乘以参数——会被Adam的规则继续除以根号下的历史梯度平方统计量。这样一来实际施加到参数上的惩罚强度就不再是稳定的λ而是变成了一个依赖当前和历史梯度状态的动态值。这个值可能在某些维度被放大在另一些维度被缩小并且随着训练推进不断变化。论文里的推演指出这种耦合行为在数学上等价于使用了“时间表化的有效权重衰减率”。也就是说你以为自己在用固定的正则化强度实际效果却像是系统自动给你做了一套随着迭代次数和梯度幅度变化的折扣方案。试想一个政策规定“固定交税”但实际账单会因为你最近的消费记录不停地打折或加价最后你根本无法判断真实的税率到底是多少。传统Adam加L2正是这种混乱状态超参数λ在训练过程中彻底失去了可解释性。1.3 解耦不是粗暴移除而是让角色各归其位FastFlow给出的解决方式很直接不让权重衰减进入梯度计算也不让它参与动量和二阶矩的累积。优化器依然正常计算一阶矩和二阶矩正常做自适应更新但在这个更新完成后直接对参数执行一次收缩θ等于θ减去学习率乘以权重衰减系数再乘以θ。这一项只依赖当前参数值本身不依赖梯度也不依赖优化器的历史状态。解耦后正则化扮演的只可能是“限制参数空间”的角色。它不再试图引导梯度方向不再影响动量记忆中“该往哪走”的判断而是在每一步更新后给出一个恒定的收缩力度。这样做数学上更干净实践上也让权重衰减重新变成了一个可以预期、可以直接调节的旋钮。与此相对如果把解耦理解成简单的“去掉正则化”那就完全走错了方向——解耦恰恰是为了让正则化能够稳定地发挥既定的作用。2. AdamW的实现差异与参数细节2.1 同一套公式变化在哪一步要直观理解FastFlow改造的到底是什么最好把传统Adam加L2和AdamW的完整更新过程放在一起看。传统方式每一步先构造一个包含L2惩罚的梯度gg等于真实梯度的估计值加上λ乘以参数然后这个g进入一阶矩和二阶矩的移动平均最后用m的偏差修正值除以根号下v的偏差修正值来计算更新方向。问题就在这个过程中λ那一项被移动平均和除法反复“蹂躏”过。AdamW则把流程拆成两段。第一阶段完全和Adam一样只是梯度里不包含任何权重衰减项动量和二阶矩完全由真实梯度塑造。第二阶段是对参数的最终修正完成自适应更新后再让参数整体乘以1减学习率乘以λ。两个阶段互不干扰λ不再以任何形式出现在动量和二阶矩的计算中。下面这张表可以更清楚地看出差异操作步骤 | 传统Adam加L2 | AdamW 构造梯度 | 梯度真实梯度λθλθ进入一阶矩和二阶矩统计 | 梯度真实梯度λθ完全隔离在外 一阶矩 | 受λθ污染 | 不受影响 二阶矩 | 受λθ污染 | 不受影响 参数更新 | 直接用缩放后的梯度更新参数 | 先按Adam规则更新再额外乘以收缩系数 正则化效果 | 实际惩罚被动态缩放 | 恒定、可预期、可解释2.2 论文实验给出的证据论文作者在多个任务家族上做了系统对比包括图像分类、语言建模、机器翻译等场景。核心观察是采用解耦方式后同样一组超参数下模型性能几乎不会变差在若干结构复杂、参数尺度差异大的任务上解耦版本明显稳定于耦合版本。尤其值得注意的结论是解耦后的优化器对学习率的敏感度降低这意味着搜索空间更容易处理不需要为了配合隐式的L2效果而反复迁就学习率。为什么会有这样的收益耦合版本的L2项会和二阶矩统计形成负反馈梯度大时L2惩罚被缩放得更小梯度小时L2惩罚又被放大容易造成参数在局部抖动。解耦去掉这种动态干扰后优化器只把权重衰减当作一个“固定边界”训练自然更平稳。2.3 在自己代码里复刻AdamW虽然主流深度学习框架都内置了AdamW但手动实现一次能帮助你真正理解内部机制。下面这段代码省略了梯度裁剪等外围逻辑只展示优化器核心状态import torch # 假设 params 是需要更新的参数列表 # grads 是反向传播后得到的梯度列表与 params 一一对应 lr 3e-4 weight_decay 1e-2 beta1, beta2, eps 0.9, 0.999, 1e-8 exp_avg [torch.zeros_like(p) for p in params] exp_avg_sq [torch.zeros_like(p) for p in params] step 0 def adamw_step(params, grads, exp_avg, exp_avg_sq, step): step 1 bias_correction1 1 - beta1 ** step bias_correction2 1 - beta2 ** step step_size lr / bias_correction1 for i, (p, g) in enumerate(zip(params, grads)): # 更新一阶矩和二阶矩 exp_avg[i].mul_(beta1).add_(g, alpha1 - beta1) exp_avg_sq[i].mul_(beta2).addcmul_(g, g, value1 - beta2) # 修正偏差后计算分母 denom exp_avg_sq[i].sqrt().add_(eps) # 自适应更新 p.addcdiv_(exp_avg[i], denom, value-step_size) # 解耦的权重衰减直接收缩参数 p.mul_(1 - lr * weight_decay) return step这段代码里最关键的一行就是p.mul_(1 - lr * weight_decay)。如果你把weight_decay * p加进梯度再走后面的动量累积就是传统耦合版本把它移到参数更新后单独执行就成了AdamW。两种实现表面只差一个步骤位置实际是正则化机制的彻底改变。2.4 超参数初值怎么给AdamW的默认值已经经过了大量实践的检验betas保持0.9和0.999eps保持1e-8基本不会出错。需要认真调试的主要是学习率和weight_decay。根据任务类型不同我给出一组常用的起步值它们不是最优解但足够让你在大多数场景下快速站稳应用场景 | 学习率建议 | weight_decay建议 | 备注 常规图像分类中小模型 | 1e-3附近 | 1e-2到5e-2 | warmup可短通常几百步即可 Transformer类模型微调 | 5e-5到1e-4 | 1e-2 | 需要线性warmup占总步数5%到10% 从头预训练语言模型 | 1e-4附近 | 1e-2 | 长余弦退火到0效果更稳 扩散模型训练 | 1e-4附近 | 1e-2到1e-1 | 通常还要搭配EMA权重滑动平均 GAN类训练 | 2e-4附近 | 1e-2到4e-2 | 两个网络可以分别设置衰减如果你在微调一个已经收敛得很好的预训练模型学习率通常要比从头训练低一个数量级甚至更多。权重衰减本身没有固定答案但它和解耦后的优化器组合在一起至少让“调参”这件事有了可复现的因果链条。3. 实战复盘从Transformer微调到日常CNN训练3.1 为什么Transformer类任务离不开AdamWTransformer类模型参数类型非常杂。Embedding矩阵希望学到稠密的语义表示注意力层的QKV矩阵对尺度非常敏感前馈层的参数方差很大LayerNorm里的缩放因子又有着完全不同的尺度分布。这样的结构用SGD训练通常非常痛苦因为一个全局学习率很难同时适配不同层级的参数尺度。Adam天然处理了这种差异而AdamW在Adam的自适应基础上又给了正则化一个稳定的支点。Transformer结构特别容易在实际任务中表现出过拟合倾向尤其是数据规模不大的微调阶段。注意力矩阵如果范数持续增长模型会更倾向于记住训练样本中的共现模式。AdamW的常数权重衰减能持续限制这些矩阵的范数增长让模型在训练后期仍然保持较好的分布外泛化能力。这也是为什么在主流预训练语言模型和生成模型的训练配置里几乎已经默认使用AdamW而不是传统Adam。3.2 一套复制粘贴的微调训练流程在实际项目中我通常会把AdamW放进这样一个标准训练流程里加载预训练权重判断要不要冻结底层特征提取层。如果数据量和任务难度都不大冻结一部分底层参数能显著降低过拟合风险。将模型参数分成两组一组是常规权重应用weight_decay另一组是偏置和所有归一化层里的缩放参数不应用weight_decay。后者的数值空间本来就小加上衰减反而容易波动。设定学习率计划。推荐线性warmup加余弦退火先在前5%到10%的训练步数里从很小的学习率线性上升到目标值再按余弦函数慢慢降到零。配置AdamW学习率按任务取前面表格里的建议值weight_decay先设1e-2。开启梯度裁剪。Transformer类任务一般把max_grad_norm设成1.0即可这是常用截断线。这套流程我曾经在不同任务之间反复搬运基本不需要改动核心结构只需要根据任务规模调整训练步数和学习率峰值。值得特别提一句的是warmup不是可选项。Adam的二阶矩在训练初期非常不稳定前几步的统计量几乎等于随机梯度本身如果没有warmup强制降低早期学习率一批次样本就可能让Embedding参数发生剧烈偏移。3.3 混合精度与分布式场景下的衰减行为很多人担心AdamW在混合精度训练或分布式训练下会“不生效”实际我测下来没有这类问题。在常用的自动混合精度流程中前向和反向可能在低精度下计算但优化器更新阶段模型参数通常仍然以高精度形式保存。weight_decay直接乘在高精度参数上衰减行为完全正常。分布式训练时每张卡各自维护一份模型参数副本梯度经过多卡聚合后计算更新后每张卡再自行执行p.mul_(1 - lr * weight_decay)。由于这是一个确定性的、与梯度无关的操作多卡之间的参数收敛到同一状态没有任何障碍。真正需要警惕的是训练过程中不同节点之间若存在随机采样差异参数会在并行路径上出现微小分叉不过这类差异在常规DDP同步中会被梯度聚合纠正过来不会积累成严重问题。3.4 一个对比实验带来的直观感受我在复现某个中等规模图像分类Demo时做过一次不算严格的对照完全相同的模型结构、数据增强和训练步数只是把优化器从传统Adam换成AdamW同时把weight_decay设为1e-2。前几十个epoch里两条训练loss几乎重合区别一开始根本看不出来。但训练进行到大约一半时传统Adam的验证集指标开始出现缓慢上升甚至停滞而AdamW的验证loss还在平缓下行。最终结果是AdamW的验证准确率高了不少。这个结果的来源不完全是“AdamW更好”更准确地说是传统Adam加L2的耦合方式让正则化力度随训练进程不断衰减到了后期几乎不起作用。模型训练得越久这种机制差异暴露得越明显。对于只跑少量epoch的任务两者差距可能不大但对于训练周期较长、数据量大的任务解耦权重衰减的价值会成倍放大。4. 常见问题与排查实录4.1 调了weight_decay却跟没调一样这是最常遇到的情况前后把wd从1e-4调到1e-2训练曲线几乎不动。排查时我一般按下面顺序来看。先确认权重衰减项在数值上是否真的对参数更新产生了可观影响。固定一个batch在step之前打印参数范数和梯度范数计算一下lr乘以wd乘以参数范数代表的那一步“收缩量”有多大。如果收缩量比自适应更新的步长小两三个数量级那自然看不到效果。第二个常见原因是梯度裁剪抢走了权重衰减的“出力空间”。梯度裁剪是全局地缩放梯度向量当梯度范数超过阈值时更新量整体打折此时权重衰减项本身没有变化但它的相对作用会被压缩。解决方法不是去掉梯度裁剪而是调高裁剪阈值或调低学习率。第三个原因藏在参数分组里。有些实现会把所有参数统一交给AdamW处理包括偏置和LayerNorm参数。这些参数数量少但对数值变化非常敏感衰减施加在上面容易让它们朝零收缩得过快进而掩盖权重衰减在主要权重上的真实作用。我建议始终对偏置和归一化参数单独分组不施加weight_decay。4.2 换成AdamW后过拟合反而加重这个现象初看反直觉其实背后逻辑并不复杂。传统Adam加L2因为正则化被隐式削弱模型长期处于一种“半正则化”状态。当你换成AdamW并设置一个固定不变的λ后正则化的方向和力度都变了如果之前没有同步调整学习率或训练时长模型有可能会在新的正则化平衡点附近走得更远。更常见的情形是你把wd调得过高模型被压得太狠训练损失下降变慢验证集自然也不好看。排查思路是看训练曲线和验证曲线的gap。gap很小说明正则化过强模型欠拟合gap很大且验证集在上升说明正则化不足需要调高wd或增加其他约束手段。不要只盯着验证集绝对数值要看训练损失和验证损失之间的剪刀差走势。4.3 训练末期验证指标震荡我在长训练周期任务里遇到过这类问题。前期曲线很漂亮到后半段验证指标开始上下抖动。一个主要原因是AdamW的权重衰减是常数不像SGD加L2那样会在后期被梯度统计量自然缩放因此后期参数的空间波动比想象中更活跃。应对办法通常是两条路。一是给学习率加余弦退火让参数在尾部收敛到局部平滑区域二是考虑对weight_decay本身做阶段性调度比如在训练后半程稍微加大衰减强度。不过按我个人经验先用余弦退火解决速率问题很少需要真的去动态调整weight_decay。AdamW的好处正是固定和稳定没有特殊必要就别自己再引入新调度器。4.4 多个正则化手段一起上怎么定位问题训练里往往会同时用早停、数据增强、Dropout、标签平滑、权重衰减等手段。一旦过拟合指标异常很难判断是谁出了问题。我以前踩过几次坑之后总结出一个笨办法把其他正则化手段全部关掉只留AdamW和weight_decay训练一小段时间观察训练和验证损失的差距然后逐个加入其他手段。这样做虽然费时间但每次你都能明确知道某个正则化手段到底带来了多少变化。如果发现数据增强很强、wd设置却很小那么wd在最终效果里几乎贡献不了太多如果wd已经设到1e-2甚至更大但验证指标依然差问题就很可能出在数据增强或样本量上而不是优化器。这个“减法定位法”比直接对一堆超参做联合搜索要高效得多也更容易建立直觉。最后说一个自己反复用的小习惯当我想快速判断某个任务是否缺少正则化时我会先把weight_decay设成0跑一小段训练记录验证曲线再分别在1e-2、1e-1下重复一次。三条曲线放在一起差距往往一眼就能看出来。再根据这个差距决定是优先调wd、加大数据增强还是调整训练步数。这个做法比上来就对一堆超参做网格搜索靠谱太多。AdamW对我来说最大的意义不是多了一个可调节的旋钮而是终于把“正则化”从优化器的黑盒效应里剥离出来让每次调整都有明确的因果关系。这也是那篇经典论文至今仍然深刻影响训练工程选型的原因。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑