Prhub

#7245 [algo, cfg, doc] feat: add DRO losses

原始 PR 作者 wyettzeng 合并时间 2026-08-05 04:25 文件变更 10 提交数 3 评论 2 代码增减 +122 / -2

执行摘要

新增 DRO 策略损失及 dro_beta 配置

PR 正文说明目标是“添加已注册的策略目标:Direct Reward Optimization (DRO),带可配置的二次 log-ratio 惩罚”,并给出了使用示例。分支名 verl_tinker_importance_sampling_dro 和第一版提交表明最初还计划加入 importance-sampling 损失,但第二次提交将其移除,最终仅保留 DRO,使变更聚焦在单一算法目标上。

值得快速精读,核心价值在于展示如何新增一个注册式策略损失并与统一聚合、rollout 校正权重对接。实现短小清晰,可作为后续扩展其他 policy loss 的样板。建议关注 agg_loss 的复用方式以及 dro_beta 的运行时校验设计;若团队计划推广 DRO,建议补充 GPU 冒烟测试和 beta 超参建议。

讨论亮点

Review 中仅有一条实质评论:审核者 Luosuuverl/workers/config/actor.py 第 85 行指出 loss_mode 的文档字符串需要列出全部选项(原文:"list all options here"),作者 wyettzeng 回复 "done" 并已补全。该问题属于文档完善类,未引发算法或设计层面的争论。

实现拆解

实现拆解如下:

  1. 注册 DRO 损失函数:在 verl/trainer/ppo/core_algos.py 中新增 compute_policy_loss_dro,通过 @register_policy_loss("dro") 注册。损失表达式为 -(log_prob * advantages - 0.5 * beta * log_ratio.square()),其中 log_ratio = log_prob - old_log_prob;若 dro_beta 缺失或非正则直接抛出 ValueError。该函数复用统一的 agg_loss 聚合入口(支持 token-mean、sample-mean 等模式)与 config.global_batch_info,并支持可选的 rollout_is_weights 逐 token 加权,与现有损失保持行为一致。

  2. 扩展配置契约:在 verl/workers/config/actor.pyPolicyLossConfig 中新增字段 dro_beta: Optional[float] = None,同时按 reviewer 意见将 loss_mode 的文档字符串补全为所有已注册选项(vanilladppo_tvdppo_klgsposapogpgclip_covkl_covgeo_meandrocispobypass_mode)。

  3. 同步 YAML 配置:在 verl/trainer/config/actor/actor.yamlpolicy_loss 段加入 dro_beta: null 及注释;四个 _generated_ppo_*_trainer.yaml(generic、megatron、torchtitan、veomni)也各加一行 dro_beta: null,确保不同后端实例化 PolicyLossConfig 时不会因缺键报错。

  4. 测试与文档配套:新增 tests/trainer/ppo/test_dynamic_policy_losses_on_cpu.py,包含 test_dro_matches_direct_formula_and_requires_positive_beta,用 torch.testing.assert_close 校验公式与 mask 平均,并验证未设置 dro_beta 时抛出 ValueError;新增 docs/algo/dro.md 说明公式与用法,并加入 docs/index.rst 目录树。根据 PR 正文,本地因缺少 pytest 环境未能实际运行测试,仅通过 pre-commit。

文件 模块 状态 重要度
verl/trainer/ppo/core_algos.py 策略损失 modified 7.35
tests/trainer/ppo/test_dynamic_policy_losses_on_cpu.py 测试 added 6.79
verl/workers/config/actor.py 配置 modified 5.22
verl/trainer/config/actor/actor.yaml 配置 modified 3.52
verl/trainer/config/_generated_ppo_megatron_trainer.yaml 配置 modified 2.88
verl/trainer/config/_generated_ppo_torchtitan_trainer.yaml 配置 modified 2.88
verl/trainer/config/_generated_ppo_trainer.yaml 配置 modified 2.88
verl/trainer/config/_generated_ppo_veomni_trainer.yaml 配置 modified 2.88
docs/algo/dro.md 文档 added 2.91
docs/index.rst 文档 modified 1.3

关键符号

compute_policy_loss_dro

关键源码片段

verl/trainer/ppo/core_algos.py core-logic

新增 DRO 损失的核心实现与注册入口,是本次功能的主干。

@register_policy_loss("dro")
def compute_policy_loss_dro(
    old_log_prob: torch.Tensor,
    log_prob: torch.Tensor,
    advantages: torch.Tensor,
    response_mask: torch.Tensor,
    loss_agg_mode: str = "token-mean",
    config: Optional[ActorConfig] = None,
    rollout_is_weights: torch.Tensor | None = None,
) -> tuple[torch.Tensor, dict[str, Any]]:
    # 校验配置:DRO 必须显式提供正数 dro_beta,否则直接报错,避免静默使用错误超参
    assert config is not None
    assert config.policy_loss is not None
​
    beta = config.policy_loss.dro_beta
    if beta is None or beta <= 0:
        raise ValueError("policy_loss.dro_beta must be a positive value when using DRO")
​
    # log_ratio 表示新旧策略的对数似然差,用于二次惩罚项
    log_ratio = log_prob - old_log_prob
    # DRO 目标:优势加权对数似然减去 0.5 * beta * log_ratio^2 惩罚;
    # 外层取负使梯度上升方向与目标最大化一致
    pg_losses = -(log_prob * advantages - 0.5 * beta * log_ratio.square())
​
    # 若提供 rollout 校正权重,则逐 token 乘到损失上,支持 off-policy 数据修正
    if rollout_is_weights is not None:
        pg_losses = pg_losses * rollout_is_weights
​
    # 统一走 agg_loss 聚合,兼容 token-mean / sample-mean 等模式及全局 batch 信息
    pg_loss = agg_loss(
        loss_mat=pg_losses, loss_mask=response_mask, loss_agg_mode=loss_agg_mode, **config.global_batch_info
    )
    pg_metrics = {
        # 暴露近似 KL 作为监控指标,帮助判断新旧策略偏差程度
        "actor/ppo_kl": verl_F.masked_mean(-log_ratio, response_mask).detach().item(),
    }
    return pg_loss, pg_metrics
tests/trainer/ppo/test_dynamic_policy_losses_on_cpu.py test-coverage

新增 CPU 测试,验证 DRO 公式与正数 dro_beta 校验,是质量保障的核心。

def _actor_config(*, loss_mode: str, dro_beta=None) -> ActorConfig:
    # 构造最小 ActorConfig,只保留计算 DRO 损失所需字段,降低测试耦合
    return ActorConfig(
        strategy="fsdp",
        rollout_n=1,
        ppo_micro_batch_size_per_gpu=1,
        clip_ratio=0.2,
        clip_ratio_low=0.2,
        clip_ratio_high=0.2,
        loss_agg_mode="token-mean",
        policy_loss=PolicyLossConfig(loss_mode=loss_mode, dro_beta=dro_beta),
    )
​
​
def test_dro_matches_direct_formula_and_requires_positive_beta():
    old_log_prob = torch.tensor([[-1.0, -0.7, -0.2]])
    log_prob = torch.tensor([[-0.8, -0.9, -0.1]], requires_grad=True)
    advantages = torch.tensor([[2.0, -1.5, 9.0]])
    response_mask = torch.tensor([[1.0, 1.0, 0.0]]) # 最后一个 token 被 mask 掉
    beta = 0.05
    config = _actor_config(loss_mode="dro", dro_beta=beta)
​
    loss, _ = compute_policy_loss_dro(old_log_prob, log_prob, advantages, response_mask, "token-mean", config)
    # 用直接公式手工计算期望值,并验证与实现一致
    log_ratio = log_prob - old_log_prob
    expected = (-(log_prob * advantages - 0.5 * beta * log_ratio.square()) * response_mask).sum()
    expected = expected / response_mask.sum()
    torch.testing.assert_close(loss, expected)
​
    # 未配置 dro_beta 时应抛出 ValueError,且错误信息包含 dro_beta 字样
    with pytest.raises(ValueError, match="dro_beta"):
        compute_policy_loss_dro(
            old_log_prob,
            log_prob,
            advantages,
            response_mask,
            "token-mean",
            _actor_config(loss_mode="dro"),
        )

评论区精华

loss_mode 文档字符串选项列表补全 documentation

审核者 Luosuu 评论 `list all options here`,要求 PolicyLossConfig 的 loss_mode 文档列出所有已注册选项,作者回复 `done`。

结论:已将 loss_mode 选项补全为 'vanilla', 'dppo_tv', 'dppo_kl', 'gspo', 'sapo', 'gpg', 'clip_cov', 'kl_cov', 'geo_mean', 'dro', 'cispo', 'bypass_mode'。 · 已解决

风险与影响

技术风险主要集中在以下方面:

  1. 缺少分布式/GPU 验证:新增测试仅在 CPU 上覆盖 token-mean 一种聚合模式,未覆盖 sample-meanseq-mean 等模式以及真实 PPO 训练下 global_batch_info 的跨设备聚合路径,存在隐性回归风险。
  2. 配置错误延迟暴露dro_beta 默认 None,且校验在损失函数执行时进行(运行时 ValueError),而非在配置解析阶段拦截;用户配置了 loss_mode=dro 却忘记设置 dro_beta 时,会在训练启动后较晚阶段才报错。
  3. 无 clipping 的稳定性依赖 beta:DRO 不使用 PPO 的 clip 机制,其稳定性完全依赖 dro_beta 的惩罚强度;若用户设置过小的 beta,对数似然比可能发散。当前文档虽给出公式,但缺少 beta 取值建议。
  4. 本地测试未跑通:PR 正文明确说明测试环境缺少 pytest 且系统 PyTorch 不可用(libnvshmem_host.so.3 缺失),虽然作者声明 pre-commit 通过,但测试的实际执行依赖 CI。

影响范围限定在策略损失选择层:

  • 对用户:新增一种可直接通过配置启用的策略损失 dro,使用方式与现有损失一致,不改变默认行为(loss_mode 默认仍为 vanilladro_beta 默认 None)。
  • 对系统PolicyLossConfig 新增一个可选字段,四个 _generated_*.yaml 同步补齐,不会破坏既有配置加载;agg_loss 的复用保证了聚合语义的一致性。
  • 对团队:提供了一篇简短的算法文档 docs/algo/dro.md,降低了使用和后续扩展门槛;但算法论文参考(文章链接未在文档中给出)和超参指导仍显不足。

总体影响程度为中低,属于功能增量而不是架构变更。

运行时而非配置期校验 新算法缺少分布式验证 仅 CPU 测试覆盖部分聚合模式

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

参与讨论