学习率调度#
TL;DR#
本页内容7 个部分
1. 背景与问题#
固定学习率让每次更新共用同一个步长系数,但训练初期、持续拟合与预算末期的需求可能不同。Warmup 先限制步幅,再提高到峰值;衰减则逐渐缩小更新,给后期调整更小的尺度。
这并不是从“离最优解远”直接推出的必然做法。先看一个局部模型,就能理解为什么步长会同时影响稳定性与进展。
2. 目标与定义#
考虑一维二次损失 ,其中曲率 。SGD 的确定性更新满足 ;固定步长要让误差收缩,需要 ,即 。
真实模型有不同曲率与随机梯度,不能直接用一个 设定日程,但这个关系说明步幅过大可能放大误差。本文用调度位置 、总预算 、warmup 长度 ,三者都按优化步计,;峰值为 ,最低值为 。
3. 方法与推导#
3.1 用阶段进度确定学习率#
先将 warmup 进度归一化为 ,从零升至峰值:,适用 。起点为非零值的实现需另定义初始学习率。
3.2 怎样得到连续的余弦衰减#
把衰减阶段进度写成 。希望它在 取峰值、 取最低值,使用余弦插值:
代入两个端点即可检查拼接;对衰减段求导,得到 ,所以两端下降速度为零,中间最快。这里使用单段余弦,不采用周期重启。[1]
图示在前 10% 预算 warmup,之后降至峰值的 10%;下面算例使用另一组明确的数值。
4. 计算过程#
设 、、、。下表按同一分段函数计算:
| 调度位置 | 学习率 | 阶段 |
|---|---|---|
| 0 | warmup | |
| 500 | warmup | |
| 1,000 | warmup | |
| 2,000 | 峰值 | |
| 10,000 | 余弦衰减 | |
| 30,000 | 余弦衰减 | |
| 50,000 | 余弦衰减 | |
| 51,000 | 余弦衰减 | |
| 70,000 | 余弦衰减 | |
| 90,000 | 余弦衰减 | |
| 100,000 | 余弦衰减 |
余弦段中点是 ,此时 ; 仍略高于这一值。表为公式演算,不是训练效果实验。
5. 比较与边界#
5.1 线性与余弦怎样比较#
| 比较项 | 线性衰减 | 余弦衰减 |
|---|---|---|
| 相同进度 的学习率 | 使用上节余弦式 | |
| 下降速度 | 恒定 | 先慢、再快、再慢 |
| 到达最低值的位置 | ||
| 选择依据 | 与任务和预算一起验证 | 与任务和预算一起验证 |
比较效果时固定峰值、最低值、warmup、有效 batch、总 token 和调参预算。曲线形状本身不能保证某个日程更优。延长训练也会改变同一步对应的衰减进度,不能只修改总步数而忽略已有日程。
5.2 实现约定与其他日程#
Warmup 与 cosine 的调用位置#
按优化步调度时,在一次有效更新后推进 scheduler;梯度累积的 microbatch 不单独推进。AMP 因溢出跳过更新时,是否推进预算应明确定义;本文训练循环采用“只随成功更新推进”的约定。组合实现见训练循环。[2]
OneCycle 的初值与终值#
OneCycle 先升后降,并可让动量反向变化。PyTorch 中 pct_start 决定升温所占比例,initial_lr=max_lr/div_factor,min_lr=initial_lr/final_div_factor。最低值还要包含 div_factor,不能写成 max_lr/final_div_factor。具体参数见 [3]。
LayerNorm 每次前向使用当前输入的统计量,不存在 BatchNorm 那样需要预热的运行均值。Warmup 的必要性仍依赖初始化、结构与优化器;它不能修复错误监督或非有限计算。按优化步调度的接口还需明确首次更新位置与调用顺序。[2]
参考资料#
- SGDR(2016). §3:余弦退火;本文只采用单段曲线。
- PyTorch 2.9 调度器. 文档:optimizer 与 scheduler 的调用位置。
- OneCycleLR(PyTorch 2.9). 文档:初值、终值与阶段比例。