想象你开着一辆性能不错的跑车,沿着一条盘山公路从山顶出发,目标是抵达海拔最低的山谷。山路有急弯、有缓坡,你的每一次油门踩踏,都决定了车速和方向。如果一上来就把油门踩到底,车会冲出赛道;如果始终只踩一点点,车会像蜗牛一样慢慢爬,天黑了也到不了谷底。老司机是怎么做的呢?刚发动时先轻踩油门让车平稳动起来——这叫“预热”;等快要接近谷底时再逐渐收油,让车稳稳地停在最低点——这叫“衰减”。机器学习训练中的学习率(learning rate),就是这脚“油门”;而学习率预热与衰减,就是老司机那套“先小后大、再逐渐收油”的节奏感。本文就用这条下山路,带你彻底搞懂学习率预热与衰减的原理、公式与实战用法。

岗位关联

学习率调度是深度学习调参的“基本功”。掌握预热与衰减的节奏设计,不仅能救活一个训练发散的模型,还能帮你把精度从“够用”推上“顶尖”,是大模型与 CV 从业者的必备技能。

目录
  1. 可视化演示
  2. 学习率基础
  3. 学习率过大
  4. 学习率过小
  5. 预热机制
  6. 衰减策略
  7. 调参组合
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

展示线性预热与余弦预热策略对比、Cosine/阶梯/指数三种衰减曲线对比、以及Warmup+Cosine组合调度完整曲线三种交互模式。

本节配有交互动效(进入视口后自动加载,约 3KB)。

预热策略对比 线性预热:0→target均匀增长 余弦预热:S形平滑过渡更稳定 lr_linear = target * step / warmup_steps 预热避免初期梯度方向混乱导致发散

学习率基础

下山路上,方向盘告诉你往哪转,油门告诉你走多重。在机器学习里,这两个角色分别由“梯度”和“学习率”扮演。模型的目标是从一个随机的初始位置出发,沿着损失函数这座“山”一路向下,找到谷底(全局最优)。方向怎么定?用梯度——它指向海拔升高最快的方向;我们偏偏要往它的反方向走,也就是下降最快的方向。

那么每一步应该走多远?这就是学习率 η 的职责。学习率是深度学习里最核心的超参数之一,它决定了参数每次更新的步幅:步子太大容易扯着跨,步子太小又迟迟走不到终点。下面这条更新公式,就是模型下山的“驾驶动作”:

公式含义:新参数=旧参数−学习率×当前梯度。公式含义:新参数 = 旧参数 − 学习率 × 当前梯度。

明白了方向和步长的关系后,你会恍然大悟:同样是下山,怎么踩油门直接决定了能不能活着到山脚。接下来我们看看“踩太大”和“踩太小”分别会经历什么。

θt+1=θt−η∇L(θt)\theta_{t+1} = \theta_t - \eta \nabla L(\theta_t)
  1. 当前参数θ_t 表示第 t 轮迭代时模型的参数,相当于车辆当前所在的位置。
    θt\theta_t
  2. 梯度方向∇L(θ_t) 是在当前位置计算出的梯度,指向损失上升最快的方向。
    ∇L(θt)\nabla L(\theta_t)
  3. 学习率η 是学习率,控制着沿梯度反方向移动的距离,就是油门大小。
    η\eta
  4. 更新一步参数沿着梯度的反方向走一步到达 θ_{t+1},这就是一次梯度下降。
    θt+1=θt−η∇L(θt)\theta_{t+1} = \theta_t - \eta \nabla L(\theta_t)

学习率过大

新手司机最容易犯的错,就是一上车就把油门踩到底。山路弯道很急,车速过快根本拐不过来,车会冲出路面,甚至翻下悬崖。训练模型也一样,学习率设得太大,参数每次迈的步子过长,会直接“跨过”最低点,冲到山坡的另一侧;下一次更新又冲回来,于是损失在高处来回弹跳,像极了新手司机在弯道里的“蛇形走位”。这种振荡不仅让损失无法下降,严重时数值还会越来越大,最终溢出成无穷大——俗称“发散”或“梯度爆炸”。

用最简单的二次函数 f(ω) = ½ a ω² 来感受一下。它的最优点是 ω* = 0,梯度是 aω,代入梯度下降更新式,可以得到:

公式说明:每次更新相当于把位置乘上系数(1−ηa),这个系数的绝对值决定了参数是收敛还是发散。公式说明:每次更新相当于把位置乘上系数 (1 − ηa),这个系数的绝对值决定了参数是收敛还是发散。

学习率过大的本质,是每一次更新都“刹不住车”。想避免翻车,接下来要认识另一种极端——油门踩得太小又会怎样?

ωt+1=ωt−ηaωt=(1−ηa) ωt\omega_{t+1} = \omega_t - \eta a \omega_t = (1 - \eta a)\,\omega_t
  1. 目标函数f(ω) = ½ a ω² 是一个开口向上的二次曲线,谷底在 ω = 0。
    f(ω)=12aω2f(\omega) = \frac{1}{2} a \omega^2
  2. 计算梯度对 ω 求导,得到梯度 aω,越远离谷底梯度越陡。
    ∇f(ω)=aω\nabla f(\omega) = a \omega
  3. 代入更新把梯度代入梯度下降更新式,化简后得到系数 (1 − ηa)。
    ωt+1=ωt−ηaωt=(1−ηa)ωt\omega_{t+1} = \omega_t - \eta a \omega_t = (1 - \eta a)\omega_t
  4. 收敛条件若 |1 − ηa| < 1,参数逐渐逼近 0;若 |1 − ηa| > 1,即 η > 2/a,参数将离谷底越来越远,训练直接发散。
    ∣1−ηa∣>1  ⟺  η>2a|1 - \eta a| > 1 \iff \eta > \frac{2}{a}

学习率过小

如果说大学习率是莽撞的“地板油”,那么小学习率就是“舍不得踩油门”。车辆确实在前进,但每一步只挪动几厘米;前面有个较大的缓坡还好,要是路上有个不起眼的小坑(局部最优),车就这么慢慢滑进去,然后卡在坑里,再也翻不过去。训练的典型表现是:损失曲线下降得非常慢,肉眼几乎看不到变化,训练好几天精度还是上不去。

回到刚才的二次函数,当学习率很小(ηa ≪ 1)时,更新系数 1 − ηa 非常接近 1,参数每轮只缩小一点点。迭代 t 轮后的参数为:

公式说明:参数按(1−ηa)的指数次方衰减,越小的学习率衰减越慢。公式说明:参数按 (1 − ηa) 的指数次方衰减,越小的学习率衰减越慢。

小学习率虽然安全,却可能让你在“半山腰的洼地”里耗到天荒地老。那有没有办法既能开得快、又能刹得住?预热机制正是为此而生。

ωt=(1−ηa)t ω0\omega_t = (1 - \eta a)^t \,\omega_0
  1. 递推展开把上一节得到的更新式反复套用 t 次,参数变为初始值乘以系数 t 次方。
    ωt=(1−ηa)tω0\omega_t = (1 - \eta a)^t \omega_0
  2. 衰减速率由于 0 < 1 − ηa < 1,系数随时间指数衰减,衰减快慢由 ηa 决定。
    1−ηa<11 - \eta a < 1
  3. 慢的代价η 越小,1 − ηa 越接近 1,需要约 1/(ηa) 步才让位置明显缩小,收敛极其缓慢。
    t≈1ηat \approx \frac{1}{\eta a}

预热机制

冬天的早晨,汽车发动机温度很低,机油还没完全润滑到位。这时候你要是一脚地板油冲出去,发动机轻则抖动,重则拉缸报废。所以老司机会先原地热车,或者用怠速慢慢滑一段,等发动机状态稳定了再加速。深度学习里的“预热”(warmup)就是这套逻辑:训练前期的参数还是“冷”的——初始参数随机、优化器的动量统计量还没累积、梯度方差大得吓人。如果一开始就用大学习率猛冲,模型不“拉缸”才怪。

于是工程师们设计了预热策略:先用一个很小的学习率起步,让模型“热热身”;然后在几百上千步内把学习率逐渐提升到目标最大值。最常用的是线性预热,公式如下:

公式说明:第t步的学习率等于目标学习率乘以热身进度t/Twarmup。公式说明:第 t 步的学习率等于目标学习率乘以热身进度 t / T_warmup。

除了线性预热,还有指数预热(如按 1/t 的平方根缩放)等变体。大模型训练动辄上千亿参数、batch size 巨大,初始梯度的噪声格外大,几乎全靠预热来避免前几步就把优化器“带偏”。一句话:预热不是炫技,而是大模型能平稳启动的保护机制。

ηt=ηmax⁡×tTwarmup,0≤t≤Twarmup\eta_t = \eta_{\max} \times \frac{t}{T_{\text{warmup}}}, \qquad 0 \le t \le T_{\text{warmup}}
  1. 目标学习率η_max 是预设的最大学习率,也就是热身后要达到的正常训练步长。
    ηmax⁡\eta_{\max}
  2. 预热进度t 是当前迭代步数,T_warmup 是预热总步数,两者相除就是热身的完成比例。
    tTwarmup\frac{t}{T_{\text{warmup}}}
  3. 线性上升预热阶段学习率从 0 附近线性增长,到 t = T_warmup 时刚好达到 η_max。
    ηt=ηmax⁡×tTwarmup\eta_t = \eta_{\max} \times \frac{t}{T_{\text{warmup}}}

衰减策略

下山路后半程的风景已经很熟悉:谷底就在眼前,路面起伏越来越小。这时候你若还踩着大油门,车会在谷底两边来回经过,永远停不下来。所以老司机会逐渐收油,让车以越来越小的速度接近最低点,最终稳稳停住。这正是“学习率衰减”:训练后期把学习率降下来,让参数在最优解附近精细地“微调落位”。

常见衰减策略有三种。阶梯衰减(step decay)像踩点刹车:每隔固定轮数,学习率乘以一个小于 1 的因子(如每 30 轮乘以 0.1),形成阶梯状下降;指数衰减(exponential decay)是连续刹车:学习率每轮都乘一个略小于 1 的系数,平滑但降得偏快;余弦衰减(cosine decay)则像“滑动收油”:按余弦曲线从高到低平滑过渡,在训练前期下降较慢、后期加速逼近最小值。余弦衰减的公式是:

公式说明:学习率从ηmax沿余弦曲线平滑下降到ηmin,t为当前步数,T为总步数。公式说明:学习率从 η_max 沿余弦曲线平滑下降到 η_min,t 为当前步数,T 为总步数。

三种衰减各有适用场景:阶梯衰减实现简单、易观察分阶段表现,常用于图像分类微调;余弦衰减全程平滑、对算力友好,是大模型预训练的主流选择;指数衰减适合需要快速收敛的轻量任务。

ηt=ηmin⁡+12 (ηmax⁡−ηmin⁡)(1+cos⁡πtT)\eta_t = \eta_{\min} + \frac{1}{2}\,(\eta_{\max} - \eta_{\min})\left(1 + \cos \frac{\pi t}{T}\right)
  1. 缩放到 0~1中间项 ½(1 + cos(πt/T)) 在 t=0 时为 1、t=T 时为 0,把总衰减幅度映射到区间 [0,1]。
    12(1+cos⁡πtT)\frac{1}{2}\left(1 + \cos \frac{\pi t}{T}\right)
  2. 学习率区间η_max − η_min 是衰减的总跨度。若 η_min = 0,学习率最终归零。
    ηmax⁡−ηmin⁡\eta_{\max} - \eta_{\min}
  3. 叠加与平移把缩放值乘上跨度,再加上最小值 η_min,就得到当前步的学习率。
    ηt=ηmin⁡+(ηmax⁡−ηmin⁡)⋅12(1+cos⁡πtT)\eta_t = \eta_{\min} + (\eta_{\max} - \eta_{\min}) \cdot \frac{1}{2}\left(1 + \cos \frac{\pi t}{T}\right)

调参组合

把整条下山路串起来,老司机的制胜法则是:热车 → 加速 → 匀速 → 收油 → 定点停车。对应到学习率调度,就是“预热 + 衰减”组合拳——先预热到最大学习率,再用衰减策略平滑收尾。这也是目前大模型和许多视觉模型训练的事实标准,常被称为 warmup + cosine schedule。

