异步近端策略优化 (Async PPO)#
1. 引言#
RLinf 中的 Async PPO 是为具身任务设计的一种异步训练实现。它在保留 PPO 核心优化思想的同时,放弃了“采样一轮、训练一轮”的严格同步模式,改为将环境交互、策略推理与 actor 训练解耦为长期并行的流水线。
这一实现在不改变 PPO 的核心优化目标的前提下,旨在缓解具身训练中的系统瓶颈,主要包括:
环境单步耗时较长,actor 长时间等待 rollout 数据;
视觉或大模型策略推理负担重,GPU 在同步等待中闲置;
单轮 rollout 较长,同步 PPO 的端到端吞吐量偏低。
Async PPO 适用于“系统吞吐为主要瓶颈”的场景。如果任务规模不大,或者同步 PPO 已能稳定高效地完成训练,优先选用同步方案通常更为简单。
2. 适用范围#
当前 RLinf 中的 Async PPO 只用于具身任务,相关入口和核心组件如下:
训练入口:
examples/embodiment/train_async.pyRunner:
rlinf/runners/async_ppo_embodied_runner.pyActor Worker:
rlinf/workers/actor/async_ppo_fsdp_worker.pyRollout Worker:
rlinf/workers/rollout/hf/async_huggingface_worker.pyEnv Worker:
rlinf/workers/env/async_env_worker.py
目前实现有以下边界条件:
仅支持具身任务,不支持 reasoning 或 agent 任务。
Async PPO 分支要求
algorithm.loss_type: decoupled_actor_critic。actor 模型必须带 value head,即
actor.model.add_value_head: True。AsyncEnvWorker和AsyncMultiStepRolloutWorker不支持 offload。当
rollout.recompute_logprobs=True时,actor 权重 offload 不受支持。AsyncPPOEmbodiedRunner当前不支持验证流程;若配置runner.val_check_interval > 0,系统会给出 warning 并跳过验证。
3. 为什么需要 Async PPO#
同步 PPO 的执行流程大致是:先采样一批轨迹,然后停止采样;接着计算优势并做若干轮更新,再同步新权重,随后进入下一轮采样。这种模式设置了很强的全局同步点——任何一个阶段出现瓶颈,其他部分都得等待。在具身任务中,这一问题尤为突出:物理仿真、渲染或重置逻辑可能拖慢环境;rollout 可能依赖较重的视觉编码或大模型推理;而训练阶段本身又需要较大的 batch 和较多 GPU 资源。
Async PPO 的思路很简单:把环境交互、策略推理和训练这三部分拆开,让它们并发执行。环境持续产生观测,rollout 持续消费观测并输出动作、累积轨迹,训练模块则持续消费已完成的 rollout batch 并进行参数更新。这样能提高整体资源利用率,但代价是训练拿到的样本不一定来自最新策略,因此需要显式处理样本陈旧性问题。
4. 系统架构#
Async PPO 在 RLinf 中的逻辑数据流可以概括为:
AsyncEnvWorker
| env_channel
v
AsyncMultiStepRolloutWorker
| actor_channel
v
AsyncPPOEmbodiedFSDPActor
| weight sync
v
AsyncMultiStepRolloutWorker
其中还存在一条反向动作流:
rollout 从环境观测中推理动作。
rollout 通过
rollout_channel将动作发送回 env。env 根据动作继续 step,并把新的观测再次写入
env_channel。
从职责划分上看:
AsyncEnvWorker负责长期运行的环境交互循环。AsyncMultiStepRolloutWorker负责策略推理、轨迹收集和版本记录。AsyncPPOEmbodiedRunner负责训练节奏控制、权重同步和指标汇总。AsyncPPOEmbodiedFSDPActor负责优势计算、近端 logprob 计算和 PPO 更新。
这套设计的关键不是“异步调用”,而是“长期存活的工作流分离”。env 和 rollout 并不会在每个训练 step 被重新启动,而是在后台持续运行。
5. 执行流程#
一次 Async PPO 训练更新在系统层面的执行顺序如下:
runner 初始化 worker,并先将 actor 权重同步到 rollout。
env 调用
bootstrap_step(),生成初始观测。rollout 持续执行
generate_one_epoch(): - 从env_channel读取环境输出。 - 基于当前 rollout 权重推理动作。 - 记录prev_logprobs、prev_values、forward_inputs。 - 为当前样本打上策略版本versions。 - 将动作发送回rollout_channel,供 env 继续执行。一个 rollout epoch 结束后,rollout 将轨迹切分并发送到
actor_channel。actor 从
actor_channel接收轨迹,执行: - 可选重算proximal_logprobs; - 计算 advantages 和 returns; - 展平[T, B, ...]维度并打乱; - 按global_batch_size和micro_batch_size训练。actor 完成一次更新后,runner 增加
global_step,并再次将新权重同步给 rollout。rollout 继续用新的版本采样;如果 rollout 跑得过快,系统会通过陈旧度控制逻辑限制其继续前进。
这里要特别注意:Async PPO 是“采样与训练重叠”,但不是“完全无序并发”。权重同步、版本推进和样本准入都有明确规则。
6. 版本控制与样本陈旧性#
异步训练最核心的问题是样本陈旧性。RLinf 的 Async PPO 通过两个层面控制它。
6.1 样本版本标记#
rollout 在生成每个 step 的数据时,会将当前 rollout 策略版本写入 versions。这个字段表示该样本是由哪一版策略产生的行为数据。
随后 actor 在训练时还会维护自己的当前版本号。这样,一批样本就同时关联了:
行为策略版本:样本真正产生时使用的策略版本。
近端策略版本:本轮 PPO 更新所依赖的 anchor 版本。
当前训练版本:正在优化的目标版本。
这使得系统能够显式感知“这批数据到底老了多少”。
6.2 actor 侧控制数据版本陈旧性#
actor 依据 staleness_threshold 筛选样本,保证所用样本的版本陈旧程度不超过该阈值。
此外,on_policy_min_ratio 用于控制 rollout store 中 on-policy 样本的比例,确保本轮训练中 on-policy 样本占比不低于该值(默认为 0)。
当每个 actor rank 累积的版本合法轨迹数量达到 rollout_store_size_per_rank 时,该 actor 开始执行训练(默认为1)。
7. 算法机制#
7.1 三个策略视角#
为了理解 RLinf 中的 Async PPO,需要区分三种策略:
行为策略 \(\pi_{b}\):实际生成样本的 rollout 策略。
近端策略 \(\pi_{p}\):当前 PPO 更新所参考的 anchor 策略。
当前策略 \(\pi_{\theta}\):正在反向传播中被优化的策略。
同步 PPO 中,这三者通常非常接近,甚至可以近似看作只涉及“旧策略”和“新策略”。但在异步场景里,行为策略可能明显落后于当前训练策略,因此需要单独处理。
7.2 解耦 actor-critic 损失#
RLinf 的 Async PPO 使用 decoupled_actor_critic 损失。它不是直接对行为策略做标准 PPO clip,而是把“近端约束”和“行为分布修正”拆开处理。
记
则 actor 部分可以理解为:先围绕近端策略 \(\pi_p\) 做 PPO clip,再用 \(w_{\mathrm{behav}}\) 修正行为策略带来的分布偏移。
其工程意义很直接:
PPO clip 负责控制“当前策略相对近端策略”的更新幅度。
行为权重负责控制“旧样本相对近端策略”的偏移影响。
7.3 dual-clip 与行为样本裁剪#
为了进一步提高稳定性,RLinf 还叠加了两层保护:
clip_ratio_c:dual-clip,上界限制极端优势值对梯度的冲击。behave_weight_threshold:当行为权重过大时,直接将这部分样本屏蔽。
如果训练中出现明显震荡,优先检查这两个参数和样本陈旧度指标,而不是直接增大学习率或 batch size。
7.4 GAE 与 value head#
优势函数仍然使用标准 GAE:
这意味着 Async PPO 仍然需要价值估计,因此 actor 模型必须带 value head。配置上至少需要满足:
algorithm.adv_type: gaealgorithm.loss_type: decoupled_actor_criticactor.model.add_value_head: True
8. proximal logprob 的两种来源#
Async PPO 里的关键量之一是 proximal_logprobs。它有两种来源:
显式重算
当
rollout.recompute_logprobs=True时,actor 会在训练前基于当前权重重新前向,显式得到 proximal logprob。近似插值
当
rollout.recompute_logprobs=False时,系统会根据versions将行为 logprob 和当前 logprob 进行插值,近似构造 proximal anchor。
两种方式的取舍如下:
显式重算更稳,适合作为默认配置。
近似插值吞吐更高,但对陈旧样本更敏感。
如果你正在排查训练不稳定问题,优先使用显式重算。
9. 配置说明#
下面是一份 Async PPO 的最小配置骨架,和 examples/embodiment/config/maniskill_async_ppo_openvla.yaml 的核心语义保持一致:
cluster:
num_nodes: 1
component_placement:
actor: 0-3
env: 0-1
rollout: 2-3
runner:
task_type: embodied
max_epochs: 1000
val_check_interval: -1
save_interval: 40
algorithm:
adv_type: gae
loss_type: decoupled_actor_critic
normalize_advantages: True
staleness_threshold: 1
behave_weight_threshold: 2.0
clip_ratio_high: 0.3
clip_ratio_low: 0.3
clip_ratio_c: 3.0
value_clip: 0.2
gamma: 0.99
gae_lambda: 0.95
entropy_bonus: 0.0
rollout_epoch: 1
rollout_store_size_per_rank: 2
on_policy_min_ratio: 0.1
env:
train:
total_num_envs: 16
max_episode_steps: 80
max_steps_per_rollout_epoch: 80
rollout:
backend: huggingface
recompute_logprobs: True
pipeline_stage_num: 1
actor:
training_backend: fsdp
micro_batch_size: 40
global_batch_size: 320
model:
add_value_head: True
这些参数中,最需要重点理解的是以下几项:
staleness_threshold控制 actor 筛选样本时,所用样本的版本陈旧程度不超过该阈值。越小越稳,越大吞吐通常越高。on_policy_min_ratio控制 rollout store 中 on-policy 样本的比例,确保本轮训练中 on-policy 样本占比不低于该值(默认为 0)。rollout_store_size_per_rank控制每个 actor rank 累积的版本合法轨迹数量达到该阈值时,该 actor 开始执行训练(默认为1)。behave_weight_threshold控制旧样本的最大行为权重。越小越保守,越大对旧样本容忍度越高。rollout.recompute_logprobs控制是否显式重算 proximal logprob。建议默认开启。actor.micro_batch_size和actor.global_batch_size直接决定训练阶段的显存占用和吞吐。应优先保证训练稳定和不 OOM,再追求更大 batch。
另外,actor 训练阶段必须满足以下批量约束:
global_batch_size必须能被micro_batch_size * actor_world_size整除。rollout 展平后的样本数必须能被单卡训练 batch 整除。
如果这些条件不满足,系统会在训练时直接报错。
10. 启动方式与模型链接#
推荐使用脚本启动:
bash examples/embodiment/run_async.sh maniskill_async_ppo_openvla
运行前需要注意:
多机训练时,Ray 必须提前启动,且只在 head 节点运行训练入口。
run_async.sh会设置MUJOCO_GL=egl和PYOPENGL_PLATFORM=egl。ROBOT_PLATFORM必须与机器人平台一致,否则动作维度和归一化逻辑可能不匹配。
目前 examples/embodiment/config/ 目录下的 Async PPO 配置都已经过测试,可以直接用来跑具身任务。
以下是对应yaml所使用模型的下载链接:
libero_goal_async_ppo_gr00t.yaml: https://huggingface.co/RLinf/RLinf-Gr00t-SFT-Goallibero_object_async_ppo_openpi_pi05.yaml: https://huggingface.co/RLinf/RLinf-Pi05-LIBERO-SFTlibero_spatial_async_ppo_openpi.yaml: https://huggingface.co/RLinf/RLinf-Pi0-LIBERO-Spatial-Object-Goal-SFTlibero_spatial_async_ppo_openpi_pi05.yaml: https://huggingface.co/RLinf/RLinf-Pi05-LIBERO-SFTmaniskill_async_ppo_openpi.yaml: https://huggingface.co/RLinf/RLinf-Pi0-ManiSkill-25Main-SFTmaniskill_async_ppo_openpi_pi05.yaml: https://huggingface.co/RLinf/RLinf-Pi05-ManiSkill-25Main-SFTmaniskill_async_ppo_openvla.yaml: https://huggingface.co/gen-robot/openvla-7b-rlvla-warmupmaniskill_async_ppo_openvlaoft.yaml: https://huggingface.co/Haozhan72/Openvla-oft-SFT-libero10-trajall
其中 maniskill_async_ppo_openvlaoft.yaml 中所使用的lora HF仓库链接为: https://huggingface.co/RLinf/RLinf-OpenVLAOFT-ManiSkill-Base-Lora
11. 监控指标#
调试 Async PPO 时,不要只看 reward。至少需要同时观察以下几类指标:
系统侧:
time/env/*:环境交互是否成为瓶颈。time/rollout/*:推理是否成为瓶颈。time/actor_training:训练阶段是否过重。
策略更新侧:
train/actor/proximal_approx_kl:当前策略相对近端策略的偏移。train/actor/clip_fraction:PPO clip 命中比例。train/actor/dual_clip_fraction:dual-clip 命中比例。
样本陈旧性侧:
train/actor/behav_approx_kl:行为策略与近端策略的偏移。train/actor/behav_clip_fraction:被行为权重裁掉的样本比例。train/actor/average_version:训练样本的平均版本号。train/actor/current_version:当前训练版本号。
如果系统吞吐很高,但 behav_approx_kl 和 behav_clip_fraction 长期偏高,通常说明 rollout 超前过多,训练已经开始受过时样本影响。
12. 调参建议#
建议按下面的顺序调参。
12.1 先求稳,再提吞吐#
推荐起点:
staleness_threshold: 1behave_weight_threshold: 2.0rollout.recompute_logprobs: True
先确认 reward、KL 和行为裁剪指标稳定,再尝试提高系统吞吐。
12.2 吞吐不足时怎么调#
优先顺序建议如下:
调整
cluster.component_placement,减少 env、rollout、actor 之间的资源争用。提高
env.train.total_num_envs,增加并行环境数。适度增大
staleness_threshold,例如从1调到2。最后才考虑关闭
recompute_logprobs。
12.3 训练不稳时怎么调#
优先顺序建议如下:
降低
staleness_threshold。降低
behave_weight_threshold。降低 actor 学习率。
降低
clip_ratio_high和clip_ratio_low。减少
update_epoch或减小 batch。
12.4 OOM 时怎么调#
优先顺序建议如下:
降低
actor.micro_batch_size。开启或保留
gradient_checkpointing。适度减少
env.train.total_num_envs。
13. 与同步 PPO 的区别#
可以用一句话概括两者差异:
同步 PPO 以“样本新鲜度优先”为核心。
Async PPO 以“系统吞吐优先,但通过版本和权重机制控制陈旧性”为核心。
因此,Async PPO 相比同步 PPO 的主要变化不是目标函数本身,而是训练系统的执行模型发生了改变。