Fairseq 学习率调度器(Learning Rate Schedulers)完整指南:从 step_update 到 step 的调度机制与源码解析
2026/9/13 23:18:59 网站建设 项目流程

Fairseq 学习率调度器(Learning Rate Schedulers)完整指南:从 step_update 到 step 的调度机制与源码解析

【免费下载链接】unilmLarge-scale Self-supervised Pre-training Across Tasks, Languages, and Modalities项目地址: https://gitcode.com/GitHub_Trending/un/unilm

导读

学习率调度器(Learning Rate Scheduler)是深度学习训练中控制模型收敛速度与最终精度的关键组件。本文以 IAD/fairseq 文档 为核心,系统讲解 Fairseq 学习率调度器的设计思想、注册机制与全部内置调度器(fixed、inverse_sqrt、cosine、reduce_lr_on_plateau、triangular、polynomial_decay、tri_stage、pass_through、manual)的配置参数与底层数学公式。读完本文,你将掌握如何在 IAD/fairseq 框架中通过--lr-scheduler与配套参数选择并调优任意调度策略,并理解调度器在训练循环中的真实调用时机。

一、核心机制:学习率在两个时机被更新

按 lr_scheduler.rst 的定义,Fairseq 的学习率调度器负责在训练过程中不断更新学习率,其更新时机分为两类:

  • step_update(num_updates)每完成一次梯度更新(update)后调用,用于实现基于步数的调度,例如逐 step 的线性 warmup、余弦退火、反平方根衰减等;
  • step(epoch, val_loss=None)在每个 epoch 结束时调用,用于实现基于 epoch 的调度,例如固定分段衰减(按 epoch 切换学习率档位)、验证损失 plateau 检测等。

此外,Fairseq 还提供step_begin_epoch(epoch)钩子,在每个 epoch 开始时更新学习率(主要用于 fixed 与 polynomial_decay 这类按 epoch 生效的调度器)。这三类钩子构成了调度器与训练主循环之间的全部交互接口,所有内置调度器均在此基础上实现。

二、调度器注册机制与构建入口

Fairseq 通过注册表(registry)机制管理学习率调度器,所有实现统一放置在 fairseq/optim/lr_scheduler/ 目录下。入口代码位于init.py:

( build_lr_scheduler_, register_lr_scheduler, LR_SCHEDULER_REGISTRY, LR_SCHEDULER_DATACLASS_REGISTRY, ) = registry.setup_registry( "--lr-scheduler", base_class=FairseqLRScheduler, default="fixed" ) def build_lr_scheduler(cfg: DictConfig, optimizer): return build_lr_scheduler_(cfg, optimizer) # automatically import any Python files in the optim/lr_scheduler/ directory for file in os.listdir(os.path.dirname(__file__)): if file.endswith(".py") and not file.startswith("_"): importlib.import_module("fairseq.optim.lr_scheduler." + file_name)

关键信息:

  • 命令行参数为--lr-scheduler,默认值为fixed
  • 目录下每个.py文件在 import 时被自动加载并完成自注册;
  • 各调度器通过@register_lr_scheduler("名字", dataclass=xxxConfig)装饰器注册,并配套一个 dataclass 配置类,配置字段会通过gen_parser_from_dataclass自动生成命令行参数;
  • 注册表中同时维护了LR_SCHEDULER_REGISTRY(类)与LR_SCHEDULER_DATACLASS_REGISTRY(配置 dataclass)两个映射。

三、基类 FairseqLRScheduler 的 API 约定

所有调度器继承自 fairseq_lr_scheduler.py 中的FairseqLRScheduler,其定义了一套完整的最小接口:

方法调用时机默认行为
step_begin_epoch(epoch)每个 epoch 开始空实现(pass)
step(epoch, val_loss=None)每个 epoch 结束若传入 val_loss,则维护self.best = min(self.best, val_loss)
step_update(num_updates)每次参数更新后返回optimizer.get_lr()(即不改变学习率)
state_dict()/load_state_dict()断点保存/恢复保存/恢复{"best": ...}

基类的__init__要求传入FairseqOptimizer实例,并持有self.cfgself.optimizerself.best。另外,类方法add_args(parser)会读取类上的__dataclass属性自动生成命令行参数——这就是配置类与 CLI 参数绑定的实现原理。同文件中还定义了仅接收argparse.Namespace旧式参数的LegacyFairseqLRScheduler,用于兼容 legacy 风格的调度器(如 manual)。

四、内置调度器逐一解析

以下调度器均可在--lr-scheduler=<名称>时启用,所有配置项既可通过命令行传入,也可写入 hydra/OmegaConf 配置文件(IAD/fairseq 的训练配置体系参见仓库中的 conf 目录)。

4.1 fixed:固定分段衰减(默认调度器)

实现见 fixed_schedule.py,注册名为fixed。它按 epoch 分段使用预先指定的学习率列表,是唯一允许--lr传入多个值(列表)的调度器之一:

参数默认值含义
lr(继承自optimization.lr必填学习率列表,如--lr 5e-4:1e-4,第 N 个 epoch 使用列表中第 N 个值
force_annealNone若设置,则从指定 epoch 起强制按lr_shrink指数衰减
lr_shrink0.1退火收缩因子,lr_new = lr * lr_shrink
warmup_updates0前 N 次更新做线性 warmup

核心逻辑在get_next_lr(epoch)epoch < force_anneal时取lrs[min(epoch-1, len(lrs)-1)];否则lrs[-1] * lr_shrink ** (epoch + 1 - force_anneal)step_begin_epoch在 epoch 开始时设置学习率,step_update负责 warmup 阶段的线性上升。

4.2 inverse_sqrt:反平方根衰减(Transformer 经典方案)

实现见 inverse_square_root_schedule.py,注册名为inverse_sqrt。这是大规模预训练中最常用的方案:

参数默认值含义
warmup_updates4000前 N 次更新线性 warmup(默认值即体现该方案的主流设定)
warmup_init_lr-1(自动推导)warmup 起始学习率;小于 0 时,若有 warmup 则取 0,否则取lr
lr必填峰值学习率(只允许单值,传列表会报错)

数学定义(源码 docstring 明示):

  • warmup 阶段:lr = torch.linspace(warmup_init_lr, lr, warmup_updates)[update_num],即从warmup_init_lr线性升至峰值lr
  • warmup 之后:decay_factor = lr * sqrt(warmup_updates)lr = decay_factor / sqrt(update_num)——学习率随步数平方根反比衰减。

实现细节:__init__中若cfg.lr为多值列表会抛出 ValueError 并提示改用 fixed;decay_factor在初始化时一次性算好;step_updatenum_updates < warmup_updates走线性段,否则走反平方根段。

4.3 cosine:余弦退火(支持 warmup 与周期缩放)

实现见 cosine_lr_scheduler.py,注册名为cosine。基于循环余弦函数(论文参考见源码 docstring),同时支持多周期(SGDR 风格):

参数默认值含义
warmup_updates0前 N 次更新线性 warmup
warmup_init_lr-1warmup 起始学习率;小于 0 时自动取min_lr
lr继承optimization.lr最大学习率,必须大于min_lr
min_lr0.0最小学习率
t_mult1.0每个周期的长度增长因子
lr_period_updates-1每个周期的初始更新数;若不设置则由max_update - warmup_updates推导
lr_shrink0.1每个周期结束后的退火收缩因子
max_update继承optimization.max_update总更新数,用于自动推导周期长度

数学定义(warmup 结束后):

lr = min_lr + 0.5 * (lr - min_lr) * (1 + cos(t_curr / t_i))

其中t_curr为当前周期内的进度,t_i为当前周期长度,每个周期结束后t_it_mult缩放、lrmin_lrlr_shrink收缩。step_updatet_mult != 1时使用对数运算计算当前周期编号i

4.4 reduce_lr_on_plateau:验证损失平台期衰减

实现见 reduce_lr_on_plateau.py,注册名为reduce_lr_on_plateau。它直接封装了 PyTorch 的torch.optim.lr_scheduler.ReduceLROnPlateau,当验证损失连续若干 epoch 无改善时按因子收缩学习率:

参数默认值含义
lr_shrink0.1衰减因子(对应 ReduceLROnPlateau 的 factor)
lr_threshold1e-4判定"新最优"的阈值,只关注显著改善
lr_patience0连续无改善多少个 epoch 后降低学习率
warmup_updates0可选的线性 warmup 阶段
warmup_init_lr-1warmup 起始学习率
lr必填峰值学习率(仅允许单值)
maximize_best_checkpoint_metric继承 checkpoint 配置为 True 时以mode="max"监控,否则mode="min"

源码要点:构造时直接把self.optimizer.optimizerpatiencefactormodethreshold传给 PyTorch 的 ReduceLROnPlateau;step(epoch, val_loss)仅在 warmup 结束(self.warmup_end为 True)且传入 val_loss 时才真正调用self.lr_scheduler.step(val_loss),否则只更新last_epochstate_dict保存bestlast_epoch以支持断点恢复。

4.5 triangular:三角周期调度(循环学习率)

实现见 triangular_lr_scheduler.py,注册名为triangular。学习率在最小值与最大值之间线性往复(循环学习率,论文见源码 docstring):

参数默认值含义
max_lr必填(???最大学习率,必须大于lr
lr继承optimization.lr作为最小学习率
lr_period_updates5000每个周期(cycle)包含的更新数,半个周期即stepsize
lr_shrink0.1每个周期结束后的收缩因子
shrink_minFalse为 True 时最小学习率也随周期收缩,否则保持lr不变

实现要点:stepsize = lr_period_updates // 2;每个周期后lr_shrink ** cycle收缩上下界;lr = min_lr + (max_lr - min_lr) * max(0, 1 - x),其中x = |num_updates / stepsize - 2*(cycle+1) + 1|形成三角波形。

4.6 polynomial_decay:多项式退火

实现见 polynomial_decay_schedule.py,注册名为polynomial_decay。学习率从峰值按幂律衰减到设定的终点值:

参数默认值含义
warmup_updates0前 N 次更新线性 warmup
force_annealNone指定 epoch 起强制退火
end_learning_rate0.0衰减终点学习率
power1.0衰减指数(power=1 即线性衰减)
total_num_update继承optimization.max_update完成衰减的总更新数
lr必填峰值学习率

step_update的三种分支:warmup 阶段线性上升;num_updates >= total_num_update后锁定在end_learning_rate;中间阶段按lr = lr_range * pct_remaining ** power + end_learning_rate衰减,其中pct_remaining = 1 - (num_updates - warmup) / (total_num_update - warmup)

4.7 tri_stage:三段式调度(warmup + hold + decay)

实现见 tri_stage_lr_scheduler.py,注册名为tri_stage。实现论文 1904.08779(源码 docstring 注明)中的三段式调度,与 inverse_sqrt 类似但更精细:

参数默认值含义
warmup_steps0warmup 阶段步数
hold_steps0hold 阶段步数(学习率保持峰值)
decay_steps0指数衰减阶段步数
phase_ratioNone若设置,按max_update自动计算三段步数(比例和须为 1.0)
init_lr_scale0.01warmup 起始学习率为lr * init_lr_scale
final_lr_scale0.01衰减终点学习率为lr * final_lr_scale
max_update继承optimization.max_update总更新数,用于 phase_ratio 推导

四阶段数学定义(源码 docstring 明示):

  • warmupinit_lr = init_lr_scale * lrlr = linspace(init_lr, lr, warmup_steps)[update_num]
  • holdlr = lr(保持峰值);
  • decaydecay_factor = -log(final_lr_scale) / decay_stepslr = lr * exp(-(update_num - warmup_steps - decay_steps) * decay_factor)(指数衰减);
  • 结束之后lr = lr * final_lr_scale(恒定保持)。

实现上通过_decide_stage(update_step)返回(stage, steps_in_stage)四元状态机(0=warmup、1=hold、2=decay、3=constant),step_update根据阶段分别计算。

4.8 pass_through:完全委托给优化器

实现见 pass_through.py,注册名为pass_through。它不实现任何调度逻辑,而是把三个钩子与状态序列化全部转发给优化器自带的lr_scheduler

def __init__(self, cfg, optimizer): assert ( hasattr(optimizer, "lr_scheduler") and optimizer.lr_scheduler is not None ), "Pass-through schedule can only be used with optimizers with their own schedulers"

适用前提是优化器自身内建调度器(构造时会强制校验),适用于使用torch.optim原生 scheduler 或自带调度功能的优化器场景。

4.9 manual:人工指定 epoch/update 到学习率映射

实现见 manual_lr_scheduler.py,注册名为manual(继承LegacyFairseqLRScheduler,使用旧式 args 接口)。通过两个字符串参数直接指定任意时刻的学习率:

参数默认值含义
--epoch2lr"{}"字典字符串,如"{5: 1e-4, 10-20: 5e-5}",按 epoch 设定学习率
--update2lr"{}"字典字符串,按 update 步数设定学习率

parse_manuallr_args使用ast.literal_eval解析字典,支持"1"单值、"1,2,3"逗号列表(每个键取同一值)与"1-5"范围展开三种键写法。get_next_lr取所有<= 当前epoch/update的键中最大的那个对应的学习率,即"就近生效"策略。

五、实战:命令行与配置中的完整用法

5.1 通过命令行选择调度器

IAD/fairseq 的训练入口为 train.py(fairseq_cli 风格),调度器参数以--lr-scheduler开头。例如:

# 反平方根衰减(Transformer 预训练经典配置) python train.py --lr-scheduler inverse_sqrt --lr 0.001 --warmup-updates 4000 # 余弦退火(含 1000 步 warmup) python train.py --lr-scheduler cosine --lr 0.001 --min-lr 1e-5 \ --warmup-updates 1000 --max-update 500000 # 固定分段衰减(多段学习率) python train.py --lr-scheduler fixed --lr 5e-4:1e-4:5e-5 # 验证损失平台期衰减 python train.py --lr-scheduler reduce_lr_on_plateau --lr 0.001 \ --lr-patience 3 --lr-shrink 0.5 # 三段式调度(按比例自动切分) python train.py --lr-scheduler tri_stage --lr 0.001 \ --max-update 100000 --phase-ratio 0.1,0.4,0.5 # 人工指定(epoch 5 起用 1e-4,epoch 10-20 用 5e-5) python train.py --lr-scheduler manual --lr 0.001 \ --epoch2lr "{5: 1e-4, 10-20: 5e-5}"

注意:inverse_sqrtcosinereduce_lr_on_plateautriangulartri_stage只允许单值--lr,传入多值列表会在构造时抛出 ValueError 并提示改用fixed

5.2 配置优先级与参数继承

从源码可见,多数调度器配置通过II("optimization.lr")II("optimization.max_update")插值引用(OmegaConf II)继承全局优化配置,因此--lr--max-update是全局参数,调度器自动读取;调度器私有参数(如--warmup-updates--lr-shrink--min-lr)则由各自 dataclass 通过add_args自动生成。

六、断点保存与恢复

FairseqLRScheduler基类定义了state_dict()/load_state_dict()契约(默认保存best)。各调度器在此基础上扩展:

  • FixedLRScheduleManualSchedule额外保存当前lr
  • ReduceLROnPlateauLRSchedule保存 PyTorch 调度器的bestlast_epoch
  • PassThroughScheduleSchedule直接委托优化器内建调度器的 state_dict。

训练中断后恢复时,调度器状态随 checkpoint 一并序列化,确保学习率曲线在断点处无缝衔接。

七、总结:如何选择合适的调度器

场景推荐调度器关键参数
Transformer 大规模预训练inverse_sqrtwarmup-updates 4000
有明确总步数的收敛cosine/polynomial_decaymax-updatemin-lr
多阶段/固定分段学习率fixedlr a:b:cforce-anneal
依赖验证损失的早停式退火reduce_lr_on_plateaulr-patiencelr-shrink
循环学习率探索triangularmax-lrlr-period-updates
精细控制 warmup/hold/decaytri_stagephase-ratio
完全人工控制manualepoch2lrupdate2lr

本文所有结论均可在 lr_scheduler.rst 及 fairseq/optim/lr_scheduler/ 目录源码中直接验证,读者可按需深入研读对应实现文件,结合自身任务的数据规模与训练预算选择最合适的调度策略。

【免费下载链接】unilmLarge-scale Self-supervised Pre-training Across Tasks, Languages, and Modalities项目地址: https://gitcode.com/GitHub_Trending/un/unilm

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询