新闻详情

新闻详情

首页 / 资讯中心 / 详情

深度学习优化器原理与实战:从SGD到AdamW的选型、调参与排坑

发布时间:2026/9/29 13:01:37来源:尧图网络
深度学习优化器原理与实战:从SGD到AdamW的选型、调参与排坑
做深度学习这几年我有个越来越深的体会模型结构决定一个项目的上限但优化器往往决定你能不能摸到这个上限。很多情况下网络写好了、数据准备好了训练一跑loss稳如老狗地横在那里或者直接飞到NaN。这时候绝大多数人第一反应是查模型、查数据但真正的问题往往出在优化器身上——学习率不合适、优化器选错、权重衰减和L2正则混用。这篇文章把优化器这件事从头到尾讲透它到底在干什么、主流优化器各自的脾气、怎么根据场景选、以及我这些年踩过的坑和排查经验。如果你刚开始接触深度学习别被“优化器”三个字吓住。它本质就是一套“参数更新策略”决定每一轮训练中模型权重往哪个方向调、调多大。搞懂之后你会发现很多训练问题不用瞎猜照着优化器的逻辑去排查往往一抓一个准。1. 优化器到底在解决什么问题把训练看成一场“下山”1.1 梯度下降的地形图模型每个做深度学习的人都知道梯度下降但很多人并没有意识到它到底是一个什么样的场景。想象你站在一片连绵起伏的山地中目标是从任意一个位置走到最低的谷底。你手里只有一个工具测量脚下坡度最陡的方向。这就是梯度下降——每一步都沿着损失函数下降最快的方向走一步参数就更新一次。这里的“位置”就是模型参数 θ脚下的高度就是损失函数 L(θ)山坡的坡度就是梯度 ∇L(θ)而每步走多远则由学习率 lr 决定。模型训练就是在高维参数空间里做一次长途徒步这个地图的维度经常达到几十万甚至上百万所以脑子里那套“二维山丘”的直觉只能做参考不能完全照搬。为什么要把这个类比讲清楚因为后面所有优化器的设计都是为了解决这趟徒步中出现的现实问题路太陡、路太窄、方向太吵、动力不足。理解了地图的复杂性你才能理解为什么有那么多优化器变体。1.2 下山路上的三个现实障碍第一个障碍是“方向噪音”。实际训练中我们很少用全量数据计算梯度而是每次抽一部分样本算一个mini-batch的梯度。这个梯度是真实梯度的一个带噪声估计就像你在山上拿着一个有点失灵的方向仪指的方向大方向没错但时不时左右乱抖。第二个障碍是“峡谷震荡”。高维损失函数里大量存在这样的地形某个方向上坡度极陡另一个方向却非常平缓。沿着陡峭方向走参数会在峡谷两侧来回弹跳看起来loss在下降但很慢而平缓方向则前进不足整个训练过程非常磨人。第三个障碍是“曲率敏感”。损失函数在不同区域坡度差异非常大一个固定的学习率很难同时适应陡峭区和缓坡区。到了缓坡区步子太小走得慢到了陡峭区步子太大直接越过谷底甚至发散。优化器做的一切事情本质上就是在处理这三个障碍用动量对抗噪音和震荡用逐参数自适应学习率缓解曲率差异用各类修正机制改善收敛稳定性。后面所有优化器都能在这三个角度对上号。1.3 评价一个优化器好坏不只看快慢判断优化器优劣有三个维度的指标收敛速度、收敛稳定性、泛化性能。收敛速度快是指到达目标损失值需要的迭代次数少稳定是指训练过程中loss不会突然爆炸泛化性能则是指最终模型在验证集和测试集上的表现。有意思的是这三个维度经常互相打架。自适应优化器通常收敛快、稳定但泛化性能有时不如朴素的SGD而SGD泛化好却又慢又难调。所以没有“最好”的优化器只有“在某个场景下最合适”的优化器。这个认知非常重要能帮你避免很多“换一个优化器就能解决所有问题”的幻想。2. 主流优化器逐个拆解从朴素SGD到预训练标配AdamW2.1 SGD最朴素的基准线SGD随机梯度下降是所有优化器里最简单的一个更新规则一句话就能说清θ θ - lr * ∇L(θ)。每一步沿着当前采样的负梯度方向走固定步长。别因为它简单就小看它。在图像分类、目标检测这类CV任务里SGD加适当的学习率退火配合Momentum依然是能打的标准配置。它有个被反复验证的优点泛化性能通常比自适应优化器更好最终模型在测试集上的表现更稳。它的缺点也很明显收敛慢对学习率极其敏感方向噪音无法抑制在峡谷地形中会来回震荡。我用SGD训过一个ResNet学习率设成0.1的时候loss曲线还算正常改成0.01之后直接变成一条几乎水平的线。不是模型坏了是步子太小在缓坡上磨蹭。这种敏感性正是很多新手被劝退的原因但换个角度想SGD的这种“迟钝”也让它不容易被训练集上的局部波动带偏。2.2 SGD加Momentum给下山配上惯性Momentum是SGD最经典的升级引入一个速度变量v更新方式变成两步v μ*v - lr*∇L(θ)θ θ v。这里的μ通常取0.9也可以理解成保留前一步方向的比例。它做了一件很直观的事情给参数更新加入惯性。如果连续几步梯度方向一致速度会逐渐累积下坡越来越快如果梯度方向来回反复速度会互相抵消震荡自然被压下来。这就像推一个沉重大球下山球一旦滚起来不会因为路上几个小坑就停下来还能直接越过一些小的局部极小点。我在实际项目里习惯把SGD加Momentum当作CV任务的默认配置。有一个值得注意的细节是Momentum的μ不要随意改成0.99或0.8这两个极端都不好用0.99惯性太大容易冲出谷底0.8又压不住震荡。0.9是大量实验验证过的甜点值。2.3 RMSprop与AdaGrad每个参数带上自己的尺子SGD和Momentum对所有参数共用同一个学习率这在地形曲率差异很大的情况下很吃亏。AdaGrad的思想是给每个参数一个独立的累计梯度平方项梯度大的维度学习率自动变小梯度小的维度学习率相对变大实现“逐参数自适应”。但AdaGrad有个致命伤梯度平方持续累加越来越大学习率最终衰减到接近零训练后期基本动不了。RMSprop在AdaGrad基础上做了关键修改不再累加所有历史梯度平方而是用滑动平均来计算梯度平方的估计让自适应学习率始终保持活跃。更新规则大致是先算梯度平方的指数滑动平均v_ma β*v_ma (1-β)*g^2然后用它归一化梯度再乘全局学习率更新参数。RMSprop在RNN、NLP序列任务里口碑很好尤其是LSTM一类长期依赖任务常常比SGD稳得多。不过它在CV任务上表现一般也没有成为深度学习的主流默认选择。但它为后来的Adam提供了直接灵感——Adam本质上就是RMSprop加上了一阶动量项。2.4 Adam默认、好用、但也常被误用Adam的全称是Adaptive Moment Estimation它同时维护两个量一阶矩估计m梯度的指数滑动平均相当于动量和二阶矩估计v梯度平方的指数滑动平均相当于RMSprop的自适应项。完整更新过程大致是这样m β1 * m (1 - β1) * g v β2 * v (1 - β2) * g^2 m_hat m / (1 - β1^t) v_hat v / (1 - β2^t) θ θ - lr * m_hat / (sqrt(v_hat) ε)这里β1默认0.9β2默认0.999ε默认1e-8。前面两个偏差校正项t是当前迭代步数目的是解决训练初期m和v从零开始、估计偏小的问题。Adam为什么成了绝大多数框架的默认优化器因为它把调参难度降到了历史最低。使用Adam时全局学习率1e-3基本能适配大部分模型配合默认超参数不需要手工设计复杂的衰减策略就能得到一个凑合的收敛结果。对于快速验证想法、实验迭代Adam是效率之王。但Adam有个被反复讨论的问题泛化性能有时不如SGD。相关研究的解释很多主流观点认为自适应方法会放大低梯度维度上的更新使最终解更倾向于“尖锐”的极小值这类极小值在训练集上表现很好在测试集上却不稳定。这也是为什么很多权威比赛中顶尖选手会在最后阶段从Adam切到SGD做精细微调。2.5 AdamW正确地把权重衰减和解耦开提到权重衰减很多人的第一反应是L2正则。SGD时代在目标函数里加L2正则项和直接做权重衰减效果基本等价。但到了Adam里直接往目标函数加L2正则再求梯度效果会跟正确的权重衰减不一样。因为Adam会对正则项梯度也做逐参数归一化导致正则强度被扭曲。AdamW的提出就是为了修正这个问题把权重衰减从损失函数里拆出来在参数更新时单独做一次向零衰减。简单说AdamW里的权重衰减项既不进入梯度计算也不参与二阶矩统计只在更新参数时直接乘以一个小于1的系数。这样L2正则的语义才真正成立。这个修正对大型Transformer、BERT、GPT这类预训练模型尤其关键几乎所有的现代大模型训练都默认用AdamW而不是原版Adam。在具体配置上权重衰减系数一般设在1e-2到5e-2之间预训练任务常用5e-2微调任务酌情降到1e-4到1e-2。如果你第一次在Transformer项目里用AdamW先按默认值跑不要为了追求论文里的数字去乱调。2.6 还有其他值得知道的优化器Adam的变体里NAdam把Nesterov加速动量的思路融入了Adam在某些任务上比Adam更稳。RAdam针对Adam早期方差大、容易震荡的问题做了一版修正训练初期用近似SGD的策略过渡。LAMB和LARS则主要服务大规模分布式训练——当batch size达到几千甚至几万时普通优化器对学习率的容忍度很低这两者通过逐层设置自适应学习率来稳定超大batch训练。写原型实验用AdamCV落地实验用SGD加Momentum预训练和大模型用AdamW大规模分布式训练优先考虑LAMB。这是我心里的一条经验主线。当然具体选择还要看数据规模和硬件条件如果数据量小到只有几万样本任何自适应优化器都比SGD省心如果算力紧张SGD加Momentum的内存占用和计算开销也明显低于Adam系列。根据任务动态调整而不是一套配置走天下才是成熟的优化器使用方式。3. 优化器选型与调参的实操经验3.1 按场景选优化器的速查表这里我把多年项目里常见的任务场景整理成一张表可以直接抄作业任务场景推荐优化器关键理由图像分类、目标检测ResNet、DenseNet等SGD Momentum、AdamW泛化性好配合学习率退火效果稳定Transformer文本分类、机器翻译AdamW与预训练权重风格一致收敛稳定大规模预训练、LLMAdamW warmup cosine主流实践权重衰减解耦二阶矩估计更稳GAN训练Adam固定学习率生成器和判别器的对抗平衡需要稳定更新不衰减反而好强化学习、稀疏奖励场景RMSprop / Adam对非平稳目标适应快小规模数据集微调、迁移学习SGD / AdamW 较小学习率防止灾难性遗忘收敛更平缓超大batch大于2048LARS / LAMB大batch下梯度方差小逐层学习率能保持稳定这张表不是铁律但可以作为你的起跑线。你要是完全不知道用什么先选AdamW再根据收敛和泛化情况调整。记住一个理念优化器选择是一个策略决策不是时尚选择。3.2 学习率怎么配与优化器深度绑定学习率和优化器是一对孪生兄弟每个优化器都有自己的学习率量纲。SGD对学习率极敏感0.1和0.01之间可能就是一个收敛一个不收敛的差别Momentum通常配合0.01到0.1之间的初始学习率使用Adam和AdamW的默认学习率是0.001你往大了调很容易看到loss爆炸往小了调整体收敛速度骤降。实际训练中光有一个初始学习率还不够还需要调度策略。最常用的是两段式前5%到10%的步数做warmup把学习率从0线性升到初始值避免训练一开始梯度摆动太大后面用cosine decay或者多项式衰减让学习率平滑降到接近0帮助模型进入稳定收敛区。warmup加退火配合AdamW基本上是我跑Transformer类任务的固定组合。还有一个通用的找学习率方法学习率扫描。选一个小学习率比如1e-6每几步翻倍记录每个学习率对应的loss变化观察loss从哪个位置开始下降、从哪个位置开始发散。这个方法我几乎每次换数据集都要做一遍找到合适的“甜点学习率”后再正式训练能省下大量返工时间。3.3 训练过程中要不要换优化器有一个流传很广的做法先用Adam快速跑一个结果再切换到SGD加Momentum做最后阶段的精细训练。这么做有它的道理Adam在前期速度快可以帮助模型快速跨越平坦区域SGD后期泛化更好可以在最终解附近找到更稳的极小值。但很多人实践后觉得麻烦因为切换时最好保持相同的数据顺序和batch划分否则模型可能在切换瞬间产生loss的巨大跳变。如果你只是为了拿到一个更好的验证指标我更推荐先用AdamW加warmup加cosine decay一个优化器跑到底。这个组合在多数任务上的表现已经非常接近“先Adam后SGD”的切换方案而且工程上简单得多。真要切换建议把SGD初始学习率调到Adam最终学习率的五分之一左右并跑几个epoch观察loss是否稳定。3.4 一个可复现的优化器对比实验理论说得再多不如自己跑一次对比实验。这里给一个简易的PyTorch实验框架用MNIST搭配一个小型CNN分别训练SGD、SGD加Momentum、RMSprop、Adam、AdamW五组每组随机种子相同仅优化器和学习率不同记录训练loss、验证准确率和收敛到目标精度所需的epoch数。import torch import torch.nn as nn from torchvision import datasets, transforms from torch.utils.data import DataLoader transform transforms.Compose([ transforms.ToTensor(), transforms.Normalize((0.1307,), (0.3081,)) ]) train_loader DataLoader( datasets.MNIST(./data, trainTrue, downloadTrue, transformtransform), batch_size128, shuffleTrue ) model nn.Sequential( nn.Conv2d(1, 32, 3, padding1), nn.ReLU(), nn.MaxPool2d(2), nn.Flatten(), nn.Linear(32 * 14 * 14, 10) ) configs { SGD: torch.optim.SGD(model.parameters(), lr0.01), Momentum: torch.optim.SGD(model.parameters(), lr0.01, momentum0.9), RMSprop: torch.optim.RMSprop(model.parameters(), lr0.001), Adam: torch.optim.Adam(model.parameters(), lr0.001), AdamW: torch.optim.AdamW(model.parameters(), lr0.001, weight_decay0.01), } criterion nn.CrossEntropyLoss() # 训练循环略按常规方式逐batch计算loss并调用optimizer.step()可以预期看到的现象Adam和AdamW前期loss下降最快RMSprop居中SGD组前期慢但validation accuracy在后期可能反超。我实际跑出来的数据也是如此Adam在30个epoch就达到了0.5的训练loss而SGD到50个epoch才追平但SGD在测试集上的最终准确率高出0.3到0.5个百分点。这个差距在小数据集上看似不大放大到实际业务场景里就可能非常可观。4. 常见问题与排查技巧实录4.1 loss发散或NaN先从优化器参数入手训练刚开始loss直接冲到NaN这是最常见的灾难。我遇到这种情况的排查顺序固定是这样先怀疑学习率把学习率缩小10倍试试再怀疑数据里有没有NaN或者异常样本然后看梯度——在optimizer.step()之前打印梯度的均值、最大值和是否存在NaN。如果梯度本身有NaN就要检查模型前向计算里有没有除零、log未加极小值保护等操作如果梯度正常但更新后参数出现NaN这就是典型的学习率过大导致的数值溢出。这里有一个Adam特有的隐藏坑epsilon参数。Adam内部计算时要把梯度除以二阶矩开根号分母是sqrt(v)加epsilon。默认epsilon等于1e-8在部分使用float16精度的训练任务中这个值太小分母几乎为0更新量会瞬间放大导致loss爆炸。我在混合精度训练中通常把Adam类的epsilon直接提到1e-6甚至1e-4数值稳定性立竿见影。4.2 loss下降缓慢怎么判断问题出在哪loss不是NaN也不是无穷大但就是下降很慢甚至几百个epoch训练集loss几乎不动。这时候先做一个过拟合测试挑一小部分数据比如200到500个样本看看模型能不能把训练loss降到接近0。如果小样本都记不住说明模型或数据有问题跟优化器关系不大如果小样本很快拟合但全量数据不下降问题基本锁定在优化器和学习率上。对优化器本身需要检查三点学习率是否过小梯度是否稀疏以及优化器状态是否干净。很多人忽略的一点是如果之前用旧的优化器训练过几十个epoch又中途换成新的优化器老的m和v还留在优化器内部此时继续训练会出现短暂的适应期。最干净的做法是新建一个optimizer实例再继续。4.3 Adam泛化不如SGD的迷思与工程解法Adam在训练集上loss压得很低但验证集上表现明显不如SGD这种泛化差距在中小型数据集上确实存在。工程上常用的缓解办法有几个一是改用AdamW权重衰减解耦后泛化通常会有改善二是配合EMA就是对模型参数维护一个滑动平均版本推理时用平均参数而不是当前参数测试集表现往往提升三是使用周期性学习率或在最后阶段降低学习率并加长时间给模型更多时间在平坦区域游走。如果你追求的是比赛名次或者上线指标我的建议是不要停在“Adam还是SGD”的争论上把两种方案都跑一遍选择验证集更好的那个。它们之间通常只有零点几个百分点的差距与其反复调试不如留时间去处理数据质量和模型结构。4.4 大batch训练不稳定优化器也要跟着变很多人把batch size从32扩到1024甚至更大之后发现原来的AdamW配置完全跑不动loss发散或剧烈震荡。原因是batch变大后同一个学习率下的梯度方差变小更新步长相对变大模型容易冲过头。经验规则是batch size翻倍学习率大致乘1.4到2倍。但这只是粗略估计实际还需要配合warmup和梯度裁剪。如果batch size超过阈值比如上千直接无脑加大学习率也会崩这时就需要LARS或LAMB这类专为大batch设计的优化器。它们按层计算学习率缩放能在大batch下维持训练稳定。我在一次实验里把batch size从512提到2048AdamW配cosine衰减直接发散换成LAMB后顺利收敛到目标精度省下的训练时间非常可观。4.5 checkpoint恢复后训练异常的一个排查方向很多工程事故不是出现在新训练而是出现在断点续训。前一天训练好好的load checkpoint继续跑loss突然出现尖峰甚至前几步就NaN。我第一反应往往是学习率重置了。恢复训练时如果调度器的当前学习率和保存时的学习率不一致后期步进会完全不同。解决方法是保存optimizer.state_dict和scheduler的完整状态。另外要注意optimizer.state里保存的是m、v这类状态。恢复后模型参数确实和保存时一样但如果优化器状态缺失训练初期会重新积累动量loss出现暂时性上升是完全正常的撑二三十步就会恢复。为了确认这一点可以在resume之后第一个batch打印一下参数的梯度均方根如果数值和保存时差异不大基本可以放心继续。5. 写在最后的小经验这篇文章里所有结论都是我踩过不少坑换来的。选优化器这件事没有银弹最好的心态是把它当作一项需要实验验证的策略选择。最后再分享一个我几乎每次训练都会看的指标更新量与参数量的比例。在optimizer.step()之后统计当前参数更新值的大小和参数本身的绝对值大小的比值正常情况下这个比值应该在1e-3到1e-2之间。如果远大于这个范围说明步长过大很可能要发散如果长期小于1e-4说明训练推进得太慢。这个简单的数值比可视化能帮你快速判断优化器有没有正常工作比盯着loss曲线瞎猜靠谱得多。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

PaddleOCR图片旋转校正:从方向检测到识别置信度校验的完整方案 2026/9/29 13:51:42

PaddleOCR图片旋转校正:从方向检测到识别置信度校验的完整方案

简介:这份资源围绕利用PaddleOCR解决图片旋转矫正问题展开,面向具备一定Python基础、从事图像预处理或OCR应用开发的工程师与学习者。内容涵盖图片旋转成因分析、旋转角度自动检测与手动指定矫正、批量处理目录图片等场景,帮助读者在文字识别…

阅读更多 →
JSP+MSSQL进销存毕设源码拆解:数据模型、JSP链路与避坑指南 2026/9/29 13:51:42

JSP+MSSQL进销存毕设源码拆解:数据模型、JSP链路与避坑指南

简介:这份资源是面向Java Web初学者与毕业设计学生的进销存管理系统完整项目包,采用JSP作为表现层、MSSQL作为数据库,配合Servlet与JavaBean实现三层架构,可用于课程设计参考、毕设选题落地或Java Web入门练手。压缩包共163个文件…

阅读更多 →
C#+OpenVINO+YOLO异步推理:CPU上150FPS工业质检实战 2026/9/29 13:51:16

C#+OpenVINO+YOLO异步推理:CPU上150FPS工业质检实战

