想象你驾驶一辆越野车,行驶在通往谷底温泉的蜿蜒山路上——山路的每一个起伏都对应着深度学习中的“损失地形”,谷底的温泉小镇就是我们要找的损失最低点,也就是模型的最优参数。方向盘告诉你往哪个方向走(梯度方向),而右脚踩油门的深度,决定了每一步冲多远。这脚油门的深浅,就是深度学习里大名鼎鼎的“学习率”(Learning Rate)。 油门踩得太狠,车会直接飞出弯道、翻下山崖(损失值爆炸发散);踩得太轻,车只能在坡道上龟速蠕行,天黑了也到不了温泉(收敛极慢)。老司机不会全程死踩一个固定油门,而是根据路段灵活调整:刚出发时路况不熟,先轻踩油门试探(Warmup 预热);途中熟悉了路况便大胆提速;接近目的地时缓缓松开油门,让车平稳滑停(Cosine 退火);而赛车手则有一套“先猛冲、再收油、最后冲刺”的节奏(OneCycle)。这篇文章将带你认识学习率调度的三大主流策略——Cosine 退火、Warmup 预热和 OneCycle,搞懂它们的原理、公式与调参技巧。

岗位关联

学习率调度是深度学习调参中“性价比”极高的手段——不改模型结构、不加训练数据,仅靠调整“油门曲线”就能显著提升收敛速度和最终精度,是每一位 AI 工程师优化训练流程的必修技能。

目录
  1. 可视化演示
  2. 学习率本质:油门决定步幅
  3. 经典调度器:三种朴素开法
  4. 余弦退火:平滑滑行到终点
  5. Warmup预热:冷启动先热车
  6. OneCycle策略:赛车手的冲刺节奏
  7. 策略对比选择:看路况选开法
  8. 工程实践:仪表盘与保养手册
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示梯度下降算法在损失函数曲面上的优化过程,通过调节学习率观察收敛行为,实时追踪损失值和迭代步数。

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

梯度下降中 球沿负梯度方向移动,逐步逼近损失函数最小值 theta_t+1 = theta_t - lr * grad(L) 当前学习率 0.30,迭代 0 步

学习率本质:油门决定步幅

想象你驾驶越野车行驶在通往谷底温泉的山路上。双手握住方向盘,右脚搭在油门上——方向盘负责方向,油门决定每一步冲多远。方向错了可以随时修正,可若一脚油门踩得太猛,车会直接飞出弯道;油门太轻,则只能在坡道上缓慢蠕行。在深度学习训练中,这脚油门的深度就叫做学习率。它是深度学习超参数里最敏感也最核心的一个:几乎所有模型训练的第一步调参,都是在找这个“油门”的合适深度。

训练神经网络的本质,是找到一组参数 $w$ 让损失函数 $L(w)$ 最小。损失函数刻画了模型在训练数据上的错误程度,它像一张起伏的损失地形图,我们随机站在某个高度上;梯度 $\nabla L(w)$ 则像一块路牌,指示哪个方向能最快下坡。沿着下坡方向迈步更新参数,就是梯度下降法:

学习率过大,参数会像猛踩油门的车一样在谷底两侧来回弹跳,甚至越跳越远,损失值不降反升——这就是发散;学习率过小,参数每步只挪动一点点,看似稳定却龟速爬行,往往还没到温泉就耗尽训练预算。学习率调度器(Scheduler)的角色,正是那个懂得随机应变的老司机:根据训练进展动态调整油门,让车又快又稳地抵达目的地。

wt+1=wt−η∇L(wt)\displaystyle w_{t+1} = w_t - \eta \nabla L(w_t)
这是几乎所有深度学习优化算法的心脏:把当前参数减去“学习率乘以梯度”,得到下一步的参数,就像“顺着路牌方向踩一脚油门往前走”。
  1. 当前参数 $w_t$第 $t$ 轮迭代时模型的位置,对应汽车当前所在的坐标点。
    wt∈Rd\displaystyle w_t \in \mathbb{R}^d
  2. 梯度 $\nabla L(w_t)$损失函数在 $w_t$ 处的梯度向量,指向损失增长最快的方向;取负号就是下坡方向,相当于路牌指示。
    ∇L(wt)=(∂L∂w(1),∂L∂w(2),…,∂L∂w(d))\displaystyle \nabla L(w_t) = \left( \frac{\partial L}{\partial w^{(1)}}, \frac{\partial L}{\partial w^{(2)}}, \dots, \frac{\partial L}{\partial w^{(d)}} \right)
  3. 学习率 $\eta$一个正数,代表油门踩多深,控制每一步沿下坡方向走多远。
    η>0\displaystyle \eta > 0
  4. 更新表达式 $w_t - \eta \nabla L(w_t)$沿着负梯度方向前进一小步,得到新位置作为下一轮起点。

