Prhub

#2247 fix: forward dual-clip PPO epsilon

原始 PR 作者 alexqdh 合并时间 2026-08-12 13:29 文件变更 4 提交数 1 评论 0 代码增减 +66 / -1

执行摘要

转发 eps_clip_c 修复双截断 PPO 未生效

PR body 明确说明:Fix Dual-clip PPO support by forwarding args.eps_clip_c from policy_loss_function to compute_policy_loss. Forward eps_clip_c=args.eps_clip_c in the PPO policy-loss path. 核心问题是 dual-clip PPO 的配置项虽然存在,但未在训练主路径被消费,用户设置 --eps-clip-c 不会影响损失计算。

值得快速阅读。改动虽小,但补上了配置与损失路径之间的断点;测试用 AST 检查参数透传的做法在缺少直接导出时可借鉴,但也提示此类『接线 bug』最好通过面向行为的集成测试覆盖。

讨论亮点

该 PR 没有任何 review 评论,由维护者 zhuzilin 直接合入;变更简单直接,无公开争议。

实现拆解

  1. 核心改动:修改 slime/backends/megatron_utils/loss.pypolicy_loss_function 的 else 分支,将 compute_policy_loss 调用改为显式关键字参数 eps_clip_c=args.eps_clip_c,从而把命令行配置透传到损失计算的裁剪逻辑;CISPO 分支保持调用 compute_cispo_loss 不变。
  2. 测试新增tests/test_policy_loss.py 新增两个 CPU 测试。第一个直接校验 slime/utils/ppo_utils.compute_policy_loss 在负优势下应用 dual-clip 的数值;第二个用 AST 解析 loss.py,断言 policy_loss_function 中存在唯一一次 compute_policy_loss 调用且带 eps_clip_c=args.eps_clip_c 关键字参数,验证训练路径接线。
  3. CI 接入:在 .github/workflows/pr-test.yml 与模板 pr-test.yml.j2num_gpus=0 CPU 测试矩阵中加入 test_policy_loss.py,保证新测试在 PR CI 中运行。
文件 模块 状态 重要度
slime/backends/megatron_utils/loss.py 损失计算 modified 5.67
tests/test_policy_loss.py 单元测试 added 6.09
.github/workflows/pr-test.yml CI 配置 modified 3.13
.github/workflows/pr-test.yml.j2 CI 配置 modified 2.24

关键符号

policy_loss_function test_compute_policy_loss_applies_dual_clip_to_negative_advantages test_policy_loss_function_forwards_eps_clip_c

关键源码片段

slime/backends/megatron_utils/loss.py core-logic

核心修改文件:在 policy_loss_function 中把 args.eps_clip_c 透传给 compute_policy_loss,接通 dual-clip PPO 配置。

# slime/backends/megatron_utils/loss.py 中 policy_loss_function 的损失计算分支
# 先根据 advantage_estimator 选择损失函数,CISPO 走独立分支,其余走通用 PPO 损失
if args.advantage_estimator == "cispo":
    # CISPO 自身处理 clip 参数,无需透传 eps_clip_c
    pg_loss, pg_clipfrac = compute_cispo_loss(
        ppo_kl, log_probs, advantages, args.eps_clip, args.eps_clip_high
    )
else:
    # 通用 PPO 路径:显式透传 args.eps_clip_c,启用 dual-clip 对负优势的二次裁剪
    pg_loss, pg_clipfrac = compute_policy_loss(
        ppo_kl,
        advantages,
        args.eps_clip,
        args.eps_clip_high,
        eps_clip_c=args.eps_clip_c,
    )
tests/test_policy_loss.py test-coverage

新增测试文件,数值验证 dual-clip 裁剪逻辑,并用 AST 断言训练路径透传参数。

# tests/test_policy_loss.py —— CPU 测试,验证 dual-clip PPO 裁剪与其在训练路径的接线def test_compute_policy_loss_applies_dual_clip_to_negative_advantages():
    ratios = torch.tensor([2.0, 2.0, 0.5])
    ppo_kl = -ratios.log() # 由 ratio 推导的 KL 项
    advantages = torch.tensor([2.0, -2.0, 2.0])
​
    losses, _ = compute_policy_loss(
        ppo_kl,
        advantages,
        eps_clip=0.2,
        eps_clip_high=0.2,
        eps_clip_c=1.5, # 开启 dual-clip 的下限
    )
​
    # 负优势样本(第二个)的损失应被 eps_clip_c 限制为 3.0
    torch.testing.assert_close(losses, torch.tensor([-2.4, 3.0, -1.0]))
​
​
def test_policy_loss_function_forwards_eps_clip_c():
    # 用 AST 解析源码,确认 policy_loss_function 向 compute_policy_loss 透传 args.eps_clip_c
    loss_path = Path(__file__).parents[1] / "slime" / "backends" / "megatron_utils" / "loss.py"
    module = ast.parse(loss_path.read_text())
    policy_loss_function = next(
        node for node in module.body
        if isinstance(node, ast.FunctionDef) and node.name == "policy_loss_function"
    )
    compute_policy_loss_calls = [
        node for node in ast.walk(policy_loss_function)
        if isinstance(node, ast.Call) and isinstance(node.func, ast.Name)
        and node.func.id == "compute_policy_loss"
    ]
    assert len(compute_policy_loss_calls) == 1
    eps_clip_c_keyword = next(
        (kw for kw in compute_policy_loss_calls[0].keywords if kw.arg == "eps_clip_c"),
        None,
    )
    assert eps_clip_c_keyword is not None
    assert ast.dump(eps_clip_c_keyword.value) == ast.dump(
        ast.Attribute(value=ast.Name(id="args", ctx=ast.Load()), attr="eps_clip_c", ctx=ast.Load())
    )

评论区精华

没有提炼出高价值讨论线程

当前评论区没有形成足够清晰的争议点或结论,后续有更多讨论时会体现在这里。

风险与影响

低风险。eps_clip_c 是可选参数,若命令行未设置且默认值与 compute_policy_loss 内部缺省一致,则对不使用 dual-clip 的用户无行为变化;但一旦 --eps-clip-c 被设置,负优势分支的损失裁剪会改变,需要回归验证。另外测试中的 AST 断言对源码结构较敏感,未来若将调用改为 functools.partial 或封装函数,该测试可能误报失败而需要同步更新。

影响启用 --eps-clip-c 的训练作业,使 dual-clip PPO 的实际行为与文档/预期一致;对于未启用该选项的绝大多数用户无影响。团队 CI 增加一个 CPU 测试,几乎无资源压力。

配置透传可能改变双截断用户训练行为 AST 断言对源码结构敏感

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论