先给答案:深度学习优化器(optimizer)接收反向传播计算出的梯度,并结合学习率、动量或历史梯度统计量,决定下一步怎样更新可训练参数。它负责“怎样走下一步”,但不负责定义训练目标,也不保证找到全局最优解。损失函数、数据、模型、批大小、精度、调度器和随机性都会共同影响结果。
优化器到底更新了什么?
设模型参数为 θ,第 t 步在一个小批次上得到损失 L 和梯度 gt=∇θL。最简单的随机梯度下降可以写成 θt+1=θt−ηgt,其中 η 是学习率。真实框架还要处理参数组、动量缓冲区、权重衰减、稀疏梯度和数值精度。
| 训练环节 | 产生或保存的对象 | 优化器是否负责 | 常见误解 |
|---|---|---|---|
| 前向传播 | 预测与中间激活 | 否 | 换优化器不会改变模型前向结构 |
| 计算损失 | 一个可求导的标量目标 | 否 | 优化器不能修复错误的标签或目标函数 |
| 反向传播 | 每个参数的梯度 | 否 | loss.backward() 计算梯度,不更新参数 |
| 参数更新 | 新参数与优化器状态 | 是 | optimizer.step() 才执行更新 |
| 学习率变化 | 下一步使用的学习率 | 部分 | 通常由独立 scheduler 驱动,调用顺序需要按文档确认 |
如果你还不熟悉目标函数,可先看站内的损失函数说明;学习率的单独定义见学习率词典页。PyTorch 官方的 torch.optim 文档明确给出的基本顺序是清梯度、前向、计算损失、反向、更新。
SGD、Momentum、Adam 与 AdamW 有什么区别?
SGD 使用当前小批次梯度更新参数;加入 Momentum 后,会保存过去梯度的指数累积,使持续一致的方向积累速度。PyTorch 的 SGD API同时暴露 momentum、dampening、weight_decay 和 nesterov,因此“SGD”并不等于只有一个固定配置。
AdaGrad按参数累积历史平方梯度,稀疏特征可能从这种逐参数缩放中受益,但累积量持续增长会压低有效步长。RMSprop用平方梯度的移动平均替代无限累积。Adam进一步同时维护梯度的一阶矩和平方梯度的二阶原始矩,并在早期做偏差修正;算法定义应以 Adam 原论文和框架实现为准,而不是把它概括成“自动找到最优学习率”。
AdamW把权重衰减从梯度更新中解耦。原论文指出,对自适应梯度算法而言,L2 正则与传统 weight decay 并不等价;这正是 Decoupled Weight Decay要解决的问题。PyTorch 的 AdamW API也明确说明权重衰减不会累积进动量和方差。站内另有Adam 专题页,本页不重复制造同意图内容。
| 方法 | 额外状态 | 值得优先测试的场景 | 不能直接下的结论 |
|---|---|---|---|
| SGD | 无;Momentum 版增加一份动量 | 建立朴素基线;已有成熟训练配方 | 不能脱离调度、批大小和训练预算宣称泛化更好 |
| AdaGrad / SparseAdam | 逐参数统计量 | 真正的稀疏梯度或稀疏嵌入 | 输入稀疏不等于梯度张量满足稀疏实现要求 |
| Adam | 通常为一阶、二阶矩 | 需要快速建立稳定基线 | 自适应缩放不等于不需要调学习率 |
| AdamW | 通常为一阶、二阶矩 | 需要解耦 weight decay 的 Transformer 配方 | 名称相同不代表不同框架默认值和内核一致 |
| Adafactor | 可对矩阵状态做因子分解 | 优化器状态内存是主要约束时 | 省状态不等于在你的模型上更快或更准 |
| Muon | 动量及正交化相关计算 | 二维隐藏层权重且愿意做严格对照实验 | 不应直接用于偏置、嵌入等全部参数 |
2026 年应该直接改用 Muon 吗?
不能只按热度切换。当前 PyTorch 稳定文档已经列出 Muon,但接口说明同时限定:Muon 面向神经网络隐藏层的二维参数,偏置、嵌入以及其他参数应使用 AdamW 等标准方法。这意味着实际代码通常要先按参数形状和用途分组,而不是把 model.parameters() 全部交给 Muon。
Muon 不是“AdamW 的无参数替换”。它对二维隐藏层权重的动量矩阵执行 Newton–Schulz 正交化,而非二维参数仍要走 AdamW/Adam 等路径,因此通常是混合优化器方案。不同参数路径可以需要不同学习率;只有把两边的参数组、学习率、衰减、调度和状态保存清楚,比较才可解释。
| 从 AdamW 试验 Muon 前 | 必须确认 | 失败时优先回退什么 |
|---|---|---|
| 参数分组 | 只把符合实现要求的二维隐藏层权重交给 Muon;偏置、归一化、嵌入和输出头单独处理 | 输出每组参数名、形状与数量,先消除漏加/重复 |
| 优化器配方 | Muon 与 AdamW 分别搜索学习率;固定数据顺序、token 数、batch、精度、调度和停止条件 | 退回已验证 AdamW 基线,而不是沿用同一学习率下结论 |
| 分布式支持 | 核对框架版本、ZeRO 阶段、梯度是否被提前展平、checkpoint 与 offload 支持 | 先在单机/小规模恢复测试通过,再扩大集群 |
| 收益口径 | 同时测达到目标质量的 token、墙钟时间、吞吐、峰值显存和失败恢复 | 不把单个 benchmark 或状态缓冲区减少外推成总训练收益 |
PyTorch 团队 2026 年 6 月 3 日发布的 DeepSpeed Muon 实现说明解释了为何展平梯度会妨碍矩阵正交化,并在当时列出 ZeRO Stage 2/3 支持以及仍在推进的 CPU offload 等项目。该文给出的质量和显存数字来自指定模型、数据、GPU、精度和 ZeRO 配置,只能作为实现样例,不能写成 Muon 对所有训练任务的固定提升。上线前应按实际安装版本重新查支持矩阵。
同样,PyTorch Adafactor 文档描述的是“次线性内存成本”的自适应方法,但是否节省端到端峰值显存,还取决于参数形状、梯度、激活、混合精度主权重和框架实现。正确问题不是“哪个最新”,而是“在相同数据顺序、token 数、硬件、精度和停止条件下,哪个达到目标质量所需的总时间与资源更可接受”。
怎样在 PyTorch 中正确建立最小基线?
import torch
model = MyModel().cuda()
optimizer = torch.optim.AdamW(
model.parameters(),
lr=3e-4,
betas=(0.9, 0.999),
weight_decay=0.01,
)
for inputs, targets in loader:
optimizer.zero_grad(set_to_none=True)
outputs = model(inputs.cuda())
loss = loss_fn(outputs, targets.cuda())
loss.backward()
torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
optimizer.step()
这段代码只是可审查的起点,不是通用最佳参数。zero_grad 文档提醒,梯度为 None 和梯度为 0 时,优化器行为可能不同;clip_grad_norm_返回总梯度范数,适合同时记录而不是盲目裁剪。恢复训练时还要保存并恢复模型、优化器、调度器、步数和随机状态。
参数组为什么经常比“换算法”更重要?
一个模型不一定只能使用一组超参数。PyTorch 允许把不同参数字典交给同一个优化器,例如为预训练骨干设置较小学习率、为新分类头设置较大学习率,或把偏置与归一化参数放入 weight_decay=0 的组。分组必须基于明确规则并输出参数名清单;如果同一参数被遗漏、重复加入,或者被冻结参数仍留在实验假设中,结果就无法解释。
微调时还应区分“冻结参数”和“梯度恰好为零”:前者通常不参与优化,后者可能仍会受到动量或衰减状态影响。解冻层后新增参数组、改变组顺序或更名,也会影响旧 checkpoint 的状态映射。正式训练前应保存每组的参数数量、名称模式、学习率、衰减、betas 和可训练状态,并在恢复后逐项核对。
怎样验证 checkpoint 真正可恢复?
不要等训练中断后才验证。可在短跑中保存一次 checkpoint,记录保存前的全局步数、学习率、优化器状态键、梯度缩放值与下一批次 loss;重新启动后继续两到三步,检查学习率曲线和 loss 是否连续。只加载模型权重而遗漏 optimizer 或 scheduler,会把“续训”变成使用旧权重重新起步,尤其会丢失 AdamW 的矩估计和 warmup 位置。
PyTorch 的保存与加载教程要求续训 checkpoint 同时保存模型和优化器状态;若还使用 scheduler、AMP scaler、采样器或梯度累积,也要保存其状态和全局步数。Optimizer.load_state_dict 还提醒,应先初始化学习率调度器,再加载优化器状态,否则加载的学习率可能被覆盖。参数组顺序、数量或模型结构改变时,不能仅以“没有抛异常”判断恢复成功。
更严格的恢复测试是做分叉对照:在同一 checkpoint 处,一条进程不中断继续运行 N 步,另一条完全退出后恢复 N 步;两边使用相同下一批数据和随机状态,比较学习率、loss、梯度范数、参数摘要与优化器状态摘要。分布式训练还要核对每个 rank 的分片和采样进度。数值内核可能带来允许范围内的小差异,但突然回到 warmup 起点、状态数变少或参数组错位必须视为恢复失败。
学习率调度不是 AdamW 的隐藏功能。PyTorch 的 学习率调整说明指出,多数 scheduler 应在优化器更新后调用;但按指标变化的调度器需要传入验证指标。应把 warmup 步数、峰值学习率、衰减曲线和总训练步数一起写入实验记录。
混合精度、显存和 ZeRO 与优化器是什么关系?
混合精度下,梯度缩放由 AMP 的 scaler 协调,不应写成“优化器原生支持 FP16 就自动防止下溢”。PyTorch 的 AMP 示例展示了 scaler.scale(loss).backward()、必要时先 unscale_ 再裁剪,以及 scaler.step(optimizer) 的顺序。不同 dtype、设备和算子支持范围也要单独确认。
Adam 类方法通常要为参数保存一阶、二阶矩,训练还可能保留更高精度主权重;但不能脱离 dtype 和实现给出固定“参数量的几倍”。峰值显存应拆成参数、梯度、优化器状态、激活、临时缓冲和通信桶逐项测量。站内的ZeRO 显存优化说明可继续阅读状态分片。
DeepSpeed 官方 ZeRO 教程把阶段边界写得很清楚:Stage 1 分片优化器状态,Stage 2 再分片梯度,Stage 3 再分片参数。它改变的是状态放置与通信,不会把 AdamW 的数学规则自动变成另一种优化器;offload 还会引入 CPU、NVMe 和互连瓶颈。
| 观测项 | 至少记录什么 | 发现异常后先查什么 |
|---|---|---|
| 训练目标 | 训练/验证 loss、任务指标、token 或样本数 | 数据与损失实现,再看优化器 |
| 更新尺度 | 实际学习率、梯度范数、参数范数、裁剪比例 | warmup、累积步数、batch scaling |
| 数值稳定 | NaN/Inf、scaler scale、跳过更新次数 | AMP 顺序、异常 batch、溢出算子 |
| 资源 | 峰值显存、step time、吞吐、通信等待 | foreach/fused、状态分片、激活与通信桶 |
| 恢复能力 | 断点前后 step、学习率、loss 连续性 | optimizer/scheduler/scaler 是否一起恢复 |
优化器应该怎样选择?
- 先复制可信配方:同一模型家族和训练目标已有官方训练配置时,先复现它,而不是一次更换优化器、学习率、批大小和数据。
- 建立 AdamW 或成熟 SGD 基线:固定数据版本、随机种子集合、训练 token/样本预算、精度、硬件与停止规则。
- 只改变一个因素:比较优化器时,分别搜索合理学习率;拿 AdamW 的最优学习率直接套给 SGD 或 Muon,不是公平对照。
- 同时比较质量和成本:记录达到同一验证指标所需的 wall-clock、GPU 时、峰值显存和失败率,而不是只看前几百步 loss。
- 至少复现实验:随机性较强时运行多个种子,报告中位数、离散程度和异常运行;没有原始日志就不要写“提升百分之多少”。
| 约束或目标 | 首轮候选 | 必须控制的变量 |
|---|---|---|
| 成熟 Transformer 微调配方 | AdamW | 学习率、weight decay、warmup、有效 batch |
| 经典视觉配方已有 SGD 基准 | SGD+Momentum 与 AdamW 对照 | 各自调度与训练时长,不能共用单一学习率 |
| 优化器状态导致 OOM | Adafactor、ZeRO-1/2、offload | 吞吐损失、通信、CPU/NVMe 带宽和恢复 |
| 稀疏嵌入梯度 | SparseAdam 或支持稀疏参数的配方 | 梯度布局与算子支持,不只看输入稀疏度 |
| 评估 Muon | Muon 处理合适二维权重,AdamW 处理其余参数 | 参数分组、正交化成本、相同 token 与质量目标 |
训练不收敛时,按什么顺序排查?
- 用一个极小数据子集确认模型能够过拟合;做不到时先检查标签、掩码、损失和梯度链路。
- 打印可训练参数数量,确认冻结、参数组和 weight decay 排除规则符合预期。
- 记录每步实际学习率、loss、梯度范数、是否出现 NaN/Inf,以及 AMP 是否跳过更新。
- 暂时关闭复杂调度、梯度累积、分布式和编译优化,建立单卡 FP32 或 BF16 的可解释基线。
- 验证 checkpoint 恢复:恢复后的 step、scheduler、optimizer state 与 scaler 必须连续。
- 最后才更换优化器;否则可能用算法变化掩盖数据或实现错误。
梯度长期接近零时,还可以结合站内的梯度消失说明检查网络与激活;框架接口和设备差异可参考PyTorch 指南。这些问题与“哪个优化器最强”不是同一个搜索意图。
常见问题
AdamW 一定比 Adam 好吗?
不能无条件断言。AdamW 明确解耦了 weight decay,通常更容易把衰减作为独立超参数管理;最终质量仍取决于任务、实现、调参与预算。报告结果时应写明框架版本和完整参数。
用了自适应优化器还需要学习率吗?
需要。Adam/AdamW 对各参数做自适应缩放,但全局学习率仍控制更新的总体尺度,warmup 和衰减策略也可能显著改变训练轨迹。
fused 或 foreach 会改变算法吗?
它们主要是实现路径。PyTorch 的 优化器总览区分 for-loop、foreach 和 fused,并给出不同设备的支持与稳定状态。速度和峰值显存需在目标版本与硬件上实测,数值也可能因运算顺序产生细小差异。
什么时候这篇文章需要再次复核?
PyTorch 稳定版更改算法默认实现、Muon/Adafactor 参数边界变化、DeepSpeed 调整 ZeRO 状态语义,或站内代码无法在当前稳定版运行时,都应重新核验。本文资料复核日期为 2026 年 7 月 16 日。
可复制的实验验收单
- 模型、数据快照、代码提交、依赖、硬件、dtype 与随机种子可追溯;
- 优化器参数组、学习率、调度、batch、累积、裁剪和 weight decay 完整保存;
- 训练/验证曲线、梯度范数、吞吐、峰值显存、异常和中断均有原始日志;
- 比较采用相同数据与预算,并为不同优化器分别搜索合理学习率;
- 断点恢复后 optimizer、scheduler、scaler 和全局步数连续;
- 只有可复现实验才写定量提升;否则使用“候选”“需要验证”等边界语言。
编辑复核记录:本文于 2026 年 7 月 16 日依据 PyTorch 稳定文档、Adam/AdamW 原论文、DeepSpeed ZeRO 文档及 PyTorch/DeepSpeed Muon 当前实现说明复核;新增 Muon 混合参数组、分布式支持边界和 checkpoint 分叉恢复验证。没有把厂商或项目单次 benchmark 写成全局排名,也没有声称本站完成相同规模实测。
结论:优化器不是模型训练的自动驾驶仪,而是一条有状态的参数更新规则。先证明数据、损失和梯度正确,再用成熟配方建立基线;只有在相同质量目标、训练预算、软件版本和资源口径下,SGD、AdamW、Adafactor 或 Muon 的比较才有意义,也才值得写入生产决策记录。
