☰
大模型训练优化器选型指南:从SGD到AdamW、EMA与Muon
2026/11/2 12:17:54 网站建设 项目流程

1. 大模型训练里优化器到底在干什么

很多人第一次接触大模型训练,注意力全在模型结构、参数量、数据规模上,觉得优化器就是个“调参附件”,随便选一个默认的就行。真跑过几百亿参数训练的人都知道,优化器选错,轻则收敛慢、loss 抖动,重则直接训练崩溃,前面烧的卡时全打水漂。这一篇就专门把优化器这件事讲透,从 SGD 到 AdamW,再到 EMA、Muon 这些近两年被反复讨论的方案,把选型逻辑、参数计算、实操踩坑一次说清楚。

先把最核心的问题摆出来:优化器到底解决什么问题。训练大模型本质是在一个超高维参数空间里找一组权重,让损失函数尽量小。这个空间维度动辄几十亿到上万亿,梯度下降就是沿着梯度反方向一点点挪。最朴素的 SGD 每次只用当前 batch 的梯度更新,方向噪声大、对学习率极其敏感;Adam 系列引入了一阶矩和二阶矩估计,相当于给每个参数配了自适应的步长;AdamW 又把权重衰减从梯度里解耦出来,成为目前大模型预训练的事实标准。而 EMA 不是优化器,它是参数平滑技术,常和优化器配合使用;Muon 则是 2024 年之后被广泛讨论的新方案,针对矩阵参数做正交化更新。

这篇文章适合三类人:一是刚上手大模型训练、搞不清 SGD 和 AdamW 区别的工程师;二是已经在跑训练、想搞清楚 weight decay、betas、eps 这些参数到底怎么设的从业者;三是想了解 EMA、Muon 这些新方案到底值不值得上的技术负责人。我会按“整体设计思路 → 核心细节与参数 → 实操流程 → 常见问题排查”的顺序展开,每个环节都给出可直接抄的配置和背后的计算逻辑。

2. 优化器整体设计与选型思路拆解

2.1 为什么大模型几乎不用纯 SGD

先说结论:在大规模预训练场景下,纯 SGD(哪怕带 momentum)基本被淘汰了,原因不是它理论不行,而是工程上不划算。SGD 对每个参数用同一个全局学习率,而大模型不同层、不同参数的梯度尺度差异极大——embedding 层梯度稀疏且量级小,attention 里的 QK 投影梯度密集且量级大。用同一个学习率,要么大梯度那边炸,要么小梯度那边几乎不动。

Adam 的核心贡献就是给每个参数维护独立的二阶矩估计 v_t,用 sqrt(v_t) 去归一化梯度,相当于自动给每个参数调步长。公式上,一阶矩 m_t = β1·m_{t-1} + (1-β1)·g_t,二阶矩 v_t = β2·v_{t-1} + (1-β2)·g_t²,更新量是 m_t / (sqrt(v_t) + eps)。这个设计让梯度大的参数步长自动缩小,梯度小的参数步长相对放大,收敛稳定性远超 SGD。

但 Adam 有个历史遗留问题:早期实现把 L2 正则直接加进梯度里,导致权重衰减和自适应学习率耦合在一起,衰减效果被二阶矩归一化扭曲了。AdamW 的“W”就是 decoupled weight decay,把衰减项从梯度更新里拆出来,直接作用在权重上:w = w - lr·(m_t/(sqrt(v_t)+eps)) - lr·wd·w。这一改动让大模型训练的泛化性能明显提升,也是现在几乎所有大模型预训练默认用 AdamW 的根本原因。

2.2 SGD、AdamW、EMA、Muon 的定位差异

这四个词经常被混在一起讨论,但它们的角色完全不同,我用一张表先理清楚:

名称类型核心作用大模型中的典型用法
SGD优化器沿梯度方向更新,可加 momentum小模型、微调、教学场景
AdamW优化器自适应步长 + 解耦权重衰减大模型预训练默认选择
EMA参数平滑技术对权重做指数滑动平均与优化器并行,用于评估/推理
Muon优化器对矩阵参数做正交化更新部分新架构预训练实验

SGD 和 AdamW 是互斥的优化器选择;EMA 不是优化器,它是在优化器更新完权重之后,额外维护一份平滑权重;Muon 是 AdamW 的替代方案,主要针对 2D 矩阵参数(比如线性层权重)做 Newton-Schulz 迭代正交化,让更新方向更接近“最速下降的正交方向”。理解这个定位差异,后面选型就不会乱。

2.3 选型的三个判断维度