经典调度器:三种朴素开法

新手司机拿到车,通常只有三种朴素的油门开法:第一种是一路保持相同速度,不管路况如何都不变;第二种是每开一段路就把速度调低一档;第三种是让速度随时间连续平滑地降低。这三种开法,恰好对应着深度学习里最经典的三种学习率调度策略。它们虽然朴素,却是理解一切高级调度器的起点。

第一种是固定学习率,全程 $\eta_t = \eta_0$,简单却难以两全:设大了后期在谷底震荡,设小了前期爬行太慢。第二种是阶梯式下降,训练每经过 $s$ 步(或 $s$ 个 epoch),学习率乘一次衰减系数 $\gamma$,像分段降档位:

第三种指数衰减与之类似,但每个 step 都乘 $\gamma$,曲线是连续平滑的指数下降 $\eta_t = \eta_0 \gamma^t$。经典调度器的优点是好理解、易实现;缺点是曲线形状突兀(阶梯式一跳一跳),且需要手动决定“何时降、降多少”。就像新手司机只知道分段减速,却不知道应该在哪个弯道前收油——而高级调度器正是为了解决这些“何时收油”的问题而生的。

ηt=η0×γ⌊t/s⌋\displaystyle \eta_t = \eta_0 \times \gamma^{\lfloor t / s \rfloor}
学习率先以较大值快速下山,每走完一段里程就收缩一次油门,后期在小范围内精细调整。
  1. 初始学习率 $\eta_0$出发时的油门设定,通常是经验值如 0.1 或 0.01。
    η0>0\displaystyle \eta_0 > 0
  2. 衰减系数 $\gamma$每次降档后学习率变为原来的比例,常取 0.5 或 0.1,对应“收油一半”还是“收油九成”。
    0<γ<1\displaystyle 0 < \gamma < 1
  3. 下降档位 $\lfloor t / s \rfloor$训练步数 $t$ 除以周期 $s$ 后向下取整,得到当前是第几个下降档位。
    ⌊t/s⌋=max⁡{k∈Z∣k≤t/s}\displaystyle \lfloor t / s \rfloor = \max\{k \in \mathbb{Z} \mid k \leq t / s\}
  4. 整体指数 $\gamma^{\lfloor t / s \rfloor}$档位越高,衰减次数越多,学习率越小,形成台阶状曲线。

余弦退火:平滑滑行到终点

老司机接近谷底温泉时,不会突然猛踩刹车,而是在最后几百米缓缓松开油门,让车依靠惯性平滑滑行到终点。这种“先快后慢、逐渐趋缓”的节奏,对应着深度学习中最受欢迎的调度策略之一——余弦退火(Cosine Annealing)。它让学习率沿一条余弦曲线从最大值平滑滑落到最小值,前期保持大步探索,后期小步精细打磨。

余弦退火的更新公式形如:

余弦退火最巧妙的地方在于“探索与利用”的平衡:前期学习率仍较高,模型大胆探索不同区域;末尾学习率极低,模型在最优解附近精细利用。而热重启(Warm Restart)更进一步——到达谷底后并不结束,而是把学习率重新拉高到 $\eta_{max}$ 再退火一次。这就像到了温泉后听说山那边还有更美的谷,于是重新点火、再次出发寻找更好的解,能有效帮助模型跳出局部最小值。

ηt=ηmin+12(ηmax−ηmin)(1+cos⁡(πtT))\displaystyle \eta_t = \eta_{min} + \frac{1}{2}(\eta_{max} - \eta_{min}) \left( 1 + \cos\left( \frac{\pi t}{T} \right) \right)
其中 $T$ 是总训练步数,$t$ 是当前步。学习率从 $\eta_{max}$ 出发,像余弦曲线一样先缓降、中段加速下降、末尾再次放缓,最终平滑到达 $\eta_{min}$。
  1. 学习率上下界 $\eta_{max}$ 与 $\eta_{min}$油门的最大和最小开度,分别对应训练初期的上限与末期的最低值。
    ηmax>ηmin>0\displaystyle \eta_{max} > \eta_{min} > 0
  2. 训练相位 $\frac{\pi t}{T}$把训练进度 $t/T$ 从 0 到 1 映射到角度 0 到 $\pi$,相当于把整个训练过程“掰”成半圈余弦。
    0≤πtT≤π\displaystyle 0 \leq \frac{\pi t}{T} \leq \pi
  3. 余弦函数 $\cos(\cdot)$在 0 到 $\pi$ 内从 1 单调下降到 -1,决定了学习率只降不升。
    cos⁡(0)=1,cos⁡(π)=−1\displaystyle \cos(0) = 1, \quad \cos(\pi) = -1
  4. 缩放因子 $\frac{1}{2}(1 + \cos(\cdot))$把余弦输出从 [-1, 1] 压缩到 [0, 1],保证学习率恒为正。
    0≤12(1+cos⁡θ)≤1\displaystyle 0 \leq \frac{1}{2}(1 + \cos\theta) \leq 1
  5. 整体公式当 $t=0$ 时等于 $\eta_{max}$,当 $t=T$ 时等于 $\eta_{min}$,中间平滑过渡。

Warmup预热:冷启动先热车

冬天的清晨,汽车在冷车库放了一整夜。如果直接猛踩油门冲上高速,发动机极易受损;老司机会先原地怠速热车,等机油润滑开、水温上来,再平顺提速。深度学习训练也有类似的“冷启动”问题:模型参数刚初始化时是随机的,前几步计算出的梯度噪声大、方向不稳,就像轮胎还没热、路面还没看清,此时如果直接上大学习率,极易被几个“错误路牌”带偏,甚至一步发散。

Warmup 预热策略的做法,是在训练开始的若干步内,让学习率从一个很小的值逐渐增长到预设的最大值。最简单的线性预热公式为:

除线性预热外,还有指数预热(如按 $\eta_t = \eta_{max} \times 0.1^{t/T_{warm}-1}$ 增长)等变体,核心思想一致:给训练一个“热身窗口”,让优化器先积累稳定的梯度方向再提速。如今,Warmup 已经是 Transformer、GPT、BERT 等大模型的标配——因为大模型参数量巨大、批次规模大,初始梯度的方向噪声尤其明显,没有热车直接上高速,翻车几乎是必然的。

ηt=ηmax×tTwarm(t<Twarm)\displaystyle \eta_t = \eta_{max} \times \frac{t}{T_{warm}} \quad (t < T_{warm})
预热步数内,学习率从 0 线性爬升到 $\eta_{max}$,之后切换到退火或固定策略继续训练。
  1. 预热总步数 $T_{warm}$热车所需的里程数,通常占总训练步数的 5% 到 10%。
    Twarm≪T\displaystyle T_{warm} \ll T
  2. 预热进度 $\frac{t}{T_{warm}}$当前步 $t$ 占预热阶段的比例,从 0 线性增长到 1,对应油门从零逐渐踩深。
    0≤tTwarm≤1\displaystyle 0 \leq \frac{t}{T_{warm}} \leq 1
  3. 目标学习率 $\eta_{max}$预热结束时要达到的油门深度,之后由主调度策略接管。
    ηmax=max⁡tηt\displaystyle \eta_{max} = \max_t \eta_t

OneCycle策略:赛车手的冲刺节奏

如果你看过赛车比赛,会发现优秀车手的油门曲线不是一路递减,而是“起步猛踩、中途收油、最后冲刺”的节奏。OneCycle 策略正是把这种赛车哲学带进了深度学习:学习率先线性上升到一个非常高的最大值(预热),再线性下降到极小值附近,最后在训练末尾的小段里反弹一点点,形成一段“最后冲刺”。整个曲线像一个不对称的三角形。

OneCycle 的分段公式可以写成:

OneCycle 最反直觉的亮点是超高学习率:它可以用比平常大 2 到 10 倍的最大学习率训练,却依然稳定收敛,这种现象被称为“超收敛”(Super-convergence)。奥秘在于,Warmup 阶段让模型在低学习率下先找到稳定的梯度方向,随后高学习率快速穿越平坦区域,最后退火精细落点。配合手段上,OneCycle 还建议动量反向调度:学习率上升时动量减小、学习率下降时动量增大,就像“踩油门时略松离合、收油时结合更紧”,进一步稳定训练。许多实验表明,OneCycle 能在相同精度下把训练 epoch 减少近一半,是加速实验迭代的利器。

ηt={ηmin+(ηmax−ηmin)⋅tt1t<t1ηmax−(ηmax−ηmin)⋅t−t1T−t1t≥t1\displaystyle \eta_t = \begin{cases}\eta_{min} + (\eta_{max} - \eta_{min}) \cdot \frac{t}{t_1} & t < t_1 \\\eta_{max} - (\eta_{max} - \eta_{min}) \cdot \frac{t - t_1}{T - t_1} & t \geq t_1\end{cases}
第一阶段(预热段)学习率线性上升,第二阶段(退火段)线性下降,完成“升—降”一次循环,因此得名 OneCycle。
  1. 拐点 $t_1$学习率从升转降的时刻,通常设在总步数的 20% 到 40% 处(如 pct_start=0.3)。
    0<t1<T\displaystyle 0 < t_1 < T
  2. 第一段 $t < t_1$学习率从 $\eta_{min}$ 线性上升到 $\eta_{max}$,对应预热提速阶段。
    ηt=ηmin+(ηmax−ηmin)⋅tt1\displaystyle \eta_t = \eta_{min} + (\eta_{max} - \eta_{min}) \cdot \frac{t}{t_1}
  3. 第二段 $t \geq t_1$学习率从 $\eta_{max}$ 线性回落到 $\eta_{min}$,对应退火滑行阶段。
    ηt=ηmax−(ηmax−ηmin)⋅t−t1T−t1\displaystyle \eta_t = \eta_{max} - (\eta_{max} - \eta_{min}) \cdot \frac{t - t_1}{T - t_1}

策略对比选择:看路况选开法

没有一种油门策略适合所有路段:城市拥堵路要轻踩缓行,高速巡航要匀速稳住,山路弯道要提前收油。学习率调度策略的选择也一样,需要看“路况”——训练数据规模、模型结构、训练时长预算等因素共同决定了哪种策略最合适。

适用场景上:从头训练大规模模型(如大语言模型预训练)时,Warmup 加余弦退火是事实标准;训练时间有限、想快速验证想法时,OneCycle 凭借超收敛能省下近一半迭代;小数据集微调预训练模型时,简单的小学习率或阶梯式下降往往就足够。效果对比上,学术界的普遍经验是:余弦退火整体优于阶梯式下降——阶梯式在每次降档时都会造成损失的小幅反弹,而余弦的平滑衰减避免了这种突兀;Warmup 则在 Transformers、大 batch 训练中几乎是“止血”级别的必要手段。组合策略上,Warmup + Cosine 是最常见黄金组合:Warmup 管起步、Cosine 管收尾;OneCycle 本身就是 Warmup 与退火的集成形态。

衡量不同策略的“总油门消耗”,可以看整个训练过程的平均学习率:

平均学习率相近的两个调度器,最终效果可能天差地别——因为分布形状比平均值更重要。阶梯式把高学习率集中在前期、后期骤降;余弦把下降过程拉得平滑;OneCycle 则把最高学习率集中在中段。理解这些差异后,你就能像老司机一样“看路况选开法”:想要稳健选 Warmup + Cosine,想要极速验证选 OneCycle,想要简单省事选阶梯下降。

ηˉ=1T∑t=1Tηt\displaystyle \bar{\eta} = \frac{1}{T} \sum_{t=1}^{T} \eta_t
固定学习率的 $\bar{\eta}$ 就是其恒定值;余弦退火的 $\bar{\eta}$ 约为最大值的一半;OneCycle 因前期冲得高,平均学习率通常高于普通余弦。
  1. 单步学习率 $\eta_t$第 $t$ 步的油门深度。
    ηt>0\displaystyle \eta_t > 0
  2. 累加求和 $\sum_{t=1}^{T}$把整个训练过程每一步的油门深度全部相加,得到总“油耗”。
    ∑t=1Tηt\displaystyle \sum_{t=1}^{T} \eta_t
  3. 平均系数 $\frac{1}{T}$除以总步数,得到平均每步的学习率,便于不同策略间横向对比。
    1T∑t=1Tηt\displaystyle \frac{1}{T} \sum_{t=1}^{T} \eta_t

工程实践:仪表盘与保养手册

再好的开车计划,也要依赖仪表盘来执行:油表、转速表、水温表实时告诉你车况。深度学习训练的“仪表盘”就是每一轮的损失曲线和学习率曲线——只定策略不看曲线,等于蒙眼开车。工程实践中,调度器的落地有一套成熟流程,这套流程决定了策略能否真正发挥威力。

代码实现上,PyTorch 内置了 CosineAnnealingLR、OneCycleLR、LambdaLR 等调度器,配合 scheduler.step() 在每个 step 或 epoch 调用即可。监控技巧上,务必将每个 step 的学习率与训练损失一起记录并可视化:如果损失在训练中后期持续震荡,说明学习率仍然偏大;如果损失长时间平台期不动,则要考虑学习率过小或已陷入局部极小。调参要点上,先通过“学习率扫描”(Learning Rate Finder)找到最大可行学习率——通常以指数增长的学习率跑几个 batch,观察损失从下降到爆发的拐点,取拐点前一个量级作为 $\eta_{max}$;Warmup 步数按总步数的 5% 到 10% 设定;余弦退火下限一般取 $\eta_{max}$ 的千分之一左右。

调度器并非孤立工作,它总是与优化器配合,最常见的搭档是带动量的 SGD:

工程中的常见错误也值得警惕:一是把 scheduler.step() 放在 epoch 循环而非 step 循环,导致阶梯式下降的“每 $s$ 步”含义被搞混;二是先设好调度器再频繁手动改学习率,两者相互覆盖;三是忽略 Warmup 直接上大学习率导致前期损失爆炸。记住,调度器是“保养手册”里的一整套流程——策略选型、参数设定、代码接入、曲线监控四者缺一不可。把仪表盘看好,把油门曲线调对,你的模型就能像经验丰富的老司机一样,又快又稳地抵达谷底温泉。

vt=μvt−1+ηt∇L(wt),wt+1=wt−vt\displaystyle v_t = \mu v_{t-1} + \eta_t \nabla L(w_t), \quad w_{t+1} = w_t - v_t
动量项 $v_t$ 相当于车的惯性:方向一致时越冲越快,方向突变时起到缓冲作用;OneCycle 正是利用动量与学习率的反向调度来增强这种惯性配合。
  1. 动量项 $v_t$历史梯度的指数滑动累积,相当于汽车保持前进趋势的惯性。
    vt=μvt−1+ηt∇L(wt)\displaystyle v_t = \mu v_{t-1} + \eta_t \nabla L(w_t)
  2. 动量系数 $\mu$控制惯性保留比例,通常取 0.9 或 0.95,数值越大惯性越强。
    μ∈(0,1)\displaystyle \mu \in (0, 1)
  3. 参数更新 $w_{t+1} = w_t - v_t$用累积动量而非单步梯度更新参数,减少震荡。
    wt+1=wt−vt\displaystyle w_{t+1} = w_t - v_t

AI 应用场景

大模型预训练中的 Warmup + Cosine

阶段:模型训练与超参调优

解决的问题:大语言模型参数量达数十亿乃至万亿,使用超大 batch 并行训练时,初始步骤的梯度噪声极大且方向不稳定。传统固定学习率极易让预训练在第一个 epoch 就损失爆炸;Warmup 稳住起步阶段,Cosine 退火则保证后期平滑收敛,避免训练最后的剧烈波动。

具体案例:GPT-3 等大模型的预训练普遍采用“数千步线性 Warmup + Cosine 退火”的组合,单次训练成本高达数百万美元,调度策略直接决定了几十万 GPU 时能否如期收敛。工程师通过记录每个 step 的 loss 曲线,若前 5% 步数内出现损失尖峰,就调低 $\eta_{max}$ 或延长 Warmup。

计算机视觉快速训练中的 OneCycle

阶段:实验迭代与超参搜索

解决的问题:算法工程师在探索新网络结构时,最宝贵的是时间。传统阶梯下降需要 200 个 epoch 才能充分收敛,OneCycle 凭借“先升温后降温”的超收敛特性,能用更大的学习率在更短步数内达到相当精度,让每一轮实验迭代都快一倍。

具体案例:在 ImageNet 上训练 ResNet-50 时,使用 OneCycle(最大学习率 1.6,pct_start=0.3,动量从 0.85 到 0.95 反向调度),仅用 90 个 epoch 就达到了接近 200 个 epoch 阶梯式调度的 Top-1 精度,训练时间几乎减半。

迁移学习微调中的小步快跑

阶段:模型部署前的下游微调

解决的问题:微调预训练模型时,学习率过大会破坏已经学到的通用特征(灾难性遗忘),学习率过小又适配不好下游任务。采用“小 Warmup + 短 Cosine 退火”,既让新任务头在前几步稳定学习,又在下游数据上平滑适配。

具体案例:BERT 文本分类微调时,使用 Warmup 占比 10% + Cosine 退火,从固定学习率 2e-5 改为最大 3e-5 的余弦曲线后,在多个 GLUE 基准任务上平均提升 0.5-1 个点,且训练过程更稳定。

Python 演示

python58 行
import numpy as np  class WarmupCosine:    # Warmup + Cosine scheduler: 先预热后余弦衰减    def __init__(self, lr_max, lr_min, total_steps, warmup_steps):        self.lr_max = lr_max        self.lr_min = lr_min        self.total_steps = total_steps        self.warmup_steps = warmup_steps     def get_lr(self, step):        if step < self.warmup_steps:            return self.lr_max * step / max(1, self.warmup_steps)        t = (step - self.warmup_steps) / max(1, self.total_steps - self.warmup_steps)        return self.lr_min + 0.5 * (self.lr_max - self.lr_min) * (1 + np.cos(np.pi * t))  class OneCycle:    # OneCycle scheduler: 先线性升温再线性降温    def __init__(self, lr_max, lr_min, total_steps, pct_start=0.3):        self.lr_max = lr_max        self.lr_min = lr_min        self.total_steps = total_steps        self.pct_start = pct_start     def get_lr(self, step):        t = step / max(1, self.total_steps)        pct = self.pct_start        if t < pct:            return self.lr_min + (self.lr_max - self.lr_min) * t / pct        return self.lr_max - (self.lr_max - self.lr_min) * (t - pct) / (1 - pct)  def train_quadratic(schedule, init_w, steps):    # 在二维二次函数上做梯度下降, 返回每步损失    w = np.array(init_w, dtype=float)    losses = []    for step in range(steps):        lr = schedule.get_lr(step)        grad = np.array([2 * w[0], 2 * w[1]])        w = w - lr * grad        losses.append(float(w[0] ** 2 + w[1] ** 2))    return losses  if __name__ == "__main__":    total = 200    warmcos = WarmupCosine(lr_max=0.3, lr_min=0.001, total_steps=total, warmup_steps=20)    onecycle = OneCycle(lr_max=0.3, lr_min=0.001, total_steps=total, pct_start=0.3)    l1 = train_quadratic(warmcos, [3.0, -4.0], total)    l2 = train_quadratic(onecycle, [3.0, -4.0], total)    avg1 = sum(warmcos.get_lr(i) for i in range(total)) / total    avg2 = sum(onecycle.get_lr(i) for i in range(total)) / total    print("Warmup+Cosine final loss:", round(l1[-1], 6), "avg lr:", round(avg1, 4))    print("OneCycle final loss:", round(l2[-1], 6), "avg lr:", round(avg2, 4))    for i in [0, 10, 20, 60, 100, 199]:        print("step", i, "lr:", round(warmcos.get_lr(i), 4), "/", round(onecycle.get_lr(i), 4))
目的:用 NumPy 从零实现 Warmup+Cosine 与 OneCycle 两种调度器,并在二维二次函数上做梯度下降,直观对比两种“油门曲线”的收敛轨迹与每步学习率。
  1. class WarmupCosine:定义预热加余弦退火调度器,前 warmup_steps 步线性升温,之后沿余弦曲线衰减。
  2. return self.lr_max * step / max(1, self.warmup_steps)预热阶段核心公式:学习率随步数线性爬升,除以 max(1,...) 防止除零。
  3. 0.5 * (self.lr_max - self.lr_min) * (1 + np.cos(np.pi * t))余弦退火主体:把余弦输出从 [-1,1] 压到 [0,1] 再缩放,实现平滑下降。
  4. class OneCycle:定义先升后降的三角调度器,用 pct_start 控制拐点位置。
  5. if t < pct: return self.lr_min + ...OneCycle 升温段:学习率线性升到最大值。
  6. return self.lr_max - ... * (t - pct) / (1 - pct)OneCycle 降温段:从最大值线性回落到最小值。
  7. w = w - lr * grad梯度下降更新核心:沿负梯度方向迈出“学习率 × 梯度”的一步。
  8. train_quadratic(warmcos, [3.0, -4.0], total)用同一初始点分别跑两种调度器,公平对比收敛效果。

常见误区

误区很多初学者在经历第一次损失发散后,会把学习率一路调小,心想“1e-7 总该稳了吧?损失肯定一直降”。结果训练了好几个小时,损失几乎纹丝不动。

正解学习率过小虽然不会发散,但会陷入“龟速蠕行”——收敛极慢,且容易困在局部极值点或平坦区域出不来。就像油门踩到几乎不动,车不会翻,但天黑也到不了温泉。正确做法是在稳定前提下尽量用大的学习率,或用学习率扫描(LR Finder)找到“即将发散前的拐点”,取拐点附近的值。

为何常见:新手先经历了“大学习率发散”的创伤,产生“越小越安全”的错误归纳,忽略了过小学习率同样致命的“慢”。

误区很多人看到 BERT、GPT 都在用 Warmup,就以为“Warmup 是大模型专属,我的小 ResNet 直接上大学习率没问题”。

正解Warmup 解决的是训练初期梯度噪声大、方向不稳定的问题,并不是大模型的专利。大 batch、深层网络、敏感数据分布、甚至某些初始化较差的场景,小模型同样会因初期梯度方向混乱而翻车。实践中,小模型加 5% 步数的预热也往往能提高稳定性和最终精度。

为何常见:因为 Warmup 常与 Transformer 大模型一起出现在论文里,曝光度极高,容易让人误以为它与超大参数量强绑定。

误区有人看到 OneCycle 的最终形态也是学习率下降,就说“这不就是先设个大学习率再降下来嘛,和余弦退火差不多”。

正解OneCycle 的核心是先升后降的三角形:先预热线性上升到最大值,再退火线性下降到最小值,而且最大学习率可以比平常大 2-10 倍,同时还要配合动量反向调度(学习率升时动量降、降时动量升)。它本身就包含了 Warmup 和退火两种机制,并不是单纯的下降曲线。

为何常见:只盯着曲线“后半段在降”这个表面现象,忽略了前面的上升段和超收敛机理,导致把 OneCycle 简单等同于普通退火。

误区有人觉得“余弦曲线开头本来就平缓,说明学习率一开始就不大,应该不需要预热了吧?”

正解余弦曲线在开头下降确实缓慢,但起始值仍然是高学习率(比如 0.1),对随机初始化、梯度噪声极大的模型来说,这一步踩下去照样可能直接飞出山路。Warmup + Cosine 是实践中的黄金组合:Warmup 管起步,Cosine 管收尾,两者职责互补。

为何常见:把“曲线平缓”误当成了“学习率起点低”,实际上余弦起点的学习率是全程最大值附近,只是变化率小而已。

误区不少工程师以为“调度器就是训练收尾时微调一下学习率,前面用固定学习率跑没有差别”。

正解调度器从第一步就在生效:Warmup 决定起步是否平稳,中段的衰减节奏决定模型能否跳出局部极小、最终落在哪里。同样的总步数和初始学习率,换一套调度曲线最终精度可以差出好几个点——它影响的是整个训练轨迹,而不是最后几轮。

为何常见:“调度”这个词听起来像后期的调整动作,让人忽略了学习率曲线每一轮都在改变每一步的步长。

面试题

Q1. 训练时学习率设置过大,最可能导致什么后果?

单选题 · easy

  • A.模型参数全部归零
  • B.损失持续震荡发散← 正确答案
  • C.训练数据加载变慢
  • D.模型收敛速度变快

答案:B。学习率过大会让参数在最优解附近来回跨越,步长超过“谷壁”宽度时就跳得越来越远,损失不降反升甚至发散。参数归零、数据加载变慢均与学习率无关,而“收敛变快”只在学习率适度增大时成立,过大反而发散。

Q2. 阶梯式学习率调度的核心做法是?

单选题 · easy

  • A.每个 batch 都改变学习率
  • B.每隔固定轮数衰减一次← 正确答案
  • C.学习率随损失自动增减
  • D.全程保持学习率不变

答案:B。阶梯式下降是每训练 $s$ 步后把学习率乘以衰减系数 $\gamma$,像“分段降档”。每个 batch 都改变是指数衰减;随损失自动增减是自适应方法;全程不变则是固定学习率。

Q3. Warmup 预热阶段通常采用的学习率变化方式是?

单选题 · mid

  • A.从较大的值迅速降低
  • B.从小值线性增长到大值← 正确答案
  • C.在大值附近来回震荡
  • D.保持随机变化不规律

答案:B。Warmup 的核心是“冷启动热车”——学习率从一个很小的值逐渐爬升到预设最大值(线性或指数),避免初期梯度噪声大时步子迈太大。选项中只有“从小值线性增长到大值”符合预热语义。

Q4. 关于余弦退火的衰减规律,下列说法正确的是?

单选题 · mid

  • A.全程匀速直线下降
  • B.一开始就急剧下降到底
  • C.两端平缓中间陡峭下降← 正确答案
  • D.先缓慢上升后逐渐回落

答案:C。余弦函数在 0 附近变化率小、在 $\pi/2$ 附近变化率最大,映射到学习率曲线上就是“开始缓、中段陡、末尾又缓”的 S 形平滑下降。它只降不升,并非直线也不是先升后降。

Q5. 以下哪些属于经典学习率调度策略?

多选题 · mid

  • A.固定学习率← 正确答案
  • B.阶梯式下降← 正确答案
  • C.指数衰减← 正确答案
  • D.批量归一化

答案:ABC。固定学习率、阶梯式下降、指数衰减是三种经典调度器,分别对应“匀速开、分段降档、连续收油”。批量归一化是网络结构技术,与学习率调度无关。

Q6. OneCycle 策略中学习率随训练进程的变化形态是?

单选题 · mid

  • A.单调线性下降
  • B.先升后降的三角← 正确答案
  • C.先降后升的倒三角
  • D.恒定为最大值的一半

答案:B。OneCycle 由预热阶段和退火阶段拼接而成:先线性上升到最大值,再线性下降到最小值,整体呈三角形。它不同于单调下降,也不是倒三角或恒定值。

Q7. OneCycle 策略常搭配哪些配套手段?

多选题 · hard

  • A.动量与学习率反向调度← 正确答案
  • B.学习率上升阶段动量增大
  • C.学习率下降阶段动量增大← 正确答案
  • D.全程保持动量恒定不变

答案:AC。OneCycle 建议动量与学习率反向调度:学习率上升时动量减小、下降时动量增大,像“踩油门时松离合、收油时结合更紧”。B 说反了,D 否定了反向调度这一关键配合。

Q8. 超收敛(Super-convergence)现象的核心表现是?

单选题 · extreme

  • A.超高学习率大幅缩短训练步数← 正确答案
  • B.阶梯衰减让损失降到更低
  • C.余弦重启提高最终测试精度
  • D.增加动量让收敛更加稳定

答案:A。超收敛指 OneCycle 用比平常大 2-10 倍的最大学习率,在更少的迭代步数内达到相当甚至更高的精度,核心是“省步数”。阶梯衰减、余弦重启、动量稳定都不是超收敛的关键表现。

Q9. 大模型预训练普遍使用 Warmup 加余弦退火,主要原因有?

多选题 · mid

  • A.大批次下初期梯度噪声大← 正确答案
  • B.预热避免早期训练发散← 正确答案
  • C.余弦退火平滑收尾更稳定← 正确答案
  • D.该组合可显著减少模型参数量

答案:ABC。大模型用超大 batch,初始梯度方向噪声大,Warmup 用低学习率积累稳定方向避免发散;后期余弦退火平滑收尾避免波动。D 完全无关——调度器不改变参数量。

Q10. 训练中发现损失从第 10 轮开始反复震荡不下降,最可能的原因是?

单选题 · easy

  • A.学习率设置过大← 正确答案
  • B.验证集样本太少
  • C.模型层数太少
  • D.训练数据量不足

答案:A。训练损失反复震荡不降是学习率过大的典型信号——参数在谷底两侧来回跨越。验证集大小、模型层数、数据量通常不会只造成“震荡不降”这种特定表现。