PyTorch C++ 前端自适应学习率优化器实战指南:Adam / AdamW / RMSprop / Adagrad
自适应学习率优化器(Adaptive Learning Rate Optimizers)是深度学习中收敛快、调参成本低的一类核心工具:它们会依据每个参数的历史梯度信息,为每个参数自动维护一个独立的学习率,从而在 Transformer、稀疏特征、RNN 等场景中大幅减少手动调度学习率的工作。本文以当前仓库 docs/cpp/source/api/optim/adaptive.md 为主线,系统讲解 PyTorch C++(libtorch)前端 torch::optim 中 Adam、AdamW、RMSprop、Adagrad 四个自适应优化器的原理、完整参数表、构造示例与源码实现细节,并结合仓库中的头文件与 .cpp 实现给出可直接复制运行的最小实践方案,帮助读者在自定义模型训练循环里正确选用与配置优化器。
自适应优化器概览
与传统 SGD 不同,自适应优化器不直接对所有参数使用同一个全局学习率,而是为每个参数维护一阶/二阶梯度统计量,并用它们对学习率逐参数缩放。当前仓库 C++ API 中对这一族优化器的实现遵循统一的“Options + ParamState + Optimizer”三段式结构(参见 torch/csrc/api/include/torch/optim/ 目录):
- Options(如
AdamOptions):继承自OptimizerCloneableOptions,承载全部超参数,采用链式.xxx(...)设置风格,并自带默认值; - ParamState(如
AdamParamState):保存每个参数在训练中持续更新的状态张量(动量、平方梯度均值等); - Optimizer(如
Adam):构造时接收model->parameters()(一个std::vector<Tensor>或std::vector<OptimizerParamGroup>),并在每次step()时遍历全部param_groups_更新参数。
每个优化器的默认值、参数取值范围校验以及状态序列化逻辑都可以从对应头文件中直接确认,下面逐一对四种优化器展开。
Adam(自适应矩估计,Adaptive Moment Estimation)
Adam 同时结合了 RMSprop 的逐参数缩放与动量思想:它维护梯度的一阶矩均值 exp_avg 与二阶矩均值 exp_avg_sq,并利用二者计算自适应的更新步长,因此在绝大多数深度学习任务中是一个收敛快、几乎无需精细调参的默认选择。原文档指出其典型适用场景为:Transformer / 注意力模型、快速原型迭代、缺少充分调参时间的场景以及通用深度学习任务。
关键参数
在 adam.h 中可以确认全部参数及默认值:
| 参数 | 默认值 | 说明 |
|---|---|---|
lr |
1e-3 |
学习率,典型范围 1e-3 ~ 1e-4 |
betas |
{0.9, 0.999} |
一阶/二阶矩的指数衰减系数(β1, β2) |
eps |
1e-8 |
数值稳定性常数 |
weight_decay |
0 |
L2 正则(注意:其耦合方式与 SGD 不同,见下文) |
amsgrad |
false |
是否启用 AMSGrad 变体(维护二阶矩的历史最大值) |
构造器会通过 TORCH_CHECK 做参数校验:lr >= 0、eps >= 0、0 <= betas < 1.0、weight_decay >= 0,传入非法值会直接抛错,避免训练悄悄进入异常状态(见 adam.h)。
示例
// 标准 Adam 配置
auto optimizer = torch::optim::Adam(
model->parameters(),
torch::optim::AdamOptions(1e-3) // learning rate
.betas({0.9, 0.999}) // momentum terms
.eps(1e-8) // numerical stability
.weight_decay(0)); // L2 penalty
// Transformer 场景:更低学习率配合 (β2=0.98) 的配置
auto optimizer = torch::optim::Adam(
model->parameters(),
torch::optim::AdamOptions(1e-4)
.betas({0.9, 0.98})); // β2=0.98 for transformers
底层更新逻辑(源码视角)
在 adam.cpp 的 Adam::step() 中,每一步的实际流程是:
- 跳过没有梯度定义的参数;稀疏梯度会直接报错(
Adam does not support sparse gradients); - 首次遇到某个参数时初始化
exp_avg、exp_avg_sq(若启用amsgrad还需max_exp_avg_sq),三者形状与参数一致、值全 0; step += 1,并计算偏差修正项bias_correction1 = 1 - β1^step、bias_correction2 = 1 - β2^step(解决训练早期矩估计向 0 偏移的问题);- 若
weight_decay != 0,将其耦合进梯度:grad = grad + weight_decay * p(这正是 Adam 与 AdamW 的本质区别); - 更新一阶矩
exp_avg = β1·exp_avg + (1-β1)·grad、二阶矩exp_avg_sq = β2·exp_avg_sq + (1-β2)·grad²; - 启用
amsgrad时,denom取max(max_exp_avg_sq, exp_avg_sq)的均方根;否则直接取exp_avg_sq的均方根,并统一加上eps; - 以
step_size = lr / bias_correction1执行参数更新p -= step_size * exp_avg / denom。
AdamW(解耦权重衰减的 Adam)
AdamW 修正了 Adam 中权重衰减与梯度更新耦合的问题。在 Adam 中,L2 惩罚先被加进梯度再参与一阶矩更新,这会让权重衰减项也被矩估计的缩放影响,导致正则化效果偏离预期;AdamW 则把权重衰减直接作用到权重上,与 SGD 的行为一致。因此原文档的建议是:只要需要 weight decay,就优先用 AdamW 而非 Adam——尤其是 BERT、GPT 等 Transformer 模型的大规模训练。
与 Adam 的数学差异
- Adam:
weight = weight - lr * (grad + weight_decay * weight) - AdamW:
weight = weight - lr * grad - lr * weight_decay * weight
实现层面,adamw.cpp 在进入 Adam 式矩更新之前先执行 p.mul_(1 - lr * weight_decay),即“先衰减、再按 Adam 规则更新”,权重衰减与梯度完全解耦。其余一阶/二阶矩更新、偏差修正与 AMSGrad 分支与 Adam 一致。
AdamWOptions 的全部参数及默认值定义在 adamw.h:lr=1e-3、betas={0.9, 0.999}、eps=1e-8、weight_decay=1e-2(注意:与 AdamOptions 默认 weight_decay=0 不同,AdamW 默认就带正则)、amsgrad=false。其取值校验规则与 Adam 完全相同。
示例
// AdamW with decoupled weight decay - preferred for transformers
auto optimizer = torch::optim::AdamW(
model->parameters(),
torch::optim::AdamWOptions(1e-4)
.betas({0.9, 0.999})
.weight_decay(0.01)); // Decoupled regularization
RMSprop(均方根传播)
RMSprop 用最近梯度的平方滑动平均来逐参数缩放学习率:梯度大则步长被压缩,梯度小则步长放大。它对非平稳目标(如 RNN/LSTM 训练、在线学习、梯度尺度波动明显的任务)尤其有效。原文档给出的典型场景是 RNN/LSTM、非平稳目标与在线学习。
关键参数
在 rmsprop.h 中确认全部默认值:
| 参数 | 默认值 | 说明 |
|---|---|---|
lr |
1e-2 |
学习率,典型范围 1e-3 ~ 1e-2 |
alpha |
0.99 |
平方梯度滑动平均的平滑常数 |
eps |
1e-8 |
数值稳定性常数 |
weight_decay |
0 |
L2 正则(耦合进梯度) |
momentum |
0 |
可选的动量项(>0 时启用动量缓冲) |
centered |
false |
是否使用 centered RMSprop(按方差归一化) |
参数校验要求 lr / eps / momentum / weight_decay / alpha 均非负。
示例
// RMSprop for RNN training
auto optimizer = torch::optim::RMSprop(
model->parameters(),
torch::optim::RMSpropOptions(1e-3)
.alpha(0.99) // smoothing constant
.momentum(0.9) // optional momentum
.centered(true)); // normalize by variance
底层更新逻辑(源码视角)
从 rmsprop.cpp 可以看出核心流程:首先更新平方梯度均值 square_avg = α·square_avg + (1-α)·grad²;当 centered=true 时额外维护梯度均值 grad_avg,并用“方差”作分母——即 avg = sqrt(square_avg - grad_avg²) + eps(等价于按方差归一化),否则 avg = sqrt(square_avg) + eps。若 momentum > 0,则维护动量缓冲并执行 buf = momentum·buf + grad/avg、p -= lr·buf;否则直接 p -= lr·grad/avg。与 Adam 一致,RMSprop 同样不支持稀疏梯度(见源码中 RMSprop does not support sparse gradients 的断言)。
Adagrad(自适应梯度)
Adagrad 依据梯度平方的累加和来调整学习率:频繁更新的参数会被分配更小的学习率,更新稀少的参数则获得更大的学习率,因此非常适合稀疏数据场景。原文档建议的使用场景包括:带稀疏特征的 NLP 任务、更新不频繁的 Embedding 层、用户/物品特征稀疏的推荐系统。
关键参数
在 adagrad.h 中确认全部默认值:
| 参数 | 默认值 | 说明 |
|---|---|---|
lr |
1e-2 |
学习率 |
lr_decay |
0 |
学习率随时间步衰减的系数 |
weight_decay |
0 |
L2 正则(与稀疏梯度不兼容) |
initial_accumulator_value |
0 |
累加器 sum 的初始值(构造时即创建,见下文) |
eps |
1e-10 |
数值稳定性常数(注意默认值比 Adam 系列更小) |
与其余优化器不同,Adagrad 在构造时就会为每个参数创建 sum 状态张量,并用 initial_accumulator_value 填充(torch::full_like(p, initial_accumulator_value, MemoryFormat::Preserve),见 adagrad.h),因此该参数直接影响首个 batch 的有效步长。
示例
// Adagrad for sparse NLP features
auto optimizer = torch::optim::Adagrad(
model->parameters(),
torch::optim::AdagradOptions(0.01)
.lr_decay(0) // learning rate decay
.weight_decay(0)
.initial_accumulator_value(0));
局限性
学习率随训练单调递减是 Adagrad 的固有特性:由于 sum 只增不减,分母持续变大,可能导致训练过早停滞。对长程训练,原文档建议改用 Adam 或 RMSprop。
底层更新逻辑(源码视角)
adagrad.cpp 展示了两个值得注意的细节:
- 学习率衰减公式:
clr = lr / (1 + (step - 1) * lr_decay),即lr_decay越大、越到后期学习率越小; - 稀疏梯度原生支持:Adagrad 是四个优化器中唯一支持稀疏梯度的——梯度为稀疏张量时先
coalesce(),仅对非零位置累加平方并更新(adagrad.cpp)。但注意weight_decay与稀疏梯度不兼容,启用会抛错。
四款优化器选型速查
| 优化器 | 默认 lr |
默认 weight_decay |
稀疏梯度 | 推荐场景 |
|---|---|---|---|---|
| Adam | 1e-3 | 0 | 不支持 | 通用任务默认选择、Transformer、快速实验 |
| AdamW | 1e-3 | 1e-2 | 不支持 | 需要正确 L2 正则时优先于 Adam、BERT/GPT 类训练 |
| RMSprop | 1e-2 | 0 | 不支持 | RNN/LSTM、非平稳目标、在线学习 |
| Adagrad | 1e-2 | 0 | 支持 | 稀疏特征 NLP、Embedding、稀疏推荐系统 |
选择建议(原文档观点与源码参数综合):
- 没有特殊需求时,优先把 Adam 作为默认优化器,配合
1e-3左右的学习率即可快速收敛; - 一旦引入
weight_decay做正则,切换为 AdamW 以获得解耦、更可控的正则行为; - 任务目标非平稳或结构偏序列(RNN/LSTM)时考虑 RMSprop,必要时开启
centered与momentum; - 特征高度稀疏、且需要稀疏梯度支持时使用 Adagrad,并接受学习率单调下降的代价。
进阶:多参数组、状态持久化与断点续训
自适应优化器在实际工程中还有两个常见需求,仓库代码均有对应支持:
按参数组差异化配置
四个优化器都提供了“接收 std::vector<OptimizerParamGroup> 的构造重载”(如 adam.h),可对模型的不同子模块设置不同学习率,例如对 Embedding 层使用更大的学习率、对主干网络使用较小的学习率。
训练状态的保存与恢复
每个 Optimizer 类都实现了 save(serialize::OutputArchive&) 与 load(serialize::InputArchive&)(见 adam.h 及各 .cpp 实现)。Options 与 ParamState 均具备 serialize 方法,step、exp_avg、exp_avg_sq、max_exp_avg_sq(Adam/AdamW)、square_avg/momentum_buffer/grad_avg(RMSprop)、sum/step(Adagrad)等状态都可整体序列化,从而支持 checkpoint 保存与断点续训。
一个兼容性细节:仓库代码保留了 1.5.0 之前旧格式的反序列化路径(如 adam.cpp)。若加载到旧格式存档,会打印 “Your serialized Adam optimizer is still using the old serialization format…” 的警告并建议重新保存——在新版本中用 save/load 即可自动读写带 pytorch_version 标记的新格式。
延伸阅读
- 文档原文:docs/cpp/source/api/optim/adaptive.md
- C++ API 头文件:torch/csrc/api/include/torch/optim/(
adam.h、adamw.h、rmsprop.h、adagrad.h、sgd.h、lbfgs.h、optimizer.h) - 底层实现:torch/csrc/api/src/optim/(对应
.cpp文件) - 统一基类与序列化工具:torch/csrc/api/include/torch/optim/optimizer.h、torch/csrc/api/include/torch/optim/serialize.h
读者可以把本文示例中的 model 替换为自己通过 torch::nn 组装的自定义模块(例如 torch::nn::Sequential 或多层 torch::nn::Linear),构造后在训练循环中依次调用 optimizer.zero_grad()、loss.backward() 与 optimizer.step() 即可完成一次自适应优化器的完整接入。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0627
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00