steplr衰减不生效的根本原因是optimizer被重复初始化,导致scheduler.last_epoch始终为0;正确做法是将optimizer和lr_scheduler在训练循环外单次初始化,并在每个epoch结束后调用scheduler.step()。

StepLR衰减不生效?检查optimizer是否被重复初始化
常见现象是调用step()后学习率没变,根本原因常是每次epoch都重新创建了optimizer对象,导致StepLR内部的last_epoch计数器始终从0开始。正确做法是把optimizer和lr_scheduler定义在训练循环外,且只初始化一次。
实操建议:
-
StepLR必须与同一个optimizer实例绑定,不能每次迭代都torch.optim.SGD(model.parameters(), lr=0.1) - 初始化时显式传
last_epoch=-1(默认值)可避免意外跳过首次更新 - 每轮训练结束调用
scheduler.step(),不是每个batch调用(除非你设step_size=1且想每batch衰减) - 验证是否生效:打印
scheduler.get_last_lr()[0]或optimizer.param_groups[0]["lr"]
CosineAnnealingLR重启后学习率突变?注意T_max和last_epoch配合
当训练中断后恢复,或做多阶段训练时,CosineAnnealingLR容易在重启点出现学习率跳变——比如上一阶段末尾是0.001,新阶段起始又回到初始值0.1。这是因为默认last_epoch=-1,scheduler认为这是全新开始。
实操建议:
- 断点续训时,加载模型后,手动设置
scheduler.last_epoch = saved_epoch(注意不是epoch+1,它内部是0-indexed) -
T_max指一个完整余弦周期的epoch数;若设为10但训练20 epoch,它会在第10 epoch降到最小值,第11 epoch又跳回最大值——这不是bug,是设计如此 - 如需平滑衔接多个周期,改用
CosineAnnealingWarmRestarts,它的T_0参数控制首周期长度,后续自动倍增 - 最小学习率由
eta_min控制,默认0,建议设为非零值(如1e-6)防止梯度更新失效
StepLR和CosineAnnealingLR选哪个?看任务收敛阶段和数据规模
没有绝对优劣,关键匹配训练行为。StepLR适合前期快速下降、后期微调的场景;CosineAnnealing更适合需要跳出局部极小、或数据量大/模型深导致优化路径复杂的任务。
实操建议:
- 小数据集(StepLR配
step_size=10, gamma=0.5更稳 - 大数据集(ImageNet级)或Transformer类模型,
CosineAnnealingLR配T_max=100, eta_min=1e-6通常泛化更好 - 混合策略可行:前50 epoch用
StepLR快速收敛,后50 epoch切到CosineAnnealingLR精细搜索,只需重新赋值optimizer.param_groups[0]["lr"]并新建scheduler - 别忽略warmup:两者都不含warmup,若需,得额外用
torch.optim.lr_scheduler.LinearLR串联
学习率变化没反映在梯度更新里?检查param_groups是否被手动修改
即使scheduler显示学习率已变,但模型权重更新量没变化,大概率是optimizer.param_groups被外部代码覆盖了。例如手动写optimizer.param_groups[0]["lr"] = 0.01会绕过scheduler逻辑,后续step()可能失效或冲突。
实操建议:
- 永远通过
scheduler.step()驱动学习率变更,不要直操作param_groups - 调试时用
print([pg["lr"] for pg in optimizer.param_groups])确认所有参数组是否同步更新(尤其多组学习率时) - 如果用了
model.requires_grad_(False)冻结部分层,确保对应参数没被包含在optimizer里,否则scheduler仍会尝试更新其lr(虽无梯度) - 分布式训练(DDP)下,scheduler只需在主进程调用
step(),但所有进程的optimizer状态必须一致,否则lr不同步
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











