最稳妥方式是使用torch.optim.lr_scheduler;手动修改param_groups0会破坏调度器内部状态,导致steplr、multisteplr等计数错乱,reducelronplateau需传入指标并设对mode,lambdalr的lr_lambda函数须正确返回乘数因子。

直接用 torch.optim.lr_scheduler 是最稳妥的方式;手动改 optimizer.param_groups[0]['lr'] 看似快,但会破坏调度器内部状态,尤其在断点续训或复合策略中极易出错。
StepLR 和 MultiStepLR 为什么训练几轮后学习率就不变了?
核心问题在于调用时机和顺序。这两个调度器依赖 epoch 计数,必须在每个 epoch 结束后、optimizer.step() 之后调用 scheduler.step(),且不能提前或漏掉。
- 常见错误:把
scheduler.step()放在optimizer.step()前,或只在验证后调用(比如误以为要等 val_loss) -
StepLR的step_size是 epoch 数,不是 batch 数;若你每轮只训 1 个 batch 却设step_size=1,它会在第 1 个 batch 后就衰减,之后再无变化 -
MultiStepLR的 milestones 列表(如[30, 60, 90])必须是升序整数,且值不能超过总 epoch 数,否则对应节点永远不触发 - 断点恢复时,需先
optimizer.load_state_dict(),再scheduler.load_state_dict(),最后手动scheduler.step()若当前 epoch > last_epoch,否则调度器仍卡在旧步数
ReduceLROnPlateau 死活不降学习率?
它根本不是按 epoch 自动走的——它只认你传进去的那个标量指标,而且只在“指标没改善”达到耐心阈值后才动作。
- 必须调用
scheduler.step(val_loss)或scheduler.step(val_acc),只写scheduler.step()相当于告诉它“没新数据”,它就啥也不干 -
mode='min'对应 loss 类指标(越小越好),mode='max'对应 acc 类(越大越好);设反了会导致“明明 loss 涨了却没降 lr” -
patience是“连续多少轮没改善”,不是“总共训多少轮”;若patience=5,前 4 轮 loss 波动但没持续变好,第 5 轮才开始计数 - val_loss 若含
nan(常见于混合精度训练),ReduceLROnPlateau会直接跳过本轮判断;务必先torch.nan_to_num(val_loss, nan=1e9)或过滤
LambdaLR 自定义 warmup + cosine 怎么写才不出错?
关键在 lr_lambda 函数签名和返回值:它接收的是 epoch 索引(从 0 开始),返回的是乘数因子(比如 0.5 表示当前 lr = 初始 lr × 0.5),不是绝对学习率值。
- 别在 lambda 里硬写
epoch + 1—— PyTorch 传入的就是当前 epoch 编号,从 0 起始,直接用即可 - warmup 阶段返回值必须从 0 开始线性上升(否则第 0 轮 lr=0,梯度更新失效);常用写法:
return min(1.0, (epoch + 1) / warmup_epochs) - cosine 部分注意
T_max是半周期长度,不是总 epoch 数;若想全程退火,T_max应设为总 epoch 数减 warmup 长度 - 函数里别调用
optimizer.param_groups[0]['lr']——LambdaLR只负责算因子,实际更新由调度器内部完成
最容易被忽略的是:所有调度器都假设 epoch 计数严格递增且无跳变。如果你在训练循环里用了 continue 跳过某些 epoch,或用了非标准的 epoch 定义(比如按 batch 数折算),last_epoch 状态就会失准,导致学习率节奏彻底乱掉。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