我在实际项目里选优化器,主要看三个维度:模型规模、训练阶段、硬件预算。

模型规模上,参数量小于 1B 时,SGD + momentum 配合学习率 warmup 也能跑出不错结果,而且显存占用低(Adam 要为每个参数存 m 和 v,显存直接翻倍)。参数量超过 1B,基本无脑 AdamW,因为 SGD 调学习率的成本太高,一个学习率扫一遍就是几天卡时。

训练阶段上,预训练用 AdamW,微调阶段可以考虑 SGD 或 AdamW 的小学习率版本。这里有个经验:微调时如果用 AdamW,weight decay 要设得比预训练小,因为微调数据少,过强衰减会让模型快速遗忘预训练知识。

硬件预算上,AdamW 的显存开销是参数量的 2 倍(m 和 v 各一份),加上梯度本身,优化器状态占用的显存约等于 3 倍参数量。如果显存吃紧,可以考虑 8-bit Adam 或者 Adafactor 这类省显存的变体,但要注意量化带来的精度损失。

3. 核心参数解析与实操配置要点

3.1 AdamW 的四个关键参数怎么设

AdamW 的核心参数就四个:lr、betas、eps、weight_decay。每个都有明确的物理含义和推荐范围。

学习率 lr 是最敏感的。大模型预训练常用 1e-4 到 3e-4 之间,配合 warmup。我实测下来,7B 模型用 3e-4 起步、cosine 衰减到 3e-5,是比较稳的组合。lr 太大前期 loss 会剧烈震荡甚至 NaN,太小则收敛极慢。判断方法:看前 1000 步的 loss 曲线,如果震荡幅度超过均值的 20%,说明 lr 偏大。

betas 默认 (0.9, 0.95) 或 (0.9, 0.999)。β1 控制一阶矩的平滑程度,0.9 是通用值;β2 控制二阶矩,大模型训练里更常用 0.95 而不是 0.999,因为 0.999 会让二阶矩更新太慢,遇到梯度尺度突变时反应迟钝。这个细节很多教程不讲,但在长训练里影响明显。

eps 默认 1e-8。它的作用是防止除以零,但在混合精度训练(fp16/bf16)下,1e-8 可能太小导致数值不稳定,建议设成 1e-6 甚至 1e-5。我踩过的坑:用 fp16 训练时 eps 保持 1e-8,跑到几万步后偶发 loss 尖刺,改成 1e-6 后消失。

weight_decay 默认 0.1 是 LLM 预训练的常见值,但要注意它和 lr 是耦合的——实际衰减强度是 lr × wd。如果 lr 调小了,wd 也要相应调整,否则衰减相对过强。

3.2 EMA 的原理与参数选择

EMA(指数移动平均)不是优化器,但对大模型训练价值很大。它的逻辑很简单:维护一份影子权重 θ_ema,每次优化器更新完 θ 后,做 θ_ema = decay · θ_ema + (1 - decay) · θ。decay 越接近 1,平滑越强,影子权重越接近历史平均。

热搜里提到的“ema 指标 20 和 50 参数”,通常指的是 EMA 的窗口或 decay 对应的等效步数。decay 和等效窗口 N 的关系是 decay = 1 - 2/(N+1)。所以 N=20 对应 decay≈0.905,N=50 对应 decay≈0.961。窗口越大,平滑越强,但滞后也越明显。

实操建议:预训练阶段用 N=50 到 N=100 的 EMA,评估时用影子权重,通常能比原始权重低 1% 到 3% 的 perplexity。微调阶段用 N=10 到 N=20,因为微调步数少,窗口太大会平滑掉有效更新。注意 EMA 权重不参与梯度计算,只做推理,所以显存上要多存一份完整参数,7B 模型就是额外 14GB(bf16),这个开销要提前算进预算。

3.3 Muon 优化器的适用边界

Muon 是近两年比较火的新方案,核心思路是对矩阵参数(2D 权重)的更新量做正交化处理,用 Newton-Schulz 迭代近似 SVD,让更新矩阵的奇异值接近 1。这样做的直觉是:Adam 对每个元素独立归一化,忽略了矩阵参数的行列结构;Muon 从矩阵整体出发,更新方向更“均匀”。

但 Muon 不是万能的。它只适用于 2D 矩阵参数,embedding 层、LayerNorm 的 1D 参数还是要用 AdamW。所以实际配置是混合的:矩阵参数走 Muon,其余走 AdamW。另外 Muon 对学习率更敏感,通常要比 AdamW 大 5 到 10 倍,且需要更长的 warmup。我建议的做法是:先在中等规模(1B 到 3B)上做 A/B 对比,确认 loss 曲线和下游任务都稳定后再上大模型,不要直接在全量预训练上赌。

