ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

深度学习优化器选型与调参指南:从SGD到AdamW

深度学习优化器选型与调参指南:从SGD到AdamW Model-Optimizer 这个词圈内人看到的第一反应多半是训练深度学习模型时负责更新参数的优化器——SGD、Adam、AdamW 这些。但说实话我的第一反应其实是又是一个模型调优项目因为最近越来越多自称 Model-Optimizer 的库干的是剪枝、量化和蒸馏这类活。为避免歧义这篇我以训练优化器为主线把它们的原理、选型逻辑和调参经验一次讲透。适合刚跑通模型但总卡在 loss 不降的入门选手也适合想把手头 Adam 换成 SGD 做微调的老朋友。1. 先搞清楚优化器在训练里的真实位置1.1 损失函数下山类比训练模型的本质其实就是在一个高维曲面上找一个最低点。每个 batch 算出来的 loss 告诉你当前点有多高梯度告诉你哪个方向下降最快而优化器就是决定你怎么迈出下一步的那个大脑。没有优化器神经网络只能在原地踏步参数永远不会发生变化。很多人把注意力放在网络结构上却忽视了优化器的重要性。我见过一个朋友把 ResNet 换成 ViTloss 曲线照样飞最后发现只是学习率没跟着改。模型表达能力的上限是网络结构决定的但能不能达到这个上限优化器起的作用往往被严重低估。一个不合适的优化器会让一个本可收敛的模型永远绕着最优解转圈。要把优化器看透可以把它拆成三步接收当前参数和梯度根据一定规则计算更新量然后把参数往更新量方向挪一步。市面上的所谓“新优化器”基本都是在第二步上做文章——要么对梯度做平滑要么对每个参数做单独缩放要么加动量。经典的 SGD 只做最简单的事参数 参数 - 学习率 × 梯度。看起来简单但它也是最容易解释、最可预测的基线。后来出现的动量优化器相当于维护了一个“速度”变量让更新方向不仅看当前梯度还看历史梯度的平均走向。自适应优化器则是给每个参数单独配一个学习率让稀疏特征也能快速更新。1.2 优化器不是“调参工具”那么简单新手常把“优化器”和“学习率”混在一起其实优化器是算法学习率是它的一个输入。同一个优化器把学习率从 0.1 调到 0.0001行为完全不同。而不同优化器在同一个学习率下表现也天差地别所以“我用了 Adam学习率 1e-3”这种描述并不能说明你已经把优化器配置好了。我习惯把优化器理解成一种“控制策略”。你的模型是一辆下山的车方向由梯度给出但油门、刹车、方向盘阻尼的配合方式由优化器决定。SGD 是纯机械操作Momentum 是加了惯性反馈Adam 则是每个轮子都有一套独立的悬挂系统。选了不同策略下山路径和最终停下的位置都不一样这不是调一个参数能弥补的。所以我的经验是先确定优化器再单独调学习率不要一上来就同时动 momentum、beta 这些参数。优化器的代码就那几行但每个参数背后的几何直觉得先建立起来。接下来我按实际使用频率逐个展开主流优化器。2. 主流优化器选型思路与避坑点2.1 SGD朴素但可靠很多入门教程一上来就教 Adam导致不少人毕业后都没写过真正裸的 SGD。其实 SGD 值得你花一周时间跑几次实验原因很简单它的行为最可预测。没有动量、没有自适应倍数更新量就是负梯度方向乘学习率梯度什么样更新就什么样出问题很容易定位。SGD 在 CV 分类任务里配合一套好的学习率衰减策略往往能收敛到比 Adam 更平坦的极小值。这里说的“平坦”不是玄学很多研究都观察到 SGD 找到的解泛化性更好。但它在训练初期确实慢尤其是特征尺度差异大的数据所以现在很少有人从头就用纯 SGD更多是先用 Adam 热身再切 SGD 微调。2.2 Momentum给下山加上惯性动量是 SGD 的重要升级。它把更新看成“惯性”当前更新方向由历史累积和当前梯度共同决定。这样做的好处是在梯度方向剧烈变化的地方更新轨迹不会来回抖动能更快穿过平坦区。实际使用中动量系数通常取 0.9 左右相当于把过去十步的方向都纳入考虑。我调试过一个小网络纯 SGD 训练 30 轮 loss 还在 0.8 附近波动加上 momentum 后第 20 轮就降到 0.4。这不是巧合因为小 batch 的梯度噪声很大动量相当于给优化器加了个低通滤波器。如果把优化器比作开车SGD 是每个路口都重新看路的司机Momentum 则是看清大方向后带点惯性转弯的司机。2.3 RMSProp 和 Adagrad自适应学习率的由来自适应优化器的核心是“每个参数有自己的学习率”。Adagrad 的做法是累积历史梯度平方让频繁更新的参数步长变小稀疏参数步长变大。问题在于累积量只增不减训练后期学习率会缩到几乎为零。RMSProp 对此做了改进用滑动平均代替全量累积使学习率保持相对稳定。RMSProp 特别适合处理非平稳目标比如 RNN 训练中梯度尺度波动大的情况。虽然现在很多人直接用 AdamAdam 的一阶动量思想就是从 RMSProp 延伸出来的。如果你用 RNN 做序列任务可以把 RMSProp 翻出来试试它往往比 Adam 少一些数值抖动尤其是梯度尺度变化剧烈的长序列场景。2.4 Adam默认选择但别盲从Adam 可以说是深度学习的事实标准。它把动量思想和 RMSProp 的自适应学习率结合在一起还加了偏差校正让训练初期更新不至于太小。对于刚入门的团队Adam 是开箱即用最稳的选择默认参数 0.001 学习率、beta10.9、beta20.999很多模型都能直接跑起来。但 Adam 并非万能。它的问题在于对学习率依然敏感默认 1e-3 只适合小 batch 场景。在 NLP 的 Transformer 模型和 CV 的大模型上1e-3 很容易发散。此外 Adam 的泛化性能在部分任务上不如 SGD所以不能盲目迷信。如果你发现训练 loss 很低但验证集表现很差可以怀疑是不是 Adam 让你陷进了尖锐的极小值。2.5 AdamW 与 LAMB权重衰减的正确打开方式AdamW 是我现在用得最多的优化器。它和 Adam 的唯一区别是把权重衰减从损失函数的梯度里拿出来直接加在更新公式里。看起来只是实现细节的变化实际效果却差很多。传统 Adam 在做 L2 正则时权重衰减会被自适应学习率放大或缩小导致正则效果不稳定AdamW 则让每一层的权重衰减按照统一规则进行。在 HuggingFace 训练 transformer 时默认优化器基本就是 AdamW。我自己用 BERT 微调批量大小从 16 增到 32Adam 的权重衰减明显失效换 AdamW 后在验证集上稳定涨了 0.5 个点。大批量训练时还会用到 LAMB它主要是对 AdamW 的更新量做 layer-wise 缩放让不同层的学习率不至于差太多。优化器核心思想适合场景常用超参数注意事项SGD梯度直给小模型、CV 基线lr0.01~0.1需要配合学习率衰减Momentum历史梯度累积小 batch、噪声大lr0.01, momentum0.9对初始学习率敏感RMSProp梯度平方滑动平均RNN、非平稳目标lr0.001, alpha0.99不要随便改 epsilonAdam动量自适应默认通用选项lr0.001, beta10.9, beta20.999注意泛化性和发散风险AdamW解耦权重衰减Transformer、大模型lr1e-4, wd0.01需要配合 warmupLAMB逐层自适应步长大批量训练lr0.001~0.01对 batch 缩放更友好3. 优化器参数调整与实操要点3.1 学习率才是优化器真正的命门先给一条经验换优化器时学习率必须跟着换。Adam 用 1e-3 能跑不代表 SGD 用 1e-3 能跑。SGD 通常需要 0.01 到 0.1 这样的大学习率配合衰减。常见做法是先跑几个 epoch观察 loss 曲线如果曲线像过山车一样上下乱窜就调低如果 loss 下降得像乌龟爬就调高。我习惯用一个简单方法先在一个对数区间里选几个候选值比如 1e-5、1e-4、1e-3、1e-2 各跑 20 个迭代看初始 loss 下降幅度。下降幅度最大的那个再往下调半个数量级作为正式训练的学习率。这比直接照抄别人参数靠谱得多因为 batch size、数据分布、模型大小都会改变最优学习率。3.2 momentum、beta1/beta2、eps 怎么设对于 Adam绝大多数情况下你只需要动 learning ratebeta1/beta2 保持默认即可。beta2 表示梯度平方滑动平均的衰减系数默认 0.999 意味着平均过去约 1000 步的梯度平方。如果训练样本非常稀疏可以把 beta2 调到 0.99让学习率更新更快适应环境。Momentum 的系数通常取 0.9偶尔也有用 0.99 的但 0.99 会保留太多历史信息方向更新滞后明显。eps 是防止除零的极小值默认 1e-8 到 1e-10。有些资料建议调高 eps 到 1e-4 来稳定训练尤其是半精度训练时因为 float16 的表示范围有限太小的 eps 会让数值溢出。我试过在混合精度训练中把 eps 从 1e-8 调到 1e-6训练稳定了不少loss 曲线不再突然变成 NaN。3.3 weight decay 和 warmup 怎么配合weight decay 的作用是让权重向零收缩防止过拟合。在 AdamW 里这个参数需要和基础学习率联动。经验公式是如果基础学习率是 1e-4weight decay 通常取 0.01 到 0.1 之间如果基础学习率调到 1e-3weight decay 也应该成比例调整否则正则强度相对变弱。warmup 是另一个重要组件。训练刚开始时模型参数随机梯度方向噪声很大一上来就用大学习率容易把参数冲到不稳定区域。warmup 让学习率从很小的值线性升到目标值前几步只做“试探”。Transformer 训练几乎离不开 warmup常见做法是前 10% 的 step 做 warmup。如果你训练的模型很深建议 warmup 占比再提高一些。3.4 局部最优点和 loss plateau 怎么办模型训练到后期loss 经常停在某个平台期让人怀疑优化器是不是坏了。这时候不要急着换优化器先检查学习率是否触发衰减。很多框架的 scheduler 会在 validation loss 连续 N 个 epoch 不下降时把学习率乘 0.1这叫 ReduceLROnPlateau。我用的时候会把 patience 设大一些比如 5 到 8给模型更多探索机会。如果衰减后用了几轮还是不动我会检查是不是跑进了尖锐的局部极小值。一个土办法是把学习率临时加回去让模型跳出当前点再继续衰减。这相当于给下山的人一个“抖动”来离开坑。另一个办法是加载之前最低验证 loss 的 checkpoint换用 SGD 动量继续跑经常能再压一个点下去。4. 常见问题与排查技巧实录4.1 loss 不降就甩锅给优化器收到最多的问题就是“我用了 Adamloss 为什么不降”但排查结果经常和数据、标签、网络实现有关。优化器只是其中的一环。先看 loss 初始值是否合理如果初始 loss 和随机猜测差不多说明数据可能没对齐。再看梯度有没有回传很多人把某一层 freeze 了却没注意到参数压根没更新。我给自己定了一个排查顺序先看数据样本归一化再看 loss 数值构造然后看梯度范数最后看优化器和参数 groups 是否对上。只有这四步都查完我才会去改优化器超参数。优化器能救的是“梯度还在但更新步长不合适”的模型不是“梯度消失或数据错误”的模型。4.2 训练震荡、发散怎么处理训练过程中 loss 变成 NaN 或直接发散常见的元凶是学习率过大或者损失函数里有 log 0、除零。当 loss 曲线呈现锯齿状幅度越来越大几乎可以断定是学习率偏高。先降低一个数量级如果图形平滑了再从中间找合适值。不要为了“让训练快点”而硬扛高学习率。还可以监控梯度范数如果前几层梯度范数爆炸考虑梯度裁剪。Transformer 里通常把 max_grad_norm 设为 1.0这能避免个别大梯度把模型参数冲走。优化器这里的经验是clip 设置在 optimizer.step 之前生效范数超过阈值会等比缩放所有梯度而不是只砍掉最大的那个。4.3 Adam 和 SGD 切换时要注意什么很多 trick 是先用 Adam 快速热身然后切 SGD 做精调。这里有个需要特别注意的点两个优化器的状态变量不兼容。Adam 内部保存一阶矩、二阶矩SGD 的 momentum 也保存各自状态直接替换会给 SGD 一个空的动量状态初期容易抖动。我通常在切换时先把学习率降下来然后重新初始化 optimizer并让 SGD 跑三五个评估点。如果验证集没有明显波动就继续如果跌了就退回 Adam 再训练几个 epoch。不要天真地以为 optimizer.step 只是一个数学公式状态管理才是切换的关键。这也是为什么很多框架里切换优化器时要显式新建 optimizer 对象。4.4 大批量训练下的优化器选择当 batch size 从 32 增加到 256梯度估计更准但每一步的更新也要相应放大。常见的线性缩放规则batch size 翻倍学习率也应该翻倍。但 Adam 的自适应特性让这个规则不那么好使因为二阶动量也在变化。这时候 LAMB 这类 layer-wise 自适应优化器更合适它会在每个层上独立计算步长。我用 LAMB 训练过一批 CV 分类模型batch size 扩大到 4096 时依然能稳定收敛Adam 却在前几百步就发散。大批量场景还有一个隐性收益训练步数减少同样的 warmup 步数占比要提升否则模型根本还没来得及“热身”就结束训练。记住大批量训练调的不是优化器一个东西而是整套学习率调度策略。5. 我的一些实操取舍与扩展思路5.1 踩过的两个真实大坑分享两个真实经历。第一次是我做图像分类时模型一直欠拟合我用默认 Adam 跑了 200 轮验证集准确率卡在 85%。后来把优化器换成 AdamWweight decay 从 0.01 调到 0.05加上 cosine 学习率衰减最后到了 89.2%几乎没改网络结构。这让我意识到优化器配置对结果的贡献可以这么大。第二次是做文本生成模型在 8 卡并行训练时 loss 高频抖动。我以为是数据并行通信出了问题排查了很久最后发现只是学习率过高且没有 warmup。把学习率从 2e-4 调低到 8e-5并加入 2000 步 warmup抖动立刻消失了。优化器的问题往往隐藏在这些细节里不跑到大模型上根本体会不到。5.2 选型建议速查任务类型推荐优化器学习率起点备注入门实验Adam1e-3快速出结果CV 分类SGDMomentum 或 AdamW1e-2 或 1e-4SGD 配合衰减Transformer 微调AdamW1e-4必须 warmup大规模预训练LAMB1e-3支持超大批量RNN 序列RMSProp1e-3梯度尺度波动大如果只能提供一个默认配置我会选 AdamW 基础学习率 1e-4 0.01 weight decay cosine decay warmup。这个组合在大多数 CV/NLP 模型上都能稳定落地剩下的就是小范围调学习率。等你跑熟了再按任务特性去替换成 SGD 或 LAMB就不会觉得难受。5.3 从训练优化到模型优化Model-Optimizer 这个名字如果放到工程里还可以指模型压缩优化剪枝、量化、蒸馏。很多训练优化器调好的模型并不是直接上线的最终形态还需要再做推理优化。我之前参与过一次端侧部署精度掉点 1% 以内完全靠蒸馏和量化校准拿回来这些也属于“模型优化”的范畴。如果你对训练优化器已经熟悉下一步可以看“优化器状态压缩”相关技术。比如 8-bit Adam 在数据并行时能省掉大量显存通信但精度和原版几乎一致。这也是一个很有意思的方向。同一个 Model-Optimizer 主题从训练优化走向推理优化能挖的东西还有很多。从我自己的实践看优化器是一个投入产出比极高的调试点。很多人迷信网络结构却忽略了一个合理的优化器配置可能带来几个点的提升。希望这篇基于 Model-Optimizer 的经验拆解能帮你少走点弯路如果暂时没看懂所有参数直接跑实验跑着跑着就明白了。
返回列表