目录

PyTorch 模型训练技术文档:求解器、参数配置与训练循环

章节主题内容概要
一、总览整体架构与文档脉络训练闭环、优化器与调度器位置、知识结构、各部分职责与关联、SGD vs Adam / 调度器选型对比
二、优化器概念与基类Optimizer 基类与通用接口是什么、为什么需要、param_groups、state、zero_grad/step/state_dict/load_state_dict/add_param_group 完整说明
三、SGD 族SGD / Momentum / Nesterov公式、构造函数全部参数、适用场景、与 Adam 对比
四、自适应学习率优化器Adam / AdamW / RAdam / RMSprop 等各算法思想、参数 betas/eps/weight_decay、Adam vs AdamW、适用场景
五、参数组与差异化配置不同层不同 lr / weight_decayparam_groups 结构、bias 不加 weight_decay、backbone vs head、示例
六、学习率调度器LRScheduler 族基类 step/get_last_lr、StepLR/MultiStepLR/ExponentialLR/CosineAnnealing/OneCycleLR/ReduceLROnPlateau、每 step 与每 epoch 调用
七、训练循环与梯度闭环、累积、裁剪最小闭环、gradient accumulation、gradient clipping、与 DDP 的衔接
八、损失函数选型常见 Loss 与使用场景MSE/CrossEntropy/NLL/BCE/L1/Huber、多任务与自定义 loss
九、参数配置经验学习率、warmup、weight decay学习率与 batch 缩放、warmup、常见坑与调参建议
十、完整示例可运行脚本从 DataLoader 到 optimizer + scheduler + 训练循环的端到端代码
十一、速查与小结组件对照与延伸优化器/调度器速查表、与 distributed_training_guide / dataloader_guide 的衔接

阅读建议:先读总览建立「数据 → 前向 → 损失 → 反向 → 优化器/调度器」的全局图景,再按需跳转优化器、调度器、参数组或配置经验;实现时按「构建 optimizer → 可选 scheduler → 循环内 zero_grad/backward/step/scheduler.step」顺序对照各章。


目标:在给定数据上,通过迭代更新模型参数 θ\theta,使损失 L(θ)L(\theta) 下降,从而拟合数据或任务目标。

最小闭环:每个 step 内依次执行:

  1. 前向:输入 → 模型 → 输出
  2. 损失:输出与目标代入损失函数 → 标量 loss
  3. 反向loss.backward() → 计算梯度 θL\nabla_\theta L
  4. 更新:优化器根据梯度更新参数,如 θθηθL\theta \leftarrow \theta - \eta \nabla_\theta L(SGD 为例)
  5. 清空梯度optimizer.zero_grad(),为下一轮 backward 做准备

其中「更新」由 优化器(Optimizer / 求解器) 完成;学习率 η\eta 可固定,也可由 学习率调度器(LRScheduler) 按 step 或 epoch 调整。因此:优化器决定「如何用梯度更新参数」;调度器决定「学习率随时间如何变化」。

┌─────────────────────────────────────────────────────────────────────────────────┐
                           训练准备(一次)                                         
  model = MyModel()                                                               
  optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)    求解器            
  scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=100)     
  loss_fn = nn.CrossEntropyLoss()                                                 