3.4 参数配置速查表

把上面的要点整理成一张可直接对照的表:

参数推荐值调整方向注意事项
lr1e-4 ~ 3e-4震荡大则降,收敛慢则升配合 warmup 和 cosine
betas(0.9, 0.95)长训练用 0.950.999 反应迟钝
eps1e-6 ~ 1e-8混合精度用大值fp16 下建议 1e-6
weight_decay0.1微调时降到 0.01与 lr 耦合
EMA decay0.96 ~ 0.99步数少用小窗口额外一份显存
Muon lrAdamW 的 5~10 倍需更长 warmup仅用于 2D 参数

4. 完整实操流程与关键环节实现

4.1 从零配置一个 AdamW 训练循环

下面给一个可直接参考的 PyTorch 配置片段,包含参数分组、warmup 和 cosine 衰减。参数分组是关键:weight decay 不应该作用在 LayerNorm 和 bias 上,这是很多新手忽略的细节。

import torch from torch.optim import AdamW from transformers import get_cosine_schedule_with_warmup # 参数分组:2D 权重做衰减,1D 参数(bias、LayerNorm)不做 decay_params = [] no_decay_params = [] for name, param in model.named_parameters(): if not param.requires_grad: continue if param.ndim >= 2: decay_params.append(param) else: no_decay_params.append(param) optimizer = AdamW([ {"params": decay_params, "weight_decay": 0.1}, {"params": no_decay_params, "weight_decay": 0.0}, ], lr=3e-4, betas=(0.9, 0.95), eps=1e-6) # warmup 占总步数 1%~3%,cosine 衰减到 lr 的 10% total_steps = 100000 warmup_steps = int(total_steps * 0.02) scheduler = get_cosine_schedule_with_warmup( optimizer, warmup_steps, total_steps )

这段代码里,param.ndim >= 2的判断是核心。LayerNorm 的 weight 是 1D,bias 也是 1D,对它们做 weight decay 会破坏归一化层的尺度,实测会让训练后期 loss 偏高。这个细节在论文里往往一笔带过,但在工程上影响不小。

4.2 EMA 的工程实现与显存计算

EMA 的实现要注意两点:一是用torch.no_grad()避免建图,二是用lerp_做原地更新省显存。

class EMA: def __init__(self, model, decay=0.99): self.decay = decay self.shadow = { k: v.clone().detach() for k, v in model.state_dict().items() } @torch.no_grad() def update(self, model): for k, v in model.state_dict().items(): if v.dtype.is_floating_point: self.shadow[k].lerp_(v, 1 - self.decay) else: self.shadow[k].copy_(v)

显存计算:7B 模型 bf16 权重约 14GB,EMA 影子权重再加 14GB,优化器状态(m + v)约 56GB(fp32),梯度约 14GB,加起来接近 100GB。这就是为什么大模型训练要用 ZeRO 或 FSDP 做分片——EMA 影子权重也要一起分片,否则单卡放不下。

4.3 学习率 warmup 的步数计算

warmup 步数不是拍脑袋定的。经验公式是 warmup 占总步数的 1% 到 3%,但更稳的做法是按“模型看到多少 token”来算。比如总训练 300B token,warmup 用 1B token,batch size 是 4M token,那 warmup 步数就是 1B / 4M = 250 步。

warmup 的作用是让优化器状态(m 和 v)在早期慢慢积累,避免一开始就用大学习率导致二阶矩估计不准。如果跳过 warmup,前几百步 loss 很容易炸。我试过在 7B 模型上直接上 3e-4 无 warmup,第 80 步就 NaN 了。

4.4 梯度裁剪的配合设置

优化器之外,梯度裁剪(gradient clipping)是必配的稳定手段。通常设 max_norm=1.0,对梯度做全局范数裁剪。注意裁剪要在optimizer.step()之前、loss.backward()之后做。

loss.backward() torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0) optimizer.step() scheduler.step() optimizer.zero_grad()

max_norm 设 1.0 是通用值,但如果发现裁剪频率过高(超过 30% 的步都在裁剪),说明 lr 偏大或数据有异常样本,要先排查这两个原因,而不是简单调大 max_norm。

5. 常见问题与排查技巧实录

5.1 loss 出现尖刺或 NaN 怎么排查

这是大模型训练最高频的问题。排查顺序我总结成一张表:

现象可能原因排查方法解决手段
偶发尖刺eps 太小检查混合精度配置eps 调到 1e-6
持续 NaNlr 太大看前 100 步 loss降 lr 或加 warmup
后期尖刺数据异常检查对应 step 的样本过滤脏数据
梯度爆炸无裁剪打印梯度范数加 clip_grad_norm

我遇到过一次典型情况:训练到 4 万步时每隔几千步出现一次 loss 尖刺,查了半天优化器参数都没问题,最后定位到是数据里混入了几条超长样本,导致梯度范数突然飙到几十。加了长度过滤后问题消失。所以优化器问题不一定是优化器本身的锅,数据质量要一起看。

5.2 AdamW 显存不够的三种解法

显存不够是大模型训练的常态。三种主流解法:

第一种是 8-bit Adam,把 m 和 v 量化成 int8,显存降到原来的 1/4,代价是精度略降,实测在 7B 以上模型上影响可接受。第二种是 Adafactor,它不存完整的 v,而是用行和列的分解近似,显存大幅降低,但收敛速度可能慢一些。第三种是 ZeRO-2 或 ZeRO-3 分片,把优化器状态切到多卡上,这是最彻底的方案,但通信开销增加。

选择逻辑:单机多卡优先 ZeRO-2;显存极度紧张且能接受精度损失用 8-bit Adam;想省显存又不想改分布式用 Adafactor。

5.3 EMA 权重反而更差的情况

EMA 通常让评估指标更好,但有两种情况会翻车。一是训练步数太少(比如微调只有几百步),EMA 窗口 N=50 意味着影子权重严重滞后于当前权重,评估自然差。二是 decay 设得过高(比如 0.9999),影子权重几乎不动,等于拿初始权重评估。

判断方法:训练中期分别用原始权重和 EMA 权重跑一次验证集,如果 EMA 明显更差,先检查 decay 和总步数的匹配关系。经验规则是 EMA 窗口不超过总步数的 1/10。

5.4 Muon 上手的三个避坑点

Muon 虽然新,但坑不少。第一,不要对所有参数用 Muon,1D 参数必须留给 AdamW,否则 LayerNorm 会崩。第二,Muon 的 lr 要大,但 warmup 要更长,建议 warmup 比例从 2% 提到 5%。第三,Muon 的 Newton-Schulz 迭代有额外计算开销,大约增加 5% 到 10% 的单步时间,要算进训练预算。

我建议的上手路径:先在一个 1B 左右的模型上,用相同数据分别跑 AdamW 和 Muon+AdamW 混合,对比 loss 曲线和验证集指标。如果 Muon 没有明显优势,就不要为了新而新,AdamW 的成熟度和生态支持是实打实的优势。

5.5 优化器状态断点续训的注意事项

大模型训练动辄几天几周,断点续训是刚需。保存 checkpoint 时,优化器状态(m、v、step)必须一起存,否则续训后优化器从零开始,loss 会突然跳变。EMA 影子权重也要存。

checkpoint = { "model": model.state_dict(), "optimizer": optimizer.state_dict(), "scheduler": scheduler.state_dict(), "ema": ema.shadow, "step": global_step, } torch.save(checkpoint, f"ckpt_{global_step}.pt")

踩过的坑:有一次只存了模型权重没存优化器状态,续训后 loss 从 2.1 跳到 2.8,花了 2000 步才恢复。所以 checkpoint 的完整性比体积重要得多。

6. 我个人的实操体会

优化器这块,我的核心体会是:不要迷信新方案,也不要死守默认值。AdamW 之所以成为大模型预训练的标准,是因为它在稳定性、收敛速度、生态支持上达到了最好的平衡。EMA 是低成本高回报的增益手段,只要显存允许就加上。Muon 这类新方案值得关注,但上生产前一定要做充分的 A/B 对比。

参数设置上,lr、betas、eps、weight_decay 这四个值的组合,比优化器选型本身影响更大。我见过太多人纠结用 AdamW 还是 Muon,结果 lr 设错,两个都跑崩。先把 AdamW 的四个参数调明白,再考虑换优化器,这个顺序不能反。

最后分享一个小技巧:训练初期每 100 步打印一次梯度范数和优化器状态里 m、v 的均值,这两个指标能提前预警大部分问题。梯度范数突然变大往往是数据或 lr 问题,m 的均值长期接近零说明梯度方向在来回震荡,可能需要降 lr 或加 batch size。这些监控成本很低,但能省下大量排查时间。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询