Skip to content

Write post-training checkpoints in MaxText's on-disk layout - #4700

Draft
ecnal-cienet wants to merge 1 commit into
mainfrom
feat/post-train-checkpoint-maxtext-layout
Draft

Write post-training checkpoints in MaxText's on-disk layout#4700
ecnal-cienet wants to merge 1 commit into
mainfrom
feat/post-train-checkpoint-maxtext-layout

Conversation

@ecnal-cienet

Copy link
Copy Markdown
Collaborator

Post-training saved through Tunix's checkpoint manager, which stores nnx.state(model) verbatim under a model_params item. MaxText's on-disk layout is the Linen one: weights in params/params, the optimizer in opt_state and step, and NNX-only state such as rngs in nnx_aux. The two never matched, so a checkpoint from an SFT, DPO, RL or distillation run could not be loaded by pre-training, and loaders had started growing branches to read the post-training shape instead.

Add MaxTextLayoutCheckpointManager, which converts in both directions and writes the same items tree everything else in MaxText reads. It lives under trainers/post_train rather than common/checkpointing because it subclasses Tunix's manager, and common/checkpointing is imported by pre-training and inference, which run without Tunix installed. Older checkpoints are still in the Tunix layout, so maybe_restore falls back to the base class for those, and load_params_from_path learns to read them by restoring into the NNX state itself.

DPO and RL train through TunixMaxTextAdapter, whose base level would otherwise reach the checkpoint. Strip it from the weights and from the optimizer accumulators that mirror them, and put it back on restore.

Two optimizer differences also blocked a full-state resume:

  • train_dpo passed gradient_accumulation_steps unconditionally, so Tunix wrapped the optimizer in optax.MultiSteps even at 1. Pass None below 2, as train_sft already did.
  • post-training chained optax.clip_by_global_norm into the optimizer, nesting its state a level deeper than pre-training, which clips raw gradients in its train step. add_gradient_clipping applies the same math inside the update and keeps the optimizer's own state tree.

A DPO checkpoint now resumes into pre-training with its weights, its optimizer state and its step counter. RL and distillation additionally wrap in optax.inject_hyperparams so Tunix can log the learning rate, which keeps their full-state resume out of reach; their weights load.

Description

Start with a short description of what the PR does and how this is a change from
the past.

The rest of the description includes relevant details and context, examples:

  • why is this change being made,
  • the problem being solved and any relevant context,
  • why this is a good solution,
  • some information about the specific implementation,
  • shortcomings of the solution and possible future improvements.

If the change fixes a bug or a Github issue, please include a link, e.g.,:
FIXES: b/123456
FIXES: #123456

You can also provide a comma-separated list. If you don't want to close a bug but
simply to reference it, use BUGS, e.g.:
BUGS: b/123456

Notice 1: Once all tests pass, the "pull ready" label will automatically be assigned.
This label is used for administrative purposes. Please do not add it manually.

Notice 2: For external contributions, our settings currently require an approval from a MaxText maintainer to trigger CI tests.

Tests

Please describe how you tested this change, and include any instructions and/or
commands to reproduce.

Checklist

Before submitting this PR, please make sure (put X in square brackets):

  • I have performed a self-review of my code. For an optional AI review, add the gemini-review label.
  • I have necessary comments in my code, particularly in hard-to-understand areas.
  • I have run end-to-end tests tests and provided workload links above if applicable.
  • I have made or will make corresponding changes to the doc if needed, including adding new documentation pages to the relevant Table of Contents (toctree directive) as explained in our documentation.

@gemini-code-assist

Copy link
Copy Markdown

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

Post-training saved through Tunix's checkpoint manager, which stores
nnx.state(model) verbatim under a model_params item. MaxText's on-disk
layout is the Linen one: weights in params/params, the optimizer in
opt_state and step, and NNX-only state such as rngs in nnx_aux. The two
never matched, so a checkpoint from an SFT, DPO, RL or distillation run
could not be loaded by pre-training, and loaders had started growing
branches to read the post-training shape instead.

Add MaxTextLayoutCheckpointManager, which converts in both directions and
writes the same items tree everything else in MaxText reads. It lives
under trainers/post_train rather than common/checkpointing because it
subclasses Tunix's manager, and common/checkpointing is imported by
pre-training and inference, which run without Tunix installed. Older
checkpoints are still in the Tunix layout, so maybe_restore falls back to
the base class for those, and load_params_from_path learns to read them
by restoring into the NNX state itself.

DPO and RL train through TunixMaxTextAdapter, whose base level would
otherwise reach the checkpoint. Strip it from the weights and from the
optimizer accumulators that mirror them, and put it back on restore.

Two optimizer differences also blocked a full-state resume:

  - train_dpo passed gradient_accumulation_steps unconditionally, so
    Tunix wrapped the optimizer in optax.MultiSteps even at 1. Pass None
    below 2, as train_sft already did.
  - post-training chained optax.clip_by_global_norm into the optimizer,
    nesting its state a level deeper than pre-training, which clips raw
    gradients in its train step. add_gradient_clipping applies the same
    math inside the update and keeps the optimizer's own state tree.

A DPO checkpoint now resumes into pre-training with its weights, its
optimizer state and its step counter. RL and distillation additionally
wrap in optax.inject_hyperparams so Tunix can log the learning rate,
which keeps their full-state resume out of reach; their weights load.
@ecnal-cienet
ecnal-cienet force-pushed the feat/post-train-checkpoint-maxtext-layout branch from 7890810 to 35dd42f Compare August 3, 2026 02:39
@codecov

codecov Bot commented Aug 3, 2026

Copy link
Copy Markdown

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant