3. 学习率策略:固定学习率 vs 学习率衰减 vs 循环学习率,如何选择与调参

学习率,说白了就是模型每次更新参数时迈的步子大小。步子太大容易扯着蛋——loss 震荡不收敛;步子太小又像老太太散步——训到天荒地老也不见底。

我见过太多同学一上来就设个 0.01,然后盯着 loss 曲线发呆。嗯,今天咱们就把三种主流策略掰开揉碎,看看它们各自适合什么场景。

3.1 固定学习率:简单但危险

固定学习率是最原始的做法。整个训练过程用一个恒定值,比如 0.01 或 0.001。

适用场景:

  • 小数据集(几千样本以内)
  • 浅层网络(3-5 层)
  • 快速原型验证

但我在项目中踩过坑。有一次做图像分类,用 ResNet-50 训 CIFAR-10,固定学习率 0.01,loss 前 20 个 epoch 降得挺好,后面直接震荡成心电图。为什么?因为随着训练进行,最优解附近的地形越来越陡峭,固定步长就变成了「过冲」。

避坑指南:我曾经在工业级推荐系统上试过固定学习率,训了 3 天 loss 死活不降。后来发现是学习率设大了,参数在最优解附近来回跳。换成衰减策略后,半天就收敛了。

3.2 学习率衰减:最常用的策略

学习率衰减的核心思想很简单:前期大步探索,后期小步精调。常见的衰减方式有四种:

衰减方式 公式/策略 典型参数 我的使用频率
Step Decay 每 N 个 epoch 乘以 gamma gamma=0.1, step=30 ⭐⭐⭐⭐
Exponential Decay lr = lr₀ × exp(-k × epoch) k=0.01 ⭐⭐⭐
Cosine Annealing 余弦曲线从 lr₀ 降到 0 T_max=50 ⭐⭐⭐⭐⭐
ReduceLROnPlateau loss 不降时自动衰减 patience=5, factor=0.5 ⭐⭐⭐⭐

我个人最常用的是 Cosine Annealing。为什么?因为它衰减得平滑,不会像 Step Decay 那样突然掉一截导致 loss 反弹。你想想看,训练到后期,模型已经在最优解附近了,突然把学习率砍掉 90%,参数更新直接卡死——这种事我遇到过好几次。

# PyTorch 实现 Cosine Annealing
import torch.optim.lr_scheduler as lr_scheduler

optimizer = torch.optim.SGD(model.parameters(), lr=0.1)
scheduler = lr_scheduler.CosineAnnealingLR(optimizer, T_max=50, eta_min=1e-6)

for epoch in range(100):
    train_one_epoch()
    scheduler.step()
    print(f"Epoch {epoch}: lr = {scheduler.get_last_lr()[0]:.6f}")

调参技巧:我习惯把初始学习率设成 0.1 或 0.01,然后观察前 5 个 epoch 的 loss 下降速度。如果 loss 下降太快(每 epoch 降 50% 以上),说明学习率偏大;如果下降太慢(每 epoch 降不到 5%),说明偏小。调整到每 epoch 降 10%-20% 左右,基本就对了。

3.3 循环学习率:跳出局部最优的利器

循环学习率(Cyclical Learning Rate, CLR)是我最近两年越来越喜欢用的策略。它让学习率在上下界之间周期性变化,而不是单调递减。

为什么有效?因为训练过程中,模型可能会陷入局部最优或鞍点。这时候如果学习率突然变大,就能「跳」出来。我曾在 NLP 任务上对比过:用 Cosine Annealing 训到 80 个 epoch 卡住了,换成 CLR 后,第 90 个 epoch 突然又降了一大截。

CLR 有三种常见模式:

  • triangular:线性上升再线性下降,最简单
  • triangular2:每次周期峰值减半,更稳定
  • exp_range:峰值指数衰减,兼顾探索和收敛
# PyTorch 实现 CLR
from torch.optim.lr_scheduler import CyclicLR

