[Feat] Add off-policy masking for partial rollouts - #2003
Conversation
|
@claude review |
1 similar comment
|
@claude review |
|
Claude: ## Summary Adds off-policy masking for partial rollouts: response tokens from prior partial-rollout rounds are masked out of the loss via IssuesCritical
Warning
Nit
ProduceBatchResult impactNot directly affected — no fields on RoutedExperts impactThe off-policy masking path replaces routed_experts instead of concatenating. Old history refs are freed via Ray concurrency impactNot affected — no changes to Ray actor methods, decorators, or concurrency groups. VerdictREQUEST_CHANGES — the missing |
|
@claude review |
|
Claude: ## Summary PATCH 4/4 addresses all critical and most warning-level issues from the previous review (missing One remaining issue: Warning
ProduceBatchResult impactNot affected — no RoutedExperts impactOff-policy path replaces (rather than concatenates) routed_experts, freeing the old ref via Ray concurrency impactNot affected. VerdictCOMMENT — the remaining |
39de861 to
0561379
Compare
|
对 partial rollout 样本做 off-policy mask 时, off-policy 的定义应该也要跟当前"过期"的定义一致?
|
|
@claude review |
04f0f12 to
1f40fff
Compare
|
@claude review |
1f40fff to
8d65892
Compare
|
@claude review |
8d65892 to
43c9e5e
Compare
|
@claude review |
8694013 to
b26bc3f
Compare
|
@claude review |
|
Claude: Summary本 PR 引入 token 级 staleness:新增 ProduceBatchResult impact: 三处 trainer-visible 变化 —(1) RoutedExperts impact: token-expiry 只对过期 state 调 Ray concurrency impact: not affected(无 Main Flowchart after this PRflowchart TD
A[put / refresh_staleness] --> B[_apply_staleness_lifecycle]
B --> C{seq_staleness >= stale_threshold}
C -- 是 --> F[标记该 state EXPIRED]
C -- 否 --> D{agentic group 或 token_stale_threshold 为空}
D -- 是 --> E[保持原状态]
D -- 否 --> G[calculate_effective_response_mask]
G --> H{存在有效 token}
H -- 是 --> E
H -- 否 --> F
F --> I{expired_groups_retryable}
I -- 是 --> J[只重置过期 state 的 response<br/>整组进 EXPIRED pool]
I -- 否 --> K[整组丢弃<br/>含仍新鲜成员]
E --> L[take_batch]
J --> M[从 EXPIRED pool rerollout]
M --> A
L --> N[take_train_batch 重算 response_mask]
N --> O[_prepare_train_data / 训练]
style K fill:#ffcccc,stroke:#cc0000
style N fill:#ffe0b2,stroke:#e65100
style G fill:#e3f2fd,stroke:#1565c0
style J fill:#e3f2fd,stroke:#1565c0
核心原理实现与单测核心实现为三段: 真实代码路径已被覆盖的部分:
覆盖缺口见「单测建议」。 抽象与信息隐藏评估
单测建议
其他 Issues
VerdictREQUEST_CHANGES |
|
|
||
| async def put_generated_group(self, group: list[RolloutState]) -> bool: | ||
| produced_tokens = sum(len(item.response_ids) for item in group if item.response_ids is not None) | ||
| produced_tokens = sum(len(item.response_ids or []) - len(item.response_model_steps or []) for item in group) |
There was a problem hiding this comment.
Claude: [测试] produced_tokens 语义改为「仅本轮新增 token」(原为整条 response 长度),这是 ProduceBatchResult 的 trainer-visible 字段,但没有任何断言覆盖。建议补一例:同一 group 连续两轮 partial rollout,断言第二轮只计入新增 token。
| @@ -92,11 +95,6 @@ def default_should_continue_fn(completed_count: int, batch_size: int, **kwargs) | |||
|
|
|||
|
|
|||
| def calculate_stale_threshold(max_staleness: int, sync_weights_interval: int) -> int: | |||
There was a problem hiding this comment.
感觉也没有必要,因为sync_weight_interval 会在RL trainer中检查是否大于0,token和seq的staleness作为AsyncproducerConfig中检查是否大于0
bbc8daa to
b191566
Compare
…Split) (#30) * [ci] change lmdeploy version and related packages (InternLM#1996) * [ci] update vllm case in gpu * fir error on rl vllm case * fix script error * fix error * [Fix] Fix Ray generate concurrency group metadata (InternLM#2005) * Fix Ray generate concurrency group metadata * fix docs build error * [Fix] Avoid scanning Transformers lazy module during test collection (InternLM#2008) * test: avoid scanning Transformers lazy module * test: call dense decoder with keyword arguments * test: preserve Qwen3.5 vision interpolation dtype * Fix custom preprocess and postprocess in judger pools (InternLM#2000) * [ci] Add Intern-S2-Preview RL ETE coverage and user examples (InternLM#2010) * [CI] Expand Claude review guidance (InternLM#2012) * [CI] Use latest Claude Opus model (InternLM#2013) * [Fix] discard expired states when tail batch is disabled (InternLM#2006) * discard expired states when tail batch is disabled * delete bind function and add retryable attr * [GLM-5.2] Preserve HF config compatibility for vLLM (InternLM#1998) * [GLM-5.2] Preserve HF export compatibility fields Keep legacy routing metadata required by vLLM and trim unused RoPE defaults from exported configs. * [Testing] Add HF config export contract checker * [Skills] Handle missing HF config exporters * [Fix] Supervise GLM-5.2 assistant stop tokens (InternLM#1997) * [Fix] Supervise GLM-5.2 assistant stop tokens * [SKILL] Add chat-template audit and implementation skill Co-authored-by: Cursor <cursoragent@cursor.com> * [SKILL] Remove Python environment instruction --------- Co-authored-by: Cursor <cursoragent@cursor.com> * [CI] Improve Claude action completion and tool access (InternLM#2015) * [CI] Improve Claude review completion * [CI] Expose Claude review turn budget * [CI] Expand sandboxed Claude review tools * [CI] Rely on isolated review runner * [CI] Expand Claude comment capabilities * [CI] Define Claude review section order * [CI] Add Claude test review guidance * [Fix] Add GLM-5.2 MuonSplit and AdamW-only gradient clipping (InternLM#2001) * Fix GLM-5.2 Muon splitting and gradient clipping * Fix MuonSplit parameter typing * Refactor gradient clipping policy * [CI] Split Claude review analysis and publication (InternLM#2016) * [CI] Split Claude review into analysis and publish phases * fix(ci): publish Claude review summary deterministically * fix(ci): scope Claude review to prepared PR diff * fix(ci): harden Claude review credentials * fix(ci): prepare deterministic Claude review bundle * refactor(ci): simplify Claude review workflow * [CI] Streamline Claude review output and publication (InternLM#2019) * [CI] Fix Claude review summary publication Allow the StructuredOutput tool required by --json-schema instead of denying every tool, and give the summary phase a second turn. Limit reported findings to Warning severity and above to keep reviews concise. * [CI] Streamline Claude review prompt and deduplication Limit review output to supported Warning+ findings, preserve the summary contract, and add a normalized discussion index for efficient deduplication. * [CI] Refine Claude review comment format Classify inline findings, keep routine comments within 150 characters, and state the required summary section order. * add qwen3.5 test cases about ep_size (InternLM#2011) * add ep case * update config * add rl mtp config * debug * update step * debug * debug * add ci debug env * update config * update description * [ci] optimize ete false positive (InternLM#2028) * Reduce ETE false positives from tight KL/time thresholds and offline resume first checks. Loosen qwen3-5 VL RL mismatch_k3_kl and qwen3-rl-lmdeploy KL/time budgets, and compare only the first-run tracker prefix when phase=first sees a merged offline file. * Check mismatch_k3_kl per-step value < 0.001 instead of baseline drift. Add method=value for RL metric bounds and apply it to all K3 KL checks so ETE no longer fails on run-to-run abs diffs. * Align qwen3-5-rl-vl-lmdeploy-mtp-ep K3 KL check to per-step value < 0.001. * update * Harden ETE checks: fix RL resume meta, drop SFT round(,2), slowdown-only time. Resolve colocate RL meta for update_meta, compare SFT relative error without two-decimal rounding, and only penalize time/step regressions for s2-preview. * drop faild (InternLM#1945) Improve agent rollout failure handling * [Feat] Add off-policy masking for partial rollouts (InternLM#2003) * Add token staleness masking during batch take * Add lifecycle-aware sampling for token-expired groups * Refine expired rollout sampling and tail-batch semantics * Refine staleness lifecycle for expired groups * fix claude comments * [Fix] fix RL MTP config handling for non-compose models (InternLM#2031) Fix RL MTP config handling for non-compose models * support transformers 5.14.1 (InternLM#2024) * support transformers 5.14.1 * update * [ci] Optimize ete case, relax sft metric after dropping round (InternLM#2034) * [ci] relax SFT metric thresholds after dropping round(,2) Align loss and text_tokens tolerances with observed run-to-run drift so ETE baseline checks match unrounded relative error semantics. Co-authored-by: Cursor <cursoragent@cursor.com> * [ci] use absolute diff for text_tokens in SFT metric checks Add per-metric comparison method support and compare token counts with exact-match threshold 0 while keeping relative drift checks for loss metrics. Co-authored-by: Cursor <cursoragent@cursor.com> --------- Co-authored-by: JuliaLin <julialin@JuliaLindeMacBook-Pro.local> Co-authored-by: Cursor <cursoragent@cursor.com> * [Fix] Preserve concurrent trace sessions in disaggregated RL (InternLM#2021) * [Fix] Preserve concurrent rollout trace sessions * [Fix] Address trace-session lifecycle review * refactor(rl): centralize terminal trace cleanup * refactor(rl): use non-retryable cleanup terminology * style(rl): apply docformatter * fix(rl): invalidate trace store cache without Ray --------- Co-authored-by: zhulinJulia24 <145004780+zhulinJulia24@users.noreply.github.com> Co-authored-by: kkscilife <126147887+kkscilife@users.noreply.github.com> Co-authored-by: Yanhui Duan <dyh10280@163.com> Co-authored-by: Penghao Zhao <henryzhao1989@163.com> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: 纪焘 (Tao Ji) <taoji.cs@gmail.com> Co-authored-by: liukuikun <24622904+Harold-lkk@users.noreply.github.com> Co-authored-by: PengchengShi00 <146822991+PengchengShi00@users.noreply.github.com> Co-authored-by: Haian Huang(深度眸) <1286304229@qq.com> Co-authored-by: JuliaLin <julialin@JuliaLindeMacBook-Pro.local> Co-authored-by: matrix72 <60974665+matrix72c@users.noreply.github.com>
|
@claude review |
|
Claude: Summary本 PR 引入 token 级 staleness:新增 ProduceBatchResult impact: 三处 trainer-visible 变化 —(1) token expiry 会把原本 RoutedExperts impact: token expiry 只对过期 state 调 Ray concurrency impact: not affected(无 核心原理实现与单测核心实现为三段,均有真实代码路径覆盖:
此外 单测建议
其他 Issues
VerdictCOMMENT |
| token_level_effective_masks = calculate_group_effective_response_masks( | ||
| group, | ||
| current_train_step=current_train_step, | ||
| token_stale_threshold=token_stale_threshold, | ||
| ) | ||
|
|
||
| for item in group: | ||
| if retryable: | ||
| # Tail batch may reroll this sample. Keep prompt and multimodal | ||
| # training inputs, but release the stale response and routed experts. | ||
| reset_rollout_response(item) | ||
| else: | ||
| # No consumer can retry this terminal group. Release all optional | ||
| # state before dropping the group's final strong references. | ||
| for index, item in enumerate(group): | ||
| if expired_mask[index]: | ||
| continue | ||
| if stale_threshold is not None and item.seq_staleness >= stale_threshold: | ||
| expired_mask[index] = True | ||
| continue | ||
| effective_mask = token_level_effective_masks[index] | ||
| if effective_mask is not None and not any(effective_mask): | ||
| expired_mask[index] = True |
There was a problem hiding this comment.
Claude: [性能] 这里为每条记录物化了完整的 per-token mask 列表(rl_data.py 内每个 state 要分配 2~3 个 len(response_ids) 长度的 list),但结果只用于 L476 的 not any(...) 判空。
该循环位于 refresh_staleness 的 async with self._lock 内且中间没有 await,会阻塞并发的 put/get/count 和宿主 event loop。改动前同一循环只做 refresh_seq_staleness(一次 min())。同时扫描范围也变大了:refresh 状态集新增了 Status.EXPIRED,disagg 路径每个 train step 还会刷新两次。
建议判空时短路,不要构造 mask(materialize 只保留给 take_train_batch):
def _has_trainable_token(item, *, current_train_step, token_stale_threshold) -> bool:
...
return any(
sem and calculate_seq_staleness(step, current_train_step) < token_stale_threshold
for sem, step in zip(semantic_mask, item.response_model_steps)
)按 .claude/CLAUDE.md,数据路径改动需要 before/after benchmark 或说明为何不需要。
ProduceBatchResult impact: 不改变任何字段取值,仅影响 produce_time_s 与整体吞吐。
| # NOTE: An EXPIRED group may still contain COMPLETED states whose responses were preserved. | ||
| # Refresh the group again so those states can also expire while waiting for rerollout. | ||
| expired_mask = [item.status == Status.EXPIRED for item in group] |
There was a problem hiding this comment.
Claude: [测试] 这条 NOTE 描述的转换没有用例覆盖:新增 5 例都从非 EXPIRED group 起步。建议补一例把混合 EXPIRED/COMPLETED 的 group 在更大的 current_train_step 上再过一次 refresh_staleness,断言保留的 fresh state 最终也翻为 EXPIRED 并被 reset。
ProduceBatchResult impact: 该转换决定 leftover_expired 与 leftover_completed 的归属,回归时不会被现有断言发现。
背景
在 partial rollout 场景下,同一条 response 中的 token 可能由不同版本的 policy 生成。
现有 sequence staleness 使用 response 中最早的模型版本表示整条样本的 staleness,无法区分:
本 PR 引入 token staleness,使系统能够:
主要改动
max_token_staleness配置,计算方式与 seq staleness 相同,在 take batch 阶段统一更新 response maskreplay_buffer.put和refresh_staleness,与更新 sequence staleness 相同说明:这个PR不改动agentic RL的过期语义
token staleness 处理关键阶段
如何采样
flowchart LR A[刷新 staleness] --> B[统计 EXPIRED groups] B --> C{tail_batch_trigger_size} C -- -1 --> D[采样 ABORTED 或新数据] C -- 0 且存在 EXPIRED --> E[优先采样 EXPIRED group] C -- 大于0且达到阈值 --> F[进入 tail batch] C -- 大于0但未达到阈值 --> D E --> G[保持正常异步生产和 oversampling] F --> H[关闭本轮 oversampling] D --> I[执行 rollout] G --> I H --> I如何判断一个样本是否过期
flowchart LR A[刷新 seq staleness] --> B{超过 seq threshold} B -- 是 --> C[state 标记为 EXPIRED] B -- 否 --> D{普通 rollout 且配置 token threshold} D -- 否 --> E[state 保持有效] D -- 是 --> F[计算 effective response mask] F --> G{是否存在有效 token} G -- 否 --> C G -- 是 --> E C --> H[StorageItem 标记为 EXPIRED] H --> I{是否允许 rerollout} I -- 是 --> J[只清空实际过期 state 的 response] I -- 否 --> K[丢弃整个 group]配置示例
该配置表示: