1. 大模型训练里,优化器到底在解决什么问题
搞大模型训练的人,绕不开一个核心组件:优化器。你手里有一个几百亿甚至上千亿参数的模型,每一轮前向传播算完loss,反向传播算出每个参数的梯度,接下来怎么办?梯度只告诉你“参数往哪个方向调能降低loss”,但具体调多少、怎么调、要不要加惯性、要不要自适应,这些全部由优化器决定。说白了,优化器就是模型训练的“方向盘加油门”,它直接决定了你这一脚踩下去,模型是平稳收敛还是直接飞出去。
我见过太多人,模型结构调得飞起,数据清洗做得一丝不苟,结果训练loss各种震荡、梯度爆炸、收敛极慢,最后排查半天发现是优化器参数没配对。这种事在大模型训练里太常见了。因为大模型和小模型不一样,小模型你用个默认的Adam跑一跑基本就收敛了,但大模型参数量大、层数深、梯度分布复杂,优化器的选择和学习率的调度直接决定了你能不能在一周内看到loss下降,还是烧了两周GPU发现loss纹丝不动。
这篇文章主要面向已经上手过大模型训练、但对优化器内部机制和实际调参还没有系统认知的从业者。我会从SGD讲起,一路拆到AdamW、EMA,再到最近讨论度很高的Muon优化器,把每个优化器的核心原理、适用场景、实操参数配置、踩坑经验全部讲清楚。不管你是刚接触大模型训练的新手,还是已经跑过几轮预训练的老手,应该都能从里面找到可以直接抄作业的东西。
2. 从SGD到AdamW:优化器的演进逻辑与核心机制
2.1 SGD和Momentum:最朴素的优化思路为什么在大模型上不够用
SGD,随机梯度下降,是所有优化器的祖宗。它的逻辑极其简单:每个step拿一个batch的数据算梯度,然后参数沿着梯度的反方向走一步。公式就一行:
θ = θ - lr * g其中θ是参数,lr是学习率,g是当前batch算出来的梯度。就这么简单。它的优势在于内存占用极小,每个参数只需要存一个梯度值,不需要额外的状态变量。对于参数量动辄几百GB的大模型来说,这一点其实很重要。
但SGD的问题也很明显。大模型训练的loss曲面不是那种光滑的碗状,而是充满了鞍点、峡谷和局部极小值的复杂地形。纯SGD在峡谷地形里会来回震荡,收敛极慢。于是有了Momentum,动量法。它的核心思想是:不光看当前这一步的梯度,还把历史梯度累积起来,用动量来平滑更新方向。
v = β * v + g θ = θ - lr * vβ通常取0.9,意思是历史动量的衰减系数。你可以把它理解成给优化过程加了一个“惯性”——之前一直往某个方向走,即使当前这一步梯度方向变了,惯性也会带着你继续往原来的方向走一段。这在峡谷地形里特别有用,能有效抑制震荡。
但即便加了Momentum,SGD在大模型训练上还是有一个致命问题:所有参数共享同一个学习率。大模型里不同层的梯度尺度差异巨大,embedding层的梯度和最后几层attention的梯度可能差好几个数量级。用同一个学习率,要么前面的层学不动,要么后面的层直接炸掉。这就是为什么自适应学习率方法成为了大模型训练的主流。
2.2 Adam和AdamW:自适应学习率是怎么成为大模型标配的
Adam的核心创新在于:它为每个参数单独维护一个自适应学习率。具体来说,它同时维护梯度的一阶矩估计(均值)和二阶矩估计(方差),然后用二阶矩的平方根来归一化学习率。
m = β1 * m + (1 - β1) * g # 一阶矩 v = β2 * v + (1 - β2) * g² # 二阶矩 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。偏差修正是为了解决初始阶段m和v偏向0的问题,t是当前的step数。
Adam的好处是:梯度大的参数,二阶矩大,实际学习率被压小;梯度小的参数,二阶矩小,实际学习率被放大。这样每个参数都能以自己的节奏更新,不需要你手动为每一层调学习率。这在深层网络和大模型上效果非常明显,也是为什么Adam长期占据大模型训练优化器的头把交椅。
但Adam有一个被很多人忽略的问题:权重衰减的实现方式。原始Adam的L2正则化是直接加在梯度上的,也就是g = g + λθ。这样做的问题是,L2正则化的效果会被自适应学习率“吃掉”——因为二阶矩归一化会把这个额外的梯度也一起缩放,导致大参数的衰减效果不如预期。
AdamW就是来解决这个问题的。它的做法是把权重衰减从梯度计算里拿出来,直接作用在参数更新上:
θ = θ - lr * (m_hat / (sqrt(v_hat) + ε) + λ * θ)这个改动看起来很小,但实际效果差异很大。AdamW的权重衰减更加稳定和可预测,在大模型训练里能有效防止过拟合,同时不会干扰自适应学习率的正常工作。现在几乎所有主流大模型的预训练都用AdamW,这不是没有道理的。
2.3 为什么大模型训练里AdamW的默认参数需要改
虽然AdamW的默认参数(lr=1e-3, β1=0.9, β2=0.999, ε=1e-8, weight_decay=0.01)在小模型上跑得挺好,但直接搬到大规模训练上往往会出问题。我踩过的坑包括:loss在前几百步就炸了、梯度norm突然飙升、训练到中期loss平台期特别长。
大模型训练里,学习率通常要设得比默认值小很多。比如GPT-3级别的模型,学习率一般在1e-4到6e-5之间,甚至更低。原因是模型参数量大,每步更新的累积效应强,学习率大了容易直接跳过最优解区域。另外β2有时候会从0.999调到0.95或0.98,目的是让二阶矩估计对近期梯度更敏感,适应大模型训练中梯度分布快速变化的特点。
weight_decay的设置也很讲究。大模型预训练通常用0.1甚至0.01,但需要配合学习率调度一起看。如果weight_decay设得太大,模型会过度惩罚大权重,导致表达能力下降;设得太小,又起不到正则化效果。我的经验是,先按0.1跑一轮短训练看loss曲线,如果验证集loss比训练集loss高很多,再适当加大weight_decay。
还有一个容易忽略的点是ε。默认的1e-8在大多数情况下没问题,但如果你的模型用了混合精度训练(fp16或bf16),梯度值可能非常小,这时候ε设得太小会导致除零或者数值不稳定。有些团队会把ε调到1e-6甚至1e-5,牺牲一点自适应精度来换取数值稳定性。
3. EMA和模型权重平均:为什么训练完还要再“拌一拌”
3.1 EMA的基本原理和在大模型训练中的实际作用
EMA,指数移动平均,严格来说不是一个优化器,而是一种模型权重的后处理技术。但它在实际大模型训练里几乎和优化器一样重要。核心思想很简单:训练过程中不光保存当前step的模型权重,还维护一份历史权重的指数移动平均。
ema_θ = decay * ema_θ + (1 - decay) * θdecay通常取0.999或0.9999。训练结束后,用ema_θ作为最终模型权重,而不是用最后一步的θ。
为什么这样做有效?因为大模型训练后期,模型权重会在最优解附近“抖动”。每个step的权重都受到当前batch噪声的影响,单独拿出最后一步的权重,可能恰好落在了一个不太好的位置上。EMA相当于把训练后期的多个权重“拌”在一起,平滑掉噪声,得到一个更稳定、泛化更好的权重。
我在实际项目里对比过,用EMA权重和不用EMA权重,在同样的验证集上,EMA版本的loss通常能低0.02到0.05,下游任务的准确率也能提升1到2个百分点。这个提升在大模型上已经非常可观了。
3.2 EMA decay参数怎么选:0.999还是0.9999
decay的选择取决于你的训练总步数和模型更新的频率。有一个经验公式可以参考:
decay = (1 + step) / (10 + step)但这个公式更适合训练步数较少的情况。对于大模型预训练,通常直接设固定值。我的经验是:
- 训练步数在10万步以内,decay取0.999比较合适,EMA权重能跟上模型的变化。
- 训练步数超过50万步,decay可以取0.9999甚至0.99999,让EMA更加平滑。
- 如果训练过程中学习率有warmup和decay,EMA的decay也可以跟着学习率一起调度,学习率大的时候decay小一点,学习率小的时候decay大一点。
有一个坑需要注意:EMA权重在训练早期和当前权重差异很大,因为模型还在快速变化。所以不要在训练刚开始就用EMA权重做验证,那时候EMA还不如当前权重。通常等到训练中后期,EMA的优势才会体现出来。我一般会在训练进度超过50%之后才开始关注EMA权重的表现。
3.3 EMA和模型保存策略的配合
实际训练里,EMA权重的保存策略也很重要。如果你每个checkpoint都保存EMA权重,存储开销会翻倍。我的做法是:训练过程中只保存当前权重,但维护EMA状态;训练结束后,用最终的EMA权重覆盖保存一次。如果训练中间需要做验证,可以每隔一定步数临时计算EMA权重做评估,但不落盘。
另外,EMA权重和优化器状态是独立的。恢复训练的时候,你需要同时恢复模型权重、优化器状态和EMA状态,否则EMA的平滑效果会断掉。这一点在写checkpoint逻辑的时候要特别注意,我见过有人恢复训练后忘了恢复EMA状态,结果EMA权重直接从头开始累积,效果大打折扣。
4. Muon优化器:大模型训练的新选择到底靠不靠谱
4.1 Muon的核心思路:为什么它可能比AdamW更适合大模型
Muon是最近在大模型训练圈子里讨论度很高的一个优化器。它的全称是MomentUm Orthogonalized by Newton-Schulz,核心创新在于:它不直接对梯度做自适应缩放,而是对梯度矩阵做正交化处理。
具体来说,对于二维参数矩阵(比如attention里的权重矩阵),Muon先对梯度做动量累积,然后通过Newton-Schulz迭代把动量矩阵正交化,再用正交化后的矩阵来更新参数。正交化的目的是让更新矩阵的奇异值都接近1,这样每个方向的更新幅度更加均匀,不会出现某些方向更新过大、某些方向更新过小的问题。
为什么这对大模型有用?因为大模型的权重矩阵通常具有低秩特性,梯度矩阵也是。AdamW的自适应学习率虽然能调整每个参数的更新幅度,但它是在元素级别操作的,没有考虑矩阵的整体结构。Muon从矩阵级别做正交化,能更好地保持权重矩阵的谱结构,理论上收敛更快、泛化更好。
4.2 Muon的实操配置和注意事项
Muon目前还没有像AdamW那样成为绝对主流,但在一些开源大模型训练项目里已经有成功案例。如果你打算尝试,以下是我总结的配置要点:
- Muon通常只用于二维以上的参数矩阵,比如attention的QKV投影矩阵、FFN的权重矩阵。对于embedding层、LayerNorm的缩放参数这些一维参数,还是用AdamW。
- 学习率方面,Muon通常需要比AdamW更大的学习率,因为正交化后的更新幅度被归一化了。有报告说Muon的学习率可以设到AdamW的5到10倍。
- 动量系数一般取0.95,比AdamW的β1=0.9稍大,因为正交化本身就有平滑效果。
- Muon对weight_decay的敏感度比AdamW低,但也不能完全不设,通常0.01到0.1之间。
需要特别注意的是,Muon的Newton-Schulz迭代次数是一个关键超参数。迭代次数太少,正交化不充分;迭代次数太多,计算开销大。通常5到10次迭代就能达到不错的效果。另外,Muon目前对混合精度训练的支持还不如AdamW成熟,如果用bf16训练,需要仔细检查数值稳定性。
4.3 Muon和AdamW的对比:什么时候该换
我的建议是:如果你正在做的是标准的大模型预训练,而且没有特别强的实验需求,AdamW仍然是首选,因为它的生态最成熟、调参经验最丰富、出问题的概率最低。Muon更适合以下场景:
- 你在做优化器相关的学术研究或消融实验。
- 你的模型结构比较特殊,AdamW收敛效果不理想。
- 你有足够的计算资源做多组对比实验,能承受Muon可能带来的额外调参成本。
不要因为Muon最近讨论度高就盲目切换。我见过有团队在预训练中途从AdamW切到Muon,结果loss直接飙升,不得不回滚。优化器的切换最好从训练一开始就确定,中途切换的风险很大。
5. 学习率调度和优化器的配合:大模型训练里最容易被低估的环节
5.1 Warmup为什么对大模型训练至关重要
学习率warmup是大模型训练里几乎必不可少的一步。它的逻辑是:训练刚开始的时候,模型权重是随机初始化的,梯度方向非常不稳定。如果这时候直接用大学习率,很容易把模型带到一个很差的区域,甚至直接梯度爆炸。
Warmup的做法是在训练的前若干步里,让学习率从0线性增加到设定的最大值。步数通常取总训练步数的1%到5%,或者固定几千步。比如你计划训练10万步,warmup可以设2000到5000步。
我踩过的坑是:warmup步数设得太少,比如只设了100步,结果模型在warmup结束后loss突然飙升。后来分析发现是因为模型还没稳定下来,学习率就冲到了最大值。对于大模型,warmup步数宁可多设一点,也不要设太少。我现在的默认值是总步数的2%,如果模型特别大(比如超过100B参数),会调到5%。
5.2 Cosine衰减和Linear衰减怎么选
Warmup结束后,学习率需要逐渐衰减。最常用的两种是Cosine衰减和Linear衰减。
Cosine衰减的公式是:
lr = min_lr + 0.5 * (max_lr - min_lr) * (1 + cos(π * step / total_steps))它的特点是前期衰减慢、中期衰减快、后期衰减又变慢。这种形状在大模型训练里表现很好,因为前期模型还在快速学习,学习率保持大一点能加快收敛;中期需要精细调整,学习率快速下降;后期模型已经接近最优解,学习率保持一个小值做微调。
Linear衰减就是线性从max_lr降到min_lr,简单直接。它的优势是可控性强,你知道每一步的学习率是多少。但在大模型上,Linear衰减后期学习率下降太快,可能导致模型还没收敛到位学习率就已经接近0了。
我的经验是:预训练用Cosine衰减,微调用Linear衰减。预训练步数多,Cosine的平滑衰减更适合长期训练;微调步数少,Linear的简单直接更可控。min_lr通常设max_lr的10%左右,不要设成0,否则训练后期模型完全学不动。
5.3 梯度裁剪和优化器的配合
梯度裁剪是大模型训练里另一个和优化器紧密配合的技术。它的作用是防止个别batch的梯度异常大,导致参数更新过猛。最常见的做法是按梯度范数裁剪:
if ||g|| > clip_value: g = g * clip_value / ||g||clip_value通常取1.0。这个操作在优化器更新之前执行,相当于给梯度加了一个上限。
但梯度裁剪和优化器的配合有一个细节:AdamW本身有自适应学习率,理论上对梯度幅度不敏感。但实际上,如果梯度突然变得极大,二阶矩估计会被拉高,导致后续几步的实际学习率被压得很低,影响收敛。所以即使是用AdamW,梯度裁剪也是必要的。
我通常会在训练初期把clip_value设小一点(比如0.5),等loss稳定后再调到1.0。另外,如果你发现训练过程中梯度norm经常超过clip_value,说明学习率可能设大了,或者数据里有异常样本,需要进一步排查。
6. 实操中常见的优化器问题与排查技巧
6.1 Loss震荡和梯度爆炸的排查思路
Loss震荡是大模型训练里最常见的问题之一。表现是loss曲线上下波动很大,没有稳定的下降趋势。可能的原因和排查方法如下:
| 问题现象 | 可能原因 | 排查方法 | 解决方案 |
|---|---|---|---|
| Loss前期剧烈震荡 | 学习率过大 | 打印每步学习率和loss | 降低学习率或增加warmup步数 |
| Loss中期突然飙升 | 梯度爆炸 | 监控梯度norm | 降低clip_value或降低学习率 |
| Loss后期平台期 | 学习率衰减太快 | 检查学习率调度曲线 | 改用Cosine衰减或提高min_lr |
| Loss周期性波动 | 数据batch分布不均 | 检查数据采样逻辑 | 加强数据shuffle或调整batch组成 |
我遇到过一次loss在训练到3万步左右突然从2.3飙升到5.6,查了半天发现是某个数据分片的格式有问题,导致模型收到了大量异常样本。所以loss异常时,除了查优化器参数,也要查数据。
6.2 优化器状态恢复时的常见坑
大模型训练通常需要中断恢复,这时候优化器状态的保存和恢复就非常关键。常见的坑包括:
- 只保存了模型权重,没保存优化器状态。恢复后优化器的动量和二阶矩估计全部重置,导致loss突然跳变。
- 保存了优化器状态但没保存学习率调度器的状态。恢复后学习率从头开始warmup,训练节奏完全乱掉。
- 使用了EMA但没保存EMA状态。恢复后EMA权重从头累积,平滑效果消失。
- 分布式训练时,优化器状态的分片保存和恢复逻辑有bug,导致部分rank的状态丢失。
我的建议是:checkpoint里必须包含模型权重、优化器状态、学习率调度器状态、EMA状态、当前step数、随机种子。恢复训练后,先跑几百步观察loss是否和中断前连续,如果出现跳变,说明状态恢复有问题。
6.3 混合精度训练下优化器的数值稳定性问题
混合精度训练(fp16或bf16)能大幅降低显存占用和加快计算速度,但对优化器的数值稳定性提出了更高要求。主要问题包括:
- fp16的动态范围窄,梯度值容易下溢到0或者上溢到inf。bf16的动态范围大一些,但精度低。
- AdamW的二阶矩估计v在fp16下容易累积误差,导致实际学习率偏离预期。
- 梯度裁剪在fp16下可能失效,因为梯度norm的计算本身就可能溢出。
解决方案通常是:优化器状态用fp32保存,即使模型权重和梯度用fp16/bf16。这样二阶矩估计的精度有保障。另外,使用loss scaling来防止梯度下溢,动态loss scaling比静态的更稳定。如果发现训练过程中频繁出现loss scaling调整,说明模型对数值精度比较敏感,可以考虑换bf16或者检查模型里是否有数值不稳定的操作。
7. 优化器选型和调参的个人经验总结
7.1 不同训练阶段的优化器策略
大模型训练通常分为预训练、继续预训练、监督微调、对齐训练几个阶段,每个阶段对优化器的需求是不一样的。
预训练阶段,数据量最大、训练步数最多,AdamW配合Cosine衰减和warmup是标准配置。学习率通常从1e-4到6e-5,weight_decay设0.1,β2可以适当调低到0.95。EMA decay设0.9999,训练后期用EMA权重做验证。
继续预训练阶段,模型已经有一个不错的初始化,学习率可以设小一点,比如1e-5到5e-5。warmup步数也可以减少,因为模型不需要从头稳定。这个阶段可以尝试Muon优化器,因为模型已经比较稳定,Muon的正交化效果可能更好。
监督微调阶段,数据量小、训练步数少,学习率通常设1e-5到2e-5,warmup步数几百步就够了。这个阶段不建议用EMA,因为训练步数太少,EMA还没平滑到位训练就结束了。
对齐训练阶段,通常用比微调更小的学习率,比如5e-6到1e-5。优化器还是AdamW,但weight_decay可以设小一点,因为对齐数据通常不多,过强的正则化会影响效果。
7.2 优化器参数调整的优先级
当你发现训练效果不理想时,优化器参数的调整优先级应该是:
- 学习率:影响最大,优先调整。先确认学习率是否在合理范围内。
- Warmup步数:影响训练初期的稳定性,如果loss前期震荡,优先加warmup。
- β2:影响自适应学习率的敏感度,大模型训练可以尝试0.95到0.98。
- Weight decay:影响正则化强度,根据验证集表现调整。
- ε:影响数值稳定性,混合精度训练时可能需要调大。
- EMA decay:影响权重平滑程度,根据训练步数调整。
不要一次性调多个参数,否则你无法判断哪个参数起了作用。我通常一次只调一个,跑短训练(比如5000步)看loss曲线,确认有效后再跑长训练。
7.3 一些不太常见但有用的技巧
最后分享几个我在实际训练里总结的小技巧:
- 学习率预热不一定要从0开始,可以从一个很小的值(比如max_lr的1%)开始,这样能减少warmup步数。
- AdamW的β1和β2可以不同步调整,比如β1=0.9不变,只调β2。
- 如果训练过程中需要切换数据分布,可以在切换点临时降低学习率,等模型适应后再恢复。
- EMA权重可以在训练结束后和当前权重做一个加权平均,有时候比纯EMA效果更好。
- 梯度裁剪的clip_value可以随着训练逐步增大,前期严格、后期放松。
这些技巧不是万能的,需要根据具体模型和数据做实验验证。但至少能给你一个调整的方向,不至于面对loss曲线无从下手。
优化器这个东西,理论看起来不复杂,但实际调起来细节非常多。我的建议是:先把AdamW加Cosine衰减加warmup这套标准组合跑通,确保训练稳定收敛,然后再去尝试EMA、Muon这些进阶技术。不要一上来就追求最新最炫的优化器,稳定收敛比什么都重要。