Skip to content

Route Lion through DistributedOptimizer and support single-moment checkpointing - #5742

Merged
deepakn94 merged 1 commit into
NVIDIA:mainfrom
deepakn94:dnarayanan/distopt_lion_checkpointing
Jul 18, 2026
Merged

Route Lion through DistributedOptimizer and support single-moment checkpointing#5742
deepakn94 merged 1 commit into
NVIDIA:mainfrom
deepakn94:dnarayanan/distopt_lion_checkpointing

Conversation

@deepakn94

@deepakn94 deepakn94 commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Summary

The DistributedOptimizer checkpointing code hardcoded the assumption that every optimizer uses exactly two moment states (exp_avg and exp_avg_sq), matching Adam. Optimizers like Lion only use a single moment (exp_avg), causing failures during checkpoint save/load.

Changes

megatron/core/optimizer/distrib_optimizer.py

  • Add optimizer_state_keys property that returns the optimizer's tensor state keys via a hardcoded mapping (e.g., ("exp_avg",) for Lion, defaulting to ("exp_avg", "exp_avg_sq") for Adam). When Muon is the top-level optimizer, resolves through config.muon_scalar_optimizer.
  • Add _get_state_key_dtype helper that maps state keys to their configured dtypes.
  • Replace six hardcoded ("param", "exp_avg", "exp_avg_sq") tuples with ("param",) + self.optimizer_state_keys in load_state_dict, get_parameter_state_dp_zero, load_parameter_state_from_dp_zero_legacy, load_parameter_state_from_dp_zero, and split_state_dict_if_needed.
  • Relax the optimizer-type assertion to allow any optimizer that provides an init_state_fn (not just Adam and HybridDeviceOptimizer).

megatron/core/optimizer/__init__.py

  • When Lion is used as Muon's scalar optimizer (muon_scalar_optimizer='lion'), rewrite the default param overrides so Lion scalar param groups route through the standard fallback path (creating a DistributedOptimizer) rather than the emerging-optimizer constructor.
  • Fix the _EMERGING_OPTIMIZERS membership check to compare against the primary emerging optimizer name (eopt_name), so scalar optimizers that happen to also be registered (e.g., Lion) fall through correctly.

Tests

  • tests/unit_tests/test_lion_optimizer.py: Unit tests for optimizer_state_keys (Lion, Adam, Muon+Lion, Muon+Adam) and _get_state_key_dtype.
  • tests/unit_tests/dist_checkpointing/test_optimizer.py: End-to-end checkpoint round-trip test using Muon + Lion (muon_scalar_optimizer='lion') with dp_reshardable and fully_reshardable sharding types. Verifies that Lion's single-moment state survives save/load through DistributedOptimizer.
  • tests/unit_tests/dist_checkpointing/utils.py: Plumb muon_scalar_optimizer parameter and Lion learning-rate default through setup_model_and_optimizer.

Test plan

  • tests/unit_tests/test_lion_optimizer.py — Lion config, factory, exactness, and DistributedOptimizer state-key unit tests
  • tests/unit_tests/dist_checkpointing/test_optimizer.py::TestDistributedOptimizer::test_lion_optimizer_checkpoint_round_trip — checkpoint save/load round-trip with Muon+Lion

🤖 Generated with Claude Code

@copy-pr-bot

copy-pr-bot Bot commented Jul 10, 2026

Copy link
Copy Markdown

Auto-sync is disabled for draft pull requests in this repository. Workflows must be run manually.

Contributors can view more details about this message here.

@deepakn94
deepakn94 force-pushed the dnarayanan/distopt_lion_checkpointing branch from 467c807 to 74dd5e8 Compare July 10, 2026 15:34
@deepakn94

Copy link
Copy Markdown
Contributor Author

/claude review

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Light review — one correctness concern (posted inline) and one test-coverage note.

Test coverage: This PR generalizes the DistributedOptimizer checkpoint save/load paths to support non-Adam optimizers, but there's no test exercising a distributed-checkpoint round-trip with a single-state-key optimizer (e.g. Lion). tests/unit_tests/test_lion_optimizer.py only covers the factory/config plumbing, and tests/unit_tests/dist_checkpointing/test_optimizer.py only uses Adam. Since setup_model_and_optimizer in tests/unit_tests/dist_checkpointing/utils.py already accepts an optimizer= argument, consider parametrizing an existing save/load test (e.g. TestDistributedOptimizer) over optimizer=['adam', 'lion'] so the new optimizer_state_keys path and the dp_zero gather/scatter are covered with a differing state-key count. This would also catch the step-key issue flagged inline.

Comment thread megatron/core/optimizer/distrib_optimizer.py Outdated
@mkhona-nvidia

Copy link
Copy Markdown
Contributor
image

Tested on a small MoE model, loss curve looks continuous

@copy-pr-bot

copy-pr-bot Bot commented Jul 16, 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.

@mkhona-nvidia

Copy link
Copy Markdown
Contributor
image Tested on a small MoE model, loss curve looks continuous
image

latest test with routing scalar optimizer fix

@deepakn94
deepakn94 marked this pull request as ready for review July 16, 2026 23:51
@deepakn94
deepakn94 requested review from a team as code owners July 16, 2026 23:51
@deepakn94

Copy link
Copy Markdown
Contributor Author

/ok to test 762efa8

@zhongbozhu zhongbozhu left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

@deepakn94

Copy link
Copy Markdown
Contributor Author

/ok to test 2c97b67

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

LGTM

…kpointing

The DistributedOptimizer checkpointing code hardcoded the assumption that
every optimizer has exactly two moment states (exp_avg and exp_avg_sq),
matching Adam. This broke optimizers like Lion that only use one moment
(exp_avg).

Add an optimizer_state_keys property that returns the correct state keys
based on the optimizer type, and replace all hardcoded key tuples with
this property. Also relax the optimizer-type assertion to allow any
optimizer that provides an init_state_fn.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Deepak Narayanan <dnarayanan@nvidia.com>
@mkhona-nvidia

Copy link
Copy Markdown
Contributor
image

Final dist ckpting check with muon + lion

@svcnvidia-nemo-ci

Copy link
Copy Markdown
Contributor

🔄 Merge queue validation started!

You can track the progress here: https://github.com/NVIDIA/Megatron-LM/actions/runs/29629381676

@svcnvidia-nemo-ci

Copy link
Copy Markdown
Contributor

🔄 Merge queue validation started!

You can track the progress here: https://github.com/NVIDIA/Megatron-LM/actions/runs/29630656179

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

Labels

Approved All necessary approvals have been made complexity: low

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants