🤖 roboto_origin_03 Wiki
首页 / RSL-RL / 自适应学习率与 KL 调度

在基于 PPO 的强化学习训练中,策略更新的步长是影响收敛稳定性的核心变量。本页深入解析 rsl_rl 中以 KL 散度为反馈信号的自适应学习率调度机制,涵盖其数学原理、代码实现路径、分布式训练适配方式以及配置方法。理解这一机制有助于你在复杂机器人控制任务中实现更稳定的策略收敛,避免因学习率过大导致的策略崩溃或过小导致的训练停滞。

核心设计思想

PPO 的 clipped surrogate objective 虽通过裁剪比率间接约束了策略更新的幅度,但固定学习率仍可能在训练不同阶段引发问题:初期策略变化剧烈时,高学习率易导致 KL 散度激增;后期接近最优时,低学习率又可能拖慢收敛。rsl_rl 采用了一种基于 KL 散度实时反馈的启发式学习率调节策略——将每次 mini-batch 更新前后新旧策略的 KL 散度作为"温度计",动态放大或缩小优化器的步长,从而在不引入外部学习率衰减调度器的情况下,实现训练过程的自我调节。

这一机制并非独立模块,而是直接嵌入在 PPO.update()PPOAMP.update() 的 mini-batch 迭代循环内部,在每个 batch 的反向传播之前完成 KL 计算与学习率调整,确保优化步长始终与当前策略的漂移程度相匹配。

Sources: ppo.py, ppo_amp.py

架构与数据流

下图展示了自适应学习率机制在整个训练更新步骤中的位置与数据流向。注意 KL 计算处于 torch.inference_mode() 上下文中,其梯度不会被追踪;学习率的调整结果则通过修改 optimizer.param_groups 直接作用于后续的反向传播。

flowchart TD
    A[Mini-batch Generator] --> B[Recompute Policy Output]
    B --> C[Extract mu_batch, sigma_batch]
    D[Stored old_mu_batch, old_sigma_batch] --> C
    C --> E{KL Divergence<br/>Computation}
    E --> F[Reduce KL across GPUs]
    F --> G{Rank 0 Decision}
    G -->|kl_mean > desired_kl × 2| H[lr = max(1e-5, lr / 1.5)]
    G -->|kl_mean < desired_kl / 2<br/>and > 0| I[lr = min(1e-2, lr × 1.5)]
    G -->|Otherwise| J[Keep current lr]
    H --> K[Broadcast lr to all GPUs]
    I --> K
    J --> K
    K --> L[Update optimizer.param_groups]
    L --> M[Surrogate Loss Backward]
    M --> N[Optimizer Step]

该流程同时适用于标准 PPO 与 AMP 扩展版本,体现了两类算法在训练基础设施层面的高度复用。

Sources: ppo.py, ppo_amp.py

KL 散度的解析计算

由于策略网络输出高斯分布,rsl_rl 没有采用采样近似,而是直接利用解析公式计算 KL 散度。对于两个单变量高斯分布 $\mathcal{N}(\mu_1, \sigma_1^2)$ 与 $\mathcal{N}(\mu_2, \sigma_2^2)$,其 KL 散度为:

$$D_{KL} = \log\frac{\sigma_2}{\sigma_1} + \frac{\sigma_1^2 + (\mu_1 - \mu_2)^2}{2\sigma_2^2} - \frac{1}{2}$$

在代码中,该公式被向量化地应用于动作空间的每一维,并通过 torch.sum(axis=-1) 沿动作维度累加,最终取 batch 均值得到 kl_mean。计算过程中加入 1.0e-5 的数值稳定项,避免标准差比值出现对数零值。

Sources: ppo.py

自适应学习率调度规则

学习率的调整遵循简单的阈值分段规则,仅在主进程(gpu_global_rank == 0)上执行决策,随后广播至所有分布式进程。具体规则如下:

KL 散度条件 学习率调整动作 边界约束
kl_mean > desired_kl × 2.0 学习率除以 1.5 下限 1e-5
kl_mean < desired_kl / 2.0kl_mean > 0.0 学习率乘以 1.5 上限 1e-2
其他情况 保持不变

上述规则的设计直觉是:当实际 KL 显著超过目标值时,说明当前学习率使策略更新过于激进,应当收缩步长;当 KL 显著低于目标值且为正时,说明策略变化保守,可以适度增大步长以加速探索。值得注意的是,代码显式排除了 kl_mean <= 0.0 的异常情形,防止因数值问题导致学习率无限膨胀。

Sources: ppo.py

分布式训练适配

在多 GPU 场景下,由于各进程独立采样不同的环境子集,计算出的 KL 散度可能存在差异。rsl_rl 通过以下三步保证学习率的全局一致性:

  1. 聚合 KL:调用 torch.distributed.all_reducekl_mean 执行求和,再除以 gpu_world_size 得到全局平均。
  2. 单点决策:仅由 rank 0 根据全局 KL 均值执行上述学习率调整规则。
  3. 广播同步:将调整后的学习率封装为 tensor,通过 torch.distributed.broadcast 从 rank 0 广播到所有进程,最后写入各进程 optimizer.param_groupslr 字段。

代码中保留了一条 TODO 注释,指出若 KL 散度在各 GPU 上天然一致,则单点决策与广播可能并非严格必要;但在当前实现中,这一保守策略确保了分布式行为的确定性。

Sources: ppo.py

配置参数与初始化

自适应 KL 调度通过 PPO(及 PPOAMP)构造函数的以下参数启用和控制:

参数名 类型 默认值 说明
schedule str "adaptive" 学习率调度模式。设为 "adaptive" 时启用 KL 自适应;其他字符串值将关闭该机制。
desired_kl float 0.01 目标 KL 散度阈值,作为学习率调整的参照基准。
learning_rate float 0.001 初始学习率,后续将被自适应机制动态改写。

OnPolicyRunner 中,这些参数通常来自配置文件的 algorithm 字典,并在 _construct_algorithm 阶段通过 **self.alg_cfg 解包传入算法实例。若需关闭自适应调度,可将 schedule 设为任意非 "adaptive" 的值,或将 desired_kl 设为 None

Sources: ppo.py, on_policy_runner.py

日志记录与可视化

OnPolicyRunner.learn() 在每轮迭代结束后,会将当前 self.alg.learning_rate 传递给 Logger.log()Logger 将其记录到 Loss/learning_rate 标量曲线下,支持 TensorBoard、Weights & Biases 与 Neptune 三种后端。这意味着你可以直接在可视化面板中追踪学习率随 KL 散度波动的动态变化,作为诊断训练稳定性的重要依据。需要留意的是,控制台打印输出中并未直接展示当前学习率数值,需通过日志查看器读取。

Sources: on_policy_runner.py, logger.py

设计权衡与使用建议

自适应 KL 调度是 rsl_rl 在工程实践中对 PPO 训练稳定性的重要增强,但理解其局限性有助于更合理地调参:

Sources: ppo_amp.py, ppo_amp.py

相关阅读与导航

自适应学习率与 KL 调度是 PPO 训练流程的内嵌环节,与以下主题紧密相关: