feat(nnx): support native Flax NNX PEFT/LoRA training loop#4346
feat(nnx): support native Flax NNX PEFT/LoRA training loop#4346RexBearIU wants to merge 4 commits into
Conversation
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
cc32ba1 to
4fe1fc8
Compare
🤖 CI Failure Investigation ReportI have analyzed the recent test failures in the CI pipeline for PR #4346 and identified the following: 🔍 What FailedAll units tests running on TPU/GPU backends failed during the Run Tests step. Specifically:
🪵 Error Details & Stack TraceWhen Traceback (most recent call last):
File "src/maxtext/trainers/pre_train/train.py", line 771, in train_loop
state_mesh_shardings = nnx.state(state_mesh_shardings, nnx.Not(nnx.Intermediate))
TypeError: ...💡 Root Cause Analysis & ContextConfidence: high (confirmed cause) In PR #4346, the author attempted to filter out if config.pure_nnx:
if isinstance(state, nnx.Module):
_ = nnx.pop(state, nnx.Intermediate)
elif isinstance(state, nnx.State):
state = nnx.state(state, nnx.Not(nnx.Intermediate))
if isinstance(state_mesh_shardings, nnx.Module):
_ = nnx.pop(state_mesh_shardings, nnx.Intermediate)
elif isinstance(state_mesh_shardings, nnx.State):
state_mesh_shardings = nnx.state(state_mesh_shardings, nnx.Not(nnx.Intermediate))However, under Flax NNX:
To correctly select or filter variables in an existing 🛠️ Recommended FixReplace the incorrect calls to diff --git a/src/maxtext/trainers/pre_train/train.py b/src/maxtext/trainers/pre_train/train.py
index e7f5ea84b6..94b2b9a92f 100644
--- a/src/maxtext/trainers/pre_train/train.py
+++ b/src/maxtext/trainers/pre_train/train.py
@@ -773,11 +773,11 @@ def train_loop(config, recorder, state=None):
if config.pure_nnx:
if isinstance(state, nnx.Module):
_ = nnx.pop(state, nnx.Intermediate)
elif isinstance(state, nnx.State):
- state = nnx.state(state, nnx.Not(nnx.Intermediate))
+ state = nnx.filter_state(state, nnx.Not(nnx.Intermediate))
if isinstance(state_mesh_shardings, nnx.Module):
_ = nnx.pop(state_mesh_shardings, nnx.Intermediate)
elif isinstance(state_mesh_shardings, nnx.State):
- state_mesh_shardings = nnx.state(state_mesh_shardings, nnx.Not(nnx.Intermediate))
+ state_mesh_shardings = nnx.filter_state(state_mesh_shardings, nnx.Not(nnx.Intermediate)) |
4fe1fc8 to
81ffa0f
Compare
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.Tests
The changes have been thoroughly tested on CPU with no regressions.
Integration tests (
tests/integration/setup_train_loop_nnx_test.py):test_pure_nnx_setup_with_lora_enabledwhich asserts thatsetup_train_loopcorrectly instantiates a model with LoRA adapters injected and the optimizer configured withwrt=nnx.LoRAParam.test_train_step_updates_only_lora_weightswhich runs 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._tiny_nnx_pyconfig) to useattention="dot_product"so that CPU backend runs are fully supported without relying on TPU/GPU Pallas attention kernels.JAX_PLATFORMS='cpu' PYTHONPATH=src pytest tests/integration/setup_train_loop_nnx_test.py -k SetupTrainLoopNNXLoraTestUnit tests (
tests/unit/lora_utils_nnx_test.py):test_sharding_extracts_only_lora_paramsto verify thatsharding.maybe_update_params_sharding_with_optextracts onlynnx.LoRAParamunder LoRA configuration.JAX_PLATFORMS='cpu' PYTHONPATH=src pytest tests/unit/lora_utils_nnx_test.pyChecklist
Before submitting this PR, please make sure (put X in square brackets):
gemini-reviewlabel.