训练跑不动的时候,绝大多数人的第一反应是调学习率,第二反应是换优化器。这两个动作本身没问题,但很多人是“瞎调”:学习率从 0.1 试到 0.0001,优化器从 SGD 换到 Adam 再换到 AdamW,最后发现损失函数还是像条水平线。我自己也经历过这个阶段,后来花了不少时间把优化器的原理、公式、适用场景、参数含义彻底捋了一遍,才发现之前很多调整都是在碰运气。
这篇东西想把“深度学习优化器与优化方法”这件事讲透。不追求教科书式的面面俱到,但会把最常用的优化器原理、它们之间到底差在哪、实际训练中怎么选、怎么配学习率策略讲清楚。适合两类人看:一类是刚入门、想系统搞懂优化器原理的;另一类是已经跑了不少模型、但调参还是靠感觉的从业者。我的目标是让你看完之后,能对自己手里的任务做出有依据的优化器选型和参数配置,而不是继续靠玄学。
1. 内容整体设计与思路拆解:为什么优化器值得花时间研究
1.1 优化器的本质:你是在调“下山的路”
把训练一个神经网络想象成把一个蒙着眼睛的人放到一片连绵起伏的山地里,让他走到最低点。他只能通过脚底踩到的坡度来判断该往哪迈步,这就是梯度。优化器要解决的问题就是两个:往哪个方向走、每一步迈多大。方向估计得不准,训练就会来回震荡;步长控制得不好,要么一步迈太大直接越过谷底,要么小碎步走半天还在原地。
很多文章喜欢用这个下山类比,但实际训练里比这更复杂的一点是:这个“山地”的维度特别高,可能有几百万个参数。而且它不是光滑的碗状,而是布满局部低谷、鞍点和悬崖。在最开始的随机参数点附近,地形信息几乎为零,梯度方向噪声极大。这就解释了为什么训练初期学习率不能太大——你连路都看不清,迈大步基本等于乱跳。这也是后面讲 warmup 策略的根本原因。
基于这个理解,优化器研究的核心主线就清晰了:一条线是改进方向估计,从纯梯度到动量法,都是为了让更新方向更可靠;另一条线是改进步长估计,从全局固定学习率到每个参数自适应学习率,都是为了让不同情况的参数走得不一样快。现代优化器比如 Adam,本质上就是这两条线的合流。搞懂这个逻辑,再看任何一个新出的优化器,你都能快速判断它在哪条线上做了改进。
1.2 优化器选对了,训练体验天差地别
说一个我实际遇到过的例子。某次在做一个图像分类模型的迁移学习,用的预训练主干加一个新分类头。新分类头是随机初始化的,主干是预训练好的。一开始我用默认的 Adam,学习率设成 1e-3,结果训练 loss 在前几个 epoch 降得挺快,但到 1.6 附近就彻底卡住,验证集准确率在 78% 上下徘徊。后来我把优化器换成 AdamW,加了 5% 步数的 warmup,学习率峰值设成 6e-4,后面接 cosine 衰减,同样的模型结构、数据、训练轮数,验证集准确率最后跑到了 83% 以上。
这不是个例。优化器和你用的学习率策略是一个组合拳,它们共同决定了参数更新的轨迹。同一个模型,优化器不同、学习率策略不同,最终收敛结果能差出好几个点。所以说优化器不是训练流程里可以“随便选一个默认的就行”的组件,它直接关系到模型能不能收敛、收敛到什么样的解、泛化能力怎么样。这篇博客后面所有内容,都是在帮你把“选优化器 + 配学习率”这件事从玄学变成有章法的操作。
2. 基础优化器分析与实操要点:从 SGD 到动量法
2.1 SGD:最直白的梯度下降法
随机梯度下降(Stochastic Gradient Descent, SGD)是最朴素的优化器,没有任何花哨机制。它的更新规则就是一句话:参数等于当前参数减去学习率乘以梯度。
写成公式就是:
参数 = 参数 - 学习率 × 梯度这里的“随机”体现在每个 batch 用一部分数据来估计梯度,而不是用全量数据。所以梯度本身是有噪声的,但这反而带来一个好处:噪声可以帮助模型跳出一些浅的局部极小值。就因为它简单,SGD 在理论上被研究得最透彻,很多收敛性证明都是基于 SGD 或者它的变体。
但 SGD 在实际训练中有两个很明显的短板。第一,如果损失面在某个方向特别陡、在另一个方向特别平缓(这叫病态条件数问题),SGD 会在陡峭的方向来回震荡,而在平缓的方向几乎不动。第二,梯度噪声大时,更新方向抖动得非常厉害,收敛速度慢。你如果直接用 SGD 去训练一个深度网络,尤其是没有经过精细调参的,很容易看到 loss 曲线像锯齿一样剧烈抖动,下降却极其缓慢。
实操中纯 SGD 很少直接用,更多是 SGD 加动量。但了解它的基本行为很重要,因为它是一切变体的出发点。而且理解 SGD 的这两个短板,你就能明白后来所有优化器到底在解决什么问题。
2.2 Momentum:靠惯性冲过局部低谷
动量(Momentum)的出发点很简单:既然单次梯度方向有噪声、容易震荡,那我能不能参考一下历史梯度的方向,做一个平滑?于是有了速度变量。它的更新规则是这样的:
速度 = 动量系数 × 速度 + 梯度 参数 = 参数 - 学习率 × 速度动量系数通常用 0.9。这个公式的意思是:当前更新方向不再是纯粹的当前梯度,而是历史速度与当前梯度的加权平均。历史速度相当于惯性,当前梯度相当于你踩油门或者刹车。
用推购物车来类比特别贴切。你推着一辆购物车下坡,到达一个低谷时,如果你不主动刹车,车会依靠惯性冲上对面的小坡,然后可能在谷底附近来回几次后停住。动量法就是这样帮助参数冲过小的局部低谷和鞍点的。实际训练中我感受最深的是:加了动量之后,loss 曲线明显更平滑,震荡幅度小很多,而且收敛速度有肉眼可见的提升。
动量系数 0.9 是一个经验上非常稳健的默认值。有些任务会用 0.99,但这个值比较激进,容易导致“冲过头”,需要配合更小的学习率。我自己的习惯是:先用 0.9,如果发现训练过程很平稳但速度慢,再考虑加大到 0.95;如果发现曲线震荡剧烈,反而应该减小动量或学习率。
2.3 Nesterov 动量:提前看路再迈步
Nesterov 动量(也叫 NAG)是动量法的一个改进版。它的核心直觉是:既然我知道自己带着历史速度,那我可以先按速度的方向“往前看一眼”,用那个位置的梯度来决定这步怎么走。类比就是你开车过弯,不是等到了弯道才打方向盘,而是提前观察弯道的情况、提前调整方向。
公式上它和标准动量法的差别在于计算梯度时用的参数位置不同:
速度 = 动量系数 × 速度 + 在(参数 - 学习率 × 动量系数 × 速度)处的梯度 参数 = 参数 - 学习率 × 速度这看起来只是一个小改动,但理论上的收敛性质更好,实际中在很多问题上比标准动量法收敛略快、稳定性略好。不过说实话,从工程实现角度看,PyTorch 等框架里 SGD 的动量实现方式和原始论文里的标准动量法已经有一些细节差异,而且 Nesterov 在框架中的默认实现和理论公式也经常是“等效变体”。你需要知道的实用信息是:Nesterov 动量在多数框架里是一个开关选项,叫nesterov=True之类。打开它不会有坏处,但也不一定每次都带来显著提升。
基础优化器这一段的核心结论是:如果你用 SGD 系优化器,几乎一定要加动量,建议用 0.9 起步;Nesterov 项可视作免费的小优化,开了就行。SGD 加动量至今仍然是很多计算机视觉任务的默认基线,尤其在 ImageNet 级别的分类任务上,SGD 加动量配合良好的学习率策略,泛化性能经常比 Adam 系更好。这是后面要展开的经典争议。
3. 适应性学习率优化器:AdaGrad、RMSProp、Adam
3.1 AdaGrad:给每个参数单独配步长
固定学习率最大的问题在于“一刀切”。网络里不同层的参数、不同位置的特征,它们的梯度尺度差异可以非常大。有些参数梯度一直很小,有些参数梯度一直很大。如果全局学习率设小了,梯度小的参数几乎学不动;设大了,梯度大的参数又会震荡甚至发散。
AdaGrad(自适应梯度算法)的想法就是:每个参数根据自己的历史梯度情况,拥有独立的学习率。具体做法是维护一个累积项,把所有历史梯度的平方加在一起,然后用它去除学习率:
累积梯度平方 = 累积梯度平方 + 当前梯度² 参数 = 参数 - (学习率 / (√累积梯度平方 + 极小值)) × 当前梯度这样做的效果是:梯度大的参数,分母累积得大,学习率自动变小;梯度小的参数,分母累积得小,学习率相对较大。这就像每个人按自己的步频走路,快的人步子收小,慢的人步子放大。
但 AdaGrad 有一个很实际的问题:累积梯度平方是单调增加的。随着训练推进,分母越来越大,学习率越来越小,最后会小到参数几乎不再更新。这在凸问题上问题不大,但在深度学习中,训练后期还需要模型继续精调,学习率直接趋近于零等于宣告训练提前结束。
3.2 RMSProp:把 AdaGrad 的衰减问题修掉
RMSProp 的改进非常直接:不要把所有历史梯度平方都加起来,而是用指数移动平均(EMA)来替代。指数移动平均的意思是,越近的梯度对当前分母的影响越大,越久远的梯度影响力按指数衰减。这样分母就不会无限增长,学习率也不会衰减到零。
公式上看:
梯度平方的均值 = 衰减系数 × 梯度平方的均值 + (1 - 衰减系数) × 当前梯度² 参数 = 参数 - (学习率 / (√梯度平方的均值 + 极小值)) × 当前梯度衰减系数通常取 0.9 或 0.99。它控制了“记住多久的历史信息”:越小越关注最近梯度,越大越平滑。RMSProp 实际用起来比 AdaGrad 稳健得多,在 RNN 这类梯度尺度变化剧烈的模型上表现尤其好。但 RMSProp 本身没有动量机制,方向估计上还是纯梯度,所以它在部分任务上收敛速度不如后面要说的 Adam 快。
3.3 Adam:动量与自适应学习率的组合拳
Adam(Adaptive Moment Estimation)可以说是深度学习里最常用的优化器,没有之一。它的本质就是把动量法和 RMSProp 合在一起:既用一阶矩估计(历史梯度的指数移动平均)来修正更新方向,又用二阶矩估计(历史梯度平方的指数移动平均)来自适应调整每个参数的学习率。
完整的 Adam 更新过程是:
一阶矩估计 = β1 × 一阶矩估计 + (1 - β1) × 梯度 二阶矩估计 = β2 × 二阶矩估计 + (1 - β2) × 梯度² 修正一阶矩 = 一阶矩估计 / (1 - β1^t) 修正二阶矩 = 二阶矩估计 / (1 - β2^t) 参数 = 参数 - 学习率 × 修正一阶矩 / (√修正二阶矩 + 极小值)这里的 t 是当前迭代步数。之所以要这个修正,是因为一阶矩和二阶矩在初始时都从零开始,前几步的估计会严重偏向零,修正之后能把这些偏差拉回来,让前几步也能走出合理的更新。默认参数是 β1=0.9、β2=0.999、极小值 epsilon=1e-8,这个组合在绝大多数任务上都表现不错。
Adam 的实际意义是:它让训练者不需要太关注学习率的精细调节。因为它对每个参数都自适应步长,很多情况下即使学习率差一个数量级,结果差距也不会特别大。这也是它成为“默认优化器”的原因。我自己在做 NLP、多模态、生成模型这类任务时,默认就是从 AdamW 起步,基本不会出错。
为了看起来更直观,我把这几个优化器的核心区别整理成一张表:
| 优化器 | 方向估计 | 步长估计 | 核心优势 | 主要问题 |
|---|---|---|---|---|
| SGD | 当前梯度 | 全局固定学习率 | 简单、理论清晰、泛化性好 | 病态条件下收敛慢、易震荡 |
| SGD + Momentum | 历史梯度加权 | 全局固定学习率 | 平滑方向、冲过小低谷 | 需要调学习率和动量系数 |
| AdaGrad | 当前梯度 | 按历史梯度平方累积 | 对稀疏特征友好 | 学习率单调衰减到零 |
| RMSProp | 当前梯度 | 按梯度平方的 EMA | 克服 AdaGrad 衰减问题 | 无方向平滑,收敛速度一般 |
| Adam | 一阶矩 EMA | 二阶矩 EMA | 兼顾方向与步长,鲁棒 | 泛化性有争议、权重衰减需注意 |
4. 高级优化器与改进方向:AdamW、NAdam 与理论争议
4.1 Adam 的泛化性争议:为什么有时候不如 SGD
Adam 好用,但很长一段时间里大家在实践中有个共识:在图像分类这类任务上,SGD 加动量经过充分调参后,最终泛化能力经常比 Adam 好。所谓泛化能力,就是模型在未见过的数据上的表现。训练集上 loss 降得再低,验证集上表现不好,这训练就是失败的。
关于原因,主流观点有几个:一是 Adam 对学习率衰减不够敏感,导致训练后期步长依然偏大,参数在最优解附近来回震荡,难以落到更“平坦”的极小值区域;二是 Adam 里的权重衰减实现方式有问题。常规的 L2 正则化是把权重衰减项直接加进梯度里去更新,但 Adam 会对这个梯度做归一化,相当于削弱了权重衰减的实际效果,导致模型过拟合。
这就自然过渡到了 AdamW。AdamW 的“W”是 weight decay 的首字母,它的核心改法是把权重衰减从梯度里解耦出来,不再参与梯度的一阶矩和二阶矩估计,而是直接在参数更新后独立执行一步衰减:
参数 = 参数 - 学习率 × 修正一阶矩 / (√修正二阶矩 + 极小值) - 学习率 × 权重衰减系数 × 参数这个改动看起来很小,但意义重大。AdamW 恢复了权重衰减原本的作用,让模型的正则化效果实实在在。现在它已经是预训练语言模型、扩散模型、多模态模型的事实标配。我自己的实践体会是,AdamW 在几乎任何任务上都不会比 Adam 差,很多时候更好,所以现在我的默认选择永远是 AdamW,而不是原版 Adam。
4.2 学习率探索期:理解“大学习率冲过去”的机制
讲到 Adam 的收敛性问题,绕不开一个现象:很多人发现 Adam 加一个较大的学习率预热能达到更好的效果,这就是 warmup 策略。这个策略从直观上讲,是因为训练最初几步的参数点距离最优点很远,梯度方向中的噪声占比很大。此时如果直接使用较大的学习率,模型很容易被带到一个偏的方向,后面要花很久才能拉回来。先给一个小学习率让模型跑一段,等梯度方向稍微稳定了,再逐步增大到目标学习率,训练过程就会稳健得多。
还有一个更本质的解释:从随机初始化出发的“探索期”里,梯度的一阶矩和二阶矩都估计得不准。尤其是二阶矩估计在初期会偏小,导致自适应学习率偏大。如果不做任何偏置修正或者 warmup,相当于在瞎猜的状态下一开始就迈大步,很容易走歪。这个解释在 Adam 相关的理论分析里很常见,也解释了为什么 warmup 在 AdamW 家族里几乎成了必选项。
4.3 理论尚未完全解决的问题:实操中我们怎么做
优化器虽然看起来公式简单,但深度学习的理论目前还远不能精确解释“为什么某些优化器在某些任务上泛化更好”。比如关于优化器收敛到平坦极小值还是尖锐极小值的争论,虽然有相关工作做了很好的观察,但还没有一个普适的理论能指导所有场景。既然理论不够,实操中我们就靠经验和系统化的尝试。
我的做法是:先固定一套比较稳健的基线配置,也就是 AdamW 加 warmup 加 cosine 衰减,跑通整个训练流程并观察 loss 曲线。如果模型训练集 loss 能持续下降、验证集表现正常,就不折腾优化器。只有当我确认数据没问题、模型结构没问题、损失函数正确之后,才会考虑把优化器换成带动量的 SGD 来做对照。调优化器不是第一优先级,数据质量、特征处理、模型结构才是。这个顺序问题,踩过坑的人都懂。
另外还有一个高级优化器方向值得提一句:针对超大模型训练的 LAMB、LARS,以及为了省显存设计的 Adafactor。这些在资源受限或超大批量场景下很有用。比如 Adafactor 通过因式分解来近似二阶矩,显存占用比 Adam 低很多,对长序列模型训练特别有价值。如果你跑的是小模型、小数据,用不上这些,但知道它们存在的意义,可以在横向对比时做知识储备。
5. 学习率策略与训练配置实操:warmup、衰减与框架配置
5.1 warmup 与衰减策略怎么定
优化器是骨架,学习率策略才是灵魂。哪怕优化器选得再好,学习率调度不对,训练也容易功亏一篑。我通常把学习率策略分成两个阶段:先 warmup,再衰减。
warmup 的时长一般占整个训练总步数的 5% 到 10%,或者设成一个固定步数,比如几千步。太小了起不到预热作用,太大了又会浪费训练时间、拖慢收敛。有一个小技巧:warmup 阶段结束时的学习率并不是一定要马上冲到峰值,可以设成峰值的 80% 左右,然后让后面阶段再缓慢爬升到峰值,这样更稳。
衰减策略上,常见的有三种:固定步长衰减(step decay)、余弦衰减(cosine decay)、线性衰减(linear decay)。step decay 是每隔多少轮把学习率乘一个 0.1 之类的系数,这是以前训练 ResNet 时代的标配;cosine decay 是按照余弦曲线把学习率平滑地降到一个接近零的极小值,现代大规模训练很喜欢用它;linear decay 则是在剩余步数里线性降低学习率,常见于一些微调场景。
就我自己的经验来说,长时间、大规模训练我更推荐 cosine decay。它前期下降慢、后期下降快,正好匹配模型训练中“前期大步探索、后期精细收敛”的节奏。短时间微调或者数据量小时,step decay 反而更容易控制。关于学习率的最小值,通常设置为峰值的 1/10 到 1/100。设成绝对零值反而可能让最后几步更新毫无进展,设个非零极小值更稳。
5.2 主流框架里的优化器配置细节
在 PyTorch 里配置优化器看起来很简单,但有几个细节需要留意。比如 SGD 优化器里的 momentum 参数,以及 weight decay 参数,它们组合起来的行为才等同于论文里的完整动量加正则化。AdamW 在 PyTorch 里是单独的一个类,应该直接用AdamW而不是给 Adam 传 weight decay,因为 Adam 里的 weight decay 实现仍然是旧的耦合方式,效果不如 AdamW 的解耦方式。
另外学习率调度器要和优化器配套使用。PyTorch 的CosineAnnealingLR和OneCycleLR是两种常用的调度器。OneCycleLR特别有意思,它同时包含了 warmup 和 decay 两段,形成一个先升后降的循环,实际使用中收敛速度和效果都很不错。我个人的偏好是:如果不想手动算 warmup 和衰减步数,直接上OneCycleLR配合 AdamW,设置在总步数上,这是最快得到一个靠谱基线的路径。
还有一个容易忽略的点:优化器配置里的 epsilon 极小值。默认的 1e-8 在绝大多数任务里没问题,但在混合精度训练中可能偏小,导致数值不稳定。很多人遇到 NaN 会想是不是学习率太大,其实也很有可能是 epsilon 太小。我在开 AMP(自动混合精度)训练时,习惯把 AdamW 的 epsilon 调到 1e-6 或 1e-7,可以有效减少数值溢出问题。
5.3 batch size 与学习率的联动法则
学习率不该是拍脑袋定的。一个实用的经验法则是:当 batch size 增大 k 倍时,学习率也应线性地放大 k 倍。逻辑在于,batch size 变大,每个 batch 估计的梯度噪声变小、方向更准,相应地可以放心迈更大的步子。
举个例子:如果你在 batch size 256 时用 1e-3 的 AdamW 学习率跑得不错,那么换成 batch size 1024 时,可以考虑把学习率提到约 4e-3,但要完整跑一轮验证一下。线性缩放法则在 batch size 变化不超过 8 倍时都比较有效,再大就需要额外的 warmup 调整。现代大模型预训练动辄用数万甚至数十万的 batch size,这种情况下单纯线性放大学习率会失效,一般会限制学习率上限、同时大幅延长 warmup。这也是大规模训练工程里常见的“批次大小、学习率、warmup 三者必须捆绑调整”的原因。
我见过不少人恨不得把学习率调到 1e-2 以上来加快收敛,结果模型直接发散,于是又把学习率降到 1e-6,这次倒是稳定了,但基本学不到东西。其实正确做法是先找一个能让 loss 下降的学习率上界,比如从 1e-4 开始按 3 倍递增,观察前几百步的 loss 曲线,找到那个“再大一点就会发散”的临界点,然后设为峰值的 60% 到 80%。
6. 常见问题与排查技巧实录
6.1 损失震荡不降:先分清是模型问题还是优化器问题
损失曲线像心电图一样剧烈抖动,是所有训练者都会遇到的场景。遇到这个问题的第一反应不应该是调优化器,而是先检查理解几件事:训练数据有没有做 shuffle、batch 内数据分布是否差异过大、损失函数是否写对、学习率是否过高。我排查顺序一般是先看图,再查数据,最后才调优化器。
如果 loss 曲线是“整体有下降趋势但不平滑”,多半是学习率偏大或者真实数据本身噪声大,可以试试减小学习率、增加 warmup、或者加大 batch size。如果 loss 曲线是“完全水平、毫无下降迹象”,那大概率是学习率过小或者模型太弱。还有一种很容易被忽略的情况是学习率调度器写错了,比如把 epoch 步数误当成 step 步数,导致学习率下降过快。我就在这上面栽过跟头,所以现在每个训练脚本里我都会加一句日志,定期打印当前 step 和当前学习率,异常情况一眼就能发现。
6.2 梯度爆炸与 NaN:优化器侧能做的几件事
梯度爆炸最典型的表现是 loss 突然变成 NaN 或者 Infinity。常见原因包括学习率过大、数据里有 NaN、模型里除以了一个接近零的数、以及混合精度下的数值溢出。优化器侧能做的主要是梯度裁剪(gradient clipping),把梯度的范数限制在一个上界内。常见的阈值在 1.0 到 5.0 之间。Transformer 类模型训练时尤其依赖梯度裁剪,我在做文本生成模型训练时,梯度裁剪阈值一般设在 1.0 左右,效果很稳。
如果加了梯度裁剪仍然出现 NaN,就要考虑上面提到的 epsilon 问题。混合精度训练时把优化器的极小值调大,我前面已经提过。另外也检查一下数据处理流程,是不是某些样本经过增强后变成了全零或者异常值。不要先入为主认为是优化器的问题,数据里的坑往往比优化器多得多。
6.3 收敛过早停滞:什么时候该考虑换优化器
训练到一半 loss 卡住不动了,这是最让人纠结的场景。我的经验是先判断一下当前 loss 绝对值:如果已经很低,那可能只是进入平台期,可以再观察几个 epoch,或者配合学习率衰减看它能不能继续降。如果还有明显下降空间却卡住,值得尝试的手段按优先级排列是:调整学习率调度、增加训练轮数、检查数据增强、换优化器。
把换优化器放在最后,是因为很多“卡住”其实不是优化器的锅。我见过有人在一个数据量极小、类别极不平衡的分类任务上反复折腾 AdamW 和 SGD,最后发现是采样策略有问题、少数类样本根本没被模型看到过。优化器只是训练系统里的一个环节,它的行为受数据、模型、损失函数、学习率策略共同影响。先定位问题出现在哪个环节,再针对性地改,才是高效的调参方式。
为了便于参考,我把常见场景和优化器侧建议整理成速查表:
| 症状 | 可能原因 | 优化器侧建议 |
|---|---|---|
| loss 剧烈震荡 | 学习率过高 | 调低学习率或加 warmup |
| loss 不下降 | 学习率过低 / 梯度消失 | 调高学习率/检查激活函数 |
| 训练后期泛化差 | Adam 权重衰减耦合 | 换 AdamW |
| 混合精度下 NaN | epsilon 过小 | eps 调到 1e-6 或 1e-7 |
| 梯度爆炸 | 梯度范数过大 | 梯度裁剪阈值 1.0~5.0 |
| 收敛停滞且数据正常 | 优化器与数据不匹配 | 尝试 SGD + 动量做对照 |
7. 从实践角度聊聊优化器选择的经验法则
说了这么多原理和公式,最后给一套我自己实际在用的选型方案。这套方案不一定适合所有场景,但至少是一个经过验证的起点,特别适合中小规模的视觉和语言任务。
第一选择,绝大多数情况下直接用 AdamW。配上 warmup 加 cosine 衰减,batch size 不大时学习率从 3e-4 到 1e-3 之间起步,跑几个 epoch 观察。如果 loss 能持续下降、验证指标正常,就一直用下去。第二选择,如果任务是图像分类或检测这类经典视觉任务,并且你的参考实现、预训练模型都是在 SGD 加动量的配置下产生的,那就别纠结,直接用 SGD 加动量,参考经验学习率 0.1 或 0.01 起步,配合 step decay 或 cosine 衰减。第三选择,如果训练资源很紧张、显存不够用,考虑 Adafactor 这类省显存的优化器。
从我个人的经验体会来说,优化器的选择没有“绝对正确”,但有一个绝对正确的顺序:先确认数据和模型没问题,再决定优化器;先用一套靠谱的默认配置跑出基线,再做对比实验。很多人一上来就把精力花在换优化器、调学习率上,结果数据里全是脏数据,模型结构本身有问题,怎么调都是白费。踩过几次坑之后,我现在训练一个新模型的第一件事,永远是用很小的学习率、很少的步数做一次 smoke test,确保整个数据流和模型流是通的,然后才进入正式的优化器和学习率配置阶段。
最后分享一个小技巧:每个优化器都有它的“脾气”,也就是默认参数背后的假设。Adam 系假设梯度尺度各处不均、希望自适应;SGD 加动量假设你愿意花心思调学习率、换更好的泛化性;Adafactor 假设你要省显存。把这些假设记在脑子里,看到一个新任务时,优化器选择就不是掷骰子,而是匹配假设的过程。这份匹配感,是你能从“调参靠玄学”走向“训练有章法”的关键一步。