如何在CIFAR-10训练中正确使用OneCycleLR学习率调度器?
关于OneCycleLR调度器的两个关键问题解答
一、epochs参数的正确设置
- OneCycleLR是单周期学习率调度器,
epochs参数代表的是整个训练过程的总epoch数,而非单次循环的epoch数。 - 若你计划训练200个epoch,必须将
epochs=200。如果设置epochs=10却训练200个epoch,前10个epoch会完成OneCycle的完整循环(从初始LR升到max_lr再降到极低值),剩余190个epoch的学习率会维持在最终的极小值(由final_div_factor参数控制),不会重复执行循环。
二、学习率异常变化的原因及解决方法
你的学习率出现先低后升的异常,核心有两个原因:
1. OneCycleLR会自动覆盖优化器的初始LR
OneCycleLR默认通过max_lr / div_factor计算训练的起始学习率。你的设置中:
optimizer = optim.Adam([x for x in model.parameters() if x.requires_grad], lr=0.001) scheduler = torch.optim.lr_scheduler.OneCycleLR(optimizer, max_lr=0.01, epochs=200, steps_per_epoch=128)
默认div_factor=25.0,所以起始LR为0.01 / 25 = 0.0004,这就是你看到的Epoch 1的LR值——OneCycleLR直接覆盖了你手动设置的lr=0.001。
若希望起始LR接近手动设置的0.001,有两种调整方式:
- 修改
div_factor:比如设置div_factor=10,此时起始LR为0.01/10=0.001,与优化器初始LR一致; - 直接将优化器初始LR设为
max_lr / div_factor,避免参数冲突。
2. scheduler.step()的调用时机错误
OneCycleLR要求每个训练step(即每个batch训练完成后)调用一次scheduler.step(),而非每个epoch结束后调用。
如果你的代码是每个epoch调用一次scheduler.step(),总步数会被错误计算为200步(而非200*128=25600步)。此时,OneCycleLR的上升阶段(占总步数的pct_start=0.3)会被大幅拉长,导致训练到118epoch还处于上升阶段,完全不符合预期。
正确的调用逻辑示例:
for epoch in range(200): for batch_idx, (data, target) in enumerate(train_loader): # 前向传播、损失计算、反向传播 optimizer.step() # 每个batch后更新学习率 scheduler.step() # epoch结束后的验证、日志记录等操作
内容的提问来源于stack exchange,提问作者CasellaJr
相关产品推荐
相关产品推荐

