学习率调度器
本教程共 60 篇 · 第 15 篇 · 更新于 2026-08-17 · 约 2 分钟阅读
本节目标:理解为什么要动态调整学习率,会用 StepLR、CosineAnnealingLR、ReduceLROnPlateau,能搭出「预热 + 余弦退火」的标准组合。
学习率:训练中最重要的旋钮
上一章说过,学习率(learning rate)就是下山时的步长。这个数设多少,直接决定训练成败:
- 太大:步子迈过头,损失来回震荡,甚至直接发散成 NaN
- 太小:走得比蜗牛慢,几百个 epoch 都到不了底
更麻烦的是,训练的不同阶段需要不同的步长。刚开始可以大步快跑,快到底了就得小步精修。固定学习率就像全程用一个档位开车,能到,但不顺。
学习率调度器(LR scheduler)就是帮你换挡的装置,按规则在训练过程中调整学习率。
标准用法
调度器的用法很统一,三步:
- 创建调度器,传入优化器
- 每个 epoch 训练完,调用
scheduler.step() - 用
get_last_lr()查看当前学习率
import torch
import torch.optim as optim
model = torch.nn.Linear(10, 1)
optimizer = optim.SGD(model.parameters(), lr=0.1)
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=30, gamma=0.1)
for epoch in range(100):
# ...训练代码...
optimizer.step() # 先更新参数
scheduler.step() # 再更新学习率
print(scheduler.get_last_lr()[0])
Warning
scheduler.step()必须在optimizer.step()之后调用。顺序反了,PyTorch 会警告,学习率变化也会错位。
阶梯型:StepLR 一族
StepLR:按固定间隔衰减
每隔 step_size 个 epoch,学习率乘以 gamma:
scheduler = optim.lr_scheduler.StepLR(optimizer, step_size=30, gamma=0.1)
# 第 0-29 epoch 是 0.1,第 30-59 是 0.01,第 60-89 是 0.001
MultiStepLR:指定里程碑
什么时候衰减由你定,更灵活:
scheduler = optim.lr_scheduler.MultiStepLR(
optimizer, milestones=[30, 60, 80], gamma=0.1)
图像分类的经典配置:总 90 epoch,在第 30、60 epoch 各降一次。
余弦退火:平滑下降的明星
CosineAnnealingLR 让学习率沿余弦曲线平滑下降,从初始值缓缓滑到 eta_min,没有阶梯的突变:
scheduler = optim.lr_scheduler.CosineAnnealingLR(
optimizer, T_max=100, # 半个周期的长度,通常设成总 epoch 数
eta_min=1e-6 # 学习率下限
)
近几年的模型(ViT、ResNet 的各种复现)几乎都默认用它。训练总轮数固定时,选它准没错。
ReduceLROnPlateau:让数据说话
不想预设衰减节奏?这个调度器监控你的验证指标,指标不涨了就自动降学习率:
scheduler = optim.lr_scheduler.ReduceLROnPlateau(
optimizer,
mode='min', # 监控 loss:越小越好
factor=0.5, # 触发时学习率减半
patience=5, # 连续 5 个 epoch 没改善就降
)
for epoch in range(100):
train(...)
val_loss = evaluate(...)
scheduler.step(val_loss) # 注意:这里要传指标进去
Note
ReduceLROnPlateau是唯一要传参的调度器:scheduler.step(指标)。监控 loss 用mode='min',监控准确率用mode='max'。
Warmup:预热的概念
训练刚开始时,参数还是随机数,梯度方向很乱。这时用大学习率,容易把模型带沟里。所以大模型训练流行先预热(warmup):学习率从很小线性爬升到目标值,再开始正式下降。
标准做法是用 SequentialLR 把两个调度器拼起来:
from torch.optim.lr_scheduler import SequentialLR, LinearLR, CosineAnnealingLR
optimizer = optim.Adam(model.parameters(), lr=0.001)
# 阶段一:前 5 个 epoch 预热,lr 从 0.0001 爬到 0.001
warmup = LinearLR(optimizer, start_factor=0.1, total_iters=5)
# 阶段二:剩余 95 个 epoch 余弦退火
cosine = CosineAnnealingLR(optimizer, T_max=95, eta_min=1e-6)
scheduler = SequentialLR(optimizer,
schedulers=[warmup, cosine],
milestones=[5]) # 第 5 个 epoch 切换
预热 + 余弦退火是 Transformer 训练的标配,BERT、GPT 一路都是这个套路。
别忘保存调度器状态
中断后恢复训练,调度器的进度也得存:
torch.save({
'model': model.state_dict(),
'optimizer': optimizer.state_dict(),
'scheduler': scheduler.state_dict(), # 别忘了这个
}, 'checkpoint.pth')
漏了它,恢复后学习率会从头开始走,和训练进度对不上。
其他值得知道的调度器
ExponentialLR:每个 epoch 都乘一个gamma(如 0.95),持续缓慢衰减LambdaLR:传一个自定义函数,完全掌控学习率曲线,最灵活CyclicLR/OneCycleLR:学习率在区间内循环升降,按 batch 调用,快速训练时很能打
# ExponentialLR:每轮衰减
scheduler = optim.lr_scheduler.ExponentialLR(optimizer, gamma=0.95)
# LambdaLR:传函数,epoch 进来,学习率倍数出去
scheduler = optim.lr_scheduler.LambdaLR(
optimizer, lr_lambda=lambda epoch: 0.95 ** epoch)
Note
CyclicLR和OneCycleLR是按 batch 调用的:每个 batch 训练完就scheduler.step(),不是按 epoch。
选型速查
| 场景 | 推荐 |
|---|---|
| 不确定训练多久 | ReduceLROnPlateau |
| 固定 epoch 数的分类任务 | CosineAnnealingLR |
| 经典 CV 复现 | MultiStepLR |
| Transformer / 大模型 | 预热 + 余弦退火 |
Tip调度器救不了「学习率本身没调对」的模型。先把学习率调到能正常下降,再谈调度。合适的调度能让同样的模型多涨一两个点。