Skip to content

Fix stale weight decay after OptimizerParamScheduler.load_state_dict - #7468

Open
Nicholas022400701 wants to merge 3 commits into
NVIDIA:mainfrom
Nicholas022400701:fix-scheduler-load-state-dict-order
Open

Nicholas022400701 wants to merge 3 commits into
NVIDIA:mainfrom
Nicholas022400701:fix-scheduler-load-state-dict-order

Conversation

@Nicholas022400701

@Nicholas022400701 Nicholas022400701 commented Sep 18, 2026 •

Copy link
Copy Markdown
  • I, the PR author, have personally reviewed every line of this PR.

What does this PR do?

Fixes a resume bug in OptimizerParamScheduler.load_state_dict. The method called self.step(increment=num_steps) before it restored start_wd, end_wd, wd_incr_steps and wd_incr_style from the checkpoint. step() writes both lr and weight_decay into every param group, so right after a resume the param groups held the learning rate from the checkpoint schedule but the weight decay from the constructor arguments. The scheduler object itself ended up with the right values, only the param groups were stale.

This matters because train_step in megatron/training/training.py calls optimizer.step() before opt_param_scheduler.step(), so the first optimizer step after a resume ran with the wrong weight decay. Every later step is fine because the next scheduler step rewrites the param groups. The bug only shows up when --use-checkpoint-opt_param-scheduler is set and the weight decay arguments differ from the ones stored in the checkpoint. With matching arguments the two schedules agree and nothing is visible.

The fix moves the num_iters / num_steps lookup and the step() call to the end of load_state_dict, after every value has been restored. Legacy checkpoints that only carry the old learning rate keys still load the same way. No other behaviour changes.

The change touches two files.

  • megatron/core/optimizer_param_scheduler.py moves the step() call to the end of load_state_dict, adds a two line comment saying why, and adds the blank line after the module docstring that black 26.3.0 requires. That blank line is the only formatting change. black already fails on this file on main without it, so the formatter run for this PR could not pass otherwise.
  • tests/unit_tests/test_optimizer_param_scheduler.py adds test_load_state_dict_applies_checkpoint_weight_decay. It loads a checkpoint whose weight decay schedule differs from the constructor and checks that the param group holds the checkpoint value and that lr and weight decay agree with the scheduler. On main the param group holds 0.05 while the scheduler reports 0.2, so the test fails there.

Tests

Run on a CPU GitHub Actions runner with torch 2.14.0+cpu and Python 3.12.

Issue tracking

Linked issue: none. I searched the issues and pull requests for this method and did not find a report. #4705 is about matching param groups on load and does not touch this ordering.

Contribution process

Pre-checks

  • I have added relevant unit tests
  • I have added relevant functional tests
  • If this PR adds or changes a GPU kernel (Triton, jit_fuser/torch.compile, CUDA extension, TE or external-library dispatch, or a scatter/index accumulation), I have added or updated its bit-exact determinism test and registered it in tests/unit_tests/determinism/kernels/manifest.py (guide)
  • I have added proper typing to my code Typing guidelines
  • I have added relevant documentation
  • I have run the autoformatter.sh on my PR

Notes on the boxes. No functional test because the change is covered by the unit test. No kernel is touched. The method keeps its existing type hints and no documentation describes this ordering. tools/autoformat.sh needs a remote and pylint, so I ran the same black, isort and ruff commands on the two changed files in the CI run linked above instead.

AI disclosure: I used an AI coding agent to help write this patch, the test and this description. I have read every line of the change myself and I will answer review comments personally.

The three commits are signed off. I can squash them into one if you prefer.

OptimizerParamScheduler.load_state_dict called step() before it restored
start_wd, end_wd, wd_incr_steps and wd_incr_style from the checkpoint. The
weight_decay written into the optimizer param groups therefore came from the
constructor arguments while the learning rate already came from the
checkpoint, so the first optimizer step after a resume ran with the wrong
weight decay whenever the two disagreed. Call step() at the end of
load_state_dict instead.

Signed-off-by: 区梓灏 <116372750+Nicholas022400701@users.noreply.github.com>
… param groups

Signed-off-by: 区梓灏 <116372750+Nicholas022400701@users.noreply.github.com>
black --check already fails on this file on main because of this one
line, so the formatter run for this change could not pass without it.

Signed-off-by: 区梓灏 <116372750+Nicholas022400701@users.noreply.github.com>
@copy-pr-bot

copy-pr-bot Bot commented Sep 18, 2026

Copy link
Copy Markdown

This pull request requires additional validation before any workflows can run on NVIDIA's runners.

Pull request vetters can view their responsibilities here.

Contributors can view more details about this message here.

@Nicholas022400701
Nicholas022400701 marked this pull request as ready for review September 18, 2026 04:46
@Nicholas022400701
Nicholas022400701 requested review from a team as code owners September 18, 2026 04:46
Copilot AI lite review requested due to automatic review settings September 18, 2026 04:46

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@svcnvidia-nemo-ci
svcnvidia-nemo-ci requested a review from a team September 18, 2026 04:47
@svcnvidia-nemo-ci svcnvidia-nemo-ci added Final Review PR is in the "final review" stage waiting-on-maintainers Waiting on maintainers to respond labels Sep 18, 2026
@Connor-XY
Connor-XY requested a review from skyw September 21, 2026 18:55
@Connor-XY

Copy link
Copy Markdown
Contributor

@skyw, could you review this scheduler resume fix? It moves the initial step until after weight-decay settings are restored, so the first resumed optimizer update uses the checkpoint schedule. The added regression checks the optimizer parameter-group values immediately after loading.

@svcnvidia-nemo-ci svcnvidia-nemo-ci removed the waiting-on-maintainers Waiting on maintainers to respond label Sep 21, 2026
@skyw

skyw commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

@skyw, could you review this scheduler resume fix? It moves the initial step until after weight-decay settings are restored, so the first resumed optimizer update uses the checkpoint schedule. The added regression checks the optimizer parameter-group values immediately after loading.

I'm not familiar with how this function works and can't provide meaningful review. The syntax LGTM based on the description of the PR.

@svcnvidia-nemo-ci svcnvidia-nemo-ci added the waiting-on-customer Waiting on the original author to respond label Sep 22, 2026
@Nicholas022400701

Copy link
Copy Markdown
Author

This PR was labelled waiting-on-customer on Sep 22 after the comment above, but I cannot find a request addressed to me. If something is needed from my side, a short note on what to change or provide is enough and I will do it.

@svcnvidia-nemo-ci svcnvidia-nemo-ci added waiting-on-maintainers Waiting on maintainers to respond and removed waiting-on-customer Waiting on the original author to respond labels Sep 23, 2026
@dimapihtar

Copy link
Copy Markdown
Contributor

/claude review

@github-actions

github-actions Bot commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

@dimapihtar /claude review has been retired. Pull-request reviews now run through /review; comment this instead:

/review

Add model=claude to keep a Claude reviewer (the default is model=codex). mode=light|strict selects the review depth. Comment /review help for all options.

@svcnvidia-nemo-ci svcnvidia-nemo-ci added waiting-on-maintainers Waiting on maintainers to respond and removed waiting-on-maintainers Waiting on maintainers to respond labels Oct 5, 2026
@guihong-nv

Copy link
Copy Markdown
Contributor

/review model=claude mode=strict

@svcnvidia-nemo-ci svcnvidia-nemo-ci added waiting-on-maintainers Waiting on maintainers to respond and removed waiting-on-maintainers Waiting on maintainers to respond labels Oct 8, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

community-request Final Review PR is in the "final review" stage waiting-on-maintainers Waiting on maintainers to respond

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants