目标:系统掌握深度学习中核心学习率调度策略的设计动机、数学公式、实现方式与适用场景,建立"调度器工具箱"的完整认知。

前置要求:了解基本的神经网络训练流程(前向传播、反向传播、参数更新)和优化器基础(SGD、Adam)。

学习率是深度学习中最重要的超参数——没有之一。选对学习率调度策略,往往比换优化器更有效。如果说优化器决定"怎么走",学习率调度器决定"每一步走多远"。本文按演进脉络系统梳理各类调度策略,给出公式、曲线形状、代码实现和记忆小贴士。


1. 学习率调度的动机(Why Scheduling?)

  在深入具体调度器之前,先理解"为什么学习率不能一直不变"。

1.1 固定学习率的困境

  使用固定学习率训练时,面临一个根本矛盾:

  • 学习率太大:训练初期收敛快,但后期在最优点附近震荡,无法精细收敛
  • 学习率太小:训练后期收敛精细,但初期收敛极慢,浪费大量时间

数值示例:假设损失函数 $\mathcal{L} = (\theta - 3)^2$,初始 $\theta = 0$:

  • $\eta = 0.5$(太大):$\theta_1 = 0 - 0.5 \times (-6) = 3$(一步到位!但实际中会跨过最优点来回震荡)
  • $\eta = 0.001$(太小):$\theta_1 = 0 - 0.001 \times (-6) = 0.006$(走了 1000 步才到 $\theta \approx 2.60$,仍未收敛到 $\theta^* = 3$)

理想方案:初期用大学习率快速接近最优点,后期用小学习率精细收敛

1.2 学习率调度的核心思想

  在训练过程中动态调整学习率,让它随时间(epoch 或 step)按某种规则衰减:

$$ \eta_t = f(\eta_0, t, T, \ldots) $$

其中 $\eta_0$ 是初始学习率,$t$ 是当前步数,$T$ 是总步数。

类比:开车下山。刚出发时可以踩油门加速(大学习率快速下降),接近山谷时要踩刹车减速(小学习率精细调整),否则会冲过头。

1.3 调度器的统一框架

  所有调度器都遵循同一个框架:在每个 step(或 epoch)结束时,根据某种规则计算新的学习率,通知优化器更新。

flowchart LR
    Train["训练循环"] --> Step["一个 step<br/>前向+反向+更新"]
    Step --> Scheduler["调度器<br/>计算新 η_t"]
    Scheduler --> Update["优化器<br/>使用新 η_t"]
    Update --> Step
for epoch in range(num_epochs):
    for batch in dataloader:
        loss = model(batch)
        loss.backward()
        optimizer.step()       # 用当前学习率更新参数
        optimizer.zero_grad()
    scheduler.step()           # 每个 epoch 结束后更新学习率

2. 基于轮次的调度器(Epoch-based Schedulers)

  最简单的调度策略——按固定规则在每个 epoch 结束后衰减学习率。

2.1 StepLR(阶梯衰减)

简介:每隔固定数量的 epoch,将学习率乘以一个衰减因子 $\gamma$。最经典、最常用的调度策略。

$$ \eta_t = \eta_0 \times \gamma^{\lfloor t / \text{step\_size} \rfloor} $$

其中 $\gamma$ 是衰减因子(通常取 0.1),$\text{step\_size}$ 是衰减间隔(通常取 30)。

数值示例:$\eta_0 = 0.1$,$\gamma = 0.1$,$\text{step\_size} = 30$:

  • epoch 0~29:$\eta = 0.1 \times 0.1^0 = 0.1$
  • epoch 30~59:$\eta = 0.1 \times 0.1^1 = 0.01$
  • epoch 60~89:$\eta = 0.1 \times 0.1^2 = 0.001$
  • epoch 90~:$\eta = 0.1 \times 0.1^3 = 0.0001$

形状:阶梯形——在衰减点突然下降,其余时间保持不变。

StepLR 学习率曲线

类比:考试降级。每隔一段时间考核一次,不合格就降一级学习率。

记忆小贴士:StepLR = “Step"——每隔固定步数衰减一次。阶梯形。

import torch.optim as optim
from torch.optim.lr_scheduler import StepLR

optimizer = optim.SGD(model.parameters(), lr=0.1, momentum=0.9)
scheduler = StepLR(optimizer, step_size=30, gamma=0.1)

for epoch in range(90):
    train(...)
    scheduler.step()

适用场景:ResNet 等 CNN 训练的经典方案(90 epoch,每 30 epoch 衰减 10 倍)。

2.2 MultiStepLR(多里程碑衰减)

简介:StepLR 的泛化版本——不是每隔固定 epoch 衰减,而是在指定的 epoch 衰减。

$$ \eta_t = \eta_0 \times \gamma^{\sum_i \mathbb{1}[t \geq m_i]} $$

其中 $m_i$ 是里程碑列表(如 $[60, 120, 160]$)。

数值示例:$\eta_0 = 0.1$,$\gamma = 0.2$,$\text{milestones} = [60, 120, 160]$:

  • epoch 0~59:$\eta = 0.1$
  • epoch 60~119:$\eta = 0.1 \times 0.2 = 0.02$
  • epoch 120~159:$\eta = 0.02 \times 0.2 = 0.004$
  • epoch 160~:$\eta = 0.004 \times 0.2 = 0.0008$

与 StepLR 的区别:StepLR 的衰减间隔固定(如每 30 epoch);MultiStepLR 的衰减点可以任意指定(如 $[60, 120, 160]$),更灵活。

from torch.optim.lr_scheduler import MultiStepLR

scheduler = MultiStepLR(optimizer, milestones=[60, 120, 160], gamma=0.2)

适用场景:需要在特定 epoch 衰减的场景(如目标检测中 YOLO 的训练方案)。

2.3 ExponentialLR(指数衰减)

简介:每个 epoch 都乘以衰减因子 $\gamma$,学习率呈指数下降。

$$ \eta_t = \eta_0 \times \gamma^t $$

数值示例:$\eta_0 = 0.1$,$\gamma = 0.95$:

  • epoch 0:$\eta = 0.1$
  • epoch 10:$\eta = 0.1 \times 0.95^{10} = 0.0599$
  • epoch 50:$\eta = 0.1 \times 0.95^{50} = 0.00769$
  • epoch 100:$\eta = 0.1 \times 0.95^{100} = 0.000592$

形状:平滑的指数下降曲线。

ExponentialLR 学习率曲线

类比:匀速刹车。不像 StepLR 那样"踩一脚松一脚”,而是持续均匀地减速。

记忆小贴士:ExponentialLR = “Exponential"——每步乘以 $\gamma$,指数下降。

from torch.optim.lr_scheduler import ExponentialLR

scheduler = ExponentialLR(optimizer, gamma=0.95)

适用场景:需要平滑衰减时的简单方案。缺点是后期学习率衰减太快,可能过早停止学习。

2.4 基于轮次的调度器对比

调度器形状灵活性特点
StepLR阶梯形最经典,ResNet 标配
MultiStepLR不规则阶梯指定任意衰减点
ExponentialLR指数曲线平滑但衰减太快

3. 基于指标的调度器(Metric-based Schedulers)

  基于轮次的调度器"盲目"衰减——不管模型是否还在进步,到了时间就衰减。基于指标的调度器"智能"衰减——只在模型不再进步时才衰减。

3.1 ReduceLROnPlateau(Plateau 检测)

简介:监控某个指标(通常是验证损失),当指标在连续若干个 epoch 内不再显著改善时,将学习率乘以衰减因子。

核心逻辑

  1. 每个 epoch 结束后,计算验证损失 $\mathcal{L}_{\text{val}}$
  2. 如果 $\mathcal{L}_{\text{val}}$ 在最近 patience 个 epoch 内没有改善超过 threshold,则 $\eta \leftarrow \eta \times \text{factor}$

关键参数

参数含义典型值
mode监控指标方向(min 越小越好,max 越大越好)min
patience等待多少个 epoch 无改善才衰减10
factor衰减因子0.1
threshold改善的最小幅度1e-4

数值示例:假设 patience=3factor=0.1,验证损失序列:

epoch 1: val_loss = 0.50  (best)
epoch 2: val_loss = 0.48  (best)
epoch 3: val_loss = 0.47  (best)
epoch 4: val_loss = 0.475 (no improve, count=1)
epoch 5: val_loss = 0.478 (no improve, count=2)
epoch 6: val_loss = 0.476 (no improve, count=3)
→ 触发衰减!η ← η × 0.1
epoch 7: val_loss = 0.46  (best,新学习率生效)

类比:耐心等公交。你等了 patience 分钟公交还没来(损失不再下降),就决定换一种交通方式(衰减学习率)。

记忆小贴士:ReduceLROnPlateau = “Reduce LR on Plateau"——在平台期减少学习率。智能但需要验证集。

from torch.optim.lr_scheduler import ReduceLROnPlateau

scheduler = ReduceLROnPlateau(optimizer, mode='min', factor=0.1, patience=10, threshold=1e-4)

for epoch in range(num_epochs):
    train(...)
    val_loss = validate(...)
    scheduler.step(val_loss)  # 注意:需要传入监控指标

适用场景:不确定何时该衰减时的"自适应"方案。缺点是需要验证集,且 patience 的选择需要经验。


4. 余弦族调度器(Cosine-based Schedulers)

  余弦调度器用余弦函数平滑地衰减学习率——既不是阶梯形的突变,也不是指数形的过快衰减。是现代深度学习训练的主流选择。

4.1 CosineAnnealingLR(余弦退火)

简介:学习率按余弦函数从初始值平滑衰减到最小值。形状像日落——从高点自然渐暗到地平线。

$$ \eta_t = \eta_{\min} + \frac{1}{2}(\eta_{\max} - \eta_{\min})\left(1 + \cos\left(\frac{t}{T}\pi\right)\right) $$

数值示例:$\eta_{\max} = 0.1$,$\eta_{\min} = 0$,$T = 100$:

  • $t = 0$:$\eta = 0 + 0.5 \times 0.1 \times (1 + \cos(0)) = 0.5 \times 0.1 \times 2 = 0.1$
  • $t = 25$:$\eta = 0.5 \times 0.1 \times (1 + \cos(0.25\pi)) = 0.05 \times (1 + 0.707) = 0.0854$
  • $t = 50$:$\eta = 0.05 \times (1 + \cos(0.5\pi)) = 0.05 \times (1 + 0) = 0.05$
  • $t = 75$:$\eta = 0.05 \times (1 + \cos(0.75\pi)) = 0.05 \times (1 - 0.707) = 0.0146$
  • $t = 100$:$\eta = 0.05 \times (1 + \cos(\pi)) = 0.05 \times (1 - 1) = 0$

形状:平滑的余弦曲线,从 $\eta_{\max}$ 降到 $\eta_{\min}$。

CosineAnnealingLR 学习率曲线

类比:日落。学习率像太阳一样,从最高点(正午)平滑地"落"到地平线($\eta_{\min}$)——不是突然关灯,而是自然渐暗。

记忆小贴士:CosineAnnealing = “余弦退火”——像金属退火一样,温度(学习率)按余弦曲线平滑降低。

from torch.optim.lr_scheduler import CosineAnnealingLR

scheduler = CosineAnnealingLR(optimizer, T_max=100, eta_min=0)

适用场景:现代深度学习训练的主流选择。常与 Warmup 组合使用(见 §5)。

4.2 CosineAnnealingWarmRestarts(带热重启的余弦退火)

简介:在余弦退火的基础上,每隔 $T_0$ 个 epoch 重启学习率到初始值,然后重新按余弦衰减。每次重启后,周期长度乘以 $T_{\text{mult}}$。

$$ \eta_t = \eta_{\min} + \frac{1}{2}(\eta_{\max} - \eta_{\min})\left(1 + \cos\left(\frac{t' }{T_i}\pi\right)\right) $$

其中 $t'$ 是当前周期内的步数,$T_i$ 是当前周期的长度。

周期长度变化($T_0 = 10$,$T_{\text{mult}} = 2$):

  • 第 1 个周期:$T_1 = 10$(epoch 0~9)
  • 第 2 个周期:$T_2 = 10 \times 2 = 20$(epoch 10~29)
  • 第 3 个周期:$T_3 = 20 \times 2 = 40$(epoch 30~69)

形状:多个余弦半波,每个半波从 $\eta_{\max}$ 降到 $\eta_{\min}$,然后突然重启。

CosineAnnealingWarmRestarts 学习率曲线

类比:间歇训练。像运动员做间歇训练——冲刺(高学习率)→ 减速(余弦衰减)→ 休息(低学习率)→ 重新冲刺(重启)。

记忆小贴士:WarmRestarts = “热重启"——学习率降到最低后,突然"加热"回最高点。

from torch.optim.lr_scheduler import CosineAnnealingWarmRestarts

scheduler = CosineAnnealingWarmRestarts(optimizer, T_0=10, T_mult=2)

适用场景:需要逃离局部最小值时(重启可以帮助跳出当前盆地)。SGDR(Stochastic Gradient Descent with Warm Restarts)论文的核心策略。

4.3 余弦族对比

调度器形状重启特点
CosineAnnealingLR单个余弦半波平滑衰减,现代主流
CosineAnnealingWarmRestarts多个余弦半波周期性重启,逃离局部最小值

5. 预热策略(Warmup)

  训练初期参数是随机初始化的,梯度方向不可靠。用大学习率更新可能导致训练发散。预热策略在训练初期用很小的学习率"试探”,等梯度方向稳定后再升到目标学习率。

5.1 线性预热(Linear Warmup)

简介:学习率从接近 0 线性增长到目标学习率,通常在前 5~10% 的训练步数内完成。

$$ \eta_t = \eta_{\text{target}} \cdot \frac{t}{T_{\text{warmup}}} \quad (t \leq T_{\text{warmup}}) $$

数值示例:$\eta_{\text{target}} = 10^{-4}$,$T_{\text{warmup}} = 1000$:

  • $t = 0$:$\eta = 0$
  • $t = 250$:$\eta = 10^{-4} \times 250/1000 = 2.5 \times 10^{-5}$
  • $t = 500$:$\eta = 5 \times 10^{-5}$
  • $t = 1000$:$\eta = 10^{-4}$(达到目标)

为什么需要预热:训练初期,随机初始化的参数产生的梯度方向不可靠。如果直接用大学习率,可能一步跨到损失曲面的"悬崖"上。预热相当于"小心试探”——用小步幅走几步,等梯度方向稳定后再大步走。

类比:热车。冬天开车前需要先热车(预热),等发动机(模型参数)稳定后再加速(大学习率)。直接猛踩油门可能伤车(训练发散)。

记忆小贴士:Warmup = “预热”——像运动员热身一样,先小幅度活动,再进入正赛。

5.2 预热 + 余弦退火(现代 LLM 标配)

  现代大语言模型的训练几乎都采用 Warmup + Cosine Annealing 的组合:先线性预热,再余弦衰减。

$$ \eta_t = \begin{cases} \eta_{\text{target}} \cdot \dfrac{t}{T_{\text{warmup}}} & \text{if } t \leq T_{\text{warmup}} \\[8pt] \eta_{\min} + \dfrac{1}{2}(\eta_{\text{target}} - \eta_{\min})\left(1 + \cos\left(\dfrac{t - T_{\text{warmup}}}{T - T_{\text{warmup}}}\pi\right)\right) & \text{if } t > T_{\text{warmup}} \end{cases} $$

形状:先线性上升(预热),再余弦下降(退火)——呈"先升后降"的山峰形。

Warmup + Cosine 学习率曲线

from torch.optim.lr_scheduler import CosineAnnealingLR, LinearLR, SequentialLR

optimizer = optim.AdamW(model.parameters(), lr=1e-4)

warmup = LinearLR(optimizer, start_factor=0.01, total_iters=1000)
cosine = CosineAnnealingLR(optimizer, T_max=9000, eta_min=1e-6)
scheduler = SequentialLR(optimizer, schedulers=[warmup, cosine], milestones=[1000])

for epoch in range(10000):
    train(...)
    scheduler.step()

适用场景:BERT、GPT、LLaMA 等 Transformer 模型预训练的标准方案。


6. 高级调度器(Advanced Schedulers)

  以下调度器在特定场景下有独特优势。

6.1 OneCycleLR(超级收敛)

简介:Leslie Smith 2018 提出的"超级收敛"策略。整个训练过程中学习率呈单峰形状——先从低升到高,再从高降到极低。

$$ \eta_t = \begin{cases} \eta_{\min} + (\eta_{\max} - \eta_{\min}) \cdot \dfrac{t}{T_{\text{up}}} & \text{if } t \leq T_{\text{up}} \\[8pt] \eta_{\max} - (\eta_{\max} - \eta_{\min}) \cdot \dfrac{t - T_{\text{up}}}{T - T_{\text{up}}} & \text{if } t > T_{\text{up}} \end{cases} $$

关键特性:学习率先升后降的同时,动量(momentum)先降后升——学习率高时动量低(探索),学习率低时动量高(精细收敛)。

形状:三角形——先线性上升,再线性下降。

OneCycleLR 学习率曲线

类比:过山车。学习率像过山车一样——先爬升到最高点(最大学习率),再俯冲到最低点(最小学习率)。上升阶段快速探索,下降阶段精细收敛。

记忆小贴士:OneCycle = “一个周期"——整个训练只有一个"先升后降"的周期。Super-convergence 的核心。

from torch.optim.lr_scheduler import OneCycleLR

scheduler = OneCycleLR(optimizer, max_lr=0.01, total_steps=10000)

for epoch in range(num_epochs):
    for batch in dataloader:
        train_step(...)
        scheduler.step()  # 注意:OneCycleLR 每个 step 更新,不是每个 epoch

适用场景:需要快速训练时(fast.ai 推荐)。可以在更少的 epoch 内达到相同精度。

6.2 CyclicLR(循环学习率)

简介:Leslie Smith 2017 提出,学习率在 $[\eta_{\min}, \eta_{\max}]$ 之间周期性振荡

三种模式

  • triangular:三角波形,线性升降
  • triangular2:三角波形,振幅每周期减半
  • exp_range:三角波形,振幅按指数衰减

类比:振荡搜索。像在山谷中来回走动——不是一路下坡,而是故意在一定范围内振荡,以探索更多区域、逃离局部最小值。

记忆小贴士:CyclicLR = “循环“学习率——在高低之间反复振荡。

from torch.optim.lr_scheduler import CyclicLR

scheduler = CyclicLR(optimizer, base_lr=1e-5, max_lr=1e-3, step_size_up=2000, mode='triangular')

适用场景:不确定最优学习率范围时,CyclicLR 可以自动在范围内探索。

6.3 LambdaLR(自定义调度)

简介:用一个 lambda 函数定义任意调度规则——最大的灵活性。

from torch.optim.lr_scheduler import LambdaLR

# 自定义:前 10% warmup,之后线性衰减
def lr_lambda(current_step):
    warmup_steps = 1000
    if current_step < warmup_steps:
        return current_step / warmup_steps
    return max(0.0, 1.0 - (current_step - warmup_steps) / (total_steps - warmup_steps))

scheduler = LambdaLR(optimizer, lr_lambda)

适用场景:标准调度器无法满足需求时的"终极方案”。


7. 调度器选择指南

场景推荐调度器核心原因记忆关键词
CNN 经典训练StepLR简单有效,ResNet 标配阶梯衰减
不确定何时衰减ReduceLROnPlateau自适应,基于验证指标平台期检测
Transformer / LLMWarmup + Cosine现代标配,训练稳定先升后余弦降
快速训练OneCycleLR超级收敛,更少 epoch过山车
探索最优 LRCyclicLR周期性振荡,自动探索循环振荡
自定义需求LambdaLR最大灵活性任意函数
不确定时CosineAnnealingLR平滑有效,万金油余弦退火

8. 调度器演进脉络

flowchart TD
    Fixed["固定学习率"] --> StepLR["StepLR"]
    Fixed --> ExpLR["ExponentialLR"]
    StepLR --> MultiStepLR["MultiStepLR"]
    Fixed --> ReduceLR["ReduceLROnPlateau<br/>2012"]
    Fixed --> Cosine["CosineAnnealingLR<br/>2016"]
    Cosine --> WarmRestarts["CosineWarmRestarts<br/>2017"]
    Fixed --> Cyclic["CyclicLR<br/>2017"]
    Cyclic --> OneCycle["OneCycleLR<br/>2018"]
    Cosine --> WarmupCosine["Warmup + Cosine"]
    Fixed --> Lambda["LambdaLR"]

调度器曲线对比

从上图可以看出,调度器的发展遵循一条清晰的脉络:

  1. 固定规则期:StepLR、ExponentialLR——简单但不灵活
  2. 自适应期:ReduceLROnPlateau——根据验证指标智能衰减
  3. 平滑衰减期:CosineAnnealingLR——余弦曲线平滑衰减,成为主流
  4. 组合策略期:Warmup + Cosine、OneCycleLR——多种策略组合,效果最优

理解了这条演进脉络,就掌握了调度器设计的核心哲学:在衰减的平滑性(避免突变)和自适应性(根据训练状态调整)之间寻找最佳平衡点


延伸阅读:关于 Warmup 为什么对 Transformer 训练至关重要的理论分析(从梯度方差的角度),以及 OneCycleLR 的"超级收敛"现象的数学解释,可参考 大语言模型训练机制全解·第一篇 的相关章节。