└─────────────────────────────────────────────────────────────────────────────────┘
                                        
                                        
┌─────────────────────────────────────────────────────────────────────────────────┐
                           每个 step(或每 N  step 做一次 scheduler.step       
  batch = next(dataloader)                                                        
  out = model(batch)            loss = loss_fn(out, target)    loss.backward()  
  optimizer.step()              optimizer.zero_grad()         [scheduler.step()] 
└─────────────────────────────────────────────────────────────────────────────────┘
  • 基础层:优化器是什么、param_groups、state、step/zero_grad(第二节);损失函数与训练循环的角色(第七、八节)。
  • 算法层:SGD 族(第三节)、Adam/AdamW 等自适应算法(第四节);参数组实现差异化 lr/weight_decay(第五节)。
  • 调度层:LRScheduler 基类与 step 时机(第六节);与 optimizer 的绑定关系。
  • 工程层:梯度累积、梯度裁剪(第七节);参数配置经验(第九节);完整示例(第十节)。

各节关系:优化器 负责单步更新;调度器 在每 step 或每 epoch 后改 optimizer 的 lr;参数组 让同一 optimizer 内不同参数用不同 lr/weight_decay;训练循环 把 DataLoader、loss、optimizer、scheduler 串起来;配置经验 指导选 lr、warmup、weight_decay 等。

组件职责/主题与其它部分的关系
Optimizer持有参数引用与更新规则,根据梯度更新参数接收 model.parameters()step() 前需先 backward() 得到梯度
LRScheduler按 step/epoch 修改 optimizer 中 param_groups 的 lr绑定一个 optimizer;通常在 optimizer.step() 之后调用 scheduler.step()
param_groups把参数分成多组,每组可有不同 lr、weight_decay 等优化器构造时传入 list of dict;scheduler 通过改 param_groups[i]['lr'] 生效
Loss把模型输出与目标变成标量,供 backward输出必须是标量;选择与任务匹配的 loss(分类 CE、回归 MSE 等)
训练循环串联数据、前向、loss、反向、optimizer.step、zero_grad、可选 scheduler.step可与 DataLoaderDDP 组合
维度SGD (含 Momentum/Nesterov)Adam / AdamW
更新方式一阶梯度 ± 动量,学习率需手调自适应步长(一阶矩+二阶矩),对 lr 不敏感
泛化大 batch 时常见更好泛化,调好 lr 后稳定小 lr 下常用,大 lr 易欠拟合或不稳定
显存仅动量缓存,显存占用小需存一阶、二阶矩,显存略大
适用大 batch 训练、追求极致精度、CV 预训练快速实验、NLP/多任务、默认首选

AdamW 相对 Adam:weight decay 不进入动量与方差,等价于解耦的 L2 正则,通常比 Adam 更稳、泛化更好,推荐作默认。

调度器特点适用
StepLR / MultiStepLR固定 epoch 降 lr简单分段衰减
CosineAnnealingLR余弦平滑降到 0 或 min_lr长训练、平滑收敛
OneCycleLR先升后降、单周期短 epoch、快速收敛
ReduceLROnPlateau按验证指标触发的衰减验证集驱动、早停配合
LambdaLR自定义函数乘 lr完全自定义策略

是什么torch.optim.Optimizer 是持有「待优化参数」和「更新规则」的对象;每次调用 step() 时,根据当前梯度按该规则更新参数。

为什么需要:梯度只给出下降方向,步长(学习率)、动量、自适应缩放等若手写易错且难以复用;抽象成 Optimizer 后,算法统一、接口一致,且便于实现参数组、状态保存与恢复。

解决什么问题:把「梯度 → 参数更新」标准化,支持 SGD、Adam、AdamW 等多种算法,并支持 per-parameter 或 per-group 的 lr、weight_decay 等。

是什么:优化器内部把参数分成若干 参数组;每个组是一个 dict,至少包含 'params'(该组参数列表),以及可选的 'lr''weight_decay' 等,与构造时传入的 defaults 合并。

为什么需要:不同层或不同模块往往需要不同学习率(如 backbone 小 lr、head 大 lr),或 bias 不加 weight_decay;参数组允许在同一 optimizer 里为不同子集设置不同超参。

结构optimizer.param_groups 是 list of dict,例如:

optimizer = torch.optim.SGD([
    {'params': model.base.parameters(), 'lr': 1e-2},
    {'params': model.head.parameters(), 'lr': 1e-1}
], momentum=0.9)
# param_groups[0]['params'] 为 base 参数,lr=1e-2
# param_groups[1]['params'] 为 head 参数,lr=1e-1

是什么:每个被优化参数在 optimizer 内有一个 id,对应 state[id] 的 dict,用于存该算法的内部状态(如 SGD 的 momentum buffer、Adam 的 exp_avg/exp_avg_sq)。

为什么需要:动量类、自适应类算法需要跨 step 记忆历史信息;state 在 step() 时被读入并写回。

用户通常不直接改 state;保存/恢复训练时用 optimizer.state_dict()optimizer.load_state_dict() 即可。

方法作用调用时机
zero_grad(set_to_none=False)把当前所有待优化参数的梯度置 0(或 None);set_to_none=True 时置为 None,可省少量内存每个 step 开始前,避免梯度累积时需在 step 前调用
step(closure=None)根据当前梯度执行一次参数更新;若传 closure(无参、返回 loss),优化器可多次调用以重算 loss(如 LBFGS)loss.backward() 之后;若优化器需多次前向(如 LBFGS),则传入 closure
state_dict()返回 state + param_groups,用于 checkpoint保存 checkpoint 时
load_state_dict(state_dict)从 state_dict 恢复 state 与 param_groups加载 checkpoint 后恢复训练
add_param_group(param_group)增加一组参数(dict 需含 'params',其余键同构造时的 defaults)动态扩展优化参数时(如微调加新层)
register_step_pre_hook / register_step_post_hook在 step 前/后执行自定义逻辑调试或统计用
register_state_dict_pre_hook / register_load_state_dict_pre_hook在 state_dict/load_state_dict 前后执行序列化/反序列化定制

注意zero_grad()step() 的先后关系。常见写法是「先 zero_grad,再 forward/backward,再 step」;若做梯度累积,则多个 backward 后再一次 step,且每步开始 zero_grad。

import torch
import torch.nn as nn

model = nn.Linear(10, 2)
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)

