AReaL 中 PPO/GRPO 系列 RL 算法实战指南:从 Vanilla PPO 到 IcePop/KPop 的配置切换与源码原理
2026/9/17 15:30:34 网站建设 项目流程

AReaL 中 PPO/GRPO 系列 RL 算法实战指南:从 Vanilla PPO 到 IcePop/KPop 的配置切换与源码原理

【免费下载链接】AReaLThe RL Bridge for LLM-based Agent Applications. Made Simple & Flexible.项目地址: https://gitcode.com/GitHub_Trending/are/AReaL

本指南系统讲解 AReaL(The RL Bridge for LLM-based Agent Applications)对类 PPO 强化学习算法族的统一实现,涵盖 Vanilla PPO、GRPO、Dr.GRPO、LitePPO、RLOO、DAPO、SAPO、GSPO,以及 IcePop/KPop 两种 token 掩码策略。这些算法共享同一套基础目标,仅在归一化策略、裁剪机制、重要性采样级别上存在差异,通过修改少量配置即可在它们之间自由切换。读完本文,你将掌握 AReaL 中这些算法的配置矩阵、CLI 运行方式、GAE 优势估计细节,以及每个算法在源码中的实现位置与验证路径。

算法总览:一个配置框架,十种算法策略

AReaL 将一系列 PPO 变体整合进统一的训练框架中,全部配置定义于areal/api/cli_args.pyPPOActorConfigNormConfig数据类。当前支持的算法包括:

  • Vanilla PPO:使用学习到的 critic 价值函数,通过 GAE 估计优势
  • GRPO(DeepSeekMath):去掉 critic,使用组内奖励归一化(论文 arXiv:2402.03300)
  • Dr.GRPO:GRPO 的改进版,移除长度归一化与标准差归一化(论文 arXiv:2503.20783)
  • LitePPO:轻量级 PPO(论文 arXiv:2508.08221)
  • RLOO:REINFORCE Leave-One-Out,用留一均值做基线(论文 arXiv:2402.14740)
  • DAPO:非对称裁剪 + 动态采样(论文 arXiv:2503.14476)
  • SAPO:软自适应策略优化,用 sigmoid 软门替换硬裁剪(论文 arXiv:2511.20347)
  • GSPO(Qwen3):序列级重要性采样(论文 arXiv:2507.18071)
  • IcePop:基于重要性比率的 token 掩码(可与其它 RL 算法组合)
  • KPop:双向二元 KL 散度 token 掩码(可与其它 RL 算法组合)

IcePop 和 KPop 是 token 掩码策略,可以叠加在上面任意 RL 算法之上。这些算法共享相同的基础目标,但在归一化策略、裁剪机制、重要性采样级别等方面有所不同——这正是 AReaL 将它们统一到一套配置框架的原因:通过调整少量配置参数即可在不同算法之间切换

运行方式:统一的执行入口

所有算法使用相同的执行模式,AReaL 官方建议直接修改配置 YAML 文件中的参数。训练入口脚本是examples/math/gsm8k_rl.py,它通过PPOTrainer驱动完整 RL 循环:加载 config、tokenizer、数据集后,以字符串路径areal.workflow.openai.math_agent.MathAgent指定 rollout workflow(详见 examples/math/gsm8k_rl.py)。

后端命令
localpython3 examples/math/gsm8k_rl.py --config examples/math/gsm8k_<algo>.yaml scheduler.type=local
raypython3 examples/math/gsm8k_rl.py --config examples/math/gsm8k_<algo>.yaml scheduler.type=ray
slurmpython3 examples/math/gsm8k_rl.py --config examples/math/gsm8k_<algo>.yaml scheduler.type=slurm

<algo>替换为ppogrpodrgrpolitepporloogspodapo_dynamic_bssapoicepopkpop。这些配置文件都真实存在于仓库中,例如 examples/math/gsm8k_grpo.yaml、examples/math/gsm8k_ppo.yaml、examples/math/gsm8k_icepop.yaml、examples/math/gsm8k_kpop.yaml 等。

通过 CLI 覆盖切换算法

除了直接编辑 YAML,你还可以通过覆盖配置参数来切换算法,不必为每个算法单独准备一份配置:

# Dr.GRPO (从GRPO配置) python3 examples/math/gsm8k_rl.py \ --config examples/math/gsm8k_grpo.yaml \ scheduler.type=local \ actor.adv_norm.mean_level=group \ actor.adv_norm.std_level=null # GSPO (从GRPO配置) python3 examples/math/gsm8k_rl.py \ --config examples/math/gsm8k_grpo.yaml \ scheduler.type=local \ +actor.importance_sampling_level=sequence # SAPO (从GRPO配置) python3 examples/math/gsm8k_rl.py \ --config examples/math/gsm8k_grpo.yaml \ scheduler.type=local \ +actor.use_sapo_loss=true \ +actor.sapo_tau_pos=1.0 \ +actor.sapo_tau_neg=1.05 \ actor.use_decoupled_loss=false

注意:添加原始 YAML 中不存在的键时,必须使用+前缀(如+actor.importance_sampling_level=sequence),否则会因键不存在而报错。

核心配置参数

所有配置都定义在areal/api/cli_args.py中的PPOActorConfigNormConfig下,完整字段清单可参考 CLI 配置参考。

奖励和优势归一化(actor.reward_normactor.adv_norm

NormConfig数据类(源码位置)控制奖励和优势的归一化方式:

参数类型选项描述
mean_levelstr | None"batch""group"None计算均值的级别
std_levelstr | None"batch""group"None计算标准差的级别
mean_leave1outbooltruefalse使用留一法平均值(排除当前样本)
std_unbiasedbooltruefalse使用无偏标准差计算(默认:true,该默认值自 v0.3.4 起由false变更而来)
epsfloat-避免除零的小常数(默认:1e-5
group_sizeint-分组级归一化的组大小

"Batch" 级在整个全局批次上计算均值/标准差,而 "group" 级在组内计算(例如,共享相同提示的轨迹)。分组边界来自 rollout 批次元数据(TrajBatchMeta.traj_group_sizes)而非group_size,因此即使各组大小不一(例如部分样本被过滤),仍会按提示逐组归一化;仅当该元数据不可用时,group_size才作为固定步长的回退方案生效。将mean_levelstd_level设为None分别跳过均值减法或标准差缩放。如果整个字段被省略(例如 YAML 中的adv_norm: null),则不执行归一化。

示例:

actor: adv_norm: null reward_norm: mean_level: group std_level: group group_size: ${gconfig.n_samples}

从源码看,NormConfig__post_init__会校验mean_level/std_level必须是"batch""group"None,且使用分组统计时group_size必须为正整数(源码位置)。PPOActorConfig还提供了uses_group_statistics属性与resolve_min_usable_group_size()方法,用于在部分 rollout 样本失败或过滤时推导"最少可用组大小":使用分组统计时至少需要 2 个组内成员,否则归一化无从谈起(源码位置)。

AReaL 默认实践:默认配置使用std_level: batch进行优势归一化。这已成为 AReaL 团队在各种 RL 应用中的标准实践,从游戏 AI(StarCraft)到 LLM 训练(RLHF、推理、agent 设置)。虽然 Dr.GRPO 建议使用std_level: null以获得潜在更好的性能,但 AReaL 保留std_level: batch以保持向后兼容性。寻求 Dr.GRPO 风格行为的用户应设置actor.adv_norm.std_level=null

作为参考,examples/math/gsm8k_grpo.yaml 中实际的默认 GRPO 配置为:

reward_norm: mean_level: group std_level: group group_size: ${gconfig.n_samples} adv_norm: mean_level: batch std_level: batch

裁剪策略(actor.eps_clip*

参数类型默认值描述
eps_clipfloat0.2下裁剪边界:比率裁剪到[1-eps_clip, ...]
eps_clip_higherfloat | NoneNone上裁剪边界:设置时,比率裁剪到[1-eps_clip, 1+eps_clip_higher]

eps_clip_higherNone时,使用对称裁剪:$\text{clip}(r, 1-\epsilon, 1+\epsilon)$。当设置eps_clip_higher时(DAPO 风格),使用非对称裁剪:$\text{clip}(r, 1-\epsilon_{\text{low}}, 1+\epsilon_{\text{high}})$。注意实际配置文件中eps_clip的取值随数据集与场景变化,例如 GRPO 示例中使用了eps_clip: 0.4(examples/math/gsm8k_grpo.yaml)。

重要性采样级别(actor.importance_sampling_level

参数类型选项描述
importance_sampling_levelstr"token""sequence"计算重要性比率的级别
  • "token"(默认):标准逐 token 重要性比率(GRPO、PPO 等)
  • "sequence"(GSPO):逐 token 比率的序列级几何平均值

在实现层面,重要性权重统一通过_compute_importance_weight()计算为exp(logp_num - logp_denom)(源码位置);sequence 级别则在此基础上对逐 token 对数概率取序列级几何平均。PPOActorConfig.__post_init__还会校验importance_sampling_level只能是tokensequence(源码位置)。

算法配置矩阵

下表展示了如何通过设置适当的参数来配置每个算法:

算法adv_norm.mean_leveladv_norm.std_leveladv_norm.mean_leave1outimportance_sampling_level特殊配置
PPObatchbatchfalsetoken需要 critic 模型
GRPObatchbatchfalsetoken-
Dr.GRPOgroupnullfalsetoken-
LitePPOgroupbatchfalsetoken-
RLOOgroupnulltruetoken-
GSPObatchbatchfalsesequence-
DAPObatchbatchfalsetoken非对称裁剪,动态采样
SAPObatchbatchfalsetokenuse_sapo_loss=true
IcePopbatchbatchfalsetokenrejection_sampling.metric=ratio
KPopbatchbatchfalsetokenrejection_sampling.metric=binary_kl

注意:"GRPO" 行反映原始 DeepSeekMath 公式。AReaL 的默认 GRPO 配置使用这些设置,但已移除长度归一化(见下文"AReaL 实现说明")。此外,源码中的PPOActorConfig还提供了算法矩阵之外的更多开关,例如use_cispo_loss(CISPO,来自 MiniMax-M1,arXiv:2506.13585),它要求eps_clip_higher > 0importance_sampling_level='token',且与 SAPO 互斥(源码位置),进一步扩展了这套"配置即算法"框架的表达能力。

算法特定选项

Vanilla PPO

Vanilla PPO 使用学习到的价值函数(critic)通过 GAE 估计优势。关键配置差异是它需要一个critic:配置部分,包含自己的模型和优化器——这在源码中对应PPOCriticConfig数据类(源码位置),其中eps_clip默认值为0.5(用于价值损失裁剪)。完整的配置示例见examples/math/gsm8k_ppo.yaml

GRPO

$$ J_{\text{GRPO}}(\theta) = \mathbb{E}{\substack{q \sim P(Q), \ {o_i}{i=1}^G \sim \pi_{\theta_{\text{old}}}(O \mid q)}} \left[ \frac{1}{G} \sum_{i=1}^G \sum_{t=1}^{|o_i|} \min\left( r_{i,t}(\theta) \hat{A}{i,t}, \text{clip}\left( r{i,t}(\theta), 1-\epsilon, 1+\epsilon \right) \hat{A}{i,t} \right) - \beta D{\mathrm{KL}}\left[ \pi_\theta \middle| \pi_{\text{ref}} \right] \right] $$

其中:

$$ r_{i,t}(\theta) = \frac{\pi_\theta(o_{i,t} \mid q, o_{i,<t})}{\pi_{\theta_{\text{old}}}(o_{i,t} \mid q, o_{i,<t})}, \quad \hat{A}{i,t} = \frac{r_i - \text{mean}({r_i}{i=1}^G)}{\text{std}({r_i}_{i=1}^G)}. $$

RLOO (REINFORCE Leave-One-Out)

RLOO 通过平均其他采样响应的奖励(排除当前响应)来估计基线。这通过设置actor.adv_norm.mean_leave1out=true实现。

$$ J_{\text{RLOO}}(\theta) = \mathbb{E}{\substack{q \sim P(Q), \ {o_i}{i=1}^G \sim \pi_{\theta_{\text{old}}}(O \mid q)}} \left[ \frac{1}{G} \sum_{i=1}^G \frac{1}{|o_i|} \sum_{t=1}^{|o_i|} \min\left( r_{i,t}(\theta) \hat{A}{i,t}, \text{clip}\left( r{i,t}(\theta), 1-\epsilon, 1+\epsilon \right) \hat{A}_{i,t} \right) \right] $$

其中:

$$ \hat{A}{i,t} = r_i - \frac{1}{G-1} \sum{j \neq i} r_j. $$

GSPO (Group Sequence Policy Optimization)

GSPO 在序列级别而非 token 级别计算重要性采样比率。

标准 PPO(token 级):

$$ r_{i,t}(\theta) = \frac{\pi_\theta(o_{i,t} \mid q, o_{i,<t})}{\pi_{\theta_{\text{old}}}(o_{i,t} \mid q, o_{i,<t})} $$

GSPO(序列级):

$$ r_i(\theta) = \exp\left(\frac{1}{|o_i|}\sum_{t=1}^{|o_i|} \log\frac{\pi_\theta(o_{i,t} \mid q, o_{i,<t})}{\pi_{\theta_{\text{old}}}(o_{i,t} \mid q, o_{i,<t})}\right) $$

这一"序列级几何平均"语义不仅用于 GSPO 的目标函数,也复用在RejectionSamplingConfig的 sequence 级过滤中:agg='mean'时比率在 log 空间聚合为几何平均,长度不变式(length-invariant),与 GSPO 语义保持一致(源码位置)。

SAPO (Soft Adaptive Policy Optimization)

SAPO 用软 sigmoid 门替换 PPO 的硬裁剪,提供平滑梯度和非对称控制。

标准 PPO:

$$ L^{\text{PPO}} = -\mathbb{E}_t[\min(r_t A_t, r_t^{\text{clip}} A_t)] $$

SAPO(带软门):

  • 对于正向优势:$g_t^+ = \frac{4}{\tau_{\text{pos}}} \sigma(\tau_{\text{pos}} (r_t - 1))$
  • 对于负向优势:$g_t^- = \frac{4}{\tau_{\text{neg}}} \sigma(\tau_{\text{neg}} (r_t - 1))$
  • 损失:$L^{\text{SAPO}} = -\mathbb{E}_t[g_t A_t]$,其中如果 $A_t > 0$ 则 $g_t = g_t^+$,否则 $g_t = g_t^-$
参数类型默认值描述
actor.use_sapo_lossboolfalse启用 SAPO 损失代替 PPO 裁剪
actor.sapo_tau_posfloat1.0正向优势的温度参数
actor.sapo_tau_negfloat1.05负向优势的温度参数

注意:SAPO 需要actor.use_decoupled_loss=false。这一约束在源码中有强制校验:PPOActorConfig.__post_init__会在use_sapo_loss=trueuse_decoupled_loss=true时直接抛出ValueError,同时校验两个温度参数必须为正数(源码位置)。

actor: use_sapo_loss: true sapo_tau_pos: 1.0 sapo_tau_neg: 1.05 use_decoupled_loss: false

DAPO

DAPO 引入非对称裁剪和动态采样,后者排除所有响应都完全正确或完全错误的样本。

$$ J_{\text{DAPO}}(\theta) = \mathbb{E}{\substack{(q,a) \sim \mathcal{D}, \ {o_i}{i=1}^G \sim \pi_{\theta_{\text{old}}}(o \mid q)}} \left[ \frac{1}{\sum_{i=1}^G |o_i|} \sum_{i=1}^G \sum_{t=1}^{|o_i|} \min\left( r_{i,t}(\theta) \hat{A}{i,t}, \text{clip}\left( r{i,t}(\theta), 1-\epsilon_{\text{low}}, 1+\epsilon_{\text{high}} \right) \hat{A}_{i,t} \right) \right] $$

其中 $\hat{A}{i,t}$ 是分组归一化优势,$r{i,t}(\theta)$ 是 token 级策略比率。

非对称裁剪参数:

参数类型默认值描述
actor.eps_clipfloat0.2下裁剪边界
actor.eps_clip_higherfloat-上裁剪边界(设置以启用非对称)

过长惩罚参数:

参数类型默认值描述
actor.overlong_reward_penaltyboolfalse启用过长响应惩罚
actor.overlong_tokensint-被视为过长的尾部 token 数量
actor.overlong_penalty_factorfloat-应用于过长响应的惩罚因子

在实现层面,过长惩罚在PPOActor._compute_advantages()的最前端通过reward_overlong_penalty()施加,然后再进入 reward scaling、clip 与归一化流程(源码位置)。

动态采样:

AReaL 通过传递给PPOTrainer.train()dynamic_filter_fn支持动态采样。该函数接收从相同提示采样的分组轨迹,并返回布尔值指示是否接受它们进行训练:

trainer.train( workflow=..., dynamic_filter_fn=lambda x: 0 < x["rewards"].mean() < 1 )

RLTrainer的训练循环中,dynamic_filter_fn作为should_accept_fn传入prepare_batch,由_collect_trainable_rollout_batch()依据dynamic_bs决定收集行为(源码位置)。

默认情况下,AReaL 使用固定批量大小的动态过滤——它等待收集到batch_size个接受样本后再进行训练。这与某些使用动态批量大小的 DAPO 实现不同,后者收集整个批次的样本然后过滤它们。以下选项控制批量大小行为:

参数类型默认值描述
dynamic_bsboolfalse启用动态批量大小

IcePop

IcePop 对重要性比率 $r_{i,t} = \frac{\pi_\theta(o_{i,t} \mid q, o_{i,<t})}{\pi_{\theta_\text{old}}(o_{i,t} \mid q, o_{i,<t})}$ 超出可配置范围 $[\alpha, \beta]$ 的 token 进行掩码(其中 $\pi_\theta$ 为当前训练策略,$\pi_{\theta_\text{old}}$ 为采样时的行为策略)。重要性比率过低或过高的 token 不参与损失计算。

通过rejection_sampling配置的metric=ratio实现:

actor: use_decoupled_loss: true rejection_sampling: level: token action: mask metric: ratio lower: 0.5 upper: 5.0
参数类型默认值描述
actor.rejection_sampling.metricstr-设置为ratio以启用 IcePop
actor.rejection_sampling.lowerfloat0.5重要性比率下界
actor.rejection_sampling.upperfloat5.0重要性比率上界

注意:IcePop 需要actor.use_decoupled_loss=true,否则rejection_sampling不生效。源码中PPOActorConfig.__post_init__会对该组合发出明确警告("rejection_sampling is configured but use_decoupled_loss=False. Filtering will be ignored.",源码位置)。完整的配置示例见examples/math/gsm8k_icepop.yaml

RejectionSamplingConfig(源码位置)是 IcePop/KPop 及更一般过滤策略的统一实现,它取代了旧版的behave_imp_weight_cap/behave_imp_weight_mode两个参数。除ratio外还支持kl_k1kl_k2kl_k3三种 KL 估计量,action可选mask(将 loss_mask 置零、完全排除梯度)或clamp(将重要性权重截断到边界、保留有界梯度)。校验规则包括:ratio度量下upper必须大于 1.0、lower必须为正;clamp动作只支持ratio度量。

KPop

KPop 对双向二元 KL 散度超过阈值的 token 进行掩码。对于每个 token,计算:

$$ \text{KL}{\text{fwd}} = \text{KL}(P\theta | P_{\theta_\text{old}}), \quad \text{KL}{\text{rev}} = \text{KL}(P{\theta_\text{old}} | P_\theta) $$

其中每个 token 概率被视为伯努利参数:$\text{KL}(P | Q) = p \log \frac{p}{q} + (1-p) \log \frac{1-p}{1-q}$。$\max(\text{KL}{\text{fwd}}, \text{KL}{\text{rev}}) > \phi$ 的 token 被掩码(此处 $\phi$ 对应actor.rejection_sampling.upper)。

通过rejection_sampling配置的metric=binary_kl实现:

actor: use_decoupled_loss: true rejection_sampling: level: token action: mask metric: binary_kl upper: 2.0
参数类型默认值描述
actor.rejection_sampling.metricstr-设置为binary_kl以启用 KPop
actor.rejection_sampling.upperfloat2.0KL 散度阈值($\phi$)

注意:KPop 仅支持action=mask(不支持clamp),且lowerbinary_kl中不使用。KPop 需要actor.use_decoupled_loss=true,否则rejection_sampling不生效。完整的配置示例见examples/math/gsm8k_kpop.yaml

核心概念

奖励:AReaL 假设基于结果的奖励。每个可能由连接的 LLM 输入-输出对组成的轨迹,在序列级而非 token 级被分配一个标量奖励。

优势:AReaL 为轨迹中的每个输出 token 计算逐 token 优势。PPO 算法将结果奖励视为最后一个 token 的奖励,所有前面的 token 奖励为 0。然后 AReaL 通过广义优势估计(GAE)应用标准折扣和 TD 误差反向传播来计算每个 token 的优势。使用默认的 token 级递推时,如果discount=1gae_lambda=1、critic value 为 0 且禁用 KL 正则化,终止结果奖励会等效广播到每个生成 token。

GAE 时间步单位

actor.gae_timestep_unit用于选择 GAE 按生成 token 还是生成 turn 推进。Prompt、tool、padding 及其他被 mask 的位置都不会消耗 GAE 时间步。该参数在源码中被限制为tokenturn两个取值(源码位置)。

Token 级 GAE

token是默认值,并保留 AReaL 原有行为。对于相邻的有效生成 token,AReaL 计算:

$$ \delta_t = r_t + \gamma V_{t+1} - V_t, \qquad A_t = \delta_t + \gamma \lambda A_{t+1}, \qquad G_t = A_t + V_t, $$

其中 $\gamma$ 为actor.discount,$\lambda$ 为actor.gae_lambda配置解析出的逐轨迹取值。奖励 $r_t$ 包含结果奖励增量和 token 级 KL 惩罚。以 EOS 结束的轨迹使用 0 作为终止 bootstrap;没有 EOS 的截断轨迹则使用最后一个 value 估计进行 bootstrap。这一递推逻辑对应_compute_token_level_gae()的实现(源码位置),它从序列末尾向前迭代,并用loss_mask跳过不贡献损失的 token。

Turn 级 GAE

turn将每个非空生成 turn 视为一个宏观时间步。对于 turn $u$,AReaL 将其中的 task reward 增量求和为 $r_u^{\mathrm{task}}$,并使用该 turn 第一个有效 action token 位置的 value 作为 $V_u$,然后计算:

$$ \delta_u^{\mathrm{task}} = r_u^{\mathrm{task}} + \gamma V_{u+1} - V_u, \qquad A_u^{\mathrm{task}} = \delta_u^{\mathrm{task}} + \gamma \lambda A_{u+1}^{\mathrm{task}}. $$

Task advantage $A_u^{\mathrm{task}}$ 和 critic target $G_u=A_u^{\mathrm{task}}+V_u$ 会广播到该 turn 中的每个有效 token。Token 级 KL 不会进入 turn 递推和 critic target;在可选的actor.adv_norm之前,turn $u$ 中 token $j$ 的 actor advantage 为 $A_{u,j}=A_u^{\mathrm{task}}+r_{u,j}^{\mathrm{KL}}$。这样可以避免先对一个 turn 的 KL 惩罚求和,再将总和广播回每个 token。

Turn 级 GAE 依赖turn_ids元数据的校验:_validate_turn_ids()要求turn_idsloss_mask形状相同、位于同一 device、使用整数 dtype,且所有有效 loss token 的 ID 非负并小于序列长度(源码位置)。

动态 GAE lambda

actor.gae_lambda既可以是静态 float,也可以是 callable 的点分路径。actor.gae_lambda_kwargs用于向 callable 传递关键字参数,配置静态 float 时会被忽略。该函数接收包含三个[B]形状 tensor 的 context(GAELambdaContext,源码位置):

  • effective_token_lengths:有效生成 token 数量,包括有效的 EOS token;
  • turn_counts:非空生成 turn 数量;token 模式缺少turn_ids时为 0;
  • timestep_lengths:由gae_timestep_unit选择的长度 $L$。

Callable 必须在相同 device 上返回[B]形状的有限浮点 tensor,即每条本地轨迹一个 lambda。返回的 lambda 会用于该轨迹的所有选定时间步。静态 float 在运行时被resolve_gae_lambda_fn()包装为constant_gae_lambda()(逐轨迹返回同一个常量),而字符串路径则通过import_from_string()动态导入(源码位置)。

内置了两个长度自适应函数:

函数路径参数定义
areal.trainer.ppo.lambda_fn.vapo_length_adaptive_gaealpha > 0$L>0$ 时 $\lambda=\max(0, 1 - 1/(\alpha L))$;$L=0$ 时取 0
areal.trainer.ppo.lambda_fn.relative_position_gae_lambda0 < q <= 1$L\ge2$ 时 $\lambda=q^{1/(L-1)}$;$L=1$ 时取 1,$L=0$ 时取 0。相对保留率解释假设 $\gamma=1$

其中vapo_length_adaptive_gae实现了 VAPO 论文的长度自适应 lambda,其timestep_lengthsgae_timestep_unit决定:token 模式下为有效生成 token 数,turn 模式下为有效 turn 数;空轨迹与过短轨迹取 lambda 0(源码位置)。relative_position_gae_lambda则保证在 $\gamma=1$ 时,终端结果奖励在等相对位置上保留相同的比例——第 1 个时间步恰好保留 $q$,最后一个时间步保留 1(源码位置)。

配置示例:

actor: gae_timestep_unit: turn gae_lambda: areal.trainer.ppo.lambda_fn.relative_position_gae_lambda gae_lambda_kwargs: q: 0.5

自定义 workflow 的turn_ids约定

Turn 级 GAE 要求 workflow 返回原始、与 token 对齐的turn_ids;actor 会在内部将其与 next-token prediction mask 对齐。该 tensor 必须满足:

  • input_idsloss_mask形状相同(batch 后为[B, S]);
  • 使用整数 dtype(建议使用有符号整数来表示-1哨兵值);
  • 为每个有效生成 token 分配[0, S)范围内的 ID;
  • 有效 ID 随时间单调不减,同一个 assistant turn 中的所有 token 使用相同 ID;
  • prompt、user、tool、padding 及其他非 loss 位置使用-1

编号允许跳跃且不会额外消耗 GAE 时间步,但建议从 0 开始连续编号。自定义 workflow 可以按如下方式构造该字段;不要在 workflow 中进行 roll

turn_ids += [-1] * input_len + [turn_idx] * resp.output_len result["turn_ids"] = torch.tensor(turn_ids, dtype=torch.int32).unsqueeze(0)

AReaL 实现说明

AReaL 的 GRPO 实现在两个关键方面与原始 DeepSeekMath 论文不同:

长度归一化:AReaL 从原始 GRPO 目标中移除了逐 token 长度归一化项。这与 Dr.GRPO 的建议一致,并消除了优势估计中的偏差。

KL 正则化:AReaL 不是将 KL 散度项直接添加到目标函数中,而是通过actor.kl_ctl将 KL 正则化纳入 actor advantage(PPO 风格)。在 token 模式下,KLEstimator计算的惩罚会在 GAE 之前加入逐 token 奖励;在 turn 模式下,它仅作为 token 局部的 actor 惩罚,不进入 turn 递推和 critic target。源码中kl_ctl默认值为0.1kl_estimator可选k1/k2/k3(源码位置),而 examples/math/gsm8k_grpo.yaml 中的默认 GRPO 配置将kl_ctl设为0.0

总结与延伸阅读

本文完整覆盖了 AReaL 中 PPO/GRPO 算法族的配置与原理:统一的gsm8k_rl.py执行入口、PPOActorConfig/NormConfig核心参数、算法配置矩阵、各算法的公式与 YAML 配置,以及 GAE 时间步单位、动态 GAE lambda 和turn_ids约定等进阶话题。相关测试用例(如tests/test_ppo_gae.pytests/test_cispo_loss.pytests/test_ppo_actor_truncation.py)可以作为验证这些行为的参考;更多 CLI 参数细节可查阅 CLI 配置参考。若想了解 RL 训练流程的整体架构,可继续阅读 RL 训练器文档 下的相关章节。

【免费下载链接】AReaLThe RL Bridge for LLM-based Agent Applications. Made Simple & Flexible.项目地址: https://gitcode.com/GitHub_Trending/are/AReaL

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询