🤖 roboto_origin_03 Wiki
首页 / RSL-RL / 经验采样与小批量生成

经验采样与小批量生成是连接环境交互与策略更新的核心数据桥梁。在 RSL-RL 中,这一环节由 RolloutStorageCircularBuffer 两大存储组件协同完成:前者负责按时间步收集并编排 on-policy 的 rollout 数据,后者为 AMP 判别器等场景提供带历史深度的循环缓冲采样。理解它们的数据布局、生成器语义以及与 PPO.update() 的交互方式,是掌握训练循环数据流的关键前提。

RolloutStorage 的数据布局与生命周期

RolloutStorage 采用时间×环境的二维张量布局,核心张量的形状为 [num_transitions_per_env, num_envs, ...]。这种布局天然适配向量化环境:每个时间步一次性写入所有并行环境的观测、动作、奖励与终止信号。

存储对象在初始化时根据 training_type 动态分配张量。当类型为 "rl" 时,除了基础字段外,还会预分配 valuesactions_log_probmusigmareturnsadvantages 等 PPO 所需的计算中间量;当类型为 "distillation" 时,则额外分配 privileged_actions 用于师生蒸馏。

Transition 作为内部嵌套类,充当单步数据的临时容器。PPO.act() 在环境步进前填充动作、价值与策略分布参数,PPO.process_env_step() 在步进后补充奖励与终止信号,最终通过 add_transition() 将整包数据深拷贝到存储的当前时间片。当步数达到 num_transitions_per_env 后,存储进入只读状态,直到 clear() 重置计数器。这一设计严格保证了 on-policy 数据不被后续交互污染。

Sources: rollout_storage.py

从 Rollout 到 Mini-batch 的三类生成器

RolloutStorage 暴露了三类生成器,分别服务于蒸馏训练、前馈策略强化学习与循环策略强化学习。它们的核心差异在于是否展平时间维度、是否保持轨迹边界,以及是否引入随机重排。

蒸馏生成器:顺序遍历

蒸馏模式下,generator() 按时间顺序逐行 yield 数据,不做任何随机打乱。其语义是让学生策略依次回放教师策略在相同观测下产生的 privileged actions,无需考虑时序断裂或优势函数计算。

Sources: rollout_storage.py

前馈 Mini-batch 生成器:全局打乱与分片

对于前馈网络,标准的 PPO 训练要求将多环境、多时间步的数据彻底展平后随机打乱,以破坏样本相关性并满足小批量梯度下降的独立性假设。

mini_batch_generator() 的执行流程如下:首先将 [T, N, ...] 的张量通过 flatten(0, 1) 转化为 [T×N, ...];随后构造一个覆盖全部样本的随机索引序列 indices;接着按 num_mini_batches 将总批量等分为若干 mini_batch_size;最后在 num_epochs 个 epoch 中反复遍历这些分片。由于每个 epoch 都复用同一套打乱索引,同一 mini-batch 在不同 epoch 中对应的数据内容保持不变,仅遍历顺序重复。

这一生成器同时展平了 observationsactionsvaluesreturnsadvantagesactions_log_probmusigma,确保策略和价值网络在每个 mini-batch 内接收到完整且一致的 transition 切片。返回的 hidden_states_batchmasks_batch 恒为 None,显式标识当前不涉及循环状态。

Sources: rollout_storage.py

循环 Mini-batch 生成器:轨迹保持与填充

循环策略(LSTM/GRU)不能随意打乱时间顺序,否则会导致隐藏状态错位。recurrent_mini_batch_generator() 的核心任务是在环境维度分片的同时保持每个子序列的时序完整性

它首先调用 split_and_pad_trajectories() 处理 observationsdones。该工具函数以 dones 为边界,将展平后的数据切分为若干条独立轨迹,对短轨迹进行零填充,并返回一个布尔掩码 trajectory_masks 标记有效时间步。此后,生成器不再按样本数均分,而是按 num_envs // num_mini_batches 的环境数均分,并在每个 mini-batch 内包含这些环境所产生的全部轨迹。

隐藏状态的处理是循环生成器的关键。saved_hidden_state_asaved_hidden_state_c 在 rollout 阶段以 [time, num_layers, num_envs, hidden_dim] 的格式保存。生成时,先通过 dones 的布尔掩码提取每个轨迹起始时刻的隐藏状态,再将其 reshape 为 [num_layers, batch, hidden_dim] 供 RNN 模块消费。masks_batch 则用于在策略前向传播中屏蔽填充位置,避免无效时间步影响隐藏状态的传播。

Sources: rollout_storage.py, utils.py

CircularBuffer:带历史深度的循环采样

CircularBuffer 是为 AMP 判别器观察缓冲等场景设计的按环境独立循环存储。与 RolloutStorage 的固定时间窗口不同,它在每个环境中维护一个长度上限为 max_len 的环形队列,支持通过 append() 持续写入,并通过 __getitem__ 以 LIFO 方式按历史深度检索。

其内部状态包括:_pointer 指向最新写入位置,_num_pushes 记录每个环境的实际写入次数(用于处理未满缓冲区),_buffer 的形状为 [max_len, batch_size, ...]reset() 可按批量索引局部清零,适配环境重启场景。

CircularBuffer.mini_batch_generator() 的采样逻辑与 RolloutStorage 有本质区别:它从当前有效长度与批量大小的所有组合中随机抽取 epoch_batch_size(history_index, env_index) 二元组,再将其映射到环形缓冲的物理索引上。这种设计使得判别器可以随机访问历史观察的任意时间切片,而不受 rollout 时间窗口的严格限制。AMP 算法通过 zip()RolloutStorage 的生成器与两个 CircularBuffer 生成器同步对齐,确保每个 PPO mini-batch 都能拿到对应的策略观察与演示观察样本。

Sources: circular_buffer.py, ppo_amp.py

与 PPO 更新循环的集成

PPO.update() 是消费上述生成器的最终环节。它首先根据 policy.is_recurrent 选择对应的生成器,随后进入一个嵌套循环:外层为 num_learning_epochs,内层为 num_mini_batches。每次迭代从生成器取出一份 mini-batch,执行以下步骤:

  1. 可选的每 mini-batch 优势归一化:当 normalize_advantage_per_mini_batch=True 时,在当前 batch 内重新计算均值与标准差,而非使用全局归一化。
  2. 对称数据增强:若启用对称性,对观测与动作进行镜像增强,并相应重复旧策略的对数概率与价值目标。
  3. 策略重评估:用当前网络参数重新计算 actions_log_probvalue,为 PPO 的 clipped surrogate 提供新旧策略对比。
  4. 自适应 KL 调度:根据新旧策略分布的 KL 散度动态调整学习率。

整个更新过程严格遵循生成器提供的批次边界,前馈与循环路径在 update() 中共享同一套损失计算逻辑,区别仅在于 maskshidden_states 是否非空。

Sources: ppo.py

三类采样策略对比

维度 Distillation Generator Feedforward Mini-batch Recurrent Mini-batch
适用训练类型 distillation rl + 前馈策略 rl + 循环策略
时间维度处理 按时间步顺序 yield flatten(0,1) 后全局打乱 dones 切分轨迹并填充
随机性 每 epoch 固定一套打乱索引 环境维度固定分片,时序内保持
隐藏状态 不涉及 None 提取轨迹起始状态并 reshape
掩码 不涉及 None trajectory_masks 屏蔽填充
典型消费者 DistillationRunner OnPolicyRunner + PPO OnPolicyRunner + PPO-Recurrent

数据流架构概览

以下 Mermaid 图展示了从环境交互到 mini-batch 消费的完整数据流:

flowchart TD
    A[向量化环境<br/>VecEnv] -->|step| B[PPO.act]
    B -->|actions| A
    A -->|obs, rewards, dones| C[PPO.process_env_step]
    C -->|Transition| D[RolloutStorage<br/>add_transition]
    
    subgraph 存储层
        D --> E[observations<br/>[T,N,...]]
        D --> F[rewards/dones<br/>[T,N,1]]
        D --> G[values/log_prob<br/>[T,N,1]]
        D --> H[saved_hidden_states<br/>[T,Layers,N,H]]
    end
    
    I[PPO.compute_returns] -->|GAE| J[returns / advantages<br/>写入 Storage]
    
    subgraph 生成器层
        K[RolloutStorage<br/>mini_batch_generator] -->|flatten & shuffle| L[Mini-batch Tensor<br/>[B,...]]
        M[RolloutStorage<br/>recurrent_mini_batch_generator] -->|split_and_pad| N[Trajectories<br/>+ Masks + Hidden States]
        O[CircularBuffer<br/>mini_batch_generator] -->|random history index| P[Disc Obs Batch]
    end
    
    G --> K
    E --> K
    J --> K
    E --> M
    G --> M
    H --> M
    J --> M
    
    subgraph 更新层
        L --> Q[PPO.update<br/>Surrogate / Value Loss]
        N --> Q
        P --> R[PPOAMP.update<br/>Discriminator Loss]
    end

关键设计原则总结

RSL-RL 的经验采样体系体现了三条紧密关联的设计原则。第一,张量形状的一致性:所有存储字段严格保持 [time, env, ...] 或展平后的 [batch, ...],使得生成器可以通过统一的索引切片提取相关数据,无需复杂的打包与解包。第二,生成器即接口RolloutStorage 不暴露原始张量,而是通过 Python Generator 向算法层提供 mini-batch,强制实现延迟计算与内存友好。第三,策略架构决定采样方式:前馈与循环策略在更新阶段共享同一套 PPO.update() 逻辑,差异完全由生成器封装,这一解耦使得新增策略架构时只需实现对应的生成器即可。

如需深入理解 GAE 回报计算与策略损失的数学细节,可继续阅读 PPO 算法实现与训练流程;若关注循环策略与注意力策略的网络结构,请参考 循环与注意力策略变体。关于 RolloutStorage 中 Transition 的数据结构与存储初始化,亦可交叉查阅 Rollout 数据存储与 Transition