# 模拟一步
x = torch.randn(4, 10)
y = model(x).sum()
optimizer.zero_grad()
y.backward()
optimizer.step()

# 查看 param_groups
assert len(optimizer.param_groups) == 1
assert optimizer.param_groups[0]['lr'] == 0.01

# 保存/恢复(仅示例,实际会连同 model)
state = optimizer.state_dict()
optimizer.load_state_dict(state)

SGD 族包括:SGD(可选 momentum)、Nesterov。本节只讨论 torch.optim.SGD 的完整参数与含义;Momentum 与 Nesterov 均通过同一类实现。

  • SGDθt+1=θtηL(θt)\theta_{t+1} = \theta_t - \eta \nabla L(\theta_t) 即用当前梯度乘以学习率更新。
  • SGD with Momentum:先累积动量 vt+1=μvt+L(θt)v_{t+1} = \mu v_t + \nabla L(\theta_t),再更新 θt+1=θtηvt+1\theta_{t+1} = \theta_t - \eta v_{t+1},减轻震荡、加速收敛。
  • Nesterov:在计算梯度前先做「临时更新」再算梯度,使动量更前瞻一步,公式上等价于对动量项做修正(PyTorch 中 nesterov=True 即启用)。

为什么需要 Momentum:纯 SGD 在病态曲面上震荡大、收敛慢;动量起到平滑梯度、利用历史方向的作用。

torch.optim.SGD(params, lr=<required>, momentum=0, dampening=0,
                weight_decay=0, nesterov=False, *, maximize=False, foreach=None, differentiable=False)
参数含义常用值
params待优化参数(或 param_groups 的 list)model.parameters() 或 list of dict
lr学习率0.01~0.1 常见,大 batch 可线性缩放
momentum动量系数0.9 常用
dampening对动量的阻尼,与 momentum 同用一般 0
weight_decayL2 惩罚系数1e-4~1e-2
nesterov是否 Nesterov 动量True 可略加速收敛
maximizeTrue 时做梯度上升默认 False
# 纯 SGD
opt = torch.optim.SGD(model.parameters(), lr=0.01)

# SGD + Momentum
opt = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9)

# SGD + Nesterov
opt = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9, nesterov=True)

# 带 weight_decay(L2 正则)
opt = torch.optim.SGD(model.parameters(), lr=0.01, momentum=0.9, weight_decay=1e-4)
  • 大 batch、长训练、追求泛化时常用 SGD + Momentum(或 Nesterov)。
  • 小 batch、快速实验更常用 Adam/AdamW;若从 Adam 换到 SGD,需要重新调 lr(通常 SGD 的 lr 比 Adam 大一个数量级左右)。

本节覆盖:AdamAdamWRAdamNAdamRMSpropAdagradAdadelta。它们共同点是按「每个参数」维护标量或向量状态,用梯度的一阶/二阶信息自适应地缩放步长。

是什么:对每个参数维护一阶矩估计 mtm_t 和二阶矩估计 vtv_t,做偏差修正后用 m^t/(v^t+ϵ)\hat{m}_t/(\sqrt{\hat{v}_t}+\epsilon) 方向更新,步长为 lr。

为什么需要:不同参数梯度量纲和曲率差异大;固定 lr 要么对某些参数过大要么过小;自适应步长能减少对 lr 的敏感度,收敛快、调参简单。

公式(简要)

mt=β1mt1+(1β1)gt,vt=β2vt1+(1β2)gt2m_t = \beta_1 m_{t-1} + (1-\beta_1)g_t,\quad v_t = \beta_2 v_{t-1} + (1-\beta_2)g_t^2


偏差修正:m^t=mt/(1β1t)\hat{m}_t = m_t/(1-\beta_1^t)v^t=vt/(1β2t)\hat{v}_t = v_t/(1-\beta_2^t)
更新:θt+1=θtηm^t/(v^t+ϵ)\theta_{t+1} = \theta_t - \eta \cdot \hat{m}_t/(\sqrt{\hat{v}_t}+\epsilon)

构造函数

torch.optim.Adam(params, lr=1e-3, betas=(0.9, 0.999), eps=1e-8, weight_decay=0, *, amsgrad=False, foreach=None, maximize=False, capturable=False, differentiable=False, fused=None)
参数含义常用值
lr学习率1e-3 最常用
betas一阶、二阶矩的指数衰减率(0.9, 0.999)
eps数值稳定项,分母加在 v\sqrt{v}1e-8
weight_decayL2 惩罚(在 Adam 中会进动量)0 或 1e-2
amsgrad是否用 AMSGrad 变体(取 vv 的单调递增)一般 False

是什么:Adam 的变体,把 weight decay 从「加在梯度里」改为「直接对参数做衰减」,即每步先做 θθηλθ\theta \leftarrow \theta - \eta\lambda\theta 再按 Adam 更新。这样 weight decay 不参与一阶/二阶矩计算,等价于解耦的 L2 正则。

为什么需要:原 Adam 里 weight decay 与梯度耦合,正则效果和收敛行为不如解耦形式;AdamW 在多数任务上更稳、泛化更好,推荐作为默认选择

构造函数:与 Adam 相同,仅实现不同;weight_decay 默认 0,常用 0.01。

torch.optim.AdamW(params, lr=1e-3, betas=(0.9, 0.999), eps=1e-8, weight_decay=0.01, ...)
  • Adam:weight_decay 在实现上等价于 L2 正则(对梯度加 weight_decay·θ,再参与动量与二阶矩),与自适应步长耦合后,对泛化有时不利。
  • AdamW:将 weight decay 从梯度更新中解耦,单独做 θθηλθ\theta \leftarrow \theta - \eta\lambda\theta,再按 Adam 公式更新;weight decay 不进入 mt/vtm_t/v_t,更符合「参数衰减」语义,通常泛化更好,推荐默认使用 AdamW
  • RAdam:在训练早期二阶矩方差大时,用修正项避免步长过大,减少 warmup 需求。
  • NAdam:把 Nesterov 思想融进 Adam,用「当前步的动量预测」更新,有时收敛略快。

两者接口与 Adam 类似,可作为替代尝试;默认仍推荐 AdamW。

  • RMSprop:只维护二阶矩的指数平均,用 η/vt+ϵ\eta/\sqrt{v_t+\epsilon} 缩放梯度;适合 RNN、非平稳目标。
  • Adagrad:累积平方梯度,步长单调降;适合稀疏梯度,但易过早变小。
  • Adadelta:在 Adagrad 基础上用滑动平均替代累积,避免步长趋 0。

日常训练中 Adam/AdamW 使用更多;RMSprop 在部分 RL 或旧代码中仍见。

以下为 torch.optim 中常见优化器及典型用途,便于按场景选择或查漏。

优化器类名典型用途 / 说明
SGDoptim.SGD通用;大 batch、需强泛化时常用 momentum + Nesterov
ASGDoptim.ASGD对强凸或近凸目标,对迭代做平均,稳定性好
Adamoptim.Adam默认首选之一,收敛快、超参少
AdamWoptim.AdamW带解耦 weight decay,NLP/Vision 常用,推荐默认
Adamaxoptim.Adamax基于无穷范数的 Adam 变体,个别任务更稳
NAdamoptim.NAdamAdam + Nesterov 动量,无需 warmup 时可用
RAdamoptim.RAdam修正早期方差,冷启动更稳
SparseAdamoptim.SparseAdam稀疏梯度(如 embedding)时省内存、省算力
Rpropoptim.Rprop仅全批量、单机,一般不用于大数据
RMSpropoptim.RMSprop按标量做自适应步长,可用于 RNN/强化学习
Adadeltaoptim.Adadelta无学习率,依赖梯度与参数更新量的移动平均
Adagradoptim.Adagrad稀疏特征/小学习率场景;易使有效学习率过小
LBFGSoptim.LBFGS小批量二阶、需 closure、多次前向,适合小模型/全批量
# 默认推荐
optimizer = torch.optim.AdamW(model.parameters(), lr=1e-3, weight_decay=0.01)

# 仅 Adam
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3, betas=(0.9, 0.999))

参数组即构造 optimizer 时传入的「list of dict」,每个 dict 一组参数 + 可选覆盖的 lr、weight_decay 等;优化器内部对所有组统一执行同一种算法,但每组用各自的超参。

1)Backbone 小 lr、Head 大 lr(微调/迁移学习)

optimizer = torch.optim.AdamW([
    {'params': model.backbone.parameters(), 'lr': 1e-5},
    {'params': model.head.parameters(), 'lr': 1e-3}
], weight_decay=0.01)

2)Bias 不加 weight_decay

bias_params = [p for n, p in model.named_parameters() if 'bias' in n]
other_params = [p for n, p in model.named_parameters() if 'bias' not in n]
optimizer = torch.optim.AdamW([
    {'params': other_params, 'weight_decay': 0.01},
    {'params': bias_params, 'weight_decay': 0}
], lr=1e-3)

3)BatchNorm 等不 weight_decay(按需)

def get_param_groups(model, base_lr, wd):
    decay, no_decay = [], []
    for n, p in model.named_parameters():
        if not p.requires_grad:
            continue
        if 'bias' in n or 'norm' in n or 'bn' in n.lower():
            no_decay.append(p)
        else:
            decay.append(p)
    return [
        {'params': decay, 'lr': base_lr, 'weight_decay': wd},
        {'params': no_decay, 'lr': base_lr, 'weight_decay': 0}
    ]
optimizer = torch.optim.AdamW(get_param_groups(model, 1e-3, 0.01))

微调中动态加入新模块时,可把新参数加进已有 optimizer:

optimizer.add_param_group({'params': model.new_module.parameters(), 'lr': 1e-4})

注意:新组的其他键(如 weight_decay)若未写,会使用 optimizer 的 defaults。


是什么torch.optim.lr_scheduler.LRScheduler 绑定一个 optimizer,在每次 scheduler.step() 时根据策略更新 optimizer.param_groups[*]['lr'],从而在训练过程中改变学习率。

为什么需要:固定 lr 前期可能震荡、后期可能过大会影响收敛;按 step 或 epoch 衰减或先升后降,往往收敛更稳、最终效果更好。

调用顺序:必须先 optimizer.step(),再 scheduler.step()(若按 step 调);若按 epoch 调,则每个 epoch 结束调一次 scheduler.step()

方法作用
step(epoch=None)前进一步;若按 epoch,可传 epoch(部分调度器已弃用该参数,建议不传)
get_last_lr()返回上一步之后各 param_group 的 lr 列表,用于打日志
state_dict() / load_state_dict()保存/恢复调度器(含 last_epoch 等),断点续训时与 optimizer 一起保存
调度器公式/行为典型参数
StepLR每 step_size 个 epoch 乘 gammastep_size, gamma
MultiStepLR在指定 milestones(epoch 列表)乘 gammamilestones, gamma
ExponentialLR每步 lr *= gammagamma
CosineAnnealingLR余弦从 lr 降到 0 或 eta_minT_max, eta_min
CosineAnnealingWarmRestarts余弦周期并周期性地重启T_0, T_mult, eta_min
OneCycleLR先线性 warmup 再余弦/线性 衰减到 min_lrmax_lr, total_steps, pct_start 等
ReduceLROnPlateau当指标不改善时 lr *= factormode, factor, patience
LambdaLRlr = base_lr * lr_lambda(epoch)lr_lambda
SequentialLR / ChainedScheduler多个 scheduler 按顺序或链式组合见文档
  • 按 step:每个 training step 后调用 scheduler.step(),如 OneCycleLR、部分 Cosine 实现。
  • 按 epoch:每个 epoch 结束调用一次 scheduler.step(),如 StepLR、MultiStepLR、ReduceLROnPlateau(plateau 的 step 传 validation 指标)。

示例(按 epoch):

scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=num_epochs)
for epoch in range(num_epochs):
    train_one_epoch(...)
    scheduler.step()

示例(按 step,OneCycleLR):

total = len(dataloader) * num_epochs
scheduler = torch.optim.lr_scheduler.OneCycleLR(
    optimizer, max_lr=1e-2, total_steps=total, pct_start=0.1
)
for batch in dataloader:
    ...
    optimizer.step()
    scheduler.step()

以下为 torch.optim.lr_scheduler 中常见调度器及要点,便于速查与补全。

调度器类名主要参数说明
StepLRStepLRstep_size, gamma每 step_size 个 epoch 乘 gamma
MultiStepLRMultiStepLRmilestones, gamma在指定 epoch 乘 gamma
ExponentialLRExponentialLRgamma每步/每 epoch 乘 gamma
CosineAnnealingLRCosineAnnealingLRT_max, eta_min余弦退火到 eta_min
CosineAnnealingWarmRestartsCosineAnnealingWarmRestartsT_0, T_mult, eta_min带周期重启的余弦
OneCycleLROneCycleLRmax_lr, total_steps, …单周期先升后降,可配 div_factor、pct_start
ReduceLROnPlateauReduceLROnPlateaumode, factor, patience按验证指标 plateau 时乘 factor
LambdaLRLambdaLRlr_lambda自定义 lr = base_lr * lr_lambda(epoch)
SequentialLRSequentialLRschedulers, milestones按 milestone 顺序切换多个调度器
ChainedSchedulerChainedSchedulerschedulers链式组合(每步依次 step)
LinearLR / ConstantLR / PolynomialLRLinearLR见文档线性/常数/多项式变化
CyclicLRCyclicLRbase_lr, max_lr, …三角或指数周期变化

基类方法:step()(部分调度器需传入 metrics)、get_last_lr()state_dict()load_state_dict();子类实现 get_lr() 返回当前各 param_group 的学习率列表。


model.train()
for batch in dataloader:
    inputs, targets = batch
    optimizer.zero_grad()
    outputs = model(inputs)
    loss = loss_fn(outputs, targets)
    loss.backward()
    optimizer.step()
    # 若按 step 调 lr:scheduler.step()

当有效 batch = N * 单次 batch 时,每 N 步才 optimizer.step() 一次,每步只 backward()step(),梯度累加;最后一步前 zero_grad(),然后 backward()step()

accum_steps = 4
for i, batch in enumerate(dataloader):
    inputs, targets = batch
    if i % accum_steps == 0:
        optimizer.zero_grad()
    outputs = model(inputs)
    loss = loss_fn(outputs, targets) / accum_steps
    loss.backward()
    if (i + 1) % accum_steps == 0:
        optimizer.step()

防止梯度爆炸,在 backward() 之后、step() 之前对梯度做范数裁剪:

torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)
# 或按值裁剪
# torch.nn.utils.clip_grad_value_(model.parameters(), clip_value=0.5)
optimizer.step()

常用于 RNN、Transformer、大 lr 或长序列训练。

在 分布式训练 中,每 rank 独立维护自己的 optimizer;DDP 在 backward 时已做梯度 AllReduce,因此各 rank 上梯度一致,各自 optimizer.step() 后参数保持同步。无需对 optimizer 做额外集体通信。


Loss任务输出与目标形状注意
CrossEntropyLoss多分类logits (N,C),target (N,) long含 softmax,不要对 logits 再 softmax
NLLLoss多分类log_probs (N,C),target (N,)需先 log_softmax
BCELoss / BCEWithLogitsLoss二分类概率或 logits,(N,) 或 (N,1)BCEWithLogits 更数值稳定
MSELoss回归(N,*),同形状对异常值敏感
L1Loss回归同形状更鲁棒
HuberLoss回归同形状小误差 MSE、大误差 L1,折中
# 分类
loss_fn = nn.CrossEntropyLoss()
loss = loss_fn(logits, targets)

# 回归
loss_fn = nn.MSELoss()
loss = loss_fn(pred, target)

# 多任务:加权和
loss = ce_loss(logits_cls, y_cls) + 0.1 * mse_loss(pred_reg, y_reg)
loss.backward()

  • 线性缩放:batch 扩大 k 倍时,lr 常线性扩大 k 倍(如 32→256,lr 1e-3→8e-3);再大 batch 可用 sqrt 缩放或 warmup 更长。
  • SGD:常用 0.01~0.1;大 batch 可试 0.1 * batch/32。
  • Adam/AdamW:1e-3 通用;2e-3~3e-3 也常见;再大易不稳定。

