# Recipe: 解耦裁剪和动态采样策略优化 (DAPO) 上次更新时间:2025年6月19日。 > 开源算法实现与实验运行:[童宇轩](https://tongyx361.github.io/),[盛光明](https://hk.linkedin.com/in/guangming-sheng-b50640211) 🏠 [主页](https://dapo-sia.github.io/) | 📝 [论文@arXiv](https://arxiv.org/abs/2503.14476) | 🤗 [数据集&模型@HF](https://huggingface.co/collections/BytedTsinghua-SIA/dapo-67d7f1517ee33c8aed059da0) | 🐱 [代码@GitHub](https://github.com/volcengine/verl/tree/recipe/dapo/recipe/dapo) | 🐱 [仓库@GitHub](https://github.com/BytedTsinghua-SIA/DAPO) > 我们提出了**D**ecoupled Clip and Dynamic s**A**mpling **P**olicy **O**ptimization (DAPO) 算法。通过公开我们的工作,我们为更广泛的研究社区和社会提供了可扩展强化学习的实际应用途径,使所有人都能从这些进步中受益。我们的系统基于优秀的 [verl](https://github.com/volcengine/verl) 框架。感谢他们的辛勤付出!将 DAPO 训练应用于 Qwen2.5-32B 基模型,在 AIME 2024 上相较于之前的 SOTA 模型 DeepSeek-R1-Zero-Qwen-32B,在训练步数减少 **50%** 的情况下,准确率达到了 **50%**。 > > ![dapo-main-result](https://dapo-sia.github.io/static/images/score.png) ## 快速开始 1. **在 Ray 集群上**准备数据集: ```bash bash prepare_dapo_data.sh # 默认将数据集下载到 ${HOME}/verl/data ``` 2. **从任何机器**将作业提交到 Ray 集群: ```bash cd verl # 仓库根目录 export RAY_ADDRESS="http://${RAY_IP:-localhost}:8265" # 要连接的 Ray 集群地址 export WORKING_DIR="${PWD}" # 要打包到 Ray 集群的本地目录 # 在 yaml 中为 Ray 集群设置运行时环境,如环境变量和 pip 包 export RUNTIME_ENV="./recipe/dapo/runtime_env.yaml" # 这将为 Ray 集群设置环境变量 bash recipe/dapo/run_dapo_qwen2.5_32b.sh # 或其他脚本 ``` ## 复现运行 | 设置 | AIME 2024 准确率 | 硬件 | 镜像 | 提交 | 环境变量 | 训练脚本 | 训练记录 | | -------------------------------------------- | -------------- | --------- | -------------------------------------------------------------------- | -------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------- | | DAPO | 52% | 16x8xH800 | `hiyouga/verl:ngc-th2.6.0-cu126-vllm0.8.3-flashinfer0.2.2-cxx11abi0` | [`4f80e4`](https://github.com/volcengine/verl/tree/4f80e465c2ec79ab9c3c30ec74b9745de61d0490) | [runtime_env.yaml](https://github.com/volcengine/verl/blob/4f80e465c2ec79ab9c3c30ec74b9745de61d0490/recipe/dapo/runtime_env.yaml) | [run_dapo_qwen2.5_32b.sh](https://github.com/volcengine/verl/blob/4f80e465c2ec79ab9c3c30ec74b9745de61d0490/recipe/dapo/run_dapo_qwen2.5_32b.sh) | [W&B](https://wandb.ai/verl-org/DAPO%20Reproduction%20on%20verl/workspace?nw=wmb4qxfht0n) | | DAPO w/o Dynamic Sampling | 50% | 16x8xH800 | `hiyouga/verl:ngc-th2.6.0-cu126-vllm0.8.3-flashinfer0.2.2-cxx11abi0` | [`4f80e4`](https://github.com/volcengine/verl/tree/4f80e465c2ec79ab9c3c30ec74b9745de61d0490) | [runtime_env.yaml](https://github.com/volcengine/verl/blob/4f80e465c2ec79ab9c3c30ec74b9745de61d0490/recipe/dapo/runtime_env.yaml) | [run_dapo_wo_ds_qwen2.5_32b.sh](https://github.com/volcengine/verl/blob/4f80e465c2ec79ab9c3c30ec74b9745de61d0490/recipe/dapo/run_dapo_wo_ds_qwen2.5_32b.sh) | [W&B](https://wandb.ai/verl-org/DAPO%20Reproduction%20on%20verl/workspace?nw=wmb4qxfht0n) | | DAPO w/o Token-level Loss & Dynamic Sampling | 44% | 16x8xH20 | `hiyouga/verl:ngc-th2.5.1-cu120-vllm0.7.4-hotfix` | [`4f80e4`](https://github.com/volcengine/verl/tree/4f80e465c2ec79ab9c3c30ec74b9745de61d0490) | [runtime_env.yaml](https://github.com/volcengine/verl/blob/4f80e465c2ec79ab9c3c30ec74b9745de61d0490/recipe/dapo/runtime_env.yaml) | [run_dapo_early_qwen2.5_32b.sh](https://github.com/volcengine/verl/blob/4f80e465c2ec79ab9c3c30ec74b9745de61d0490/recipe/dapo/run_dapo_early_qwen2.5_32b.sh) | [W&B](https://wandb.ai/verl-org/DAPO%20Reproduction%20on%20verl/workspace?nw=wmb4qxfht0n) | > [!IMPORTANT] > > **📢 贡献征集!** > > 欢迎提交您的复现运行和配置! ## 配置 ### 分离的 Clip Epsilons (-> Clip-Higher) 配置示例: ```yaml actor_rollout_ref: actor: clip_ratio_low: 0.2 clip_ratio_high: 0.28 ``` `clip_ratio_low` 和 `clip_ratio_high` 指定了 DAPO objective 中的 $\varepsilon_{\text {low }}$ 和 $\varepsilon_{\text {high }}$。 核心相关代码: ```python pg_losses1 = -advantages * ratio pg_losses2 = -advantages * torch.clamp(ratio, 1 - cliprange_low, 1 + cliprange_high) pg_losses = torch.maximum(pg_losses1, pg_losses2) ``` ### 动态采样 (带分组过滤) 配置示例: ```yaml data: gen_batch_size: 1536 train_batch_size: 512 algorithm: filter_groups: enable: True metric: acc # score / seq_reward / seq_final_reward / ... max_num_gen_batches: 10 # 非正值表示无上限 ``` 将 `filter_groups.enable` 设置为 `True` 将过滤掉其输出 `metric` 全都相同的组,例如,对于 `acc`,会过滤掉输出准确率全为 1 或 0 的组。 训练器将以 `gen_batch_size` 重复采样,直到有足够的合格组用于 `train_batch_size` 或达到 `max_num_gen_batches` 指定的上限。 核心相关代码: ```python prompt_bsz = self.config.data.train_batch_size if num_prompt_in_batch < prompt_bsz: print(f'{num_prompt_in_batch=} < {prompt_bsz=}') num_gen_batches += 1 max_num_gen_batches = self.config.algorithm.filter_groups.max_num_gen_batches if max_num_gen_batches <= 0 or num_gen_batches < max_num_gen_batches: print(f'{num_gen_batches=} < {max_num_gen_batches=}. 继续生成...') continue else: raise ValueError( f'{num_gen_batches=} >= {max_num_gen_batches=}. 生成过多。请检查您的数据。' ) else: # 对齐批次 traj_bsz = self.config.data.train_batch_size * self.config.actor_rollout_ref.rollout.n batch = batch[:traj_bsz] ``` ### Flexible Loss Aggregation Mode (-> Token-level Loss) 配置示例: ```yaml actor_rollout_ref: actor: loss_agg_mode: "token-mean" # / "seq-mean-token-sum" / "seq-mean-token-mean" # 注意:“token-mean”是默认行为 ``` 将 `loss_agg_mode` 设置为 `token-mean` 意味着计算小批量中所有序列的所有 token 的(策略梯度)损失。 核心相关代码: ```python if loss_agg_mode == "token-mean": loss = verl_F.masked_mean(loss_mat, loss_mask) elif loss_agg_mode == "seq-mean-token-sum": seq_losses = torch.sum(loss_mat * loss_mask, dim=-1) # token-sum loss = torch.mean(seq_losses) # seq-mean elif loss_agg_mode == "seq-mean-token-mean": seq_losses = torch.sum(loss_mat * loss_mask, dim=-1) / torch.sum(loss_mask, dim=-1) # token-mean loss = torch.mean(seq_losses) # seq-mean else: raise ValueError(f"无效的 loss_agg_mode: {loss_agg_mode}") ``` ### Overlong Reward Shaping (过长奖励塑形) 配置示例: ```yaml data: max_response_length: 20480 # 16384 + 4096 reward_model: overlong_buffer: enable: True len: 4096 penalty_factor: 1.0 ``` 将 `overlong_buffer.enable` 设置为 `True` 将会惩罚那些虽然超长但仍落在硬上下文限制内的输出。 具体来说,当输出长度超过 `max_response_length` 段落 `0` 到 `overlong_buffer.len` 个 token 时,惩罚将从 `0` 线性增加到 `overlong_buffer.penalty_factor`。 核心相关代码: ```python if self.overlong_buffer_cfg.enable: overlong_buffer_len = self.overlong_buffer_cfg.len expected_len = self.max_resp_len - overlong_buffer_len exceed_len = valid_response_length - expected_len overlong_penalty_factor = self.overlong_buffer_cfg.penalty_factor overlong_reward = min(-exceed_len / overlong_buffer_len * overlong_penalty_factor, 0) reward += overlong_reward ``` ## FAQ ### 论文中的“Overlong Filtering”在哪里? 论文中的大多数实验,包括表现最佳的实验,都没有启用 Overlong Filtering,因为在恰当学习最长输出方面,它与 Overlong Reward Shaping 有些重叠。因此,我们没有在此处实现它。 ### [main 分支中的 `recipe/dapo` 目录](https://github.com/volcengine/verl/tree/main/recipe/dapo) 与 [`recipe/dapo` 分支](https://github.com/volcengine/verl/tree/recipe/dapo/recipe/dapo) 有什么区别? [ `recipe/dapo` 分支](https://github.com/volcengine/verl/tree/recipe/dapo/recipe/dapo) 用于**原样复现**,因此不会更新新功能。 [main 分支中的 `recipe/dapo` 目录](https://github.com/volcengine/verl/tree/main/recipe/dapo) 作为示例,展示了如何扩展最新的 `verl` 来实现算法 recipe,该目录将随新功能进行维护。 ### 为什么修改后我无法产生相似的结果? 如今的 RL 基础设施仍然存在固有的不稳定性,我们正在努力改进。 我们强烈建议一次只修改一个地方。 我们在此列出了一些已知的问题: 1. 启用 CUDA Graph (`enforce_eager=False`) 可能会导致模型性能下降,其原因仍在调查中。