Skip to content

Commit 4ba3d2d

Browse files
Merge pull request #3970 from AI-Hypercomputer:jackyf/qlora-docs
PiperOrigin-RevId: 938215559
2 parents ebdd8d8 + bf747b7 commit 4ba3d2d

8 files changed

Lines changed: 1054 additions & 4 deletions

File tree

.github/workflows/run_jupyter_notebooks.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ jobs:
109109
# Run Hugging Face authentication
110110
hf auth login --token "$HF_TOKEN"
111111
112-
for notebook in "$MAXTEXT_NOTEBOOKS_ROOT"/{sft,rl}*.ipynb; do
112+
for notebook in "$MAXTEXT_NOTEBOOKS_ROOT"/{sft,rl,lora}*.ipynb; do
113113
filename=$(basename "$notebook")
114114
# TODO: Update runnner to v6e-8 as RL with LLama3.1-8b doesn't fit on v6e-4
115115
if [[ "$filename" == "sft_llama3_demo_gpu.ipynb" || "$filename" == "maxtext_with_gepa.ipynb" ]]; then

docs/guides.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,13 @@ Interactive development guides for running MaxText on Google Colab or local Jupy
6363
A step-by-step guide for the community to help expand MaxText's model library.
6464
:::
6565

66+
:::{grid-item-card} 🎗️ LoRA Model Bringup
67+
:link: guides/lora_model_bringup
68+
:link-type: doc
69+
70+
Learn how to integrate Low-Rank Adaptation (LoRA) support for a new model architecture.
71+
:::
72+
6673
:::{grid-item-card} 🎓 Distillation
6774
:link: guides/distillation
6875
:link-type: doc
@@ -89,6 +96,7 @@ guides/checkpointing_solutions.md
8996
guides/monitoring_and_debugging.md
9097
guides/run_python_notebook.md
9198
guides/model_bringup.md
99+
guides/lora_model_bringup.md
92100
guides/distillation.md
93101
guides/eval_framework.md
94102
```

docs/guides/lora_model_bringup.md

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
<!--
2+
Copyright 2026 Google LLC
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
https://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
-->
16+
17+
# Adding a New Model for LoRA Fine-Tuning
18+
19+
This guide explains how to add Low-Rank Adaptation (LoRA) support for a new model architecture in MaxText.
20+
21+
MaxText leverages [Tunix](https://github.com/google/tunix) and [Qwix](https://github.com/google/qwix) to support Parameter-Efficient Fine-Tuning (PEFT) on JAX/NNX model definitions. Since the architecture uses modular APIs, adding LoRA support for a new model is highly streamlined.
22+
23+
______________________________________________________________________
24+
25+
## 1. Step-by-Step Bring-up Guide for NNX LoRA
26+
27+
To enable LoRA support for a new model, follow these two simple steps:
28+
29+
### Step 1.1: Verify Base Model Support
30+
31+
The target model architecture must already be implemented and supported as a base model in MaxText.
32+
33+
- The JAX/NNX model definition should be located under `src/maxtext/models/` (e.g., \[gemma3.py\](../../src/maxtext/models/gemma3.py)).
34+
- The model configurations must be registered and runnable for baseline pre-training or full fine-tuning.
35+
36+
### Step 1.2: Define Trainable LoRA Target Modules
37+
38+
Add a recommended target pattern for your model architecture prefix in \[src/maxtext/configs/post_train/lora_module_path.yml\](../../src/maxtext/configs/post_train/lora_module_path.yml):
39+
40+
```yaml
41+
your_model_prefix: "decoder/layers/.*(self_attention/(query|key|value|out)|mlp/(wi_0|wi_1|wo))"
42+
```
43+
44+
> [!NOTE]
45+
> MaxText's `_get_lora_module_path` in `lora_utils.py` automatically handles both **scanned** (e.g., `layers/0/self_attention/...`) and **unscanned** (e.g., `layers/self_attention/...`) layer formats by injecting an optional layer index regex. You only need to define standard, unscanned paths.
46+
47+
If no prefix matches your model name, MaxText falls back to the `default` pattern:
48+
49+
```yaml
50+
default: "decoder/layers/.*(self_attention/(query|key|value|out)|mlp/(wi_0|wi_1|wo))"
51+
```
52+
53+
______________________________________________________________________
54+
55+
## 2. Integrating Custom Weight Mappings (When is it needed?)
56+
57+
Determining whether you need to implement custom weight mappings depends entirely on your downstream workflow:
58+
59+
### Scenario A: SFT Training & Conversion to PEFT (No Mapping Needed)
60+
61+
If you only need to run SFT fine-tuning with LoRA and then export the adapter back to Hugging Face format using `to_huggingface.py`, **you do not need to write any custom weight mappings.**
62+
63+
- The conversion utility automatically maps, scales, and formats the LoRA adapter parameters back into standard Hugging Face PEFT format based on the base model's existing weight mapping.
64+
65+
### Scenario B: Decoding with the MaxText vLLM Adapter (Mapping is Required)
66+
67+
If you want to perform decoding or run high-performance serving on your adapted model using the **MaxText vLLM adapter** (e.g., via `vllm_decode`), **you must define and register a custom weight mapping.** This allows the vLLM JAX wrapper to dynamically map and feed weights to the vLLM engine.
68+
69+
To add weight mapping for vLLM decode:
70+
71+
1. **Create a Weight Mapping Config**:
72+
Create a new file in \[src/maxtext/integration/tunix/weight_mapping/\](../../src/maxtext/integration/tunix/weight_mapping/) (e.g., `your_model.py`) defining a mapping dataclass. You can refer to \[gemma3.py\](../../src/maxtext/integration/tunix/weight_mapping/gemma3.py) or \[llama3.py\](../../src/maxtext/integration/tunix/weight_mapping/llama3.py) as templates.
73+
74+
Your class should specify:
75+
76+
- `to_hf_mapping()`: Maps MaxText base parameters to Hugging Face parameters and specifies their sharding axes.
77+
- `to_hf_hook_fns()`: Custom hook functions for complex weight transformations (e.g., RoPE reordering or query scaling).
78+
- `lora_to_hf_mappings()`: Custom mapping for LoRA weights if they require different handling.
79+
80+
2. **Register the Mapping**:
81+
Register your new class in \[src/maxtext/integration/tunix/weight_mapping/__init__.py\](../../src/maxtext/integration/tunix/weight_mapping/__init__.py) inside the `StandaloneVllmWeightMapping` class:
82+
83+
```python
84+
# Inside StandaloneVllmWeightMapping
85+
if name.startswith("your_model_name"):
86+
return YOUR_MODEL_VLLM_MAPPING
87+
```
88+
89+
______________________________________________________________________
90+
91+
## 3. Verifying Your Custom LoRA Targets
92+
93+
If you are developing or bringing up your model architecture interactively (e.g., in a Python interpreter or Jupyter notebook), you can verify which layers are wrapped with LoRA adapters by inspecting the model's module graph:
94+
95+
```python
96+
import re
97+
from flax import nnx
98+
from maxtext.utils import model_creation_utils, lora_utils
99+
100+
# 1. Create model, mesh, and load config
101+
model, mesh = model_creation_utils.from_pretrained(mt_config)
102+
103+
# 2. Extract lora path regex and compile
104+
compiled_module_path = re.compile(lora_utils._get_lora_module_path(mt_config))
105+
106+
# 3. Iterate over modules to see exactly which ones matched your pattern
107+
for path, _ in nnx.iter_modules(model):
108+
module_path = "/".join(str(p) for p in path)
109+
if compiled_module_path.search(module_path):
110+
print(f"Matched and wrapped with LoRA: {module_path}")
111+
```
112+
113+
This programmatic verification allows you to inspect and traverse parameters interactively during development.

docs/guides/model_bringup.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,10 @@ Please ensure all items on the following checklist are completed before finalizi
134134

135135
- [ ] Create a user guide and post an announcement in the MaxText repo.
136136

137+
5. (Optional) LoRA Support
138+
139+
- [ ] Integrate LoRA support for the newly onboarded model by following the [LoRA Model Bringup Guide](lora_model_bringup.md).
140+
137141
## Community Q&A (FAQ)
138142

139143
**Q: How do I debug code inside a JAX JIT function?**

docs/tutorials/post_training_index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ MaxText was co-designed with key Google led innovations to provide a unified pos
2828
- [SFT on Multi-Host TPUs](./posttraining/sft_on_multi_host.md)
2929
- **LoRA (Low-Rank Adaptation)**
3030
- [LoRA on Single-Host TPUs](./posttraining/lora.md)
31+
- [LoRA on Multi-Host TPUs](./posttraining/lora_on_multi_host.md)
3132
- **DPO (Direct Preference Optimization) and ORPO (Odds-Ratio Policy Optimization)**
3233
- [DPO/ORPO on Single-Host TPUs](./posttraining/dpo.md)
3334
- **Multimodal SFT**
@@ -76,6 +77,7 @@ posttraining/rl_on_multi_host.md
7677
posttraining/rl_qwen3_30b.md
7778
posttraining/knowledge_distillation.md
7879
posttraining/lora.md
80+
posttraining/lora_on_multi_host.md
7981
posttraining/multimodal.md
8082
posttraining/full_finetuning.md
8183
posttraining/gepa_optimization.md

docs/tutorials/posttraining/lora.md

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,6 @@ export DATASET_NAME=<DATASET_NAME> # e.g., openai/gsm8k
6060
export TRAIN_SPLIT=<TRAIN_SPLIT> # e.g., train
6161
export HF_DATA_DIR=<DATASET_PATH> # e.g., main
6262
export TRAIN_DATA_COLUMNS=<DATA_COLUMNS> # e.g., ['question','answer']
63-
export CHAT_TEMPLATE_PATH=<TEMPLATE_PATH> # e.g., maxtext/examples/chat_templates/math_qa.json
6463

6564
# -- LoRA Conversion configuration (Optional) --
6665
export HF_LORA_ADAPTER_PATH=<HF_LORA_ADAPTER_PATH> # e.g., 'username/adapter-name'
@@ -118,7 +117,6 @@ python3 -m maxtext.trainers.post_train.sft.train_sft \
118117
per_device_batch_size="${PER_DEVICE_BATCH_SIZE?}" \
119118
max_target_length="${MAX_TARGET_LENGTH?}" \
120119
learning_rate="${LEARNING_RATE?}" \
121-
chat_template_path="${CHAT_TEMPLATE_PATH?}" \
122120
enable_nnx=True \
123121
pure_nnx_decoder=True \
124122
lora.enable_lora=True \
@@ -176,7 +174,6 @@ python3 -m maxtext.trainers.post_train.sft.train_sft \
176174
per_device_batch_size="${PER_DEVICE_BATCH_SIZE?}" \
177175
max_target_length="${MAX_TARGET_LENGTH?}" \
178176
learning_rate="${LEARNING_RATE?}" \
179-
chat_template_path="${CHAT_TEMPLATE_PATH?}" \
180177
enable_nnx=True \
181178
pure_nnx_decoder=True \
182179
lora.enable_lora=True \

0 commit comments

Comments
 (0)