feat(nnx): support native Flax NNX PEFT/LoRA training loop#4501
Open
RexBearIU wants to merge 1 commit into
Open
feat(nnx): support native Flax NNX PEFT/LoRA training loop#4501RexBearIU wants to merge 1 commit into
RexBearIU wants to merge 1 commit into
Conversation
RexBearIU
requested review from
A9isha,
NuojCheng,
RissyRan,
SurbhiJainUSC,
abhinavclemson,
aireenmei,
bvandermoon,
darisoy,
dipannita08,
gagika,
gobbleturk,
hengtaoguo,
huytransformer,
igorts-git,
jiangjy1982,
khatwanimohit,
parambole,
richjames0,
shralex,
shuningjin,
suexu1025,
vipannalla and
xibinliu
as code owners
July 16, 2026 01:29
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
RexBearIU
force-pushed
the
nnx-lora-support
branch
from
July 16, 2026 07:44
7ee8331 to
d54e2a3
Compare
| model = _create_model_partial() | ||
| optimizer = nnx.Optimizer(model, tx, wrt=nnx.Param) | ||
| wrt = nnx.Param | ||
| if config.lora.enable_lora: |
Collaborator
There was a problem hiding this comment.
nit: getattr(getattr(config, "lora", None), "enable_lora", False)
Collaborator
There was a problem hiding this comment.
Make sure this is uniform across all files to ensure safe configuration check.
Collaborator
Author
There was a problem hiding this comment.
Done! Made the config check uniform across all relevant modified files in the change list using the safer nested getattr pattern: getattr(getattr(config, "lora", None), "enable_lora", False).
…rained base weight restoration and optimized upfront conversion
RexBearIU
force-pushed
the
nnx-lora-support
branch
from
July 17, 2026 09:45
d54e2a3 to
108f993
Compare
SurbhiJainUSC
approved these changes
Jul 17, 2026
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.
Description
This PR implements native parameter-efficient fine-tuning (PEFT) and LoRA training support within the Flax NNX training loop inside MaxText (
train.pyandtrain_utils.py).Previously, training inside
train.pywith Flax NNX was limited to full-parameter fine-tuning. This was due to:setup_train_loopinsidetrain_utils.pyinitializingnnx.Optimizerwithwrt=nnx.Param, which allocated optimizer states for the entire base model.train_stepanddiff_wrapperinsidetrain.pyhardcoding model splits usingnnx.Param.sharding.pyhardcodingnnx.Paramextraction, which failed to cleanly separate base parameters from LoRA parameters.This PR addresses those limitations through the following design:
wrtdynamically based on configuration (nnx.LoRAParamifconfig.lora.enable_lorais True, otherwisennx.Param). This is used consistently across optimizer initialization, training step splits/merges, and sharding parameter extraction.setup_train_loop, Qwix LoRA adapters are dynamically injected whenconfig.lora.enable_lorais True. On fresh training runs (no previous checkpoint step), pre-trained adapters can be loaded fromlora.lora_restore_path.apply_lora_to_modelinsidelora_utils.pyto only invokejax.set_mesh(mesh)if tracing is not currently active (jax_core.trace_state_clean()), avoiding compilation errors during eager/eval paths under NNX._raise_on_weight_mismatchrecursively validates that the restored checkpoint matches the target model structure, fully supporting standard model and optimizer state restorations.Tests
The changes have been thoroughly tested on CPU and verified with extensive multi-device TPU benchmarks.
1. Local Verification Tests
tests/integration/setup_train_loop_nnx_test.py):test_pure_nnx_setup_with_lora_enabledasserts thatsetup_train_loopcorrectly instantiates a model with LoRA adapters injected and the optimizer configured withwrt=nnx.LoRAParam.test_train_step_updates_only_lora_weightsruns a full forward and backward pass for a single training step and asserts that only the adapter parameters (nnx.LoRAParam) are modified, while the base weights (nnx.Param) remain unchanged.tests/unit/checkpointing_nnx_load_test.pycovers full structural checkpointer loading logic.2. TPU E2E Verification & Performance Comparison (8x TPU v6 Lite)
To thoroughly validate native Flax NNX QLoRA (int8) support in multi-device TPU environments, we executed end-to-end 20-step benchmarks across all three trainers (
pre_train,sft_native,sft_custom) under bothscan_layers=Trueandscan_layers=Falseconfigurations.All performance metrics (step times, tokens/s/device) are averaged across steps 10 to 15, and training losses (including MoE load balancing losses, where applicable) are reported at step 15.
A. Llama 3.1 8B QLoRA Performance Comparison
TrueFalseTrueFalseTrueFalseTechnical Architecture Insights (Llama 3.1 8B):
scan_layers=True) results in a ~2x performance increase in step times and throughput. This is due to compiler optimization through sequential loop unrolling and reduced JIT compilation overhead on TPUs.lora_tile_size=32when embedding dimensions are small (or on small FSDP meshes) can causeIndivisibleErrorsince the tiled dimension doesn't divide evenly across the mesh devices. Settinglora.lora_tile_size=16guarantees a tile count (128 / 16 = 8tiles) that perfectly divides across the 8-way FSDP mesh.B. Gemma 4 26B (Mixture of Experts) QLoRA Performance Comparison
TrueFalseTrueFalseTrueFalseChecklist
Before submitting this PR, please make sure (put X in square brackets):
gemini-reviewlabel.