资讯动态

权重衰减原理与调参实战:从L2正则到AdamW的全面解析

发布时间:2026/9/9 5:12:07 来源:尧图企业网站定制
1. 权重衰减到底在解决什么问题1.1 从“训练很好测试翻车”说起如果你训练过稍微大一点的神经网络大概率遇到过一个让人头疼的现象训练集上的损失一路往下掉甚至掉到接近于零但验证集或者测试集上的表现却越来越差。这就是最常见的“过拟合”overfitting。模型没有在学数据背后的规律而是把训练样本里各种带噪声的细节都背了下来。我一开始处理这类问题时习惯性反应是加数据、加dropout、加数据增强但有时候这些手段受条件限制不是你想加就能加。后来我才意识到有一个特别便宜、特别省事的正则化手段被我忽略了那就是权重衰减Weight Decay。很多框架里它甚至只是一个优化器参数写一行代码就能打开效果却往往出奇地稳定。权重衰减本质上做了一件非常朴素的事在每一次更新参数的时候顺手把权重的绝对值往小拉一把。别小看这个“往小拉一把”它能让模型权重保持在一个合理的尺度上迫使模型用尽量小的参数去拟合数据。你可以把它理解成一种“防膨胀”机制专门防止模型把某些输入特征过度放大。权重衰减适合谁适合所有用梯度下降类方法训练神经网络的人。尤其是你发现模型的训练损失和验证损失差距越来越大时权重衰减往往是最值得优先检查的工具之一。很多人以为它只是老掉牙的L2正则化其实它背后还有一些跟优化器交互的细节这些细节直接影响最终效果。1.2 为什么权重“大”不是一件好事要理解权重衰减的作用先要理解为什么神经网络里权重大了容易出问题。假设我们的网络输入是x输出是y中间有一层线性变换。如果某个权重w非常大那么这个神经元几乎就是在做“赌单一特征”的事只要输入里某一项稍微有点噪声乘上一个大权重之后输出可能就被剧烈放大。这在训练集上也许没问题因为训练集里的噪声模式和整体分布是固定的模型可以把这些噪声当成“规律”来利用。但在新数据上噪声不会按照训练集的样子重现于是模型立刻翻车。更麻烦的是大权重会让模型对输入的微小变化极其敏感。比如图像分类任务中一张图片的像素值有小范围扰动大权重会把这种扰动放大成完全不同的预测结果。这显然不是我们想要的泛化能力。权重衰减的做法就是给“权重过大”这件事在损失函数里加一笔账。每个参数w每“膨胀”一点总损失就会多一点点。于是优化器在最小化损失的时候就会自动在“拟合数据”和“控制权重规模”之间找一个平衡点。这个平衡点一旦找准模型通常会更平滑、更稳定泛化能力也会更好。你可能会问为什么不干脆把权重限制在某个固定小范围内比如加一个硬约束强制|w|不能超过某个阈值理论上可以但这种硬约束会让优化问题变得很难处理梯度下降过程中容易在边界上反复震荡。权重衰减用的是软约束它在损失上加的是连续惩罚梯度下降处理起来非常平滑。这也是它比其他正则手段更“省心”的原因之一。1.3 为什么偏偏选平方惩罚损失函数里加正则项最常用的形式是L L_data λ/2 × Σ_w w²也就是对所有权重求平方和再乘一个系数λ。为什么用平方而不是用绝对值也就是L1范数从梯度角度很好理解。平方项对w求导得到w本身所以惩罚带来的梯度方向和w的方向一致是“w越大拽得越狠”。这会让权重的变化非常平滑既不会突然归零也不会突然跳变。L1正则绝对值的梯度是±1它是一个恒定的力量不管权重当前多大每一轮都往零的方向推固定的一步。结果就是L1更容易让很多权重被推成精确的零形成稀疏解。稀疏性在某些场景下是好东西比如特征选择但对于深度网络的多数层来说我们其实不希望权重整个变成零只希望它们保持在一个比较健康的量级。所以L2式的平方惩罚也就是权重衰减的常用形式往往在实践中更顺手。另外还有一点细微之处平方惩罚对不同大小的权重是有“天然优先级”的。如果一个权重已经很大它的梯度惩罚就大优化器会更用力地把压回来如果权重本来就很小它受到的拉扯也小几乎不影响正常学习。这种“按需收缩”的特性和我们想要的训练过程比较匹配也是为什么绝大多数深度学习框架默认支持的是weight_decay实现为L2惩罚而不是稀疏约束。2. 公式推导与优化器之间的微妙关系2.1 一次等价推导L2正则与权重衰减在多数资料里权重衰减和L2正则被当成一回事初学者也经常混着用。严格说它们在最朴素的SGD优化器里的确是等价的我先走一遍推导你可以看到它们之间的关联。第一种方式直接在损失函数上做文章加L2惩罚项L L0(w) λ/2 × ||w||²梯度下降到这一步需要求L对w的梯度得到∇L ∇L0(w) λw于是参数更新为w_{t1} w_t - η∇L0(w_t) - ηλw_t把后面两项合在一起w_{t1} (1 - ηλ)w_t - η∇L0(w_t)第二种方式权重衰减。它不修改损失函数而是在参数更新之后人为地对权重做一个等比缩小这一步通常被称为decayw_{t1} w_t - η∇L0(w_t) w_{t1} (1 - ηλ)w_t - η∇L0(w_t)可以看到在没有任何动量、没有任何自适应学习率的SGD中这两种写法完全等价。这就是为什么很多老派优化器参数直接叫weight_decay但内部实现实际上是在梯度里加了λwPyTorch的SGD优化器就是这么干的。从简化后的式子你会得到一个非常直观的认识设ηλ 0.01那么每更新一次权重先缩到原来的0.99倍再沿梯度方向调整。这就相当于每走一步都有一只手在轻轻把权重往回拉。decay率越大这只手越用力。2.2 Adam和AdamW出现后事情起了变化如果所有优化器都跟SGD一样那L2正则和权重衰减确实可以永远被当成同一个东西。但Adam这类自适应学习率优化器出现后情况开始不一样了。Adam会为每个参数维护一个二阶动量估计也就是历史梯度平方的指数滑动平均然后每一个参数的更新步长都会用这个二阶动量去归一化。这带来的结果是不同参数实际的有效学习率差异很大梯度的绝对值会被自适应地“拉平”。问题就在这里。如果我们还是用L2正则的写法即在损失上加λ/2×||w||²那么L2带来的梯度是λw。这个梯度进入Adam的更新规则后会和所有其他梯度一样被二阶动量归一化处理。也就是说某个权重w如果本身比较大它附近的梯度历史可能比较大那么λw这个正则梯度反而会被“缩小”如果某个权重本来很小它附近的梯度历史也小那么λw反而会被“放大”。这样正则化的力度就变得非常不均匀跟你最初设想的“按权重大小惩罚”不再完全一致。Loshchilov和Hutter在ICLR 2019那篇关于解耦权重衰减的论文里把这事讲得很清楚他们提出了AdamW核心改动就是不要让L2正则去参与Adam的自适应计算而是在Adam更新完参数之后再老老实实地做一个乘法缩放。也就是真正意义的权重衰减。如果用一句话概括L2正则和真正的weight decay在SGD上等价但在Adam上不等价。AdamW把“衰减”从“优化器内部的自适应机制”里摘出来让权重每轮都做一次同等的等比缩小。实验结果也表明在很多任务上AdamW比AdamL2正则更稳尤其是Transformer类模型常常能拿到更低的验证损失。这也是为什么现在你在PyTorch里用Transformer训练时主流推荐基本都是torch.optim.AdamW而不是Adam。我早期在项目里一直用Adam调L2正则总觉得模型的泛化不稳定换成AdamW以后很多莫名奇妙的问题自动消失了。后来回看代码才发现就是衰减位置这一个小小的差异累积几百上千步后效果会差出一大截。2.3 偏置和BN层到底该不该衰减细节控会继续追问是不是所有参数都适合做权重衰减先说偏置b。很多框架默认会把bias也一并施加权重衰减但很多经典做法和开源代码里会单独把bias从衰减列表里摘出去。原因有两点偏置的作用是平移决策边界它的“绝对大小”不会像权重那样直接放大输入特征的某个维度因此偏置过大导致过拟合的风险相对较低其次偏置通常数量少加权权重衰减对整体损失影响不大但没必要让它拖慢偏置的收敛。所以像torchvision的ResNet训练脚本里你经常会看到他们对bias和BatchNorm层单独构造参数组设置weight_decay0。再说BatchNorm层。BN层里有两个可学习参数γ缩放和β平移。很多经典实现不对γ做权重衰减有一个特别重要的原因BN层内部每一轮都在用当前batch的均值和方差做归一化。如果γ整体被等比缩小那么归一化之后的特征会被同等缩小而BN层输出后面通常还有后续层前面层同时缩小和放大在某些网络中是可抵消的。换句话说γ的绝对大小在BN结构里并不决定“影响力的上限”它可能被后续层重新缩放回来所以对它做权重衰减的实际效果不明显还可能引入一些训练不稳的问题。我的经验是大多数情况下你不需要手动精细区分bias和BN直接用默认参数训练也能取得不错的结果。但当你进入精细调参阶段或者发现模型验证损失差一点点下不去时把bias和BN层排除在衰减之外往往能带来一个不明显的稳定收益。这个小改动不会伤害你的模型还让超参数语义更清晰。3. 实操主流框架里的设置与验证3.1 PyTorch优化器参数怎么写PyTorch里最直接的设置方式就是在构造优化器时传weight_decay参数。举两个常用场景import torch # 经典CNN训练标配SGD Momentum Weight Decay optimizer torch.optim.SGD( model.parameters(), lr0.05, momentum0.9, weight_decay5e-4, )# Transformer训练主流配置AdamW 较大的Weight Decay optimizer torch.optim.AdamW( model.parameters(), lr1e-3, betas(0.9, 0.999), eps1e-8, weight_decay0.01, )我建议你把weight_decay理解成一个“独立的超参数”不要跟学习率绑定在同一个量级里瞎调。尤其在AdamW里weight_decay的常用值比SGD里高出不少很多人第一次用AdamW时会习惯性地沿用SGD的5e-4结果发现正则力度小得像没有这非常正常。AdamW里0.01到0.1都是不错的搜索区间视觉Transformer模型常用0.05如果你用默认只有0.01也没问题关键要通过验证集判断。如果你想让bias和BatchNorm层不做衰减需要给优化器传两个参数组。这是很多CV训练脚本里的常见写法decay_params [] no_decay_params [] for name, param in model.named_parameters(): if not param.requires_grad: continue if len(param.shape) 1 or name.endswith(.bias): no_decay_params.append(param) else: decay_params.append(param) optimizer torch.optim.SGD( [ {params: decay_params, weight_decay: 5e-4}, {params: no_decay_params, weight_decay: 0.0}, ], lr0.05, momentum0.9, )这里用len(param.shape) 1判断BN层是因为BN的γ和β以及所有bias都正好是一维参数。一维参数做衰减意义不大剔掉后通常能让模型更稳一点。注意这是一个经验判断不是数学上的绝对铁律你如果做的是MobileNet这类深度可分离结构也可以自己实验决定保留还是剔除。3.2 一个小实验看有效果对比空谈概念不如看一点实际感受。我给一个小型图像分类任务做过对比实验模型是一个6层卷积网络优化器使用SGDmomentum一共训练了30个epoch。第一组weight_decay 0。训练损失在第25个epoch降到0.12验证集准确率最高只有82.3%。训练集准确率接近100%明显过拟合。第二组weight_decay 5e-4。训练损失最终比第一组高一点约0.19但验证集准确率提高到85.7%。训练损失和验证损失之间的差距明显缩小。第三组weight_decay 5e-2。正则力度过大训练损失一直降不下去验证集准确率反而掉到78.5%。这说明权重衰减也不是越大越好压得太狠模型的能力会被严重限制陷入欠拟合。我用这个例子想说明的是权重衰减确实是一种容量控制手段它的存在会让训练损失看起来“没那么漂亮”但只要验证损失在降你就该知道这是好事情。很多人只盯着训练集loss误以为weight decay把模型带坏了其实这是对正则化的误解。3.3 训练过程怎么观察反馈实操中我最推荐的观察方式不只是看最终测试准确率而是把每一轮的训练损失和验证损失画在一起。如果看到两条曲线之间存在一个稳定的gap而且gap的绝对值随着训练在不断扩大那么过拟合正在发生。此时提高weight_decay是一种非常直接的对症下药。如果提高后验证损失变好说明原来模型确实在“硬背数据”。如果提高后训练损失和验证损失一起升高说明你已经加过头了需要往回调。如果gap很小但训练损失和验证损失都迟迟降不下来那说明模型容量不够或优化有问题这时候去乱加weight_decay只会雪上加霜。很多人调参的一个误区是只要性能不行就盲目加正则其实得先判断是欠拟合还是过拟合权重衰减只对过拟合一侧有效。4. weight_decay参数的调法与搜索思路4.1 初始值的合理范围不同的优化器、模型结构和任务weight_decay的可用范围差得很远。我根据自己的经验整理了一个起点参考表优化器常见任务初步搜索范围我常用的起始值SGD MomentumCNN图像分类1e-5 ~ 1e-21e-4AdamWTransformer / ViT1e-3 ~ 0.10.05Adam一般小模型1e-6 ~ 1e-31e-4LAMB大批量BERT训练0.01 ~ 0.10.01这个表格不是什么金科玉律它只是给你一个搜索起点。如果你用SGD做ResNet-50在ImageNet上训练torchvision官方脚本里weight_decay通常设1e-4在小型自定义数据集上我经常从1e-4开始必要的时候放大到1e-3或者缩小到1e-5。另外要注意weight_decay对训练“总时长”有影响。训练epoch数越长每次更新都做一次衰减累积效应越大。如果你把训练从30个epoch延长到300个epoch原本合适的weight_decay可能会显得过大模型在后期容易欠拟合。这时候可以搭配学习率衰减策略或者适当降低weight_decay。4.2 和momentum、学习率的相互作用权重衰减并不孤立起作用它和momentum之间有一个容易被忽视的关系。在带momentum的SGD中momentum会累积历史梯度的指数滑动平均。如果你在损失里加L2正则L2那部分梯度同样会被累积进动量里。可以推出使用momentum时L2正则的等效衰减率会被放大。具体推导这里不展开但实践经验是momentum取0.9时你实际感受到的正则强度可能比SGD不带momentum时稍大一点。调节时不要死记某个数值要学会看验证集反馈。还有一个更值得注意的点weight_decay相当于在每步更新中缩小权重这本身可以被理解为一种“隐式学习率衰减”。当一个权重绝对值比较大时衰减缩小它的绝对值也更大当所有权重都衰减到一定程度后后续训练的“有效学习率”会被影响。所以weight_decay过大的时候模型不只是欠拟合有时候你会看到后期loss波动明显变小整个模型像是“凝固”了。遇到这种状况马上降低weight_decay不要硬着头皮继续训练。4.3 数据量、数据增强和decay的关系数据量越少过拟合风险越高很多人自然会把weight_decay往上加这是一个合理的方向。但要注意数据增强本身也是一种正则化。如果你已经使用很强的数据增强比如RandomCrop、Cutout、Mixup再叠加很大的weight_decay正则化总量就容易“超标”导致模型欠拟合。我做过一个实验同一个分类任务只用弱增强时weight_decay1e-3效果最好把增强强度提升一个档位后weight_decay还是1e-3时验证准确率反而有些下降降到1e-4以后才恢复稳定。它背后体现的是“正则预算”这一概念数据增强、dropout、weight_decay、label smoothing等都在分同一份正则预算你要做的是让总预算保持在合适的水平而不是把每一种正则的方法都调满。反过来数据量很大时模型本身不太容易过拟合甚至权重衰减的作用会变小。我曾经在大规模数据集上训练发现weight_decay从1e-4调到1e-5验证集指标几乎没有变化因为数据规模本身的多样性已经提供了足够的正则化。此时你可以少在weight_decay上花时间把精力放到学习率调度上。5. 常见问题与避坑清单5.1 最容易被误导的几个点我整理了一些经常在新手项目里出现的问题也是我自己踩过的坑挨个说清楚。第一个问题是把weight_decay设得太小。很多人在AdamW里用0.0001甚至0.00001结果模型表现很奇怪与不用正则时差别不大甚至个别情况下训练不稳。如果是Transformer类结构这一般不是模型代码问题而是decay太小起不到压制作用。把weight_decay提到0.01以上试试很多“玄学”问题会突然消失。第二个问题是把weight_decay当成万能药。模型验证效果不好第一反应是加正则这是很危险的。如果当前模型处于欠拟合阶段比如训练损失和验证损失都在高位加weight_decay只会雪上加霜。应该先确认模型有没有拟合训练数据的能力只有训练损失压得很低而验证损失偏高时正则类手段才登场。第三个问题是混淆L2正则和weight_decay在Adam里的作用。用PyTorch的Adam时传weight_decay进去实际上内部实现是往梯度里加λw也就是L2正则的形式不是严格意义上的解耦权重衰减。AdamW才是把decay解耦出来的版本。如果你要严谨实验尽量用AdamW而不是Adam否则你调的weight_decay会和不一致的正则机制纠缠在一起。第四个问题是不管bias和BN层。这个问题前面已经讲了不少概括成一句话对它们做衰减并不是绝对不能但收益很有限有时候还添乱。精细调优阶段把它们剔除出去是成本低、收益稳的选择。5.2 一个问题一个对策排查速查表现象常见原因处置方向训练损失低验证损失高gap持续扩大过拟合增大weight_decay先按2~3倍往上走训练损失和验证损失一起偏高欠拟合或优化不足降低或关闭weight_decay优先检查学习率训练早期loss就不正常跳动weight_decay过大至少降一个数量级再观察训练后期loss曲线停顿、几乎不动权重被压制得过小调小weight_decay或配合warmup/衰减策略Transformer模型用Adam调不出好效果L2和自适应优化器耦合问题换成AdamW并把weight_decay调到0.01~0.1加了强数据增强后效果反而下降正则总预算超标降低weight_decay或者同步降低其他正则方法想调优时改动weight_decay无效总epoch数太短或数据太大延长时间或减小decay重新观察这个表不是让你照搬而是给你一个排查思路方向。很多问题需要结合具体的学习率、batch_size和模型大小一起判断我个人建议每次只改一个变量不要同时把学习率、weight_decay、增强策略全换一遍否则你根本不知道是哪一个改动起了作用。5.3 个人习惯从1e-4开始的小窍门我自己做项目时习惯用一个收敛比较快的策略做快速判断先在完整数据的一个小验证集上关闭weight_decay跑通模型确认模型具有足够的拟合能力之后再把weight_decay从1e-4或0.01开始逐步放大每轮都看验证集变化。放大方式我不推荐线性搜索比较高效的是按对数尺度搜索也就是在1e-5、1e-4、1e-3、1e-2这类数量级之间跳跃观察。一旦找到了能让验证损失降到最低的区间再在附近细调比如在1e-4到5e-4之间试三个值。整个调参过程通常控制在两三轮以内不要没完没了地搜索。因为weight_decay对模型最终成绩的影响一般不如学习率、batch_size和模型结构来得大它更多是“压住过拟合”的守门员角色。从我个人感受来说只要在正确范围内选了值对最优结果的差距绝大多数能控制在1%以内所以别把时间全部耗在它的精调上。还有一个常常被忽略的点如果你用了预训练模型做微调weight_decay通常可以设得比从零训练时更小有时甚至设0都比较稳。因为预训练权重本来就处在不错的局部最优区域微调我们要做的是让它适配下游任务过强的正则反而把预训练学到的知识洗掉了。我在做迁移学习时经常直接把decay设成0或很小的1e-5然后观察few-shot等场景下的效果多数情况下都是正向的。这一点值得所有做迁移学习的朋友试一试。

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

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

免费获取报价