前若干 step 或 epoch 从 0 或小 lr 线性升到目标 lr,减轻初期大梯度导致的震荡。Transformer、大 batch 训练常用。

# 可用 LambdaLR 或 OneCycleLR 的 pct_start 实现
def warmup_lambda(step):
    if step < warmup_steps:
        return step / warmup_steps
    return 1.0
scheduler = torch.optim.lr_scheduler.LambdaLR(optimizer, warmup_lambda)
  • AdamW:0.01 常用;0.1 也有用。
  • SGD:1e-4~1e-2;bias / BN 通常设为 0。
  • 忘记 zero_grad():梯度会累积,等价于放大 lr。
  • scheduler.step()optimizer.step() 之前其实并不会出错,但大多数调度器(如 StepLR、CosineAnnealingLR)推荐在 optimizer.step() 之后调用,这样每 step 用的是当前 step 的 lr,和论文/官方实现一致。部分调度器(如 ReduceLROnPlateau)需根据文档操作。
  • 恢复 checkpoint 时先恢复 scheduler 再恢复 optimizer,否则 scheduler 会覆盖加载进来的 lr。
  • ReduceLROnPlateau 的 step(metric) 要传验证指标,且注意 mode(‘min’/‘max’)。

下面是一段可独立运行的训练脚本(CPU 即可),串联 DataLoader、模型、损失、优化器、调度器与梯度裁剪。

import torch
import torch.nn as nn
from torch.utils.data import DataLoader, TensorDataset

def main():
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
    batch_size = 32
    num_epochs = 10
    lr = 1e-3
    weight_decay = 0.01
    max_grad_norm = 1.0

    # 数据
    X = torch.randn(1000, 20)
    y = (X.sum(dim=1, keepdim=True) > 0).long().squeeze(1)
    dataset = TensorDataset(X, y)
    dataloader = DataLoader(dataset, batch_size=batch_size, shuffle=True, num_workers=0)

    # 模型、损失、优化器、调度器
    model = nn.Sequential(
        nn.Linear(20, 64),
        nn.ReLU(),
        nn.Linear(64, 2),
    ).to(device)
    loss_fn = nn.CrossEntropyLoss()
    optimizer = torch.optim.AdamW(model.parameters(), lr=lr, weight_decay=weight_decay)
    scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=num_epochs)

    model.train()
    for epoch in range(num_epochs):
        total_loss = 0.0
        for step, (inputs, targets) in enumerate(dataloader):
            inputs, targets = inputs.to(device), targets.to(device)
            optimizer.zero_grad()
            logits = model(inputs)
            loss = loss_fn(logits, targets)
            loss.backward()
            torch.nn.utils.clip_grad_norm_(model.parameters(), max_grad_norm)
            optimizer.step()
            total_loss += loss.item()
        avg_loss = total_loss / len(dataloader)
        scheduler.step()
        current_lr = scheduler.get_last_lr()[0]
        print(f"Epoch {epoch+1}/{num_epochs}  loss={avg_loss:.4f}  lr={current_lr:.2e}")

if __name__ == "__main__":
    main()

将上述代码保存为 train_example.py,在项目根目录执行 python train_example.py 即可运行(无 GPU 时自动用 CPU)。


算法构造示例典型 lr
SGDoptim.SGD(params, lr=0.01, momentum=0.9)0.01~0.1
Adamoptim.Adam(params, lr=1e-3)1e-3
AdamWoptim.AdamW(params, lr=1e-3, weight_decay=0.01)1e-3
调度器构造示例
StepLRlr_scheduler.StepLR(optimizer, step_size=30, gamma=0.1)
CosineAnnealingLRlr_scheduler.CosineAnnealingLR(optimizer, T_max=100)
OneCycleLRlr_scheduler.OneCycleLR(optimizer, max_lr=1e-2, total_steps=total)
ReduceLROnPlateaulr_scheduler.ReduceLROnPlateau(optimizer, mode='min', factor=0.5, patience=5)
  • 数据流:数据来自 DatasetDataLoader → 本训练的 for batch in dataloader
  • 多卡:本训练的 optimizer/scheduler 每 rank 一份,与 DDP 无冲突;checkpoint 保存/加载时需同时保存 optimizer、scheduler 的 state_dict。

以上为模型训练中求解器、参数配置与训练循环的完整技术解读;按文档索引可快速定位到优化器、调度器、参数组或配置经验等小节。

相关内容