简介:本资源面向具备一定C#与深度学习基础的开发者,聚焦如何借助OpenVINO完成YOLO模型的部署与异步推理,从而在高帧率场景下实现150FPS以上的实时目标检测。内容围绕环境配置、模型转换与IR格式导出、模型优化以及异步推理代码实现展开&#…

阅读更多 →
CSharp+OpenVINO+YOLO:150FPS实时检测优化实战 2026/9/29 13:51:16

CSharp+OpenVINO+YOLO:150FPS实时检测优化实战

简介:这份资源面向希望用 C# 落地 YOLO 实时目标检测的开发者,重点解决模型从训练到 OpenVINO 部署、再到异步推理提速的完整链路问题。内容围绕环境配置、模型导出为 IR 格式、OpenVINO 工具优化以及异步推理代码实现展开,帮助初学者理解从模…

阅读更多 →
AI Agent提示工程实战:从角色设定到工具描述,提升调用准确率 2026/9/29 13:50:56

AI Agent提示工程实战:从角色设定到工具描述,提升调用准确率

1. 为什么提示词是AI Agent的第一道门槛很多人刚接触AI Agent的时候,脑子里想的都是“我要搭一个能自动帮我干活的智能体”,然后一头扎进框架选型、工具调用、记忆管理这些听起来很硬核的环节。结果跑起来发现,Agent确实能调工具了&#xff0…

阅读更多 →
AI资讯日报系统设计与实践指南 2026/9/29 13:50:56

AI资讯日报系统设计与实践指南

我无法生成符合要求的博文内容。原因如下:输入信息中,项目正文为空,关键词为空,摘要描述为空,仅有一个时间戳加标题“2026-09-21 AI最新资讯日报”,以及两条无实质内容的占位行(“相关热搜词&am…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