optimizer = torch.optim.SGD(model.parameters(), lr=0.001)
scheduler = CyclicLR(
    optimizer,
    base_lr=0.0001,      # 下界
    max_lr=0.01,         # 上界
    step_size_up=2000,   # 上升步数
    mode='triangular2'   # 模式
)

for batch in dataloader:
    optimizer.zero_grad()
    loss = criterion(model(batch), batch)
    loss.backward()
    optimizer.step()
    scheduler.step()

关键参数速查表:

参数 含义 推荐范围
base_lr 学习率下界 max_lr 的 1/10 ~ 1/3
max_lr 学习率上界 最优固定 lr 的 2~10 倍
step_size_up 上升步数(通常 = 每个周期的 1/2) 2~8 个 epoch 的步数
mode 循环模式 triangular / triangular2 / exp_range

3.4 三种策略对比与选择

我整理了一张对比表,方便你快速决策:

维度 固定学习率 学习率衰减 循环学习率
收敛速度 慢(需手动调优) 中 快(自动探索)
最终精度 低(易震荡) 高 高(可跳出局部最优)
调参难度 低(1 个参数) 中(2-3 个参数) 高(4-5 个参数)
适用数据量 小(<1 万) 中(1 万-100 万) 大(>10 万)
适用模型深度 浅层(<10 层) 中深层(10-100 层) 深层(>50 层)
我的推荐指数 ⭐ ⭐⭐⭐⭐ ⭐⭐⭐⭐⭐

我的建议是:

  • 新手或快速实验:先用 Cosine Annealing,初始 lr=0.01,T_max=50
  • 追求极致精度:上 CLR,用 triangular2 模式,base_lr=0.0001,max_lr=0.01
  • 资源有限(只能训 1-2 次):用 ReduceLROnPlateau,patience=5,factor=0.5

注意:CLR 不是万能药。我曾在只有 5000 样本的小数据集上试过,结果 loss 震荡得更厉害。因为数据太少,每次学习率变化都会导致模型「失忆」。小数据集老老实实用衰减策略就好。

3.5 核心知识图谱

下面这张图总结了三种策略的适用场景和选择逻辑:

学习率策略选择流程图 选择学习率策略 固定学习率 学习率衰减 循环学习率 适用条件 • 数据量 < 1万 • 模型层数 < 10 • 快速原型验证 适用条件 • 数据量 1万-100万 • 模型层数 10-100 • 追求稳定收敛 适用条件 • 数据量 > 10万 • 模型层数 > 50 • 需跳出局部最优 推荐参数 lr = 0.001 ~ 0.01 无衰减 推荐参数 Cosine: lr₀=0.01, T_max=50 Plateau: patience=5 推荐参数 base=0.0001, max=0.01 mode=triangular2 核心原则:小数据固定 → 中数据衰减 → 大数据循环 先用 Cosine Annealing 保底,再根据收敛情况切换 CLR

3.6 实战调参流程

最后,我分享一个自己常用的调参流程,你直接照着做就行:

  1. 第一步:跑 5 个 epoch 找初始 lr
    • 用学习率范围测试(LR Range Test),从 1e-6 到 1.0 指数增长
    • 找到 loss 下降最快的区间,取中间值作为初始 lr
  2. 第二步:用 Cosine Annealing 训 50 个 epoch
    • 初始 lr = 上一步找到的值
    • T_max = 50,eta_min = 1e-6
  3. 第三步:观察 loss 曲线
    • 如果最后 10 个 epoch loss 还在降 → 增加 epoch 数或换 CLR
    • 如果 loss 震荡 → 降低初始 lr 或换 ReduceLROnPlateau
    • 如果 loss 卡住不动 → 换 CLR 尝试跳出
  4. 第四步:最终调优
    • 用 CLR 的 triangular2 模式,base_lr = 初始 lr / 10,max_lr = 初始 lr × 3
    • 周期长度设为 4-8 个 epoch

我的经验:80% 的情况下,Cosine Annealing 就能拿到不错的结果。只有当你发现 loss 在最后 20% 的训练时间里完全不动了,才值得花时间去调 CLR。别一上来就搞复杂策略——先跑通,再优化。