Prhub

#6709 [reward] fix: only require max_resp_len when DAPO overlong penalty is enabled

原始 PR 作者 discobot 合并时间 2026-06-13 14:49 文件变更 3 提交数 1 评论 3 代码增减 +132 / -5

执行摘要

修复 DAPO overlong penalty 配置门控条件

Issue #5858指出,当overlong_buffer_cfg.enable=False时,构造函数仍要求max_resp_len不为None,与文档'To disable the overlong penalty, set overlong_buffer.enable = False'矛盾。另外,当overlong_buffer_cfg=None时,__call__因直接访问enable属性而抛出AttributeError。PR旨在修复这两个不一致问题。

值得参考其防御性编程思路:断言条件应精准匹配实际激活路径,避免与配置语义矛盾。同时建议合并前确保实验版本的 run_single 也有 None 检查,但本 PR 未涉及。

讨论亮点

PR作者 discobot 在评论中指出 CI 失败已存在于 main 上,与 PR 无关。审核人 Luosuu 批准合并,无其他争议。

实现拆解

  1. verl/workers/reward_manager/dapo.pyverl/experimental/reward_loop/reward_manager/dapo.py__init__ 方法中,将断言条件从 if self.overlong_buffer_cfg is not None 改为 if self.overlong_buffer_cfg is not None and self.overlong_buffer_cfg.enable,并移除原来的 not self.overlong_buffer_cfg.enable or 条件,简化为 assert self.overlong_buffer_cfg.len > 0

  2. verl/workers/reward_manager/dapo.py__call__ 方法中,将 if self.overlong_buffer_cfg.enable 改为 if self.overlong_buffer_cfg is not None and self.overlong_buffer_cfg.enable,防止 None 时崩溃。实验版本已存在类似安全处理,无需修改。

  3. 新增 tests/workers/reward_manager/test_dapo_on_cpu.py,包含6个测试用例:禁用时不需要 max_resp_len、启用时需要 max_resp_len、启用时拒绝过短的 max_resp_len、默认 None 配置不崩溃、启用时惩罚正确计算、实验版本禁用场景。

文件 模块 状态 重要度
tests/workers/reward_manager/test_dapo_on_cpu.py 测试 added 7.53
verl/workers/reward_manager/dapo.py 奖励管理器 modified 5.59
verl/experimental/reward_loop/reward_manager/dapo.py 实验模块 modified 5.07

关键符号

DAPORewardManager.__init__ (workers) DAPORewardManager.__call__ (workers) DAPORewardManager.__init__ (experimental)

关键源码片段

verl/workers/reward_manager/dapo.py core-logic

核心修复文件,修改构造函数和 __call__ 中的条件逻辑,是 bug 修复的主要场所。

def __init__(self, tokenizer, num_examine, compute_score=None,
             reward_fn_key="data_source", max_resp_len=None,
             overlong_buffer_cfg=None):
    # ... 其他初始化 ...
    self.overlong_buffer_cfg = overlong_buffer_cfg
    self.max_resp_len = max_resp_len
​
    # 仅当 overlong buffer 启用时才要求 max_resp_len
    if self.overlong_buffer_cfg is not None and self.overlong_buffer_cfg.enable:
        assert self.max_resp_len is not None, (
            f"max_resp_len must be provided if {overlong_buffer_cfg=}, but got None"
        )
        assert self.max_resp_len >= self.overlong_buffer_cfg.len, (
            "max_resp_len must be larger than overlong_buffer.len"
        )
        assert self.overlong_buffer_cfg.len > 0, (
            "overlong_buffer.len must be positive when overlong penalty is enabled,"
            f" but got {self.overlong_buffer_cfg.len}."
        )# __call__ 中的修改
def __call__(self, data, return_dict=False):
    # ... 前面的分数计算 ...
    reward = score
    # 应用 overlong 惩罚时同样检查 None
    if self.overlong_buffer_cfg is not None and 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

评论区精华

CI 失败是否与 PR 相关 other

PR 作者 discobot 指出两个 CI 失败(e2e_fully_async_policy_fsdp2 和 e2e_ppo_trainer_megatron-deepseek)已在 main 上复现,与本次改动无关。

结论:确认与 PR 无关,不阻塞合并。 · 已解决

风险与影响

风险较低。改动集中在条件分支,启用 penalty 的行为完全不变。新增测试覆盖了所有标识路径,包括禁用、启用、None 三态。唯一潜在风险是实验版本的 run_single 中是否还有类似的 None 访问(但本次未改动),不过该模块已存在类似守卫。建议后续确认实验版本的 run_single 是否也需要同步 None 检查。

影响使用 DAPO reward manager 的用户,特别是那些配置了 overlong_buffer_cfg 但 enable=False 的用户,修复后不再因缺少 max_resp_len 而报错。overlong_buffer_cfg=None 的用户也不再因 call 崩溃。影响范围限于 DAPO reward manager 的使用者,即使用 DAPO 算法的训练脚本。

配置条件变更 潜在 None 访问 实验模块需同步检查

关联 Issue

#5858 DAPORewardManager maybe error

完整报告

参与讨论