ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

verl One-Step-Off Async Trainer 实战指南:并行化生成与训练,缓解 RL 长尾生成 GPU 空转

verl One-Step-Off Async Trainer 实战指南:并行化生成与训练,缓解 RL 长尾生成 GPU 空转 verl One-Step-Off Async Trainer 实战指南并行化生成与训练缓解 RL 长尾生成 GPU 空转【免费下载链接】verlverl/HybridFlow: A Flexible and Efficient RL Post-Training Framework项目地址: https://gitcode.com/GitHub_Trending/ve/verl本篇指南完整介绍 verlHybridFlow中One Step Off Policy Async Trainer的设计背景、核心实现与使用方法。该方案面向 PPO/GRPO/DAPO 等 RL 后训练中生成阶段占时过长、长尾样本拖慢整体节奏的痛点通过将生成与训练异步重叠、显式切分资源实现吞吐与训练效率的大幅提升。读完本文你将掌握该 trainer 的运行原理、NCCL 参数同步机制、FSDP2 与 Megatron 两种引擎下的启动方式以及卡数配比与动态调优的完整方法论。背景同步训练流程的效率瓶颈verl 默认的强化学习训练流程是同步的严格遵循 PPO、GRPO、DAPO 等经典算法的执行序每一步都用最新模型生成训练样本等生成完成后才进行模型更新。这种流程虽然契合 off-policy RL 的算法语义、有利于训练稳定但存在严重的效率问题模型更新必须等待生成阶段中最长的那条输出完成在长尾样本long-tail samples生成期间大量 GPU 处于空闲状态利用率显著不足样本生成的长尾问题越严重整体训练效率越低。以 DAPO 32B 训练为例Rollout生成阶段约占总耗时的 70%而且单纯增加资源并不能缩短 Rollout 时长——因为同步流程中生成与训练天然串行瓶颈在最长样本而非总算力。解决方案One Step Off Async Trainer为缓解上述问题verl 实现了One Step Off Async Trainer代码位于 verl/experimental/one_step_off_policy核心思想包括三点生成与训练并行Parallel Generation and Training当前 batch 训练的同时异步地为下一个 batch 生成样本两阶段完全重叠资源隔离Resource Isolation与hybrid_engine不同本方案要求显式为 rollout 分配资源剩余资源自动分配给训练。通过削减生成阶段的资源占比缓解长尾生成期间的 GPU 空转NCCL 参数同步NCCL Parameter Synchronization使用 NCCL 通信原语在生成与训练模块之间无缝传输模型参数开销极低。整个过程始终保持一步滞后one-step off的策略语义当前训练所用样本来自上一步生成的样本即训练用策略永远比当前在线策略滞后一步。该方案的设计思路可参考异步 RL 相关公开研究如 AReaL: A Large-Scale Asynchronous Reinforcement Learning System for Language Reasoning、Asynchronous RLHF: Faster and More Efficient Off-Policy RL for Language Modelsverl 仓库中的实现则落地为上述独立 trainer。实验效果端到端提速 23%40%原文档记录了在 2 节点每节点 16 张 H20 GPU上的对比实验机器配置2 节点 × 16 张 H20 GPU生成Generation4 张 GPU训练Training12 张 GPU模型Qwen2.5-Math-7B最大生成长度FSDP2 为 20,480 tokensMegatron 为 8,192 tokens算法DAPORollout 引擎vLLMtraining modeenginestepgenwait_prev_gengenerate_sequencesold_log_probupdate_actortotal timeacc/best32/meanacc/maj32/meancolocate syncVLLMFSDP2749321-2478828619h18m0.59480.417one-step-overlap asyncVLLMFSDP2520-4545810833715h34m23%0.61650.494colocate syncVLLMMegatron699207-16211934418h21m0.6050.4217one-step-overlap asyncVLLMMegatron566-5950112034713h06m (40%)0.65690.4038两步耗时的构成差异一目了然colocate sync同步共置step ≈ gen old_log_prob update_actor——生成时长完全暴露在关键路径上one-step-overlap async一步重叠异步step ≈ wait_prev_gen old_log_prob update_actor——生成被重叠隐藏关键路径上只剩等待上一步生成的未重叠残余。FSDP2 后端整体提速约 23%19h18m → 15h34mMegatron 后端整体提速约 40%18h21m → 13h06m且 best32 精度在异步模式下不降反升说明异步策略在效率与效果上均成立。实现原理一步滞后异步流水线One Step Off Policy Async Pipeline该流水线以极小的侵入成本融入现有训练逻辑不需要额外的样本存储管理。核心机制由两部分构成源码见 ray_trainer.py_create_continuous_iterator构造跨 epoch 的连续迭代器保证 epoch 切换时训练流不断_async_gen_next_batch读取下一 batch 样本 → 同步参数 → 发起异步序列生成并把结果封装成 Future对应文档中的GenerationBatchFuture返回生成与训练由此重叠。主循环的伪代码逻辑如下# iterator generator, simplify one-step integration of the training process def _create_continuous_iterator(self): for epoch in range(self.config.trainer.total_epochs): iterator iter(self.train_dataloader) for batch_dict in iterator: yield epoch, batch_dict # read next batch samples, parameters sync and launch asyn gen_seq def _async_gen_next_batch(self, continuous_iterator): # read train_data try: epoch, batch_dict next(continuous_iterator) except StopIteration: return None batch DataProto.from_single_dict(batch_dict) gen_batch batch_pocess(batch) # sync weights from actor to rollout self.sync_rollout_weights() # async generation gen_batch_output self.rollout_wg.async_generate_sequences(gen_batch) # future encapsulated return GenerationBatchFuture(epoch, batch, gen_batch_output) continuous_iterator self._create_continuous_iterator() # run rollout first to achieve one-step-off batch_data_future self._async_gen_next_batch(continuous_iterator) while batch_data_future is not None: # wait for the gen_seq result from the previous step batch batch_data_future.get() # launch the next async call to generate sequences batch_data_future self._async_gen_next_batch(continuous_iterator) # compute advantages batch critic.compute_values(batch) batch reference.compute_log_prob(batch) batch reward.compute_reward(batch) batch compute_advantages(batch) # model update critic_metrics critic.update_critic(batch) actor_metrics actor.update_actor(batch)注意两个关键细节主循环先执行一次异步生成batch_data_future先于 while 循环被赋值这是实现一步滞后的起点随后每轮训练先get()上一步的生成结果再立刻发起下一步的异步生成从而让生成持续与训练重叠。在真实源码中该逻辑由 OneStepOffRayTrainer 实现它继承自SeparateRayPPOTrainer并在此基础上重构出fit/fit_step/_fit_generate等异步协程fit中通过asyncio.create_task(self._async_gen_next_batch(continuous_iterator))启动首个生成任务fit_step内则在各阶段之间插入await asyncio.sleep(0)确保事件循环能及时推进generate_sequences等异步任务避免某个阶段的阻塞拖垮整个异步工作流源码中_fit_generate先等待上一步batch_data_future随后调用self._fit_update_weights()完成 actor→rollout 权重同步再创建下一个 batch 的生成任务。参数同步基于 NCCL 的权重广播该方案最亮眼的性能点在于基于 NCCL 的 rollout 权重更新绝大多数情况下同步延迟低于300ms对 RLHF 训练几乎可忽略不计。整个同步分为建组与广播两个阶段1元信息交换与通信组建立。actor 端暴露get_actor_weights_infoONE_TO_ALL 分发收集所有参数张量的(key, size, dtype)元信息rollout 端通过set_actor_weights_info接收随后由驱动进程创建 actor rollout 的联合 NCCL 通信组# rollout obtains the meta-info of model parameters from the actor for parameter sync weights_info self.actor_wg.get_actor_weights_info()[0] self.rollout_wg.set_actor_weights_info(weights_info) # Create an actor-rollout communication group for parameter sync actor_rollout_workers self.actor_wg.workers self.rollout_wg.workers collective.create_collective_group( actor_rollout_workers, len(actor_rollout_workers), list(range(0, len(actor_rollout_workers))), backendnccl, group_nameactor_rollout )2逐张量 broadcast。驱动进程分别驱动 actor 与 rollout 执行sync_rollout_weightsactor 侧取参数若为 FSDP 分片则先full_tensor()聚合rollout 侧定位 vLLM 推理模型随后按_weights_info中记录的元信息逐张量创建空 tensor 并执行collective.broadcast(tensor, src_rank0, group_nameactor_rollout)rollout 侧最终通过inference_model.load_weights(...)载入权重# fsdp model parameter sync register(dispatch_modeDispatch.ONE_TO_ALL, blockingFalse) def sync_rollout_weights(self): params self._get_actor_params() if self._is_actor else None if self._is_rollout: inference_model ( self.rollout.inference_engine.llm_engine.model_executor.driver_worker.worker.model_runner.model ) from verl.utils.vllm.patch import patch_vllm_moe_model_weight_loader patch_vllm_moe_model_weight_loader(inference_model) # Model parameters are broadcast tensor-by-tensor from actor to rollout for key, shape, dtype in self._weights_info: tensor torch.empty(shape, dtypedtype, deviceget_torch_device().current_device()) if self._is_actor: assert key in params origin_data params[key] if hasattr(origin_data, full_tensor): origin_data origin_data.full_tensor() if torch.distributed.get_rank() 0: tensor.copy_(origin_data) from ray.util.collective import collective collective.broadcast(tensor, src_rank0, group_nameactor_rollout) if self._is_rollout: inference_model.load_weights([(key, tensor)])从实现看该广播是全量参数、逐张量进行的非增量胜在 NCCL 带宽极高且无需序列化对 MoE 模型还会调用patch_vllm_moe_model_weight_loader处理专家权重的加载。若希望进一步压缩传输量可参考 Delta Weight Sync见下文它支持只广播变更参数的 delta 模式。PPO 正确性rollout log_probs 重要性采样异步训练会带来策略滞后样本由旧策略生成为保证 PPO 算法正确性verl 使用rollout生成时的 log_probs进行 PPO 重要性采样importance sampling即用生成策略的概率评估当前策略下的优势。相关算法细节参见 rollout_corr_math.md。默认配置下该功能以bypass_ppo_clip模式启用但也可按需探索其他修正策略。对应配置项位于 rollout_correction.yamlrollout_is重要性采样IS聚合级别null表示禁用可选token逐 token或sequence逐序列rollout_is_thresholdIS 权重阈值上限或 IcePop 上下界rollout_rs/rollout_rs_threshold拒绝采样rejection sampling级别与阈值bypass_mode操作模式false为 Decoupled3 策略true为 Bypass2 策略loss_typeBypass 模式下的损失类型ppo_clip默认PPO 截断目标或reinforce显式 IS 权重的 REINFORCErollout_is_batch_normalize是否将 IS 权重 batch 归一化到均值 1.0。值得注意的是One Step Off 的配套配置文件将algorithm.rollout_correction.bypass_mode显式设为True见下文配置小节且该机制可与其它 rollout correction 算法组合使用。AgentLoop多轮工具调用支持当前实现中One Step Off 方案不再提供 SPMD 模型 rollout 模式而是全面切换到AgentLoop模式同时支持多轮工具调用multi-turn tool calling。从源码看ray_trainer.py训练器会创建LLMServerManager与AgentLoopManager作为异步 rollout 管理器并要求actor_rollout_ref.rollout.mode asyncRole.Rollout会从 worker 映射中移除交由 AgentLoop 内部创建。若配置了自定义agent_loop_manager_class还会通过 FQN 动态加载对应的 Manager。使用方法目录与入口One Step Off 相关代码全部位于 verl/experimental/one_step_off_policy包括main_ppo.pyHydra 入口基于OneStepTaskRunner启动训练入口文件 中会将顶层rollout.nnodes/rollout.n_gpus_per_node注入actor_rollout_ref.rollout.*ray_trainer.py核心训练器OneStepOffRayTrainerconfig/FSDP2 与 Megatron 两套配置文件shell/可直接运行的示例脚本覆盖 FSDP2/Megatron、vLLM/SGLang、colocate 对照、delta_sharded 等场景。FSDP2 配置示例python3 -m verl.experimental.one_step_off_policy.async_main_ppo \ --config-pathconfig \ --config-nameone_step_off_ppo_trainer.yaml \ actor_rollout_ref.actor.strategyfsdp2 \ # actor and rollout are placed separately actor_rollout_ref.hybrid_engineFalse \ # actor and rollout resource trainer.nnodes1 \ trainer.n_gpus_per_node6 \ rollout.nnodes1 \ rollout.n_gpus_per_node2说明文档中的模块名async_main_ppo在仓库内实际对应 main_ppo.py即python3 -m verl.experimental.one_step_off_policy.main_ppo仓库内的 shell 脚本均使用后者两种写法指向同一入口按你所在仓库版本选择即可。Megatron 配置示例python3 -m verl.experimental.one_step_off_policy.async_main_ppo \ --config-pathconfig \ --config-nameone_step_off_ppo_megatron_trainer.yaml \ actor_rollout_ref.actor.strategymegatron \ # actor and rollout are placed separately actor_rollout_ref.hybrid_engineFalse \ # actor and rollout resource trainer.nnodes1 \ trainer.n_gpus_per_node6 \ rollout.nnodes1 \ rollout.n_gpus_per_node2配套配置文件要点两份配置文件one_step_off_ppo_trainer.yaml、one_step_off_ppo_megatron_trainer.yaml中除了资源隔离用的顶层rollout段还强制了两条必须遵守的约束# config for the rollout (only for resource isolation) rollout: nnodes: 1 n_gpus_per_node: 8 # To adapt to the current logic of AgentLoopManager actor_rollout_ref: rollout: # Must be turned off! Otherwise, Parameter synchronization cannot be performed. free_cache_engine: False # Must be enabled! Otherwise, log_probs cannot be calculated. calculate_log_probs: True checkpoint_engine: backend: nccl # Only then will the use of log probs be correct. # And it can be used in conjunction with other rollout_correction algorithms. algorithm: rollout_correction: bypass_mode: Truefree_cache_engine: False必须关闭否则无法进行参数同步calculate_log_probs: True必须开启否则无法计算 log_probsPPO 重要性采样所必需checkpoint_engine.backend: ncclcheckpoint 引擎使用 NCCL 后端algorithm.rollout_correction.bypass_mode: True启用 Bypass 模式log_probs 的使用才是正确的。直接运行的实战脚本仓库 shell 目录提供了开箱即用的脚本例如 dapo_7b_math_fsdp2_4_12.shDAPO Qwen2.5-Math-7B FSDP24 卡生成 / 12 卡训练的经典配比。脚本中的资源分配方式很典型每节点 8 卡中抽出n_gpus_rollout2给生成剩余n_gpus_training$((NGPUS_PER_NODE - n_gpus_rollout))给训练再分别传给trainer.n_gpus_per_node与rollout.n_gpus_per_noden_gpus_rollout2 n_gpus_training$((NGPUS_PER_NODE - n_gpus_rollout)) python3 -m verl.experimental.one_step_off_policy.main_ppo \ data.train_files${TRAIN_FILE} \ data.val_files${TEST_FILE} \ data.max_prompt_length$((1024 * 2)) \ data.max_response_length$((1024 * 8)) \ data.train_batch_size512 \ actor_rollout_ref.rollout.n12 \ algorithm.adv_estimatorgrpo \ actor_rollout_ref.actor.fsdp_config.strategyfsdp2 \ actor_rollout_ref.hybrid_engineFalse \ actor_rollout_ref.rollout.namevllm \ ... trainer.nnodes${NNODES} \ trainer.n_gpus_per_node${n_gpus_training} \ rollout.nnodes${NNODES} \ rollout.n_gpus_per_node${n_gpus_rollout}脚本还展示了 DAPO 相关的超参clip_ratio_low0.2/clip_ratio_high0.28、overlong buffer 惩罚、loss_agg_modetoken-mean等以及 vLLM 推理侧调优参数enable_chunked_prefillTrue、gpu_memory_utilization0.80、tensor_model_parallel_size、max_num_batched_tokens等可直接作为复现基线。同目录下另有 Megatron、SGLang、colocate 对照、delta_sharded 等变体脚本以及 NPU昇腾脚本grpo_qwen3_8b_gsm8k_fsdp2_8_8_npu.sh。配置指导卡数关系与动态调优1. 卡数关系Card Number Relationships为保证部分资源用于生成时训练样本能在各训练 GPU 上均匀分配需满足以下任一关系actor_rollout_ref.rollout.n应为trainer.n_gpus_per_node * trainer.nnodes的整数因子即能整除actor_rollout_ref.rollout.n * data.train_batch_size应能被trainer.n_gpus_per_node * trainer.nnodes整除。理由生成资源与训练资源分离后只有满足整除关系训练样本才能被均匀切分到每张训练 GPU 上避免负载不均。2. 动态资源调优通过调整trainer.nnodes、trainer.n_gpus_per_node、rollout.nnodes、rollout.n_gpus_per_node四个参数使 Rollout 与训练两阶段耗时相当理想状态Rollout 与训练阶段耗时相近重叠率最高诊断指标监控wait_prev_gen时长——即等待上一次 rollout 结束未被完全重叠的部分所消耗的时间分析sequence_length分布——判断长尾程度调整策略wait_prev_gen高 序列长度均匀 →增加 rollout 资源wait_prev_gen高 序列长尾明显 →优化停止条件单纯加资源无效因为瓶颈是最长样本的尾部。3. 资源受限 vs 资源充裕场景资源受限场景通过调整 GPU 分配比例优化资源利用率保持节点数相等让训练与 rollout 共享节点配置trainer.nnodes rollout.nnodes且trainer.n_gpus_per_node rollout.n_gpus_per_node physical_gpus_per_node通过调节n_gpus_per_node控制 rollout 资源占比资源充裕场景通过调整节点数优化性能保持每节点 GPU 数相等使训练与 rollout 的并行度可独立伸缩配置trainer.n_gpus_per_node rollout.n_gpus_per_node通过调节trainer.nnodes与rollout.nnodes控制 rollout 资源占比。4. 节点总数计算重要系统所需的总节点数并不是简单相加trainer.nnodes rollout.nnodes实际计算取决于 GPU 容量当trainer.n_gpus_per_node rollout.n_gpus_per_node physical_gpus_per_node时所需节点数为max(trainer.nnodes, rollout.nnodes)训练与 rollout 可共享节点当trainer.n_gpus_per_node rollout.n_gpus_per_node physical_gpus_per_node时所需节点数为trainer.nnodes rollout.nnodes必须分置在不同节点。Delta Weight Sync瘦身权重同步对于完全分离disaggregated的运行trainer→rollout 的权重广播可以只传输变化的参数delta而非全量权重。verl 为此提供了独立的设计文档Delta Weight Sync其中覆盖delta与delta_sharded两种 checkpoint-engine 后端对应的配置方式shell 目录下的grpo_0.6b_gsm8k_fsdp2_sglang_delta_sharded_2_6.sh即 delta_sharded 示例后续演进路线Megatron、fp8 支持等。在生成与训练跨机、跨节点部署且通信带宽受限时delta 同步能进一步压低参数同步开销。功能支持矩阵CategorySupport Situationtrain engineFSDP2Megatronrollout enginevLLMSGLangAdvantageEstimatorGRPOGRPO_PASSKREINFORCE_PLUS_PLUSRLOOOPOREINFORCE_PLUS_PLUS_BASELINEGPGRewardall从仓库现状看shell 目录脚本rollout 引擎除 vLLM 外还覆盖了 SGLang含 delta_sharded 变体并在昇腾 NPU 上提供了 FSDP2 运行脚本说明该 trainer 的适配面正在持续扩展。总结One Step Off Async Trainer 是 verl 在 RL 后训练工程化上的一个重要实践以一步滞后的 off-policy 语义换取生成与训练的深度重叠借助 NCCL 实现毫秒级参数同步配合显式资源隔离与灵活的节点/卡数配比在 DAPO 等长生成任务上实现了 23%40% 的端到端提速。其核心代码集中在 verl/experimental/one_step_off_policy入口 main_ppo.py、训练器 ray_trainer.py配套配置文件与可直接运行的 shell 脚本一应俱全。若你的场景中生成阶段尤其长尾样本占比高、GPU 空转严重可参考本文的配置方法论直接评估该方案若跨节点通信成为新瓶颈则可进一步结合 delta 权重同步机制优化。【免费下载链接】verlverl/HybridFlow: A Flexible and Efficient RL Post-Training Framework项目地址: https://gitcode.com/GitHub_Trending/ve/verl创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进