🤖 roboto_origin_03 Wiki
首页 / RSL-RL / 观测归一化与网络工具函数

本页聚焦于 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 在线算法的变体,能够在仅存储 meanvarcount 三个标量/张量的前提下,精确维护全部历史数据的二阶统计量。值得注意的是,方差计算采用 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 变体均遵循统一的归一化集成契约:

  1. 声明阶段:在 __init__ 中,根据配置决定是否实例化 EmpiricalNormalization,否则退化为 nn.Identity()
  2. 前向阶段:在 act()evaluate() 中,先拼接观测张量,再调用归一化器进行变换,最后送入 MLP。
  3. 更新阶段:提供 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,支持以下特性:

Sources: networks/mlp.py

配置解析工具函数

框架通过 YAML/字典配置驱动几乎所有组件的实例化。utils/utils.py 提供了一组解析函数,将字符串名称映射为实际的 Python 可调用对象,避免在核心逻辑中硬编码类导入。

激活函数与优化器解析

resolve_nn_activationresolve_optimizer 是最基础的字符串映射器,分别维护了一个内置名称到 torch.nn.Moduletorch.optim.Optimizer 的字典。支持的激活函数包括 eluselurelutanhgeluswish 等 11 种;优化器覆盖 adamadamwsgdrmsprop

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 兼容传统写法
简单名称 PPOActorCritic 框架内置类自动搜索

该函数被 训练运行器生命周期管理 广泛用于解析 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_velprojected_gravity)。resolve_obs_groups 负责验证并解析配置中的 obs_groups,确保 policycritic 等保留键对应的观测组真实存在于环境输出中。若某保留键缺失,函数会按以下优先级自动兜底:

  1. 检查环境中是否存在同名观测组,若有则直接采用;
  2. 否则回退到 policy 的观测集合。

这一设计显著降低了配置冗余,同时通过明确的 warning 提示用户完善配置。

Sources: utils/utils.py

轨迹处理工具

针对循环策略(Recurrent Policy)和需要序列级处理的场景,框架提供了 split_and_pad_trajectoriesunpad_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 热插拔。

若你已理解本页内容,建议按以下顺序继续深入:

  1. 观察归一化在复杂策略中的具体接线方式:Actor-Critic 基础架构设计
  2. 了解归一化统计量如何在训练步中被更新:PPO 算法实现与训练流程
  3. 查看 until 参数在 AMP 判别器中的典型用法:AMP 对抗动作先验算法
  4. 探索 RND 中奖励归一化的实践:RND 随机网络蒸馏探索