NNX: save checkpoints in the Linen on-disk layout (interchangeable with Linen)#3929
Merged
Merged
Conversation
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
ecnal-cienet
force-pushed
the
feat/checkpoint-linen-to-nnx-adapter
branch
4 times, most recently
from
May 18, 2026 00:49
f77e99c to
c035e07
Compare
ecnal-cienet
force-pushed
the
feat/checkpoint-linen-to-nnx-adapter
branch
4 times, most recently
from
May 19, 2026 22:50
c1753ce to
9fccf6c
Compare
ecnal-cienet
force-pushed
the
feat/checkpoint-linen-to-nnx-adapter
branch
from
May 19, 2026 23:14
9fccf6c to
9fbf75a
Compare
4 tasks
ecnal-cienet
marked this pull request as ready for review
May 20, 2026 01:09
ecnal-cienet
requested review from
A9isha,
NuojCheng,
RissyRan,
SurbhiJainUSC,
abhinavclemson,
aireenmei,
bvandermoon,
gagika,
gobbleturk,
hengtaoguo,
jiangjy1982,
khatwanimohit,
richjames0,
shralex,
suexu1025 and
vipannalla
as code owners
May 20, 2026 01:09
Contributor
|
🤖 Hi @ecnal-cienet, I've received your request, and I'm working on it now! You can track my progress in the logs for more details. |
Contributor
There was a problem hiding this comment.
The Pull Request successfully implements the interchangeability of Linen and NNX checkpoints by providing bi-directional conversion logic. The implementation is robust, handling differences in top-level keys, weight wrapping, optimizer state representations, and step dtypes.
🔍 General Feedback
- Well-Structured Conversion: The separation of concerns between the checkpointing logic and the layout transformation in
train_state_nnx.pyis excellent. - Abstract State Handling: The use of
_cast_stepand_default_for_sdsto handlejax.ShapeDtypeStructensures that the conversion works correctly during abstract restoration, which is critical for performance and multi-host compatibility. - Comprehensive Testing: The addition of unit tests for both full-state and weight-only restoration, as well as round-trip conversion, provides high confidence in the implementation.
bvandermoon
approved these changes
May 27, 2026
ecnal-cienet
force-pushed
the
feat/checkpoint-linen-to-nnx-adapter
branch
from
May 27, 2026 17:28
ad16cdc to
4a091cb
Compare
NuojCheng
approved these changes
May 27, 2026
…th Linen) pure_nnx runs now write the exact Linen checkpoint layout, so Linen and NNX checkpoints are interchangeable across all load paths (default auto-resume, load_parameters_path, load_full_state_path) in both directions. The format conversion lives on TrainStateNNX (to_linen_checkpoint_dict / from_linen_checkpoint_dict): top-level params/step/opt_state, the params collection wrap, the optax chain list-with-None, and step dtype. NNX-only rngs/dropout are dropped on save and re-initialized on load, filled under jit with out_shardings so it works on multi-host meshes. Verified byte-identical to a real Linen checkpoint, with all six cross-framework load combinations passing on gpt3-52k, gpt3-6b, gemma2-2b, gemma2-9b, llama2-7b, and qwen3-8b.
ecnal-cienet
force-pushed
the
feat/checkpoint-linen-to-nnx-adapter
branch
from
May 27, 2026 22:18
4a091cb to
ea7f696
Compare
ecnal-cienet
added a commit
that referenced
this pull request
May 28, 2026
PR #3929 moved src/maxtext/layers/train_state_nnx.py to src/maxtext/common/train_state_nnx.py. Update remaining imports in diloco.py and three test files so PR11 still imports correctly.
ecnal-cienet
added a commit
that referenced
this pull request
May 28, 2026
PR #3929 moved src/maxtext/layers/train_state_nnx.py to src/maxtext/common/train_state_nnx.py. Update remaining imports in diloco.py and three test files so PR11 still imports correctly.
ecnal-cienet
added a commit
that referenced
this pull request
May 28, 2026
PR #3929 moved src/maxtext/layers/train_state_nnx.py to src/maxtext/common/train_state_nnx.py. Update remaining imports in diloco.py and three test files so PR11 still imports correctly.
ecnal-cienet
added a commit
that referenced
this pull request
May 28, 2026
PR #3929 moved src/maxtext/layers/train_state_nnx.py to src/maxtext/common/train_state_nnx.py. Update remaining imports in diloco.py and three test files so PR11 still imports correctly.
ecnal-cienet
added a commit
that referenced
this pull request
May 28, 2026
PR #3929 moved src/maxtext/layers/train_state_nnx.py to src/maxtext/common/train_state_nnx.py. Update remaining imports in diloco.py and three test files so PR11 still imports correctly.
ecnal-cienet
added a commit
that referenced
this pull request
May 28, 2026
PR #3929 moved src/maxtext/layers/train_state_nnx.py to src/maxtext/common/train_state_nnx.py. Update remaining imports in diloco.py and three test files so PR11 still imports correctly.
ecnal-cienet
added a commit
that referenced
this pull request
May 29, 2026
PR #3929 moved src/maxtext/layers/train_state_nnx.py to src/maxtext/common/train_state_nnx.py. Update remaining imports in diloco.py and three test files so PR11 still imports correctly.
ecnal-cienet
added a commit
that referenced
this pull request
May 30, 2026
PR #3929 moved src/maxtext/layers/train_state_nnx.py to src/maxtext/common/train_state_nnx.py. Update remaining imports in diloco.py and three test files so PR11 still imports correctly.
ecnal-cienet
added a commit
that referenced
this pull request
Jun 1, 2026
PR #3929 moved src/maxtext/layers/train_state_nnx.py to src/maxtext/common/train_state_nnx.py. Update remaining imports in diloco.py and three test files so PR11 still imports correctly.
ecnal-cienet
added a commit
that referenced
this pull request
Jun 1, 2026
PR #3929 moved src/maxtext/layers/train_state_nnx.py to src/maxtext/common/train_state_nnx.py. Update remaining imports in diloco.py and three test files so PR11 still imports correctly.
ecnal-cienet
added a commit
that referenced
this pull request
Jun 1, 2026
PR #3929 moved src/maxtext/layers/train_state_nnx.py to src/maxtext/common/train_state_nnx.py. Update remaining imports in diloco.py and three test files so PR11 still imports correctly.
ecnal-cienet
added a commit
that referenced
this pull request
Jun 2, 2026
PR #3929 moved src/maxtext/layers/train_state_nnx.py to src/maxtext/common/train_state_nnx.py. Update remaining imports in diloco.py and three test files so PR11 still imports correctly.
ecnal-cienet
added a commit
that referenced
this pull request
Jun 2, 2026
PR #3929 moved src/maxtext/layers/train_state_nnx.py to src/maxtext/common/train_state_nnx.py. Update remaining imports in diloco.py and three test files so PR11 still imports correctly.
ecnal-cienet
added a commit
that referenced
this pull request
Jun 2, 2026
PR #3929 moved src/maxtext/layers/train_state_nnx.py to src/maxtext/common/train_state_nnx.py. Update remaining imports in diloco.py and three test files so PR11 still imports correctly.
ecnal-cienet
added a commit
that referenced
this pull request
Jun 3, 2026
PR #3929 moved src/maxtext/layers/train_state_nnx.py to src/maxtext/common/train_state_nnx.py. Update remaining imports in diloco.py and three test files so PR11 still imports correctly.
ecnal-cienet
added a commit
that referenced
this pull request
Jun 3, 2026
PR #3929 moved src/maxtext/layers/train_state_nnx.py to src/maxtext/common/train_state_nnx.py. Update remaining imports in diloco.py and three test files so PR11 still imports correctly.
ecnal-cienet
added a commit
that referenced
this pull request
Jun 4, 2026
PR #3929 moved src/maxtext/layers/train_state_nnx.py to src/maxtext/common/train_state_nnx.py. Update remaining imports in diloco.py and three test files so PR11 still imports correctly.
ecnal-cienet
added a commit
that referenced
this pull request
Jun 5, 2026
PR #3929 moved src/maxtext/layers/train_state_nnx.py to src/maxtext/common/train_state_nnx.py. Update remaining imports in diloco.py and three test files so PR11 still imports correctly.
ecnal-cienet
added a commit
that referenced
this pull request
Jun 8, 2026
PR #3929 moved src/maxtext/layers/train_state_nnx.py to src/maxtext/common/train_state_nnx.py. Update remaining imports in diloco.py and three test files so PR11 still imports correctly.
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.
Summary
A
pure_nnxrun now writes the same on-disk checkpoint layout as a Linen run, so Linen and NNX checkpoints are interchangeable. After the NNX migration this lets existing Linen checkpoints keep working, and lets a checkpoint saved by either framework be loaded by the other.The conversion lives on
TrainStateNNX(no separate load-time adapter): the state serializes to the Linen layout on save and reads it back on load.Why
NNX's natural
nnx.state(...).to_pure_dict()differs from Linen's checkpoint in three ways, none of which are in the model/decoder modules — the weight names already match. They come from the train state and optimizer:{model, optimizer}vs Linen{params, step, opt_state}model/...vs Linenparams/params/...(the Linenparamscollection)opt_state: NNX serializes the optax chain as an int-keyed dict; Linen as a list withNonefor eachEmptyState, and wrapsmu/nuin theparamscollectionPlus
stepdtype (NNXuint32vs Linenint32) and NNX-onlyrngs/dropoutstate that Linen never had.What changes
Two source files plus their tests:
src/maxtext/layers/train_state_nnx.py—to_linen_checkpoint_dict(save) andfrom_linen_checkpoint_dict(load) reshape between the two layouts, handling the three differences above, thestepdtype, and dropping NNX-onlyrngs/dropout.src/maxtext/common/checkpointing.py:maybe_save_checkpointwritesto_linen_checkpoint_dict(state.to_pure_dict()).rngs/dropoutfrom defaults —load_state_if_possible(auto-resume),_load_full_state_from_path(load_full_state_path), andload_params_from_path(load_parameters_path).Linen runs are unchanged; they already read and write this layout.
Test plan
Byte-exactness. A
pure_nnxgemma2-2b checkpoint is structurally identical to aLinen one — same 76 leaves, no path or shape/dtype differences.
Compatibility matrix. For each model: save with both frameworks, then load each
of the three mechanisms in both directions (Linen→NNX and NNX→Linen). All eight
phases pass on six models on v6e-8:
load_parameters_path)load_full_state_path)(3 model families, both optimizer types: adam_pax single-state and adamw 3-element chain.)
Unit tests.
tests/unit/train_state_nnx_checkpoint_test.pyandtests/unit/checkpointing_nnx_load_test.pycover the converters (round-trip, opt_state list/dict,stepdtype, RNG strip) and a real Orbax round-trip for the weight-only NNX load. 16 tests pass.Checklist
Before submitting this PR, please make sure (put X in square brackets):
gemini-reviewlabel.