Prhub

#2369 fix(rollout): normalize rewards per rollout

原始 PR 作者 Shi-Dong 合并时间 2026-08-13 00:35 文件变更 4 提交数 6 评论 0 代码增减 +280 / -35

执行摘要

按 rollout 归一化奖励,修复 session v2 变扇出统计偏差

PR body 描述的症状:Variable session-v2 leaf fanout gives one rollout multiple segment rewards, so row-level normalization weights prompt statistics by segment count and lacks one rollout target. 即同一 rollout 因叶子数不同,在按行归一化时一个 rollout 会被重复计入统计,且缺少单一代表奖励。同时两个 CI 任务失败:v2 agentic samples require input Sample.rollout_id or Sample.indexsample.rollout_id is not bitwise equal,说明 session v2 已开始传播 rollout 身份而测试夹具没有跟上。修复目标正如标题所述:Normalize one mainstream reward per rollout without weighting statistics by leaf fanout

值得精读。重点看 _reward_group_segments 的三级 fallback 和 _normalize_rewards_by_rollout 的主流样本假设,这是本 PR 最核心的设计决策;建议配合 test_train_data_conversion.py 中新用例阅读,理解从旧行级归一化到新 rollout 级归一化的契约变化。若要改动主流选择策略,需同步审视 _trainable_token_countremove_sample 的处理。

讨论亮点

本 PR 没有 line-level review 评论,guapisolo 直接批准(APPROVED)。核心设计权衡体现在 6 个提交的演进里:第一个提交尝试“保留每个 leaf reward、按等权聚合再变换”;随后 guapisolo 改为“每个 rollout 选主流样本”,并明确假设“最长可训练兄弟最能代表 rollout”;再逐步补齐命名(segment 术语)、文档(fallback 说明)和测试夹具对齐。这说明该改动经历过一轮内部设计迭代后收敛,而非一次性拍板。

实现拆解

  1. 恢复 prompt 边界:新增 _reward_group_segmentsmiles/ray/rollout/train_data_conversion.py),按 prompt_group_sizes(Multi-LoRA 显式边界)→ 完整 group_index → 旧版固定 fanout 连续布局 → 单组兜底 的优先级还原每个 prompt 的行范围,替换旧逻辑里依赖 n_samples_per_prompt * rollout_batch_size 的 reshape 推断。
  2. 每个 rollout 选主流样本:新增 _trainable_token_count_normalize_rewards_by_rollout。在 prompt 组内用 rollout_id(或 index)聚合兄弟样本,选择 _trainable_token_count 最大的样本作为主流,取其原始奖励参与组内零均值(及可选 std 归一化),然后把归一化后的 advantage 广播给同 rollout 的所有叶子;无任何身份时每行独立成组。
  3. 替换旧归一化逻辑_post_process_rewards 删除原来的 reshape/view/split 分组分支,统一委托给 _normalize_rewards_by_rollout;estimator 白名单(grpo/gspo/reinforce_plus_plus_baseline)与 grpo_std_normalization 开关逻辑保持兼容。
  4. 测试与夹具对齐tests/fast/ray/rollout/test_train_data_conversion.py 新增主流选择、平局取首个、兄弟广播、prompt 边界恢复、空输入等 8+ 用例;tests/fast/rollout/generate_hub/test_multi_turn.py 为 agentic variant 补 index=0 并在结构比较时剥离 index/rollout_id 字段;tests/fast/router/test_session_v1_v2_parity.py 的 fixture 显式设置 rollout_id=index。无配置、schema 或部署配套改动。
文件 模块 状态 重要度
miles/ray/rollout/train_data_conversion.py 奖励归一化 modified 8.18
tests/fast/ray/rollout/test_train_data_conversion.py 数据转换 modified 7.43
tests/fast/rollout/generate_hub/test_multi_turn.py 多轮生成 modified 4.16
tests/fast/router/test_session_v1_v2_parity.py 会话对齐 modified 4.03

关键符号

_reward_group_segments _trainable_token_count _normalize_rewards_by_rollout

分析完成后,这里会展示 LLM 生成的相对完整源码片段和详细注释。

评论区精华

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

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

风险与影响

数值语义变化:所有启用 rewards_normalization 的 GRPO/GSPO/Reinforce++ 任务,advantage 计算从“行级”改为“rollout 级”,训练曲线会与旧版本不同,属于预期内但影响面广。主流样本假设:若最长可训练段的奖励恰是异常值(如被截断或含噪声),该 rollout 的整体 advantage 会被带偏,目前没有对主流选择的置信度或回退机制。零方差行为变化:旧代码在 std=0 时仍除以 1e-6 产生无意义放大,新代码跳过缩放,结果更合理但可能与旧快照不一致。兜底路径:无 rollout_id/index/group_index 的旧数据会走“每行独立”或“整体一组”的兜底,行为与旧版不同,迁移老数据时需验证。回归保护:新的 8+ 用例覆盖主流选择、平局、广播、边界恢复与空输入,CI 全绿,风险可控。

影响所有启用 rewards_normalization 的 GRPO/GSPO/Reinforce++ 训练任务,尤其 session v2 agentic 多叶子和 Multi-LoRA 显式分组的场景;行级训练数据结果与历史版本数值不同(语义上更合理)。对用户而言,同一 rollout 的多个叶子现在共享同一个 advantage,prompt 统计不再被扇出倍数放大;对团队而言,reward 语义从“按行”变为“按 rollout 单元”,需要同步更新相关文档和基于旧数值的基准对比。

核心训练路径数值变更 主流样本启发式假设 旧数据兜底行为差异 测试覆盖充分

关联 Issue

未识别关联 Issue

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

完整报告

参与讨论