配方:完全异步策略训练器
作者: https://github.com/meituan-search
最后更新:2025 年 10 月 18 日。
本文档介绍了一个完全异步的 PPO 训练系统,该系统完全解耦了训练器(Trainer)和采样器(Rollouter), 支持异步样本生成和训练。 在该系统下,我们在使用 128 个 GPU 训练 Qwen2.5-7B 模型时,实现了 2.35 倍到 2.67 倍的性能提升,且未显著影响训练结果。
简介
背景
相比于共址(colocate)架构,分离式采样和训练架构可以更灵活地分配资源、设计更灵活的训练逻辑,从而解决由长尾问题(long-tail problems)导致的 GPU 利用率低和训练效率不高等问题。
one_step_off_policy 通过设计分离式架构并对一轮采样和训练进行异步处理,缓解了长时间采样的问题,并在一定程度上提高了训练效率。
然而,它强制使用一轮异步训练的数据,不够灵活,也无法完全消除长尾问题对训练效率的影响。
在 AReaL、Magistral、StreamRL 和 AsyncFlow 等其他框架中,基于分离式架构已经实现了异步训练和流式训练,并取得了成效。
我们借鉴了它们的方法,并将其应用到了 VERL 中。fully_async_policy 支持异步、流式和部分采样(partial rollout)训练。
通过合理设置资源分配和参数同步频率等参数,fully_async_policy 可以显著提高训练效率。
Magistral https://arxiv.org/abs/2506.10910
AReaL: A Large-Scale Asynchronous Reinforcement Learning System for Language Reasoning https://arxiv.org/abs/2505.24298
StreamRL: Scalable, Heterogeneous, and Elastic RL for LLMs with Disaggregated Stream Generation https://arxiv.org/abs/2504.15930
AsyncFlow: An Asynchronous Streaming RL Framework for Efficient LLM Post-Training https://arxiv.org/abs/2507.01663
核心贡献
资源隔离:与
hybrid_engine不同,采样器(Rollouter)和训练器(Trainer)使用独立的计算资源,并且需要分别指定它们占用的资源。并行生成与训练:在训练器(Trainer)进行训练的同时,采样器(Rollouter)正在生成新的样本。
多步异步:相比于单步离策略(one step off policy),它支持从 0.x 步到多步的异步设置,使异步解决方案更加灵活。
NCCL 参数同步:使用 NCCL(Nvidia Collective Communications Library)通信原语进行采样器(Rollouter)和训练器(Trainer)之间的参数通信。
流式推理与训练:采样器(Rollouter)逐个样本生成数据,数据传输以单个样本作为最小传输单元。
异步训练与新鲜度控制:通过设置参数
async_training.staleness_threshold,支持使用旧参数生成的样本进行训练。部分采样(Partial Rollout):采样器(Rollouter)的推理过程支持部分采样逻辑。在参数同步期间,通过添加
sleep()和resume()逻辑,它会保存正在进行的采样中的样本,并在下一次采样中继续使用,从而减少在参数同步期间等待进行中任务完成所花费的时间。
目前支持的使用模式是 fsdp+vllm。vllm 必须使用基于 AgentLoop 的服务器模式。
设计
fully_async_policy 的整体架构如图所示。fully_async_policy 主要包含四个部分:采样器(Rollouter)、消息队列(MessageQueue)、训练器(Trainer)和参数同步器(ParameterSynchronizer)。
采样器(Rollouter)逐个样本生成序列,并将生成的样本放入消息队列(MessageQueue),生成速度由新鲜度(freshness)控制。
消息队列(MessageQueue)用于临时存储采样器(Rollouter)生成的样本。
训练器(Trainer)逐个样本从消息队列(MessageQueue)中获取样本。在获取了
require_batches*ppo_mini_batch_size个样本后,它将执行训练。训练async_training.trigger_parameter_sync_step轮后,它将触发与采样器(Rollouter)的参数同步。参数同步器(ParameterSynchronizer)实现 NCCL 同步参数同步功能。
与基础方案相比,其优势在于:在共址(colocate)情况下,使用更多资源进行采样无法解决由长尾样本引起的空闲问题。 在我们进行资源隔离后,采样和训练的时间可能会比之前长(因为使用的资源较少), 但它们时间消耗的重叠减少了端到端的总时间消耗。
用法
参数说明
超参数 |
含义 |
|---|---|
|
训练器(Trainer)的节点数 |
|
训练器(Trainer)的每节点 GPU 数 |
|
采样器(Rollouter)的节点数 |
|
采样器(Rollouter)的每节点 GPU 数 |
|
在完全异步策略下,此值无效(默认为 0) |
|
在完全异步策略下,使用流式样本生成逻辑(默认为 1) |
|
总采样步数(总采样样本数) |
|
采样器(Rollouter)更新参数多少次后执行一次验证 |
|
|
|
FullyAsyncTrainer 一次性获取的 |
|
指示 FullyAsyncTrainer 在执行参数同步前执行多少次本地更新 |
|
新鲜度控制 |
|
是否执行部分采样(partial_rollout) |
|
使用采样器(Rollouter)生成的 |
详细解释:
rollout.total_rollout_steps与共址(colocate)相比,可以通过
train_batch_size和“步数”来对齐数量:rollout.total_rollout_steps = data.train_batch_size * step。async_training.trigger_parameter_sync_step在完全异步策略下,它表示训练器(Trainer)在与采样器(Rollouter)进行参数同步前执行多少次本地更新(即获取
require_batches * ppo_mini_batch_size样本的次数)。 在采样器(Rollouter)和训练器(Trainer)的两次参数同步之间,训练器(Trainer)将处理trigger_parameter_sync_step* require_batches*ppo_mini_batch_size个样本。 为了公平地与共址(colocate)比较速度,trigger_parameter_sync_step应设置为data.train_batch_size / (require_batches * ppo_mini_batch_size)。async_training.staleness_threshold在完全异步策略下,它表示允许使用的陈旧样本(stale samples)的最大比例。
staleness_threshold=0,表示同步训练。 采样器(Rollouter)将在两次参数更新之间生成固定数量的样本,样本数量为: $$rollout_num = (trigger_parameter_sync_steprequire_batchesppo_mini_batch_size)$$staleness_threshold>0,表示异步训练,可以设置为小数以实现更灵活的异步调用。 采样器(Rollouter)将在两次参数更新之间最多生成以下数量的样本: $$rollout_num = (1+staleness_threshold)(trigger_parameter_sync_steprequire_batches*ppo_mini_batch_size) - num_staleness_sample $$
num_staleness_sample表示上次采样过程中超额生成的陈旧样本数量。由于这是一个流式系统,采样器(Rollouter)会持续生成,训练器(Trainer)会持续消耗。如果采样器(Rollouter)速度较慢,训练器(Trainer)会提前触发参数同步,而采样器(Rollouter)实际上不会生成
rollout_num个样本。 当采样器(Rollouter)速度足够快时,将staleness_threshold设置为 1 基本上等同于one_step_off_policy。 为避免过多的陈旧样本影响训练精度,建议将此值设置为小于 1。async_training.partial_rolloutpartial_rollout仅在staleness_threshold>0时才真正生效。async_training.use_rollout_log_probs在强化学习算法中,
log_probs与参数版本和 token 存在隐式关联。由于 PPO/GRPO/DAPO 等算法的设置,在计算重要性采样时,old_log_prob必须使用与采样参数和 token 相对应的log_probs,以确保算法的正确性。在完全异步策略中,我们默认old_log_prob由采样器(Rollouter)计算,而不是由训练器(Trainer)计算。async_training.require_batches在流式训练中,
require_batches应设置为 1,表示生成足够的ppo_mini_batch_size样本后进行训练。 在实际测试中,我们发现一次发放的样本数较少时,由于数据分发的顺序问题,可能导致训练不稳定和响应长度变长。 在此,我们额外提供了require_batches用于流式分发,并控制一次参与训练的样本数量。
支持的模式
On policy pipeline(同步策略流水线):
trigger_parameter_sync_step=1,staleness_threshold=0采样器(Rollouter)一次性生成
require_batches*ppo_mini_batch_size个样本,训练器(Trainer)获取这些样本进行训练,训练完成后,训练器(Trainer)和采样器(Rollouter)进行参数同步;在采样阶段,如果存在长尾样本但采样样本数很少,短样本无法填满空闲资源,导致部分资源浪费。
如图 a 所示;
Stream off policy pipeline(流式离策略流水线):
trigger_parameter_sync_step>1,staleness_threshold=0将执行同步流式训练。采样器(Rollouter)一次性生成
require_batches*ppo_mini_batch_size*trigger_parameter_sync_step个样本,训练器(Trainer)在每次获取require_batches*ppo_mini_batch_size个样本后执行一次本地训练,训练trigger_parameter_sync_step次后,训练器(Trainer)和采样器(Rollouter)进行参数同步;与模式 a 相比,由于一次生成更多样本,资源空闲率会更低。
在单步训练中,会存在两个资源空闲期:在获取第一批样本时,训练(Trainer)等待
require_batches*ppo_mini_batch_size个样本生成;在最后一次参数更新时,采样器(Rollouter)等待训练完成。如图 b 所示;
Async stream pipeline with stale samples(带陈旧样本的异步流式流水线):
trigger_parameter_sync_step>=1,staleness_threshold>0,partial_rollout=False每次参数更新后,采样器(Rollouter)计划生成最多
rollout_num个样本(实际上,样本生成数量可能少于此值,取决于采样速度)。如果采样过程相对较快,采样器(Rollouter)将在参数同步前生成一些额外的样本
num_stale_samples,供训练器(Trainer)在同步后立即使用。 触发参数同步时,如果采样器(Rollouter)有正在进行的任务,它将等待任务完成,而不是添加新任务;与模式 b 相比,除了第一次训练步骤外,后续训练不会有等待第一批采样完成的时间,但会有等待活动任务完成的时间。
如图 c 所示;
Async stream pipeline with partial rollout(部分采样异步流式流水线):
trigger_parameter_sync_step>=1,staleness_threshold>0,partial_rollout=True与模式 c 相比,在触发参数同步时,如果采样器(Rollouter)有正在生成的样本,它将中断采样过程并进行参数同步。中断的样本将在同步后继续生成。这减少了等待活动任务完成的时间。
如图 d 所示;
关键指标
指标 |
含义 |
|---|---|
|
训练器(Trainer)的空闲率 |
|
采样器(Rollouter)的空闲率 |
|
训练中使用的陈旧样本总数 |
|
训练中使用的陈旧轨迹总数(一个样本产生 |
|
两次 |
|
两次 |
|
两次 |
参数调整建议
资源分配与调整:
合理的资源分配是达到良好训练效率的前提。理想的资源分配应使采样时间(rollout time)和训练时间(train time)接近,从而最小化整个训练过程中的流水线气泡(pipeline bubbles),避免资源空闲,并确保训练器(Trainer)不使用陈旧样本。在实际训练场景中,可以根据实际训练期间采样器(Rollouter)和训练器(Trainer)的空闲时间进行调整,这些信息可以从
rollouter/idle_ratio和trainer/idle_ratio中获取。如果rollouter/idle_ratio较高而trainer/idle_ratio较低,则应增加训练器(Trainer)的资源并减少采样器(Rollouter)的资源,反之亦然。
关键参数:
staleness_threshold:设置过高会导致使用更多陈旧样本,影响模型性能。建议设置为小于 1。require_batches:越接近 1,越接近纯流式处理,训练气泡越小,可实现的加速效果越快,但会影响样本处理顺序;trigger_parameter_sync_step:设置值越小,越接近同步策略(on policy),但会导致频繁的参数同步。长尾样本浪费了短样本无法填补的资源,导致资源利用率低下。 设置值越大,计算效率越高,但准确性会受到离策略(off policy)的影响。rollout.test_freq:它会占用采样器(Rollouter)的资源,不建议设置得过小。
模式选择:通过调整不同参数,完全异步(Fully Async)架构支持不同级别的优化加速,适用于不同场景下的任务。
对于需要确保训练稳定性、同步策略性质,且对速度要求不高的中小型任务,可以尝试同步策略流水线模式(模式 1)。
对于需要提高训练吞吐量但对陈旧样本敏感的场景,可以尝试流式离策略流水线模式。即通过设置
trigger_parameter_sync_step>1来提高训练效率,同时保持同步机制(staleness_threshold=0)(模式 2)。对于大规模、高训练速度要求且能容忍一定程度离策略和陈旧样本的任务,设置为
staleness_threshold>0和partial_rollout=True可以提高训练效率,使用异步流式流水线模式(模式 3 或 4)。
快速开始
rollout_mode="async"
rollout_name="vllm" # sglang 或 vllm
if [ "$rollout_mode" = "async" ]; then
export VLLM_USE_V1=1
return_raw_chat="True"
fi
train_prompt_bsz=0
gen_prompt_bsz=1
n_resp_per_prompt=16
train_prompt_mini_bsz=32
total_rollout_steps=$(((512*400)))
test_freq=10
staleness_threshold=0
trigger_parameter_sync_step=16
partial_rollout=False
python -m recipe.fully_async_policy.fully_async_main \
train_batch_size=${train_prompt_bsz} \
data.gen_batch_size=${gen_prompt_bsz} \
data.return_raw_chat=${return_raw_chat} \
actor_rollout_ref.rollout.n=${n_resp_per_prompt} \
actor_rollout_ref.actor.strategy=fsdp2 \
critic.strategy=fsdp2 \
actor_rollout_ref.hybrid_engine=False \
actor_rollout_ref.actor.use_dynamic_bsz=${use_dynamic_bsz} \
actor_rollout_ref.ref.log_prob_use_dynamic_bsz=${use_dynamic_bsz} \
actor_rollout_ref.rollout.log_prob_use_dynamic_bsz=${use_dynamic_bsz} \
actor_rollout_ref.rollout.name=${rollout_name} \
actor_rollout_ref.rollout.mode=${rollout_mode} \
actor_rollout_ref.rollout.calculate_log_probs=True \
trainer.nnodes="${NNODES_TRAIN}" \
trainer.n_gpus_per_node="${NGPUS_PER_NODE}" \
rollout.nnodes="${NNODES_ROLLOUT}" \
rollout.n_gpus_per_node="${NGPUS_PER_NODE}" \
rollout.total_rollout_steps="${total_rollout_steps}" \
rollout.test_freq="${test_freq}" \
async_training.staleness_threshold="${staleness_threshold}" \
async_training.trigger_parameter_sync_step="${trigger_parameter_sync_step}" \
async_training.partial_rollout="${partial_rollout}"
实验
7B 模型异步训练
我们使用 Qwen2.5-Math-7B 来验证完全异步策略在长候选(long candidates)和多资源下的优势。 使用“带陈旧样本的异步流式流水线(async stream pipeline with stale samples)”策略,我们在 32 卡、64 卡和 128 卡上实现了约 2 倍的性能提升,且未显著影响实验结果。
机器:H20
模型:Qwen2.5-Math-7B
采样长度:
max_response_lengthFSDP2: 28K tokens;算法:DAPO
数据集:
TRAIN_FILE: dapo-math-17k.parquetTEST_FILE: aime-2024.parquet引擎:vllm+FSDP2
rollout.n: 16ppo_mini_batch_size: 32test_freq: 20共址同步(colocate sync):
步数(step):400
训练批次大小(
train_batch_size):512
完全异步策略(
fully_async_policy):total_rollout_steps: 512*400require_batches: 4trigger_parameter_sync_step: 4staleness_threshold: 0.5partial_rollout: True
训练模式 |
资源分配 |
步数(step) |
生成(gen) |
旧 |
更新 actor |
100 步总时长 |
200 步总时长 |
300 步总时长 |
400 步总时长 |
准确率/均值@1 |
|---|---|---|---|---|---|---|---|---|---|---|
共址同步 (colocate sync) |
32 |
790.10 |
357.41 |
107.71 |
313.81 |
13h 44m |
1d 3h 43m |
2d 9h 22m |
3d 17h 5m |
max: 0.3313 |
完全异步策略 (fully_async_policy) |
16:16 |
294.77 |
21.26 |
\ |
269.80 |
7h 58m |
16h 21m |
1d 0h 53m |
1d 9h 26m |
max: 0.3302 |
共址同步 (colocate sync) |
64 |
365.28 |
150.72 |
70.26 |
133.41 |
10h 22m |
20h 45m |
1d 7h 6m |
1d 17h 32m |
max: 0.3365 |
完全异步策略 (fully_async_policy) |
32:32 |
189.26 |
28.46 |
\ |
156.98 |
4h 57m |
10h 14m |
16h 58m |
21h 40m |
max: 0.3677 |
共址同步 (colocate sync) |
128 |
356.30 |
177.85 |
53.92 |
113.81 |
8h 36m |
17h 56m |
1d 5h 6m |
1d 16h 48m |
max: 0.3573 |
完全异步策略 (fully_async_policy) |
64:64 |
150.63 |
33.14 |
\ |
113.16 |
3h 13m |
6h 46m |
10h 53m |
17h 22m |
max: 0.3521 |
原始数据来源:https://wandb.ai/hou-zg-meituan/fully-async-policy-colocate_async?nw=nwuserhouzg
128 卡 7B 模型异步模式实验
我们使用 Qwen2.5-Math-7B 来验证完全异步策略所支持的各种模式的效果。 我们可以看到,流式处理带来的收益约为 1.6 倍,结合陈旧样本和部分采样后,收益达到了 2.35 倍。
模式 |
步数(step) |
生成(gen) |
旧 |
更新 actor |
100 步总时长 |
200 步总时长 |
300 步总时长 |
400 步总时长 |
准确率/均值@1 |
|---|---|---|---|---|---|---|---|---|---|
共址同步 (colocate sync) |
356.30 |
177.85 |
53.92 |
113.81 |
8h 36m |
17h 56m |
1d 5h 6m |
1d 16h 48m |
max: 0.3573 |
|
231.34 |
128.47 |
\ |
98.77 |
4h 25m |
9h 41m |
15h 2m |
1d 1h 53m |
max: 0.2844 |
|
|||||||||
|
150.63 |
33.14 |
\ |
113.16 |
3h 13m |
6h 46m |
10h 53m |
17h 22m |
max: 0.3521 |
原始数据来源:https://wandb.ai/hou-zg-meituan/fully-async-policy-stream_stale_partial?nw=nwuserhouzg
128 卡陈旧样本消融实验
在“带部分采样的异步流式流水线(async stream pipeline with partial rollout)”模式下,我们验证了陈旧样本设置对训练效率的影响。
我们发现,陈旧度(staleness)越大,最终收益越明显。
我们还注意到,staleness_threshold 为 0.3 和 0.5 时的耗时非常接近,这是因为随着训练步数的增加,响应长度发生显著变化,导致训练不稳定。
这个问题还需要进一步的分析和优化。
|
步数(step) |
生成(gen) |
旧 |
更新 actor |
100 步总时长 |
200 步总时长 |
300 步总时长 |
400 步总时长 |
准确率/均值@1 |
|---|---|---|---|---|---|---|---|---|---|
0 |
231.34 |
128.47 |
\ |
98.77 |
4h 25m |
9h 41m |
15h 2m |
1d 1h 53m |
max: 0.2844 |
0.1 |
171.30 |
58.17 |
\ |
109.12 |
3h 53m |
8h 37m |
14h 25m |
19h 59m |
max: 0.3542 |
0.3 |
146.11 |
38.88 |
\ |
103.22 |
3h 18m |
6h 49m |
11h 40m |
17h 20m |
max: 0.3469 |
0.5 |
150.63 |
33.14 |
\ |
113.16 |
3h 13m |
6h 46m |
10h 53m |
17h 22m |
max: 0.3521 |
原始数据来源:https://wandb.ai/hou-zg-meituan/fully-async-policy-stream_stale_partial?nw=nwuserhouzg
128 卡 7B require_batches 消融实验
在多次测试中,我们发现流式处理中每次发放的样本数量会影响训练时的响应长度,进而影响训练时间。我们通过修改
async_training.require_batches 来验证其对结果的影响。
|
步数(step) |
gen |
旧 |
更新 actor |
100 步总时长 |
200 步总时长 |
300 步总时长 |
准确率/均值@1 |
|---|---|---|---|---|---|---|---|---|
1 |
203.47 |
30.88 |
\ |
181.08 |
3h 31m |
8h 29m |
17h 36m |
max: 0.349 |
2 |
158.72 |
26.32 |
\ |
128.08 |
3h 35m |
7h 38m |
13h 57m |
max: 0.351 |
4 |
124.64 |
25.62 |
\ |
95.06 |
3h 13m |
6h 46m |
10h 53m |
max: 0.3521 |
原始数据来源:https://wandb.ai/hou-zg-meituan/fully-async-policy-ablation_require_batches?nw=nwuserhouzg
30B 模型模式实验
待续:30B 模型实验仍在进行中。
机器:H20
模型:Qwen2.5-32B
采样长度:
max_response_lengthFSDP2: 20K tokens;算法:DAPO
引擎:vllm+FSDP2
rollout.n: 16ppo_mini_batch_size: 32test_freq: 20共址同步(colocate sync):
步数(step):200
训练批次大小(
train_batch_size):512
完全异步策略(
fully_async_policy):total_rollout_steps: 512*200trigger_parameter_sync_step: 512/32 = 16staleness_threshold: 0partial_rollout: False
training mode |
Resource allocation |
mode |
step |
generate_sequences |
old_log_prob |
update_actor |
total time |
acc/best@32/mean |
|---|---|---|---|---|---|---|---|---|
colocate sync |
128 |
|||||||
fully_async_policy |
64:64 |
stream off policy pipeline |
||||||
fully_async_policy |
64:64 |
async stream pipeline with stale samples |
||||||
fully_async_policy |
64:64 |
async stream pipeline with partial rollout |
未来计划
GRPO 实验
Megatron 适配
SGLang 集成
Transfer Queue 集成
异步参数同步
AReaL 异步算法实现
TPPO 算法实现
多轮对话和工具支持