完整的分段调度公式可以写成一个分段函数:

公式说明:前Tw步线性预热到峰值,之后进入余弦衰减直到训练结束。公式说明:前 T_w 步线性预热到峰值,之后进入余弦衰减直到训练结束。

调参上有几条经验法则值得记住:预热步数通常取总步数的 1%~10%(大模型建议 1%~2%);衰减终点 η_min 一般设为 η_max 的 1/10 或 1/100,不必硬降到 0;batch size 增大时,学习率上限也应成比例调大。选择一个支持 warmup 的调度器(如 PyTorch 的 get_cosine_schedule_with_warmup),你的训练稳定性会立刻提升一个档次。

ηt={ηmax⁡⋅tTwt<Twηmin⁡+12(ηmax⁡−ηmin⁡)(1+cos⁡π(t−Tw)T−Tw)t≥Tw\eta_t = \begin{cases}\eta_{\max} \cdot \dfrac{t}{T_w} & t < T_w \\[8pt]\eta_{\min} + \dfrac{1}{2}(\eta_{\max} - \eta_{\min})\left(1 + \cos \dfrac{\pi(t - T_w)}{T - T_w}\right) & t \ge T_w\end{cases}
  1. 预热分段当 t < T_w 时,学习率按线性预热公式从 0 升到 η_max。
    ηmax⁡⋅tTw\eta_{\max} \cdot \frac{t}{T_w}
  2. 衰减分段当 t ≥ T_w 时,以预热终点为起点,剩余步数作为余弦周期,平滑下降。
    cos⁡π(t−Tw)T−Tw\cos \frac{\pi(t - T_w)}{T - T_w}
  3. 拼接连续两个分段在 t = T_w 处学习率均为 η_max,曲线连续无跳变。
    lim⁡t→Tw−ηt=lim⁡t→Tw+ηt=ηmax⁡\lim_{t \to T_w^-} \eta_t = \lim_{t \to T_w^+} \eta_t = \eta_{\max}

AI 应用场景

大语言模型预训练

阶段:模型预训练(Pre-training)

解决的问题:LLM 动辄千亿参数,batch size 巨大,初始梯度噪声极强。若全程固定学习率,前几步就可能让模型输出变成 NaN。预热先稳住早期参数,余弦衰减保证后期收敛到低损失区。

具体案例:LLaMA 系列预训练采用 warmup + cosine 调度,预热 2000 步、峰值学习率 3e-4,最终在万亿 token 上稳定训练并显著提升下游任务表现。

图像分类模型微调

阶段:迁移学习与微调(Fine-tuning)

解决的问题:预训练模型权重已经处于较好的局部最优附近,微调用大学习率容易把学到的特征“冲毁”。先用小学习率预热、再逐步衰减,可以在保留原有特征能力的同时适配新数据集。

具体案例:用 ResNet-50 微调医学影像分类任务时,设置 warmup 为 5 个 epoch、初始学习率 1e-3 并阶梯衰减,比固定学习率的方案 Top-1 准确率提升约 3%。

语音识别模型训练

阶段:模型训练与收敛调优

解决的问题:语音序列输入长度不一,梯度波动大,固定学习率导致 loss 曲线反复震荡。预热加余弦衰减让模型在训练后期做“精细落位”,词错误率更低。

具体案例:某语音识别团队训练 Conformer 模型时采用 2500 步预热 + 余弦衰减,训练 200 个 epoch 后 WER 相对下降 8%,且训练过程无一次发散。

Python 演示

python28 行
import math def cosine_warmup_lr(step, total_steps, warmup_steps, lr_max, lr_min=1e-6):    """warmup + cosine 调度:前 warmup_steps 步线性升到 lr_max,    之后余弦衰减到 lr_min。"""    if step < warmup_steps:        return lr_max * (step + 1) / warmup_steps    t = (step - warmup_steps) / max(1, total_steps - warmup_steps)    return lr_min + 0.5 * (lr_max - lr_min) * (1 + math.cos(math.pi * t)) def train():    x = 2.0    total_steps = 200    warmup_steps = 20    lr_max = 0.5     for step in range(total_steps):        lr = cosine_warmup_lr(step, total_steps, warmup_steps, lr_max)        grad = 4 * x           # 损失函数 f(x)=2x^2 的梯度        x -= lr * grad         # 沿梯度反方向更新        if step % 20 == 0:            loss = 2 * x * x            print(f"step {step:3d}  lr {lr:.4f}  x {x:+.4f}  loss {loss:.6f}")     print(f"final x = {x:.6f}") if __name__ == "__main__":    train()
目的:演示学习率预热 + 余弦衰减的完整调度逻辑,直观看到学习率先升后降、参数与损失同步收敛。
  1. def cosine_warmup_lr(...)定义调度函数:预热阶段线性上升,预热结束后余弦下降。
  2. if step < warmup_steps:预热分支:学习率从 lr_max/warmup_steps 起步,随 step 线性逼近 lr_max。
  3. lr_min + 0.5 * (lr_max - lr_min) * (1 + math.cos(...))余弦衰减核心公式。
  4. grad = 4 * x对 f(x)=2x² 求导得 4x,方向是“上山方向”。
  5. x -= lr * grad参数沿梯度的反方向更新一步,等价于 θ ← θ − η∇L。
  6. if step % 20 == 0:每 20 步打印一次,观察学习率变化与收敛轨迹。

常见误区

误区既然小步慢慢走太慢,那把学习率调大一点,模型岂不是能飞快收敛?反正有衰减兜底。

正解学习率大到一定程度,参数会迈过最优点来回振荡,甚至直接发散,训练直接失效。预热和衰减能救一部分,但峰值学习率本身必须处在安全区间内,并非越大越好。

为何常见:人们容易把“步长大”类比成“效率高”,忽略了目标函数非凸且地形复杂,步子太大更容易翻车。

误区小模型训练加不加预热都差不多,所以预热就是可有可无的调参花招。

正解对大模型和超大 batch 训练,初始梯度方差极大、优化器统计量尚未建立,缺少预热极易在第一步就走入损失峭壁甚至 NaN。预热已是大模型训练的“刚需”,而非可选优化。

为何常见:小模型训练时间短、对初始波动不敏感,让人误以为它没有用,但规模放大后问题就暴露了。

误区预热既然是热车,那就要大脚轰油门让发动机快速热起来——先把学习率拉高再降下来。

正解预热的本质是“先小后大”,从小学习率平滑升到目标值。如果一开始就用大学习率,恰恰违背了避免早期振荡的初衷。

为何常见:把“热车”误当成“加速”,陷入了望文生义。真实的热车恰恰是低速让机油润滑开。

误区既然是逐渐收油,那就收到完全松开油门,让学习率最后变成 0。

正解学习率降为 0 会让参数在训练末尾完全停止更新,一旦错过最优点就再难纠正。实践中一般衰减到峰值的 1/10~1/100,保留一点微调能力。

为何常见:直觉上“越小越精细”,却忽略了过小的学习率会让后期模型缺乏“纠错余地”。

误区要么用预热,要么用衰减,组合起来会让训练变得复杂还容易出错。

正解预热负责起步稳定,衰减负责后期精细收敛,二者负责的阶段不同、互不冲突。现代框架里一条 get_cosine_schedule_with_warmup 就能同时搞定,已成为主流配置。

为何常见:把两个概念看成竞争方案,实际上它们是“前半程”和“后半程”的接力关系。

面试题

Q1. 学习率设置过大时,训练最可能出现的现象是?

单选题 · easy

  • A.损失快速降为零
  • B.损失振荡甚至发散← 正确答案
  • C.损失平稳缓慢下降
  • D.训练速度显著加快

答案:B。学习率过大,参数每次更新步长过长,会跨过最优点来回振荡,严重时数值溢出发散。损失不会为零,更不会平稳,所谓“加快”只是虚假的表象。就像油门踩到底,车直接冲出赛道。

Q2. 预热(warmup)阶段学习率随时间的变化趋势是?

单选题 · easy

  • A.从大到小逐渐降低
  • B.保持恒定不变
  • C.从小到大逐渐升高← 正确答案
  • D.随机波动震荡

答案:C。预热的目标是先小后大:用小的学习率起步,再逐步升到目标值,避免早期梯度方差大导致参数振荡。选项 A 描述的是衰减,B 是固定学习率,D 是无调度。

Q3. 在余弦衰减公式中,当训练步数 t = T 时学习率约等于?

单选题 · mid

  • A.η_max
  • B.η_min← 正确答案
  • C.两者平均值
  • D.负值溢出

答案:B。余弦衰减中 cos(πt/T),当 t=T 时 cos(π) = −1,公式变为 η_min + ½(η_max − η_min)(1−1) = η_min,学习率恰为最小值。

Q4. 下列哪种方法不属于学习率衰减策略?

单选题 · mid

  • A.阶梯衰减
  • B.余弦衰减
  • C.动量加速← 正确答案
  • D.指数衰减

答案:C。阶梯、余弦、指数衰减都改变学习率本身;动量加速是通过累积历史梯度来稳定更新方向的独立技巧,不改变学习率取值。

Q5. 大模型预训练普遍采用 warmup 的原因包括?

多选题 · mid

  • A.稳定早期大幅波动的梯度← 正确答案
  • B.让优化器动量估计逐步准确← 正确答案
  • C.减少显卡显存占用
  • D.避免初始几步跨入损失剧增区域← 正确答案

答案:ABD。预热用小学习率起步,能压住初始梯度噪声、让 Adam 等优化器的动量统计量慢慢建立,防止开头就冲进损失峭壁。显存占用由 batch size 和模型结构决定,与 warmup 无关。

Q6. 阶梯衰减每个阶段学习率的变化方式通常是?

单选题 · hard

  • A.每步连续变小
  • B.每隔固定步数乘以一个小于 1 的因子← 正确答案
  • C.周期性增大再减小
  • D.全程保持恒定

答案:B。阶梯衰减像“踩点刹车”:每隔固定 epoch 数,学习率乘一次因子(如 0.1),曲线呈台阶状。每步连续变小是指数衰减,周期增减是循环调度。

Q7. 学习率过小时训练最可能遇到的问题是?

单选题 · easy

  • A.损失发散到无穷大
  • B.收敛极其缓慢甚至停滞← 正确答案
  • C.梯度自动消失后恢复
  • D.参数被直接置为零

答案:B。学习率太小,参数每次只挪动一点点,收敛速度极慢,还容易卡在局部最优附近。发散是大学习率的典型表现,参数也不会因为小学习率而归零。

Q8. 在实际训练中把预热和衰减结合使用时,正确的做法有?

多选题 · mid

  • A.预热阶段学习率从极小值线性升到目标峰值← 正确答案
  • B.预热结束后立刻把学习率设为 0
  • C.衰减阶段学习率平滑下降至较小值← 正确答案
  • D.整个训练过程学习率始终保持不变

答案:AC。正确组合是预热从 0 附近升到峰值,预热结束后进入衰减阶段平滑收尾。B 设为 0 会让模型失去后期微调能力,D 则是完全没用调度,二者都与组合训练的直觉相悖。

Q9. 余弦衰减在训练初期(t 接近 0)学习率下降的速率是?

单选题 · hard

  • A.接近 0,几乎不变← 正确答案
  • B.最快,直线下跌
  • C.恒定匀速下降
  • D.先降后升

答案:A。cos 在 0 处的切线导数为 0,所以余弦衰减初期下降极其缓慢,越到后期下降越快,给模型更多时间在收敛早期探索。它不是直线,也没有回升过程。

Q10. 设总步数 T、预热步数 W,关于 warmup 阶段学习率的说法正确的有?

多选题 · extreme

  • A.线性预热时第 t 步学习率为 lr_max × t / W← 正确答案
  • B.在 t = W 时学习率达到峰值 lr_max← 正确答案
  • C.预热结束后学习率会突然降到极小值
  • D.预热步数一般占总步数的 1% 到 10%← 正确答案

答案:ABD。线性预热公式从 0 升到峰值,t=W 时恰好等于 lr_max,曲线连续。C 是断崖式跳变,违背预热平滑过渡的初衷。预热占比通常取 1%~10%,大模型常取 1%~2%。