DAPO: An Open-Source LLM Reinforcement Learning System at Scale

Paper: arXiv:2503.14476 Code: BytedTsinghua-SIA/DAPO Code reference: main @ 33fe3176 (2025-05-11) Authors: Qiying Yu, Zheng Zhang, Ruofei Zhu, Yufeng Yuan, Xiaochen Zuo, Yu Yue (Algorithm), Weinan Dai, Tiantian Fan, et al. (Infrastructure), Jiaze Chen, et al. (Dataset) Affiliations: ByteDance Seed, Institute for AI Industry Research (AIR), Tsinghua University, The University of Hong Kong, SIA-Lab of Tsinghua AIR and ByteDance Seed Project Page: dapo-sia.github.io Training implementation refs: verl-recipe main@99fc8b1d + verl main@2eab9364 (2026-05-18)

1. Motivation (研究动机)

这篇论文研究的是 大规模 LLM 强化学习训练系统。作者指出,尽管 test-time scaling(如 OpenAI o1、DeepSeek R1)通过 RL 训练出长 Chain-of-Thought 推理能力,但实际的训练细节一直被隐藏: 作者在 Qwen2.5-32B 上用 naive GRPO 训练,AIME 2024 只达到 30 分,远低于 DeepSeek-R1-Zero 的 47 分; 深入分析后发现 naive GRPO 存在 entropy collapse(策略熵快速下降)、reward noise(截断样本的 reward 不准确)、和 training instability(训练不稳定)等问题; 社区也普遍反映难以复现 R1 的结果,说明关键训练细节可能被遗漏。 因此,这篇论文的目标是:公开一个完整的、可复现的、达到 industry-level 性能的大规模 LLM RL 训练系统,包括算法、代码和数据。

2. Idea (核心思想)

DAPO(Decoupled Clip and Dynamic sAmpling Policy Optimization)的核心思想是:在 GRPO 框架上引入四个关键技术改进,解决 long-CoT RL 训练中的 entropy collapse、zero-gradient、loss imbalance 和 reward noise 问题。

四个技术分别是:

  1. Clip-Higher:解耦上下 clip 阈值,放宽上限让低概率 token 有更大提升空间,防止 entropy collapse;
  2. Dynamic Sampling:过滤掉 accuracy=0 和 accuracy=1 的 prompt,保证每个 batch 都有有效梯度;
  3. Token-Level Policy Gradient Loss:按 token 而非 sample 聚合 loss,避免长序列被低估;
  4. Overlong Reward Shaping:对超长截断样本用软惩罚替代硬惩罚,减少 reward noise。

与 DeepSeek-R1-Zero 的根本区别是:DAPO 不依赖 KL 惩罚项(因为 long-CoT 场景下模型需要大幅偏离初始分布),而是通过上述四个技术共同维持训练稳定性。

3. Method (方法)

3.1 DAPO 目标函数

DAPO 的完整目标函数如下: 其中: 约束条件 表示一个 prompt 的 个回复中,必须既有正确又有错误的才参与训练(Dynamic Sampling 的核心约束)。

3.2 Clip-Higher

Figure 2 解读:左图是 AIME 准确率,右图是 actor model 的生成熵。没有 Clip-Higher 时(紫线),entropy 在约 500 步后快速降到接近 0,说明策略崩溃为几乎确定性输出;有 Clip-Higher 后(蓝线),entropy 保持在合理范围内持续上升,准确率也更高。

问题:标准 PPO/GRPO 使用对称 clip 。当 时,一个概率为 0.01 的 token 最多只能被提升到 ,而概率为 0.9 的 token 可以被提升到 (截断到 1)。上限 clip 严重限制了低概率 “exploration” token 的概率提升空间。

解决方案:解耦上下 clip 阈值:。放宽上限给低概率 token 更多空间,同时保持下限不变以避免不确定 token 概率被压到 0。

3.3 Dynamic Sampling

Figure 3 解读:左图是被 up-clip 的 token 的平均概率(都低于 0.2,验证了 Clip-Higher 的直觉);右图是 accuracy=1 的样本比例随训练增长到 60%+,意味着越来越多 prompt 的 G 个回复全部正确,advantage 为零,产生零梯度。

问题:GRPO 用组内 reward 归一化算 advantage。如果一个 prompt 的 个回复全部正确(reward 相同),advantage 全为零,这个 prompt 不产生梯度。训练中这种 prompt 比例不断增长,有效梯度信号越来越稀疏。

解决方案过采样 + 过滤。在采样时持续生成直到 batch 被 accuracy 既非 0 也非 1 的 prompt 填满。这保证了 batch 中每个 prompt 都有有效梯度。

3.4 Token-Level Policy Gradient Loss

Figure 4 解读:左图是生成熵,右图是平均回复长度。没有 token-level loss 时(紫线),entropy 不稳定,回复长度在 2000-4000 之间剧烈波动;有 token-level loss 后(蓝线),entropy 稳定上升,长度增长更健康。

问题:原始 GRPO 先按 token 对每个 sample 内部取平均,再按 sample 取平均。这意味着不管序列长短,每个 sample 权重相同。长序列中的 token 贡献被稀释,短序列中的 token 贡献被放大。

解决方案:改为按 token 总数做全局平均(分母从 变成 )。这样长序列的 reasoning pattern 有更大影响力,同时长序列中的 gibberish/repetitive pattern 也会被更有效地惩罚。

3.5 Overlong Reward Shaping

Figure 5 解读:左图是 AIME 准确率,右图是 entropy。没有 overlong filtering 时(蓝线),准确率震荡且 entropy 爆炸到 4+;有了之后(紫线),训练稳定得多。

问题:RL 训练通常设置最大生成长度 ,超过的样本被截断。截断样本默认被判为错误并给 reward,但实际上其推理过程可能是正确的,只是太长。这引入了 reward noise。

解决方案:先做 Overlong Filtering(mask 掉截断样本的 loss),再加 Soft Overlong Punishment 其中 是软惩罚缓冲区长度。当回复接近最大长度时,施加渐进惩罚,避免突然切断。

3.6 Algorithm 1: DAPO 完整算法

Algorithm: DAPO
Input: initial policy π_θ, reward model R, task prompts D, hyperparams ε_low, ε_high
1: for step = 1, ..., M do
2:     Sample batch D_b from D
3:     Update old policy π_θ_old ← π_θ
4:     Sample G outputs {o_i}_{i=1}^G ~ π_θ_old(·|q) for each q ∈ D_b
5:     Compute rewards {r_i}_{i=1}^G for each o_i via R
6:     Filter out o_i and add remaining to dynamic sampling buffer  (Eq.11)
7:     if buffer size n_b < N:
8:         continue  # keep sampling until batch is full
9:     For each o_i in buffer, compute Â_{i,t} for each token (Eq.9)
10:    for iteration = 1, ..., μ do
11:        Update π_θ by maximizing DAPO objective (Eq.8)
Output: π_θ

3.7 Pseudocode(基于 verl 公开实现)

组件 A:Clip-Higher (decoupled clip ratios)

# Source: verl/trainer/ppo/core_algos.py
# Corresponds to: actor.clip_ratio_low, actor.clip_ratio_high in config
 
def compute_policy_loss_with_clip_higher(log_prob, old_log_prob, advantages,
                                          clip_ratio_low, clip_ratio_high):
    ratio = torch.exp(log_prob - old_log_prob)
    # Decoupled clipping: different lower and upper bounds
    clipped_ratio = torch.clamp(ratio, 1 - clip_ratio_low, 1 + clip_ratio_high)
    pg_loss1 = ratio * advantages
    pg_loss2 = clipped_ratio * advantages
    loss = -torch.min(pg_loss1, pg_loss2)
    return loss

组件 B:Dynamic Sampling (filter accuracy=0 and accuracy=1 groups)

# Source: verl/trainer/ppo/ray_trainer.py (training loop)
# The dynamic sampling buffer continues sampling until batch is full
 
def dynamic_sampling_step(policy, prompts, G, batch_size):
    buffer = []
    while len(buffer) < batch_size:
        batch = sample_batch(prompts)
        for q in batch:
            outputs = [policy.generate(q) for _ in range(G)]
            rewards = [reward_fn(q, o) for o in outputs]
            n_correct = sum(1 for r in rewards if r > 0)
            # Filter: keep only prompts with 0 < n_correct < G
            if 0 < n_correct < G:
                buffer.append((q, outputs, rewards))
    return buffer[:batch_size]

组件 C:Token-Level Policy Gradient Loss

# Source: verl/trainer/ppo/core_algos.py
# loss_agg_mode="token-mean" in config
 
def token_level_loss_aggregation(per_token_loss, response_mask):
    # Instead of: mean over tokens per sample, then mean over samples
    # Do: sum all token losses, divide by total token count
    total_loss = (per_token_loss * response_mask).sum()
    total_tokens = response_mask.sum()
    return total_loss / total_tokens

组件 D:Overlong Reward Shaping (Soft Overlong Punishment)

# Source: verl reward manager (reward.reward_manager.name=dapo)
 
def compute_overlong_penalty(response_length, max_resp_len, buffer_len, penalty_factor):
    cache_start = max_resp_len - buffer_len
    if response_length <= cache_start:
        return 0.0
    elif response_length <= max_resp_len:
        return penalty_factor * (cache_start - response_length) / buffer_len
    else:
        return -1.0 * penalty_factor

3.8 Code-to-paper mapping table

Code reference: BytedTsinghua-SIA/DAPO main @ 33fe3176 (2025-05-11); training recipe cross-checked against verl-project/verl-recipe main@99fc8b1d and verl-project/verl main@2eab9364.

Paper ConceptSource FileKey Class/Function
DAPO 官方论文/模型/评估仓库BytedTsinghua-SIA/DAPOREADME.md, dapo_paper.pdf, eval/*, img/*
AIME 2024 官方评估入口BytedTsinghua-SIA/DAPO/eval/eval_aime24.pyvLLM/Ray Serve client evaluation script
模型服务封装BytedTsinghua-SIA/DAPO/eval/llm.pyRay Serve + vLLM deployment wrapper
DAPO 训练主入口verl-recipe/dapo/main_dapo.pypython -m recipe.dapo.main_dapo Hydra/Ray entry
32B 复现实验脚本verl-recipe/dapo/run_dapo_qwen2.5_32b.shQwen2.5-32B DAPO full hyperparameters
训练配置模板verl-recipe/dapo/config/dapo_trainer.yamlreward_manager.name=dapo, data.gen_batch_size
Dynamic Samplingverl-recipe/dapo/dapo_ray_trainer.pyfilter_groups, metric=acc, max_num_gen_batches loop
Clip-Higher (decoupled clip)verl/trainer/ppo/core_algos.pyclip_ratio_low, clip_ratio_high, clipped policy loss
Token-Level Policy Gradient Lossverl/trainer/ppo/core_algos.pyagg_loss(..., loss_agg_mode="token-mean")
Overlong Reward Shapingverl/workers/reward_manager/dapo.pyDAPORewardManager, overlong_buffer_cfg
数学答案验证verl/utils/reward_score/math_dapo.pyboxed-answer / AIME integer rule-based scorer
分布式 RL 编排verl/trainer/ppo/ray_trainer.pyRay trainer, rollout / actor / ref worker orchestration

4. Experimental Setup (实验设置)

4.1 模型与数据

Base model: Qwen2.5-32B(预训练模型,非 instruct 版本) 训练数据: DAPO-Math-17K,从 web 和竞赛主页爬取并经 LLM 转换为整数答案格式的数学题 Reward: Rule-based, if correct, otherwise(无 reward model,直接判对错)

4.2 训练配置

Paper-reported optimizer: AdamW, lr = , 20 rollout-step linear warmup(论文 §4.1) Current reproduction script: actor.optim.lr=1e-6, actor.optim.lr_warmup_steps=10, weight_decay=0.1, grad_clip=1.0verl-recipe/dapo/run_dapo_qwen2.5_32b.shRollout: train_prompt_bsz=512, gen_prompt_bsz=1536, 每个 prompt 采样 16 个回复 (n_resp_per_prompt=16) Mini-batch: train_prompt_mini_bsz=32

论文文本描述为 mini-batch 512 / 16 gradient updates,这里需要区分 paper 叙述与当前 recipe 脚本 Max length: paper uses tokens;当前 script 写为 max_prompt_length=2048, max_response_length=20480, overlong_buffer_len=4096 Clip-Higher: ,

script 对应 clip_ratio_low=0.2, clip_ratio_high=0.28 Dynamic Sampling: enable_filter_groups=True, filter_groups_metric=acc, max_num_gen_batches=10 Token-level loss: loss_agg_mode="token-mean",避免长 CoT token 在 sample-level mean 下被过度稀释 评估: 论文 AIME 2024 使用 temperature=1.0、top_p=0.7、重复 32 次取 avg@32;

官方 DAPO 仓库 eval 命令使用 --temperature 1.0 --top_p 0.7 --max_tokens 20480 框架: verl / HybridFlow + Ray;当前 full script 默认 NNODES=16, trainer.n_gpus_per_node=8, tensor_model_parallel_size=4, ulysses_sequence_parallel_size=8, use_dynamic_bsz=True, offload=True

4.3 Baselines

DeepSeek-R1-Zero-Qwen-32B: 47 on AIME 2024(之前 SOTA) Naive GRPO: 30 on AIME 2024(作者的 baseline)

5. Experimental Results (实验结果)

5.1 主结果:逐步添加技术的 ablation

Figure 1 解读:DAPO 在 Qwen2.5-32B 上的 AIME 2024 训练曲线。avg@32(蓝线实线)在约 6000 步达到 50,超过 DeepSeek-R1-Zero-Qwen-32B 的 47(虚线),且只用了约 50% 的训练步数。pass@32 和 cons@32 也在持续提升。

ModelAIME24 avg@32
DeepSeek-R1-Zero-Qwen-32B47
Naive GRPO30
+ Overlong Filtering36
+ Clip-Higher38
+ Soft Overlong Punishment41
+ Token-level Loss42
+ Dynamic Sampling (DAPO)50

每个技术都有正向贡献,从 naive GRPO 的 30 逐步提升到 DAPO 的 50。

5.2 Training Dynamics 分析

Figure 7 解读:左图是平均回复长度,右图是 reward score。回复长度在 1000-4000 之间波动但整体上升,说明模型在学习更长的推理链。Reward 则相对稳定上升,没有明显的 reward hacking。

Figure 7c-d 解读:左图是生成熵,右图是平均生成概率。Entropy 保持在 0.3-0.7 的健康范围内缓慢上升;生成概率从 0.84 下降到 0.74,说明模型在探索更多样的 token 选择。

5.3 Dynamic Sampling 的效率

Figure 6 解读:有无 Dynamic Sampling 的对比。虽然 Dynamic Sampling 需要更多采样次数(因为要过滤掉 accuracy=0/1 的 prompt),但它到达相同性能所需的训练步数更少。总训练时间基本不受影响,因为采样时间在 RL 系统中通常不是瓶颈。

5.4 涌现行为

论文发现在训练过程中,模型逐渐出现了反思和回溯行为——这在训练初期几乎不存在。例如,模型会输出 “However, wait a moment, let’s rethink…” 这样的自我修正模式,说明 RL 训练不仅在强化已有的推理模式,还在涌现全新的推理能力。

5.5 Limitations

论文只在数学任务上验证,未展示代码、科学推理等其他领域的结果; DAPO 的四个技术虽然各自有效,但它们之间的交互效应没有被系统分析; 训练成本仍然很高(512 prompt × 16 responses = 8192 次生成/step),对小团队不够友好。

总体来说,DAPO 的最大贡献是:把一个 industry-level 的 LLM RL 系统从算法到代码到数据全部开源,填补了 DeepSeek-R1 和 OpenAI o1 之间的可复现性空白。

5.6 复现视角下的关键检查点

如果把 DAPO 当作可复现实验而不是只读论文,最容易踩坑的是“论文算法名”和“实际 recipe 参数”之间的错位。论文的四个技术点都落在配置或 trainer 逻辑里;只开 adv_estimator=grpo 并不等于运行 DAPO full recipe。

必须确认 code ref:官方 DAPO 仓库当前 main@33fe3176 主要提供 paper、README、评估脚本、AIME parquet 和图像资源;训练复现入口在 verl-recipe/dapo,核心 loss / reward manager 在 verl必须确认 Dynamic Sampling 真开了:full script 中 enable_filter_groups=Truefilter_groups_metric=accgen_prompt_bsz=3×train_prompt_bsz,否则 batch 中会混入 accuracy 全 0 或全 1 的 prompt group。

必须确认 token-level reductionloss_agg_mode="token-mean" 是 DAPO 相对 GRPO sample-level loss 的核心区别之一;如果回到 sample-level mean,长回答 token 的梯度贡献会被稀释。必须确认 overlong buffer:full script 使用 max_response_length=20480overlong_buffer_len=4096;reward manager 中的惩罚是线性负项,只有在 overlong_buffer.enable=True 时才会生效。

5.6.1 github_ref 与实现 ref

RefVerified valueWhy it matters
Official paper/release repoBytedTsinghua-SIA/DAPO main@33fe3176笔记 frontmatter 的 github_ref;README 声明 AIME 50、AIME 44、数据/权重/评估入口
Training recipe repoverl-project/verl-recipe main@99fc8b1ddapo/run_dapo_qwen2.5_32b.sh, dapo/main_dapo.py, dapo/dapo_ray_trainer.py
Framework implementation repoverl-project/verl main@2eab9364core_algos.py, workers/reward_manager/dapo.py, utils/reward_score/math_dapo.py
arXiv version2503.14476v2, revised 2025-05-20v2 abstract / HTML 与 note 中的 50 AIME、四个技术点、DAPO-Math-17K 一致

5.6.2 Full DAPO script 中的核心超参

CategoryScript valueInterpretation
adv_estimatorgrpoDAPO 仍使用 group-relative advantage,不引入 value model
KL in reward / actor KL lossFalse, Falselong-CoT RL 中不再强行贴近初始 reference policy
clip_ratio_low, clip_ratio_high0.2, 0.28decoupled clip;上界更高以释放低概率探索 token
max_prompt_length2048prompt 截断长度;当前 recipe 明确写为 2K
max_response_length20480rollout 最大生成长度;和论文 16K + 4K soft buffer 对齐
overlong_buffer_len4096soft overlong punishment 的线性缓冲区
train_prompt_bsz512每轮保留的有效 prompt 数
gen_prompt_bsz1536Dynamic Sampling 先过采样,再过滤无梯度 group
n_resp_per_prompt16每个 prompt 的 group size
train_prompt_mini_bsz32当前 recipe 的 actor mini batch;和论文叙述的 mini-batch 512 要区分
filter_groups_metricacc按同一 prompt 下 16 个回复的准确率分布过滤
max_num_gen_batches10防止无限重采样;太难/太易数据会触发上限
temperature, top_p, val_top_p1.0, 1.0, 0.7训练 rollout 更开放,验证按论文使用 top_p 0.7

5.6.3 代码路径到论文概念的解释

dapo_ray_trainer.py 里的 filtering 逻辑是 Dynamic Sampling 的实现核心:它先把新生成 batch 的每条 trajectory 按 uid 汇总到 prompt group,再看该 group 的 acc 是否全相同。如果 16 条回复全错或全对,该 prompt 的 advantage 近似没有有效排序信号,因此被过滤;只有 mixed-outcome group 才进入训练 batch。

core_algos.py 里的 policy loss 是 Clip-Higher 和 token-level loss 的交汇点:clip_ratio_low / clip_ratio_high 分别控制 ratio 下界和上界,agg_loss(..., loss_agg_mode="token-mean") 则把有效 token 当作全局平均对象,而不是先在每条 sample 内平均再对 sample 平均。

workers/reward_manager/dapo.py 负责 Soft Overlong Punishment:当 response length 进入 max_resp_len - overlong_buffer_len 之后的 buffer 区间,reward 会额外加上 min(-exceed_len / overlong_buffer_len * penalty_factor, 0)。这不是简单“超过上限给 -1”,而是在截断前逐渐施压,减少把正确但长的推理链误判为坏样本的噪声。

utils/reward_score/math_dapo.py 与官方 DAPO eval 脚本共同说明 reward 的边界:DAPO 的 reward 是 verifiable math outcome reward,重点是最后答案能否被规则解析并匹配 ground truth;这也是论文把 DAPO-Math-17K 转成整数答案格式的原因。

5.6.4 图表核验与读图要点

Figure assetNote locationWhat to check
score.svg§5.1AIME 2024 从 Naive GRPO 30 逐步到 DAPO 50;DeepSeek-R1-Zero-Qwen-32B 47 是外部对照
3.1.1.svg, 3.1.2.svg§3.2Clip-Higher 后 accuracy 与 entropy 同时改善,支撑“upper clip 限制探索”的解释
3.2.1.svg, 3.2.2.svg§3.3随训练进行,accuracy=1 的 prompt group 增多,Dynamic Sampling 针对的是有效梯度比例下降
3.3.1.svg, 3.3.2.svg§3.4sample-level loss 容易让长度和 entropy 失控,token-level loss 让长 CoT 学习更健康
3.4.1.svg, 3.4.2.svg§3.5overlong shaping 的价值是稳定 reward 与 entropy,而不是单纯压短回答
4.1.1.svg§5.3Dynamic Sampling 虽增加生成尝试,但减少达到同等性能所需训练步数
length.svg, reward.svg, entropy.svg, prob.svg§5.2这些不是最终指标,而是排查 RL 是否跑偏的训练动态监控面板

5.6.5 这篇论文最值得借鉴的实验习惯

第一,DAPO 把“最终分数”拆成了可监控的中间现象:length、reward、entropy、probability。对于 long-CoT RL,最终 AIME score 很稀疏且有滞后;如果只看 score,很难定位是探索塌缩、奖励噪声、过长回答还是 batch 有效梯度不足。 第二,DAPO 的 ablation 顺序是工程诊断式的:先处理 overlong/truncation 噪声,再处理 entropy collapse,再处理 soft overlong,再处理 token-level credit assignment,最后用 Dynamic Sampling 保证每个 batch 的有效 prompt 数。这个顺序体现了系统 RL 的特点:稳定性和可观测性优先于单点算法美感。 第三,DAPO 的开源方式强调“recipe”而不是只开权重:同一论文同时给 paper、dataset、model weights、eval script、wandb record、verl recipe 和 framework code path。对后续工作来说,可复现的最小单元不是一个公式,而是固定 ref 下的 paper + data + launch script + reward/parser + eval。

5.7 Verified References