Chapter 09

算法:PPO / GRPO / RLOO / REINFORCE++ / DAPO

📌 commit 6c6056daa522 同一入口 5 种算法 — 公式 + flag + 何时用

OpenRLHF 把所有"PPO 家族"算法统一收口到 train_ppo_ray 入口,通过 --algo.advantage_estimator 一个开关切换。本章把支持的 5 种算法的数学公式、 关键差异、何时选哪个讲清楚。

9.1算法谱系

算法advantage_estimator需要 Criticn_samples核心思路
PPO gae 1经典 actor-critic + clipped ratio
GRPO group_norm 4-16组内归一替代 critic
RLOO rloo 2-8leave-one-out 平均
REINFORCE++ reinforce_plus_plus 1reward 标准化 + 一些工程改进
REINFORCE++-baseline reinforce_plus_plus_baseline ≥2组均值当 baseline(ScaleRL 验证)
DAPO(变体) 同 group_norm + 特殊 flag 16+动态采样 + clip higher

9.2共同的 policy loss

无论哪种算法,policy 更新公式都是 PPO clipped objective:

$$ \mathcal{L}_\text{policy} = -\mathbb{E}\!\left[\min\!\big(\rho_t \hat A_t,\;\mathrm{clip}(\rho_t,\,1-\epsilon,\,1+\epsilon)\hat A_t\big)\right] $$

差别只在 advantage $\hat A_t$ 怎么算。这就是为什么 OpenRLHF 能用一份代码支持所有算法——只是 advantage estimator 不同。

9.3PPO:经典 GAE

公式

$$ \hat A_t^\text{GAE} = \sum_{l=0}^{\infty} (\gamma\lambda)^l \delta_{t+l}, \quad \delta_t = r_t + \gamma V(s_{t+1}) - V(s_t) $$

这是 PPO 标配。需要 critic 给 $V$。

典型配置

--algo.advantage_estimator gae \
--algo.gamma 1.0 \
--algo.lam 0.95 \
--rollout.n_samples_per_prompt 1 \
--critic.model_name_or_path <RM 路径>

何时用

9.4GRPO:组内归一

公式

同一 prompt 采样 $G$ 个 response $\{y_1, ..., y_G\}$,对每个的 reward $r_i$ 做组内归一:

$$ \hat A_i = \frac{r_i - \mathrm{mean}(\{r_j\})}{\mathrm{std}(\{r_j\})} $$

每个 response 内的所有 token 共享同一个 advantage(即 outcome-level)。

典型配置

--algo.advantage_estimator group_norm \
--rollout.n_samples_per_prompt 8 \
# 不需要 critic:
# 删掉 --critic.* 参数

何时用

OpenRLHF 0.10 的 GRPO 增强

flag含义
--algo.dynamic_filtering_enable 组内 reward 全相同(全对或全错)时丢掉该组
--algo.dynamic_filtering_range 0.0 1.0定义"全对/全错"阈值
--algo.advantage.is_correction_enableimportance sampling 修正(训推 mismatch)
--algo.advantage.normalize_by_std 是否除 std(关掉 = 只减均值)

9.5RLOO:Leave-One-Out

公式

同一 prompt 的 $G$ 个 response,对每个 $i$,advantage = 自己的 reward 减其他 $G-1$ 个的平均

$$ \hat A_i = r_i - \frac{1}{G-1}\sum_{j\neq i} r_j = \frac{G}{G-1}\big(r_i - \bar r\big) $$

和 GRPO 的差别:不除 std

典型配置

--algo.advantage_estimator rloo \
--rollout.n_samples_per_prompt 4

何时用

9.6REINFORCE++

OpenRLHF 自家提出的简化算法。原理:

$$ \hat A_t = r_\tau - \mu_\tau / \sigma_\tau $$

其中 $r_\tau$ 是带 KL 惩罚的整段 trajectory reward,$\mu_\tau, \sigma_\tau$ 是当前 batch 上的全局均值方差。 不需要 critic、不需要 leave-one-out。

典型配置

--algo.advantage_estimator reinforce_plus_plus \
--rollout.n_samples_per_prompt 1

何时用

9.7REINFORCE++-baseline(v0.10 新增)

ScaleRL 论文(2025/10)验证有效。原理:

$$ \hat A_i = r_i - \mathrm{mean}(\{r_j\}_{j=1}^G) $$

看着像 GRPO 不除 std。差别:

GRPOREINFORCE++-baselineRLOO
减均值 ✓(leave-one-out 形式)
除 std
leave-self-out

典型配置

--algo.advantage_estimator reinforce_plus_plus_baseline \
--rollout.n_samples_per_prompt 8

ProRL V2 / ScaleRL 经验

2026/2 NVIDIA ProRL V2 用 REINFORCE++-baseline + 长训练复现了 1.5B SOTA 推理模型。仓库有专用脚本:

bash examples/scripts/train_prorlv2_math_hybrid_engine.sh

9.8DAPO:动态采样 + clip higher

字节 DAPO 论文提出的 GRPO 改进,OpenRLHF 通过组合 flag 实现:

--algo.advantage_estimator group_norm \
--algo.dynamic_filtering_enable \              # 全对/全错的组丢掉
--algo.dynamic_filtering_range 0.0 1.0 \
--algo.clip_ratio_low 0.2 \                     # ★ 非对称 clip
--algo.clip_ratio_high 0.28 \
--algo.token_level_loss \                       # ★ token 级 loss(避免长 response 主导)
--rollout.n_samples_per_prompt 16

仓库有专用脚本:

bash examples/scripts/train_dapo_ray_hybrid_engine.sh

9.9KL 项的两种放法

放法含义flag
KL → reward $r_\text{total} = r - \beta_\text{KL} \cdot (\log\pi_\theta - \log\pi_\text{ref})$ --algo.kl.in_reward(默认)
KL → loss policy loss 里加额外 KL 正则 --algo.kl.in_loss

KL 系数控制

flag含义
--algo.kl.init_coef 初始系数(典型 0.001 - 0.01)
--algo.kl.target 目标 KL(开自适应)
--algo.kl.horizon 自适应控制器周期
--algo.kl.use_adaptive 开自适应(推荐)

自适应 KL:

$$ \beta_{t+1} = \beta_t \cdot \begin{cases} 1 + \alpha & \text{if } \mathrm{KL}_t > 2\,\text{target} \\ 1 - \alpha & \text{if } \mathrm{KL}_t < 0.5\,\text{target} \\ 1 & \text{otherwise} \end{cases} $$

OpenAI 经典做法,让 KL 维持在 target 附近。

9.10选哪个算法

场景推荐理由
有偏好数据 + RM 训好了 PPO 能用 token-level advantage
可验证 reward(数学 / 代码)GRPO / RLOO 不用 critic,省显存
R1 风格长思考链 GRPO + DAPO token-level loss + clip higher 稳定长 response
1.5B 小模型推理 RL REINFORCE++-baselineProRL V2 / ScaleRL 验证
显存极紧 REINFORCE++ n_samples=1 也能跑
偏好对齐(不是推理) PPO 或 DPO DPO 见下一节

9.11顺便讲:DPO(offline)

OpenRLHF 也支持 DPO 系,但走独立 CLI(不通过 train_ppo_ray):

python -m openrlhf.cli.train_dpo \
  --pretrain <SFT 模型> \
  --dataset <preference 数据> \
  --apply_chat_template \
  --max_len 4096 \
  --train_batch_size 256 \
  --micro_train_batch_size 4 \
  --learning_rate 5e-7 \
  --beta 0.1 \
  --save_path ./dpo_output

# 其他变体
--ipo                       # IPO
--label_smoothing 0.1       # cDPO

DPO 是 offline 算法,不需要 vLLM rollout 和四个 Ray group,资源消耗只有 PPO 的 1/4。偏好对齐任务首选

9.126 个 advantage estimator 完整对照

本章前面提了 PPO / GRPO / RLOO / REINFORCE++。实际仓库 --algo.advantage.estimator 接受 6 种值(openrlhf/trainer/ppo_utils/experience_maker.py:35)。这是切算法时的查表:

estimatorbaseline需要 critic典型用
"gae"(默认) GAE: $\hat A_t = \sum_l (\gamma\lambda)^l \delta_{t+l}$ 经典 PPO
"reinforce" 纯收益 $\sum \gamma^t r_t$(无 baseline) 极简 REINFORCE
"reinforce_baseline" batch mean: $R - \mathrm{mean}(R)$ ★ REINFORCE++-baseline,推理任务首选
"rloo" 留一法:$\tfrac{1}{n-1}\sum_{j\neq i} R_j$ 多样本 per-prompt 时
"group_norm" GRPO group z-score:$\tfrac{R-\mu}{\sigma+\varepsilon}$ 组内归一
"dr_grpo" Dr.GRPO:组内减均值不除 std 简化版 GRPO,避免 std 退化

选 estimator 的速决:

9.13vLLM Importance Sampling 矫正三种

多样本 / 异步训练时 vLLM 采样的 logp 和训练侧重新算的 logp 不完全一致(quantization、kernel 差异),需要 IS 矫正。openrlhf/models/loss.py:188–210PolicyLoss 提供三种:

--algo.is.mode原理什么时候用
"tis"(Token IS) 对 token-level ratioclamp(ratio, lo, hi)常规多样本
"icepop" 把 ratio 偏离 1 太远的 token 系数置 0(filter outlier)vLLM/training 数值差异大时
"seq-mask-tis" 序列级 mask + token 级 TIS(混合) 长 CoT,部分 token 失真

启用方式:--algo.advantage.is_correction_enable --algo.is.mode icepop。ProRL V2 默认走 icepop。

9.14DAPO 的两个独门改进

DAPO 不是新算法本体,而是"GRPO + 训练技巧组合"。看 train_dapo_ray_hybrid_engine.sh 能找到这两个关键 flag:

flag含义
--actor.eps_clip_low_high 0.2 0.27 非对称 PPO clip:上界 0.27 大于下界 0.2,鼓励探索
--algo.dynamic_filtering_enable --algo.dynamic_filtering_range 0 1过滤 group 内"全对/全错"无信号样本,省 RL step
--actor.token_level_loss_enable token 级 loss reduction(更适合长 CoT)
--reward.stop_properly_penalty_coef 0.0 关掉"未停止"惩罚,让长 CoT 自由产生

这四个 flag 单独看都不大,但配合起来让 R1 风格训练长度涌现更稳定。

9.15这章你需要带走的