Repository navigation
fix(grpo): drop top-k/top-p -inf from token_mask - #4154
Conversation
|
Auto-sync is disabled for ready for review pull requests in this repository. Workflows must be run manually. Contributors can view more details about this message here. |
|
/ok to test 5f2b68f |
5f2b68f to
bc23541
Compare
|
/ok to test bc23541 |
yuki-97
left a comment
There was a problem hiding this comment.
Thanks for pushing both implementations up rather than picking one — having d4d8a09 and bc23541 side by side made the tradeoff much easier to judge. The megatron side derives has_token_mask from config rather than from the data so every PP rank agrees before the broadcast (megatron_policy_worker.py:2202-2203).
Let's go with this implementation. Carrying the mask explicitly means a downstream consumer only has to read token_mask to be correct, whereas d4d8a09 also requires every consumer of prev_logprobs to know to test isfinite — an unwritten rule that is easy to miss.
Scope: the new token_mask entry on LogprobOutputSpec and its propagation through the three policy workers, the GRPO/PPO drivers, and prepare_loss_input / prepare_packed_loss_input.
Checked:
- Every implementation of
get_logprobsand every caller of it, for who propagates the new key and who drops it. - Shape and layout of the new mask across sequence packing, CP, the dynamic-batching reorder, the PP broadcast, and the DP
from_batchesmerge. - Whether the in-place
data["token_mask"]rewrite reaches the loss fn on all four wrapper paths, and whether it leaks across microbatches or PPO epochs. - The reference-policy path, where
use_reference_modeldisables top-k/top-p, so no analogous key is needed there. - Existing coverage —
tests/functional/grpo_topp_topk.shandgrpo_megatron_generation_topp_topk.shdo exercise the headline config on dtensor v2 and megatron.
Remaining comments: two callers that do not propagate the new key — the first of them decides whether the "just read token_mask" guarantee actually holds on the SingleController path — one on the reference-KL reduction losing the same positions, and three nits (a pre-existing test gap, a duplicated _pad_and_concat, and a presence check keyed off data rather than config).
Signed-off-by: Teodor-Dumitru Ene <teodord.ene@gmail.com>
Signed-off-by: Teodor-Dumitru Ene <teodord.ene@gmail.com>
Co-authored-by: Jorge Albericio <jalbericiola@nvidia.com> Signed-off-by: Teodor-Dumitru Ene <teodord.ene@gmail.com>
Signed-off-by: Teodor-Dumitru Ene <teodord.ene@gmail.com>
bc23541 to
ab86ede
Compare
tdene
left a comment
There was a problem hiding this comment.
Team review of PR #4154 at ab86ede (agents: rl-expert, bug-finder, test-agent, comment-reviewer, design-reviewer, devil-advocate; every inline finding was independently re-verified by the leader, and all CPU-runnable checks were executed against this head).
Overall: the token_mask design is implemented consistently across the three producers, the four driver sites and the TQ mixin; the five addressed threads from the previous review (T1, T3, T4, T5, T6) are complete at this head, and the rebase port is byte-identical to the reviewed hunks apart from the pinned-memory return adaptation. One correctness issue (inline, two one-line casts): the TQ write-back changes the token_mask column's dtype, which TransferQueue rejects silently; it regresses the two shipped topp0.9-topk50-tq_mooncake nightlies.
Merge gates (not code asks):
- No
CICD NeMo RLrun exists on this head; the last one was on bc23541 (2026-09-17, Lfast, pre-rebase, 51 main commits ago). The commit that addressed the review touches 12 files, so/ok to test ab86edeab3e9c8b597f04de822ca1ee9e38e3d3eis needed before anything is CI-proven. The GPU-gatedtest_clipped_pg_loss_kl_penalty[1-...]parametrization andtests/unit/utils/test_tensor_ops.pyhave never executed in CI; the gate is the literalPASSEDlines in theL0_Unit_Tests_Algorithms/L0_Unit_Tests_Otherlogs. (A CPU copy of the GPU test passes here with the hand-computed values, and a mutant with the narrowing removed fails, so a pass is expected.) CI:Lfastis adequate for the merge round: it runs both headline functionals (grpo_topp_topk.sh,grpo_megatron_generation_topp_topk.sh) plus the PPO/distillation/topk paths touched bypad_and_concat, andCI:L1would add only one more filtering test (grpo_multiturn.sh, dtensor,top_k=8000; the megatron multiturn twin is disabled). What no tier covers pre-merge is the data plane with filtering on — the two shipped nightlies that do (nightly.txt:189-190) are exactly the configuration the inline dtype finding breaks.- No tier runs megatron PP>1 with top-k/top-p (every top-k functional and both TQ nightlies are PP=1), so the PP broadcast of the new key is untested anywhere; low risk, since
broadcast_tensorships shape and dtype metadata. - The packing/CP layout cell of the previous review's "Checked" list was re-derived at this head except where #4106 (megatron/data.py) landed after that review; the fast-tagged top-k/top-p functionals on this head are the current-stack evidence for it.
Context — no action:
- Order of consumers is identical on the Ray drivers,
grpo_syncand SingleController (logprobs → seq-logprob-error masking → advantages → train), so every path sees the narrowed mask; the only divergence is metrics-only (response_advantagesselection uses the rollout mask in grpo.py:3815-3819 and the narrowed one in grpo_sync.py:1168). - On PPO the write-back precedes GAE, so if the final response token is a
-infposition the terminal reward lands one token earlier — consistent with the design. test_clipped_pg_loss_kl_penaltycomputesglobal_valid_toksafterprepare_loss_input(=1) while workers all-reduce it before the forward from the un-narrowed mask (=3), so 0.0368 is valid loss arithmetic rather than the value a worker would log.- T2 (reference-KL at
-infpositions) stays an open discussion with yuki-97; the inline docs comment onloss_input.py:278is the minimal outcome if the current behaviour stands.
Linter: pre-commit run --all-files at this head: every hook passes (ruff, ruff-format, taplo, pyrefly, minimize-check).
Devil's advocate: 6 confirmed (the dtype finding upgraded to critical) | 0 disputed | 1 downgraded and dropped (a dtensor-worker predicate hoist with no concrete divergence scenario). PR necessity: needed — a non-trivial functional change that removes the exp(-gen) importance-weight blow-up at -inf positions.
Generated by Claude Code
Signed-off-by: Teodor-Dumitru Ene <teodord.ene@gmail.com>
|
/ok to test 5f1b1af |
Sibling of grpo-llama3.2-1b-instruct-1n8g-megatron-temp0.8-topp0.9-topk50 with loss_fn.use_importance_sampling_correction enabled and gates on the IS ratio; requested in the review of NVIDIA-NeMo#4154. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Signed-off-by: Teodor-Dumitru Ene <teodord.ene@gmail.com>
Signed-off-by: Teodor-Dumitru Ene <teodord.ene@gmail.com>
|
/ok to test 10da9fa |
yuki-97
left a comment
There was a problem hiding this comment.
Scope: re-review at 10da9fa — the fixes since the previous pass (ab86ede, 5f1b1af) and the new -topp0.9-topk50-is nightly.
Checked:
- Previous threads — each fix re-read at head; the resolved ones are replied to with the commit that carried them.
- Data-plane write-back of
token_mask— packed and ordered likeprev_logprobs, andgrpo_sync,train_preshardedand the SingleController advantage stage all read it only after the logprob stage. The dtype cast keeps whatever dtype the column was written with (int64 from the GRPO rollout, float32 from the SingleController reassembler). pad_and_concat— equivalent to every copy it replaced (2D, 3D top-k, teacher payload, both value workers).- New nightly —
use_importance_sampling_correction: trueon top of the Megatron top-p/top-k recipe, and the nightly budget cap moves to exactly the total with the new entry.
Remaining comments are on documenting, in the GRPO guide and next to the unfiltered KL recompute, that -inf positions are also dropped from the reference-KL term — keeping the current behaviour.
nit, not blocking merging — the data-plane stage→column note in schema.py:44-46 does not yet mention the token_mask rewrite.
Signed-off-by: Teodor-Dumitru Ene <teodord.ene@gmail.com>
|
/ok to test 12485ed |
|
/ok to test 2cb0fe0 |
|
/ok to test ba53070 |
Preserve main's corrected mask transport from #4154 and retain tokenless sample exclusion and loss sanitization. Target main before backporting to r0.8.0. Signed-off-by: Meng Xin <164952785+mxinO@users.noreply.github.com>
…ask (#4722) Signed-off-by: Teodor-Dumitru Ene <teodord.ene@gmail.com> Co-authored-by: Jorge Albericio <jalbericiola@nvidia.com>
…s end The prefix-recovery parity checks located a restored call at the start of its row's last unmasked segment. When top-k/top-p filtering is on, the trainer drops positions whose recomputed logprob is -inf from token_mask (#4154), and the rows these checks read are dumped after that pass, so a masked token inside the call made the check start the call too late, miscount its tokens, and fail a correct restore. A restored call closes its row, so the total_generation_tokens it logs on completion end the row. _assert_prefix_boundary now counts back from the row's end, requires the token before the call to be untrained, checks the completion's prefix + tail = total accounting, and requires the restored prefix at that position; a dropped or duplicated token moves the position. It returns where each call starts, which _rows_diverged_before_cut reuses to compare the reference row at the same position. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Anish Mahishi <amahishi@nvidia.com>
What does this PR do ?
Fix issue with IS and topp/topk masking; see offline discussion.
Issues
List issues that this PR closes (syntax):
Usage
# Add a code snippet demonstrating how to use thisBefore your PR is "Ready for review"
Pre checks:
Additional Information