本页聚焦于 RSL-RL 框架中两类容易被忽视却至关重要的基础设施:观测归一化模块与网络/配置工具函数。它们不直接参与策略梯度计算,却决定了神经网络输入的数值稳定性、配置系统的灵活性,以及轨迹数据的批处理能力。理解这些组件的设计意图,是阅读 Actor-Critic 基础架构设计、PPO 算法实现与训练流程 等上层模块的前提。
Sources: networks/init.py, utils/init.py
观测归一化架构总览
框架提供了两种归一化器,分别服务于观测输入与奖励信号。它们均继承自 nn.Module,因此其统计量(mean、variance)会被自动注册为 buffer,随模型一起保存和加载,无需额外的序列化逻辑。
flowchart TB
subgraph 归一化器家族
A[EmpiricalNormalization<br/>经验均值方差归一化]
B[EmpiricalDiscountedVariationNormalization<br/>折扣奖励标准差归一化]
end
A --> C[ActorCritic / CNN / Attention / Recurrent]
A --> D[AMP Discriminator]
A --> E[StudentTeacher]
A --> F[RND State Normalizer]
B --> F
C -->|update_normalization| A
F -->|update| A
Sources: networks/normalization.py
经验观测归一化(EmpiricalNormalization)
EmpiricalNormalization 是框架中使用最广泛的归一化器,其核心职责是在训练过程中在线估计输入数据的均值与标准差,并在前向传播时执行 (x - mean) / (std + eps) 的标准化。
增量统计更新
与批量计算全局统计量不同,该模块采用增量式更新策略,支持流式数据场景(如 RL 的逐环境步采集)。当调用 update(x) 时,模块会根据当前批次数据与历史累计统计量,在线修正均值与方差:
rate = count_x / self.count
delta_mean = mean_x - self._mean
self._mean += rate * delta_mean
self._var += rate * (var_x - self._var + delta_mean * (mean_x - self._mean))
这一算法等价于 Welford 在线算法的变体,能够在仅存储 mean、var 和 count 三个标量/张量的前提下,精确维护全部历史数据的二阶统计量。值得注意的是,方差计算采用 unbiased=False,即除以样本数而非样本数减一,这对于大规模样本的 RL 场景更为稳定。
Sources: networks/normalization.py
前向与反归一化
forward 方法仅执行标准化变换,不修改内部统计量;而 inverse 方法可将标准化后的数据还原为原始尺度。这种双向能力在需要对网络输出进行反归一化的场景中(如还原 value 估计到原始奖励尺度)非常有用。
def forward(self, x: torch.Tensor) -> torch.Tensor:
return (x - self._mean) / (self._std + self.eps)
def inverse(self, y: torch.Tensor) -> torch.Tensor:
return y * (self._std + self.eps) + self._mean
Sources: networks/normalization.py
训练步数限制(until)
until 参数允许设定一个累计样本数上限。当 self.count >= until 时,update() 自动退化为空操作,统计量停止更新。这在 AMP 判别器与 RND 模块中尤为重要——这些模块通常希望在训练早期快速适应数据分布,后期则固定统计量以保证训练稳定性。
Sources: networks/normalization.py
与策略模块的集成模式
所有 Actor-Critic 变体均遵循统一的归一化集成契约:
- 声明阶段:在
__init__中,根据配置决定是否实例化EmpiricalNormalization,否则退化为nn.Identity()。 - 前向阶段:在
act()与evaluate()中,先拼接观测张量,再调用归一化器进行变换,最后送入 MLP。 - 更新阶段:提供
update_normalization(obs)接口,由上层算法(如 PPO)在process_env_step中调用。
sequenceDiagram
participant PPO as PPO Algorithm
participant AC as ActorCritic
participant EN as EmpiricalNormalization
PPO->>AC: act(obs)
AC->>EN: forward(obs_tensor)
EN-->>AC: normed_obs
AC-->>PPO: action
PPO->>AC: update_normalization(obs)
AC->>EN: update(obs_tensor)
Note over EN: update mean/var buffers
以 ActorCritic 为例, actor 与 critic 各自拥有独立的归一化器,互不干扰。这种解耦设计允许 policy 网络与 value 网络对各自输入采用不同的统计特性。
Sources: modules/actor_critic.py, modules/actor_critic.py, modules/actor_critic.py
奖励归一化变体(EmpiricalDiscountedVariationNormalization)
该归一化器专门用于处理非平稳奖励信号,源自 Pathak 等人关于大规模 PPO 研究的实践。其设计思想是:用折扣累计奖励的滑动标准差作为分母,对原始奖励进行缩放。
实现上,它组合了两个内部组件:
| 组件 | 职责 |
|---|---|
_DiscountedAverage |
维护折扣累计奖励 \bar{R}_t = \gamma \bar{R}_{t-1} + r_t |
EmpiricalNormalization |
基于折扣累计奖励的滑动窗口估计标准差 |
前向传播时,模块先用 _DiscountedAverage 更新折扣均值,再将该均值送入 EmpiricalNormalization 更新其内部统计量,最后用 EmpiricalNormalization 的标准差对原始奖励做除法归一化。该变体目前主要用于 RND 随机网络蒸馏探索 模块中的内在奖励归一化。
Sources: networks/normalization.py
多层感知机工具(MLP)
MLP 并非复杂的网络架构,但其提供了针对 RL 场景的工程便利,值得在此一并说明。它继承自 nn.Sequential,支持以下特性:
- 动态维度推断:
hidden_dims中任何-1会被替换为input_dim,便于配置文件中复用输入维度。 - 多维输出整形:当
output_dim为tuple时,自动在最后一层后追加nn.Unflatten,无需手动 reshape。 - 权重初始化:
init_weights(scales)对每一层nn.Linear执行orthogonal_初始化,偏置置零,并通过get_param支持按层指定缩放系数。
Sources: networks/mlp.py
配置解析工具函数
框架通过 YAML/字典配置驱动几乎所有组件的实例化。utils/utils.py 提供了一组解析函数,将字符串名称映射为实际的 Python 可调用对象,避免在核心逻辑中硬编码类导入。
激活函数与优化器解析
resolve_nn_activation 与 resolve_optimizer 是最基础的字符串映射器,分别维护了一个内置名称到 torch.nn.Module 和 torch.optim.Optimizer 的字典。支持的激活函数包括 elu、selu、relu、tanh、gelu、swish 等 11 种;优化器覆盖 adam、adamw、sgd、rmsprop。
activation = resolve_nn_activation("elu") # -> torch.nn.ELU()
optimizer_cls = resolve_optimizer("adamw") # -> torch.optim.AdamW
Sources: utils/utils.py
通用可调用对象解析(resolve_callable)
resolve_callable 是配置系统的核心枢纽,支持三种引用风格:
| 引用风格 | 示例 | 适用场景 |
|---|---|---|
| 直接传递 | MyClass |
代码内直接实例化 |
| 冒号限定名 | module.path:ClassName |
跨模块引用,最推荐 |
| 点号限定名 | module.path.ClassName |
兼容传统写法 |
| 简单名称 | PPO、ActorCritic |
框架内置类自动搜索 |
该函数被 训练运行器生命周期管理 广泛用于解析 class_name 配置字段,例如:
actor_critic_class = resolve_callable(self.policy_cfg.pop("class_name"))
alg_class = resolve_callable(self.alg_cfg.pop("class_name"))
Sources: utils/utils.py, runners/on_policy_runner.py
观测分组解析(resolve_obs_groups)
在向量化环境中,观测通常以 TensorDict 分组形式返回(如 base_lin_vel、projected_gravity)。resolve_obs_groups 负责验证并解析配置中的 obs_groups,确保 policy 与 critic 等保留键对应的观测组真实存在于环境输出中。若某保留键缺失,函数会按以下优先级自动兜底:
- 检查环境中是否存在同名观测组,若有则直接采用;
- 否则回退到
policy的观测集合。
这一设计显著降低了配置冗余,同时通过明确的 warning 提示用户完善配置。
Sources: utils/utils.py
轨迹处理工具
针对循环策略(Recurrent Policy)和需要序列级处理的场景,框架提供了 split_and_pad_trajectories 与 unpad_trajectories 一对互逆操作。
split_and_pad_trajectories 的输入为 [time, num_envs, ...] 维度的张量以及 dones 标记。它会将每个环境在 done 处截断为独立轨迹,按长度排序后用 pad_sequence 补齐到最长轨迹长度,并返回有效位置掩码。输出维度变为 [num_trajectories, max_length, ...],便于在循环网络中做批次化前向传播。
flowchart LR
A[输入: [T, N, ...]<br/>dones] --> B{按 done 切分}
B --> C[得到若干变长轨迹]
C --> D[pad_sequence 补零]
D --> E[输出: [M, L_max, ...]<br/>+ masks]
unpad_trajectories 则执行相反操作,将补齐后的张量按掩码还原为原始密集格式。
Sources: utils/utils.py
在训练流程中的调用链
以下表格总结了各工具函数/模块在典型 PPO 训练周期中的调用方与被调用方关系:
| 工具/模块 | 调用方 | 被调用方/作用对象 | 触发时机 |
|---|---|---|---|
EmpiricalNormalization.forward |
ActorCritic.act / evaluate |
观测张量 | 每个环境步前向推理 |
EmpiricalNormalization.update |
ActorCritic.update_normalization |
观测缓冲区统计量 | PPO.process_env_step |
resolve_nn_activation |
MLP.__init__ / CNN.__init__ |
字符串配置 -> nn.Module |
网络构建阶段 |
resolve_optimizer |
PPO.__init__ / Distillation.__init__ |
字符串配置 -> Optimizer 类 | 算法初始化阶段 |
resolve_callable |
OnPolicyRunner / DistillationRunner |
字符串配置 -> Python 类 | Runner 初始化阶段 |
resolve_obs_groups |
OnPolicyRunner |
环境 TensorDict 与配置字典 |
Runner 初始化阶段 |
split_and_pad_trajectories |
循环网络/记忆模块内部 | RolloutStorage 数据 |
训练更新阶段 |
Sources: algorithms/ppo.py
小结与延伸阅读
观测归一化与网络工具函数构成了 RSL-RL 的“底座层”。EmpiricalNormalization 通过增量统计和 nn.Module 的 buffer 机制,实现了与 PyTorch 生态的无缝集成;resolve_callable 等解析工具则赋予框架声明式配置的能力,使算法、网络、运行器均可通过 YAML 热插拔。
若你已理解本页内容,建议按以下顺序继续深入:
- 观察归一化在复杂策略中的具体接线方式:Actor-Critic 基础架构设计
- 了解归一化统计量如何在训练步中被更新:PPO 算法实现与训练流程
- 查看
until参数在 AMP 判别器中的典型用法:AMP 对抗动作先验算法 - 探索 RND 中奖励归一化的实践:RND 随机网络蒸馏探索