fix: capture GRPO reference policy from base weights, not resumed checkpoint#3048
Open
CanadaApollo6 wants to merge 18 commits into
Open
fix: capture GRPO reference policy from base weights, not resumed checkpoint#3048CanadaApollo6 wants to merge 18 commits into
CanadaApollo6 wants to merge 18 commits into
Conversation
…ckpoint The v2 dtensor policy worker snapshotted the reference policy after setup_model_and_optimizer(), which since NVIDIA-NeMo#1709 applies the resume checkpoint internally. On every restart the KL reference therefore silently became the resumed policy, zeroing the KL penalty (NVIDIA-NeMo#2955). The v1 worker snapshots the reference before load_checkpoint(); this restores that ordering for v2 by deferring the checkpoint load to the worker when a reference model is requested: setup runs with weights_path=None, the reference is snapshotted from the base weights, then the worker issues the same load_checkpoint() call setup would have made. Alternative shape considered: capturing the reference inside setup_model_and_optimizer and returning it on ModelAndOptimizerState. Rejected for blast radius: the automodel NamedTuple is consumed via strict positional unpacks by both the policy and value v2 workers, and a second ModelAndOptimizerState exists for megatron. Adds a regression test that fingerprints the reference via reference-policy logprobs: train, checkpoint, resume, then assert the reference still matches the base policy rather than the checkpoint. Fixes NVIDIA-NeMo#2955 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Riel St. Amand <riel.stamand@gmail.com>
Contributor
|
/claude review |
Author
|
Hey @hemildesai noticed the review command you sent didn't seem to fire. Pinging so you have visibility |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do ?
Fixes the v2 dtensor worker snapshotting the GRPO KL reference policy after the resume checkpoint has been applied, which silently resets the reference to the resumed policy on every restart and zeroes the KL penalty.
Issues
Fixes #2955
Root cause
DTensorPolicyWorkerV2.__init__snapshots the reference aftersetup_model_and_optimizer()returns — but since the v2 init refactor in #1709 (commit4cbf8d58), that call applies the resume checkpoint internally (automodel/setup.py:855). The v1 worker has the correct ordering: snapshot atdtensor_policy_worker.py:450beforeload_checkpointat:496.The fix
One file, no API change. When
init_reference_modeland aweights_pathare both requested, the worker callssetup_model_and_optimizer(..., weights_path=None, optimizer_path=None), snapshots the reference from the base weights, then issues the samecheckpoint_manager.load_checkpoint(model=..., weights_path=..., optimizer=..., optimizer_path=..., scheduler=...)call setup would have made. This restores the v1 ordering. All other paths (no reference model, fresh start) are unchanged.Alternative shape considered: capture the reference inside
setup_model_and_optimizerand return it onModelAndOptimizerState. Rejected for blast radius: that NamedTuple is consumed via strict positional unpacks by both the v2 policy worker and the v2 value worker (dtensor_value_worker_v2.py), and a second, unrelatedModelAndOptimizerStateexists for megatron. Happy to switch shapes if maintainers prefer it.Evidence
Regression test (red on
main, green with this fix)The included test trains a tiny llama, checkpoints, resumes with
init_reference_model=True, and fingerprints the reference via reference-policy logprobs on a fixed batch.On
main:The reference logprobs are bit-identical to the resumed checkpoint's (
max|ref-resumed| = 0.0).With this fix:
The resumed worker's log also shows the corrected ordering directly:
Weight-space provenance (diagnostic instrumentation, not part of this PR)
L2 distance from the reference snapshot to the base weights vs. the resumed checkpoint, measured at worker init on a resume (Qwen2.5-0.5B / tiny-llama, all 16,462,144 compared params):
Before (main):
After (this fix):
Both distances are exact zeros in opposite directions: before the fix the reference is the resumed checkpoint; after, it is the base policy.
A training-level KL-curve comparison (continuous vs. interrupt-and-resume GRPO run) is in progress and can be attached as a follow-up comment if useful.
Local evidence environment: torch 2.11.0+cu130 (CUDA 13.0), 1x RTX 3080, Python 3.13, buggy baseline =
main@5ae12621; re-validated after rebase onto78a581bb.Usage
Needs 1 GPU and an HF token with
meta-llama/Llama-3.2-1Baccess (tokenizer only; the model under test is a tiny random-weight llama built by the existing fixture).Before your PR is "Ready for review"
Pre checks:
Additional Information
The commit is DCO signed off. The regression test reuses the existing
test_dtensor_worker_v2.pyfixtures and adds a single-GPU cluster fixture, so it runs on any 1-GPU machine.🤖 Generated with Claude Code