哪个优化器最好?这问题我几乎每周都会被问一次。按惯例我会反问一句:你的 batch size 是多少?如果对方回答不上来,那讨论优化器基本没有意义,就像不考虑路况只问买越野车还是轿车好,最后大概率得到一堆无法落地的建议。
真正在训练里摸爬过的人都知道,优化器和 batch size 从来不是独立的两件事情。batch size 决定了梯度估计的噪声水平,而优化器恰恰又是靠梯度来更新参数的,二者叠加在一起,才会出现“我换了个优化器,效果差了一截”这类困惑。所以这篇文章我打算换一个角度,不是列一堆优化器的优缺点列表,而是从 batch size 出发,把什么场景该用什么优化器、学习率该配多大、踩过哪些坑一次讲透,希望能帮你少走几趟弯路。
1. 先说结论:优化器之间没有“最好”,只有“匹配”
1.1 核心需求解析:到底在选什么
很多人在选优化器时只看一个指标:最终准确率。但准确率是后验结果,真正决定训练曲线走向的是两个东西:梯度质量和参数更新策略。
梯度质量由数据决定。batch size 越小,每次计算用的样本越少,梯度越“抖”,噪声越大;batch size 越大,梯度越接近全量梯度的真值,噪声越小。参数更新策略由优化器决定。SGD 系只会按当前梯度方向以固定学习率迈步,Adam 系则会根据历史梯度自动缩放每一步的步长。
理解了这两点,你就能想明白一个关键事实:不同优化器对梯度噪声的承受能力是不同的。一个对梯度噪声极其敏感的优化器配上小 batch,可能训练曲线狂抖;一个依赖梯度精细度的优化器配上大 batch,也可能因为梯度太平滑而走不远。所以选优化器的本质,是让你的更新策略匹配当前 batch size 下的梯度统计特性。
1.2 batch size 才是那个被你忽略的变量
我在前面强调过,batch size 决定了梯度噪声水平,这个噪声对训练的影响比大多数人想象的要大。
先说噪声的数学来源。设每个样本的梯度为 (g_i),当前 batch 的平均梯度为 (\bar{g}),那么梯度的方差大致按 (1/B) 衰减,其中 (B) 是 batch size。也就是说,batch size 从 32 变成 128,梯度噪声立刻降了几倍,更新方向会变得更加稳定。生产环境里很少有人同时记录梯度的方差变化,但其实它对训练的每一步都有实际影响:loss 曲线的抖动幅度、精度是否跳高、是否在局部极小值处停住,背后都是噪声在起作用。
有人会问:既然噪声大不好,那干脆把 batch 设大不就行了?事情没那么简单。梯度噪声在训练中并不全是坏事,它给参数更新带来了随机扰动,这种扰动在一定条件下能帮模型跳出尖锐的极小值盆地,最终泛化更好。这正是业界一直存在“大 batch 泛化差”讨论的原因之一——大 batch 的梯度更准,但也更容易走到一个看似平坦、实则过拟合的区域。优化器在这里又插了一脚:SGD 老老实实沿着梯度走,噪声大的时候天然有探索行为;Adam 则通过二阶矩归一化,把不同参数的更新尺度拉平,噪声被部分“抹平”,小 batch 的优势也被削弱了。
1.3 两条主线的碰撞
简单画一下整个故事的主线。第一条线是 batch size 的梯度噪声曲线,从大噪声到小噪声,对应小 batch 到大 batch。第二条线是优化器的噪声容忍特性,有的优化器对噪声敏感,有的优化器对噪声钝感。
两条线叠加之后会形成几个常见组合:
- 小 batch + SGD:梯度噪声大,SGD 不缩放、不修正,每一步都受当前 batch 的随机波动影响,像是在凹凸不平的路上骑着自行车走,路线曲折但不容易陷在原地。
- 小 batch + Adam:Adam 会对梯度做指数滑动平均和尺度归一化,相当于给自行车加了减震和导航,抖动明显下降,但是原本属于小 batch 的“探索性”也被削减。
- 大 batch + SGD:梯度几乎不抖,更新方向稳定,像是走上了一马平川的直路,但如果学习率没跟上,就会一直以同样的步幅慢慢走,训练效率低。
- 大 batch + Adam:Adam 的归一化在这种场景下优势不大,因为梯度本身已经很稳定,但它有可能把更新步长算得过大,造成后期震荡。
所以你看,优化器好不好,取决于它和你当前的 batch size 是否顺路。判断顺不顺路,最好先从你已经确定使用的 batch size 出发。
2. 小 batch size:SGD 系与 Adam 系的较量
2.1 小 batch 的真正优势:梯度噪声与隐式正则
小 batch 一般指 batch size 在 2 到 64 这个范围。训练模型时,尤其是视觉模型,很多人习惯从 32 或 64 开始跑,原因之一是显存够用,另一个原因是小 batch 训练在不少任务上泛化表现并不差。
我自己的理解是,小 batch 的梯度噪声相当于一个隐式正则项。每次参数更新时,方向不是朝着精确的梯度方向走,而是有一个随机偏转。这个偏转如果幅度合适,会让模型在损失曲面上走出更宽泛的路径,不容易扎进某个过窄的沟里。很早之前某团队在公开分享里就提到过类似结论,他们用小 batch 加较大的学习率训练出了泛化能力相当好的模型,很多人以为秘诀在数据增强,实际上随机梯度噪声贡献了相当一部分。
当然,噪声不是免费的午餐。小 batch 也意味着收敛不稳定,least loss 可能要经历更多轮次才稳定。而且使用某些自适应优化器时,噪声会被“滤掉”一部分,隐式正则效果也要打个折扣。
2.2 SGD+Momentum 在小 batch 下的表现
SGD 加 Momentum 是我在小 batch 场景下最先会尝试的组合。Momentum 解决的是 SGD 抖动过大的问题:它让更新方向不仅依赖当前梯度,还依赖历史的梯度累积,像滚雪球一样越滚越有惯性。在小 batch 条件下,单步梯度噪声大,但 Momentum 将这些噪声做了时间维度的平均,方向性就出来了,训练曲线不会失控。
我踩过的一个坑是 momentum 系数配得太大。默认值通常是 0.9,对于小 batch 训练没问题,但如果数据分布很不均匀,0.9 的惯性会让参数在 loss 曲面陡峭区域停不下来,出现 loss 反复横跳。建议从 0.9 起步,如果曲线抖得厉害,降到 0.85 甚至 0.8 看看。
小 batch 配 SGD 时还有一个常见误操作:学习率照搬 Adam 的默认值。SGD 对学习率的敏感度远高于 Adam,Adam 用 1e-3 能正常收敛的任务,SGD 用 1e-3 很可能发散。我通常给 SGD 的学习率设在 0.01 到 0.2 之间,具体要看任务和 batch size,先从 0.1 试,再二分法调整,每一步都观察前 200 轮的 loss 走势。
2.3 Adam 系在小 batch 下的实测表现
Adam 系列在 NLP、推荐、多模态领域几乎是默认选项。它对小 batch 的适应能力很强,因为一阶矩让更新方向平滑,二阶矩让不同参数的更新尺度不至于差太多。在小 batch 下,Adam 训练出来的模型通常比 SGD 更容易达到一个不错的 baseline,这就是为什么大家都喜欢用它做快速验证。
但 Adam 在小 batch 下并非没有代价。它在接近收敛时可能出现 loss 震荡,原因在于二阶矩估计在小样本上偏大,导致有效步长偏大。实际训练里我习惯评估模型时选一段窗口内表现最好的 checkpoint,而不是用最后一轮。另外 Adam 在在小 batch 场景下的 weight decay 要注意:很多框架里 Adam 的 L2 正则与 weight decay 没有直接对应,直接用了效果会偏弱或者偏强。现在几大深度学习框架也都支持解耦权重衰减的 AdamW,这是替代 Adam 的比较稳妥的选择。
2.4 小 batch 的避坑提醒
小 batch 场景下最容易出问题的,其实是数据读取和预处理环节。batch 小时,每个 batch 的统计量不稳定,如果用了依赖全局统计量的机制,比如某些 normalization 方式,很容易产生偏差。实践里可以把 batch 设到 16 或 8 测试一下,如果训练曲线异常跳动,优先检查是不是数据预处理中的均值方差不一致,而不要一味地怀疑优化器。
另外,小 batch 下梯度裁剪也有讲究。Adam 对梯度量级有归一化能力,梯度裁剪倒不是必须;但 SGD 对极端梯度非常敏感,偶发一个异常样本就可能带崩整条曲线。如果你只能用 SGD,建议把全局梯度范数裁剪在 5.0 左右,先跑通再微调,这个值在大多数图像任务里都能避免发散。
3. 大 batch size:从崩溃到 LAMB/LARS 的救场
3.1 为什么大 batch 容易训不动、甚至直接发散
大 batch 一般指 batch size 在 256 以上,现在很多大规模训练甚至用 2048、4096、8192 甚至更大。大 batch 的优势是训练吞吐高,GPU 利用率好,梯度方向稳定。但随之而来的问题很现实:模型训不动或者直接训练失败。
理论原因在之前已经提过:大 batch 使得梯度噪声极小,模型很容易被“推向”尖锐的极小值区域。尖锐的极小值泛化通常较差,同时在后续训练时只要学习率稍大,参数就会在陡峭边缘振荡,造成 loss 忽高忽低。还有另一个原因:大 batch 会改变损失曲面的有效几何结构,让某些原本对优化器友好的路径变得不可达。这就好比导航重新规划了路线,可是那条路根本不适合你的车,开上去就颠簸。
我亲身经历过的典型情况:把 batch size 从 128 提到 1024,其他一切不变,第一轮迭代就出现 NaN。当时第一反应是学习率太大,降到原来的四分之一,虽然不再 NaN,但 loss 下得非常慢,最终效果还不如原来的小 batch。换了带 warmup 的方案之后才勉强正常。这些现象都指向一个根源:大 batch 必须从头适配学习率和优化器,不是简单翻倍 batch size 就能白捡吞吐量的。
3.2 学习率缩放到底该怎么算
关于学习率缩放,最早被广泛讨论的是一次经验法则:线性缩放。核心思想很简单:batch 变大,每个 batch 里累计的梯度信息更多,如果学习率不变,更新步长相对于有效梯度信息会变小,收敛变慢,所以学习率应该按比例放大。
形式化地写:如果基准配置是 batch size (B_0) 和学习率 (\eta_0),新配置 batch size (B),那么理论学习率 (\eta) 可以取 (\eta_0 \times (B / B_0))。举个例子,原本 batch 128 配 lr 1e-3,扩到 512 时理论 lr 应该是 4e-3。
但线性缩放不是万能的。当 batch size 增长特别大,比如几百到几千时,线性缩放会让学习率变得过大,直接发散。这时候平方根缩放更稳妥:(\eta = \eta_0 \times \sqrt{B / B_0})。还拿上面的例子,平方根缩放给的是 2e-3,明显温和得多。
选择哪种缩放,我的标准是这样:如果只是从 64 扩到 256,线性缩放没问题;如果从 256 扩到 1024 以上,或者任务本身对超参敏感,先用平方根缩放,再配合 warmup 观察前几百轮。还可以做一个简单校验:训练前几十步内 loss 不应该出现爆炸性上升,如果出现了,就回到更小的学习率。
3.3 大 batch 下的优化器选型
大 batch 场景下,纯粹的 SGD 会显得很吃亏,因为梯度稳定后,SGD 更像一个不带地图的司机,learning rate 稍微没调好就偏航。Adam 比 SGD 稳定,但它对大 batch 的适应性也不是天然就好,关键在于它的二阶矩归一化可能把更新步长算得偏大,尤其是在训练后期。
专门为大规模分布式训练设计的优化器,比如 LARS 和 LAMB,把大 batch 的使用体验往前推了一大步。它们的核心思路是 layer-wise 自适应的学习率:对每一层的参数,根据其梯度和权重的比例计算专属学习率,使不同层不至于因为量级差异而训练失衡。在目标检测、图像分类、大规模语言模型预训练这类场景,我用 LAMB 跑过 4096 的 batch size,训练收敛明显比 AdamW 更平滑,而且对学习率的选择没有那么敏感。
不过 LARS/LAMB 也不是万能。它们引入的额外超参会增加调参成本,而且如果你只需要单卡或双卡训练,分布式带来的收益远小于超参调试的成本,我建议优先用 AdamW,没必要为了追新而换。
3.4 warmup 不是可选项,是必选项
大 batch 训练的另一个铁律是 warmup。为什么需要 warmup?因为大 batch 在训练早期就遭遇了非常精确的梯度,而模型参数还是随机初始化状态,损失曲面又陡峭又复杂,此时如果学习率一开始就很高,步长会直接飞出去。warmup 让学习率在前若干步从一个很小的值线性升到目标值,等于给参数一个从平庸到靠谱的缓冲过程。
具体实现上,多数框架都有内置的 warmup 参数。我一般用前 1% 到 5% 的总步数完成 warmup,比如总步数 10000,warmup 设 200 到 500 步。如果学习率目标很高,比如线性缩放到 4e-3 或更高,warmup 步数就多给一些,宁可多花一点时间在热身阶段,也别让前几十步把网络训练坏。
4. 实操指南:用 batch size 当路标来选优化器
4.1 三个典型 batch size 区间与对应方案
把 batch size 分成三个区间来看,实际操作会清晰很多。
区间一:2 到 64。典型任务是微调、少量样本训练、在线学习。这种情况下推荐 SGD Momentum 或 AdamW。如果样本质量不高、噪声大,优先 SGD Momentum,配合 0.85 的 momentum 能平滑不少;如果任务偏 NLP 或者你不知道怎么调 SGD,那就 AdamW,默认超参基本能跑,不需要大量干预。
区间二:64 到 512。这是大多数单人项目和中等规模团队的标准区间。在这个区间里,SGD Momentum 和 AdamW 都能用,甚至可以说各有千秋。我习惯先同时跑两个小实验:相同 batch size,分别用 SGD Momentum 和 AdamW,各跑几百轮,看 loss 下降曲线和验证集表现。如果差距不大,直接选 AdamW,因为在后续修改模型结构、增加数据增强时,AdamW 的鲁棒性更高。
区间三:512 到 8192 及以上。基本上进入了大 batch 世界。优化器层面优先考虑带自适应调节的版本,如果训练任务又大又吃分布式吞吐,那 LAMB/LARS 基本是必须的;如果规模刚好在 512 到 1024 之间,也可以先用 AdamW 配合线性缩放跑通,再对比是否换 LAMB。这里的关键配套是 warmup 和目标学习率,不要单独只动优化器。
4.2 一张速查表照抄
下面这张表是我从多个项目里总结出来的速查配置,可以直接当起点。注意“起点”两个字很重要,实际还是要根据 loss 曲线微调。
| batch size 区间 | 推荐优化器 | 初始学习率参考 | warmup 步数 | 备注 |
|---|---|---|---|---|
| 2-32 | SGD Momentum / AdamW | SGD: 0.01-0.05,AdamW: 1e-4 到 3e-4 | 可省略 | 注意数据加载器和 normalization 统计量不稳定的问题 |
| 32-128 | AdamW / SGD Momentum | AdamW: 3e-4,SGD: 0.05-0.1 | 100 步以内 | 最常见的区间,优先跑通流程 |
| 128-512 | AdamW / SGD Momentum | 按 batch 比例适度缩放,一般从 3e-4 出发 | 建议加 | 线性缩放可以用,但别超过 4 倍 |
| 512-2048 | AdamW / LAMB | 平方根缩放起步,配合 warmup | 必须加 | 优先跑小规模的 LAMB 对比实验 |
| 2048 以上 | LAMB / LARS | 极保守起步,如 1e-4 到 5e-4 | 必须加且步数适当放大 | 强调分布式吞吐优化,别死磕单卡 |
这张表里的学习率,普通情况下可以跑出不错的效果,但它不是一个保证值。你的任务、数据、模型架构都有影响,我把表放在这里的意义是让你少试几组魔幻组合,而不是替你终结所有调参工作。
4.3 我的实测流程(从 baseline 到生产)
我刚接手一个新任务时,不会一上来就大规模调超参。第一步固定 batch size 为 32,优化器选 AdamW,学习率 3e-4,跑一个短周期。这个组合小、快、稳,能迅速暴露模型结构和数据上的问题。模型能正常达到一个baseline之后,再决定要不要换大 batch。
如果目标是提升训练吞吐量,我会先把 batch size 翻倍到 64,再线性缩放学习率到年左右 6e-4,跑通再说。第二步从 64 到 128,继续线性缩放。到 256 附近我会停下来,比较当前配置与最初配置的效果差距。如果差距明显,我不会急着扩大 batch,而是先回退一步,在当前的 batch 下重新优化学习率和 momentum。
还有一点实践经验:在生产训练里,不要为了“显存刚好能塞下最大 batch”就把 batch 设到最大。batch size 要匹配你的显卡数量和分布式方案,找到吞吐拐点比最大 batch 更关键。GPU 利用率、通信开销、data pipeline 都会影响总吞吐。一次训练用 batch 512 和 1024 的总吞吐可能几乎一样,但效果上 1024 的调参难度高不少,这种收益不划算。
5. 常见问题与排查实录
5.1 大 batch 一冲就 NaN
大 batch 训练第一轮迭代就 NaN,优先级最高的问题就是学习率和初始化。我的排查步骤是:先看是不是学习率太高,把学习率降一个数量级,比如从 1e-3 降到 1e-4,观察前 20 轮;如果还在 NaN,再看梯度里有没有异常值,输出当前层的梯度范数;最后再检查数据,看看有没有 NaN 样本进入 batch。
一个比较容易忽略的现象:当 batch 变大时,现有的数据增强逻辑可能引入了重复样本或相关性极高的样本,这也会让梯度异常。还有,某些 normalization 层在 batch 很大时统计量稳定了,但与之前小 batch 训练时的统计量完全不同,导致极端激活值出现。遇到这类问题,不应盲目认为是优化器的问题,先把数据和网络输出检查一遍再调超参。
5.2 loss 不降或降得很慢
这种情况往往不是因为优化器选错了,而是学习率没跟上 batch size 的变化。举一个具体例子:原本 batch 64 配 lr 1e-3,扩到 256 之后还保持 1e-3,loss 下降明显变慢,原因前面说过:梯度信息多了但步长没变,等效于每步的推进变小了。
解决方法是按残留公式缩放:从 64 到 256,线性缩放到 4e-3;如果怕不稳,用平方根缩放到 2e-3。但也要注意另一面:有的任务在扩 batch 后 loss 曲线确实更平滑,可收敛精度反而更低,这时就要考虑是不是优化器的探索能力不够,可以试着换回 SGD Momentum 或调大 momentum。
5.3 batch 翻倍后效果变差
这个问题的本质其实是泛化变差。大 batch 收敛到了过拟合程度更高的尖锐极小值,这在视觉任务里尤其常见。如果只是精度低一两个百分点,最简单有效的办法是把 batch size 往回退,然后用数据增强、Dropout 等正则手段来补效果,而不是硬扛。
如果你想在保持大 batch 的情况下提升效果,可以考虑加更强的权重衰减或者学习率退火。一个大 batch 训练里,后期的学习率衰减速度直接影响最终收敛位置,我在实践中发现余弦退火配合大 batch 往往要优于等间隔下降,效果能追回不少差距。
5.4 微调场景下该怎么选
微调通常算小 batch 场景,但问题是预训练模型已经在一个大类分布上收敛过,直接用小 batch 和自适应优化器很容易破坏原有特征结构。我的做法是:先用很小的学习率跑,比如 2e-5 到 5e-5,batch size 32 到 64,优化器选 AdamW,关掉 or 极度减小权重衰减。
如果微调的损失函数与原预训练任务差异很大,我建议把某些层的学习率单独控制,通常最后几层的学习率可以放得大一点,前面的特征层稳步保底。另外要特别小心优化器的 epsilon 参数,某些模型在低精度推理时对 epsilon 敏感,实测中建议把 epsilon 从默认的 1e-8 提到 1e-6 以增强稳定性,这个细节在混合精度训练里尤其重要。
6. 写在最后:选优化器的最终思路
说了这么多,我想强调的只有一句话:优化器从来不是孤立变量,它的好坏要放到 batch size、学习率、训练任务组成的大上下文里判断。
我个人的拿手做法是:每个新任务先跑一个 AdamW + 小 batch 的短实验,确认模型和数据没问题;然后根据目标 batch size 范围选择主优化器和配套学习率;最后用 warmup、学习率调度和必要的正则手段处理大 batch 或微调场景下的特殊问题。这套流程不复杂,但能避免大量无效换优化器的折腾。
如果非要给一个最通用的起点:单卡环境、batch 64 左右、AdamW、学习率 3e-4、先跑通,再从曲线出发做下一步决策。这不是什么高深技巧,却是很多项目里最常被忽略的第一原则:先让训练跑起来,再谈优化器好不好。