This guide explains how to configure and use PEFT in Megatron Bridge—covering LoRA and DoRA, required checkpoints, example configurations, and the internal design and training workflow—so you can integrate, scale, and checkpoint adapters efficiently.
Customizing models enables you to adapt a general pre-trained model to a specific use case or domain. This process produces a fine-tuned model that retains the broad knowledge from pretraining while delivering more accurate outputs for targeted downstream tasks.
Model customization is typically achieved through supervised fine-tuning, which falls into two main approaches: Full-Parameter Fine-Tuning, known as Supervised Fine-Tuning (SFT), and Parameter-Efficient Fine-Tuning (PEFT).
In SFT, all model parameters are updated to align the model’s outputs with the task-specific requirements. This approach often yields the highest performance but can be computationally intensive.
PEFT, by contrast, updates only a small subset of parameters that are inserted into the base model at strategic locations. The base model weights remain frozen, and only the adapter modules are trained. This significantly reduces the number of trainable parameters—often to less than 1%—while still achieving near-SFT levels of accuracy.
As language models continue to grow in size, PEFT is gaining popularity for its efficiency and minimal hardware demands, making it a practical choice for many real-world applications.
PEFT is configured as an optional attribute in ConfigContainer:
from megatron.bridge.peft.lora import LoRA
from megatron.bridge.training.config import CheckpointConfig, ConfigContainer
config = ConfigContainer(
# ... other required configurations
peft=LoRA(
target_modules=["linear_qkv", "linear_proj", "linear_fc1", "linear_fc2"],
dim=16,
alpha=32,
dropout=0.1,
),
checkpoint=CheckpointConfig(
pretrained_checkpoint="/checkpoints/base_model", # Required for PEFT
save="/path/to/peft/checkpoints",
),
)**Requirement:** PEFT requires `checkpoint.pretrained_checkpoint` to load the frozen base model weights before adapters are inserted. This path may be a native Megatron checkpoint base directory, a specific native Megatron `iter_N` directory, or a local Hugging Face full-model directory containing `config.json` and model weight files. A remote Hugging Face model ID is not a checkpoint path; download it locally or convert it to Megatron format first.
When resuming adapter training, keep `checkpoint.pretrained_checkpoint` pointed at the same base model and set `checkpoint.load` to the PEFT adapter checkpoint directory.
When expert-bias routing is enabled, gradient finalization updates the router's
expert_bias buffer even when the router weights are frozen. PEFT checkpoints
therefore retain these buffers alongside adapter parameters. Adapter reload and
merge into a full model preserve this state.
Older adapter checkpoints may omit these buffers. The adapter inference/merge
loader rejects such checkpoints for a bias-enabled model, including when its
strict=False option is used: that option does not permit missing distributed
checkpoint tensors. The separate training-resume loader follows
checkpoint.dist_ckpt_strictness; a permissive policy may retain the base-model
bias, which is not exact recovery of the trained router state. Missing trained
biases cannot be reconstructed from adapter weights alone.
Standalone Hugging Face adapter export does not serialize router-bias buffers. For models whose biases changed during PEFT, merge the native adapter checkpoint into the base model and export the resulting full Hugging Face model instead.
LoRA makes fine-tuning efficient by representing weight updates with two low-rank decomposition matrices. The original model weights remain frozen, while the low-rank decomposition matrices are updated to adapt to the new data, keeping the number of trainable parameters low. In contrast with adapters, the original model weights and adapted weights can be combined during inference, avoiding any architectural change or additional latency in the model at inference time.
In Megatron Bridge, you can configure both the adapter bottleneck dimension and the target modules where LoRA is applied. LoRA supports any linear layer, which in transformer models typically includes:
- Query, key, and value (QKV) attention projections
- The attention output projection
- One or both MLP layers
Megatron Bridge fuses the QKV projections into a single linear layer. As a result, LoRA learns a unified low-rank adaptation for the combined QKV representation.
from megatron.bridge.peft.lora import LoRA
lora_config = LoRA(
target_modules=["linear_qkv", "linear_proj", "linear_fc1", "linear_fc2"],
dim=16, # Rank of adaptation
alpha=32, # Scaling parameter
dropout=0.1, # Dropout rate
)The following table lists key hyperparameters for configuring LoRA, which control its module targeting, adaptation rank, scaling behavior, and regularization strategy.
| Parameter | Type | Default | Description |
|---|---|---|---|
target_modules |
List[str] |
All linear layers | Modules to apply LoRA to |
dim |
int |
32 |
Rank of the low-rank adaptation |
alpha |
float |
32 |
Scaling parameter for LoRA |
dropout |
float |
0.0 |
Dropout rate for LoRA layers |
normalize_moe_lora |
bool |
False |
Reduce expert-layer rank to dim // moe_router_topk while keeping dense layers at the full rank |
share_expert_adapters |
bool |
True |
Share one adapter across all local experts on an EP rank instead of creating one adapter per local expert |
The following table lists specific submodules within transformer architectures that are commonly targeted for LoRA, enabling efficient fine-tuning of attention and feedforward components:
| Module | Description |
|---|---|
linear_qkv |
Query, key, value projections in attention |
linear_proj |
Attention output projection |
linear_fc1 |
First MLP layer |
linear_fc2 |
Second MLP layer |
For more granular targeting, individual layers can be targeted for the adapters.
# Target specific layers only
lora_config = LoRA(
target_modules=[
"*.layers.0.*.linear_qkv", # First layer only
"*.layers.1.*.linear_qkv", # Second layer only
]
)LoRA and CanonicalLoRA expose MoE-specific controls for grouped expert
MLP layers such as linear_fc1, linear_fc2, linear_fc1_up, and
linear_fc1_gate:
| Parameter | Type | Default | Description |
|---|---|---|---|
share_expert_adapters |
bool |
True |
Preserve the original behavior and share one adapter across all local experts on each EP rank |
normalize_moe_lora |
bool |
False |
Use dim // moe_router_topk for expert layers only so MoE adapter capacity stays comparable to a dense model |
experts_shared_outer_loras |
bool |
False |
LoRA only. Shared-outer layout for grouped experts: the gate/up LoRA-A and the down LoRA-B are shared across experts while the other factor stays per expert (SGLang's experts_shared_outer_loras serving contract) |
share_expert_adapters=Truekeeps the original shared-adapter behavior for grouped expert linears.share_expert_adapters=Falseopts into per-local-expert adapters.normalize_moe_lora=Trueonly changes expert layers; non-expert layers still use the fulldim.dimmust be divisible bymoe_router_topkwhennormalize_moe_lora=True.- When expert tensor parallelism shards a non-input-parallel expert layer, Megatron Bridge may round the effective expert rank up to the expert-TP granularity so the adapter can be partitioned cleanly.
from megatron.bridge.peft.lora import LoRA
lora_config = LoRA(
target_modules=["linear_fc1", "linear_fc2"],
dim=8,
normalize_moe_lora=True,
share_expert_adapters=False,
)Adapter weights that serve every expert (the default shared adapter, and the shared
factor of each experts_shared_outer_loras=True adapter) are replicated across
expert-parallel ranks, but Megatron-Core's expert DDP only reduces gradients over
expert-data-parallel replicas. Megatron Bridge therefore sums those gradients across
the expert-parallel group once per step, coalesced into a single collective right
after the data-parallel gradient sync, the same point where Megatron-Core sums
sequence-parallel layernorm gradients across tensor parallelism. The training loop
installs this automatically for PEFT runs through finalize_model_grads_with_expert_adapter_sync
(megatron.bridge.peft.utils), and it stays correct with
gradient_accumulation_fusion enabled because it reads the fused main_grad buffers.
External training loops that call Megatron-Core's finalize_model_grads themselves
must install the Bridge wrapper (or call allreduce_expert_parallel_replicated_grads
right after their own finalize step):
from functools import partial
from megatron.bridge.peft.utils import finalize_model_grads_with_expert_adapter_sync
model_config.finalize_model_grads_func = partial(
finalize_model_grads_with_expert_adapter_sync, pg_collection=pg_collection
)A per-layer fallback hook keeps eagerly computed weight gradients in sync until the finalize path takes over (Bridge's setup removes the hooks before the first step; otherwise the wrapper's first call does). The fallback cannot cover fused weight-gradient accumulation and logs a warning in that case.
AutoBridge.export_adapter_weights emits shared-outer adapters in the legacy (SGLang)
layout unless a serving layout is requested: the shared factor once as a [1, ...] tensor
under the expert-agnostic name, plus per-expert factors under experts.N.* names. Three
opt-in flags select a serving layout instead, listed from highest to lowest priority:
| Option | Default | Export layout when enabled |
|---|---|---|
stack_3d_moe |
False |
vLLM 3D-MoE layout: stacked tensors under experts.base_layer (gate/up) and experts (down) |
moe_shared_loras |
False |
vLLM 2D shared-MoE layout under experts.w1/w2/w3, with the shared factors kept at expert-dim 1 |
expand_shared_outer |
False |
vLLM 2D pack_moe layout: the shared factor replicated under per-expert experts.N.* names |
The flags can be combined; when several are set, the highest-priority one decides the
layout. With all three unset, existing callers keep the legacy layout. moe_shared_loras=True
is the 2D serving format used when vLLM's enable_moe_shared_loras setting is enabled; it
does not imply a 3D model. Callers can select stack_3d_moe from the model's MoE tensor
layout and moe_shared_loras from the serving backend's adapter mode.
LoRA+ trains the B matrix (linear_out) at a higher learning rate than A (linear_in) by a fixed ratio lr_B = lora_plus_ratio * lr_A (paper default 16). The ratio is set on the optimizer, not the LoRA adapter: when it differs from 1.0, get_lora_plus_config_overrides (megatron.bridge.peft.lora) builds Megatron-Core per-group overrides giving the *.linear_out.weight group max_lr/min_lr scaled by the ratio. Pass 1.0 (or skip the call) to keep a single learning rate.
from megatron.bridge.peft.lora import get_lora_plus_config_overrides
from megatron.core.optimizer import OptimizerConfig, get_megatron_optimizer
opt_config = OptimizerConfig(optimizer="adam", lr=1e-4, bf16=True)
# LoRA+ LR split: lr_B = lora_plus_ratio * lr_A; pass 1.0 (or skip) to disable.
lora_plus_ratio = 16
config_overrides = get_lora_plus_config_overrides(opt_config, lora_plus_ratio)
optimizer = get_megatron_optimizer(
config=opt_config,
model_chunks=model_chunks,
config_overrides=config_overrides,
)There are two variants of LoRA implemented in Megatron Bridge: "performant LoRA" (LoRA) and "canonical LoRA" (CanonicalLoRA).
The distinction comes from the fact that Megatron Core optimizes the implementation of the following two linear modules by fusing multiple linear layers into one layer. When these layers are adapted with LoRA, the performant version also uses only one adapter for the linear module. The two linear modules are:
linear_qkv: The projection matrix in self attention that transforms hidden state to query, key and value. Megatron Core fuses these three projection matrices into a single matrix to efficiently parallelize the matrix multiplication. Hence, performant LoRA applies a single adapter to the qkv projection matrix, whereas canonical LoRA applies three adapters.linear_fc1: The first linear layer in the MLP module before the intermediate activation. For gated linear activations, Megatron Core fuses the up and gate projection matrices into a single matrix for efficient parallelization. Hence, performant LoRA applies a single adapter to the up and gate projection matrices, whereas canonical LoRA applies two adapters.
The following two figures illustrate the difference between canonical and performant LoRA, using the linear_qkv layer as an example. Canonical LoRA runs three adapters sequentially, while performant LoRA runs one adapter.
:width: 640
:align: center
:width: 400
:align: center
Canonical LoRA conforms more closely to reference implementations, though it is slower in comparison since it performs several matrix multiplications sequentially, as described above. Performant LoRA has fewer parameters than canonical LoRA and can often achieve the same level of accuracy as canonical LoRA.
Though not immediately apparent, performant LoRA is mathematically equivalent to canonical LoRA when the linear_qkv, and similarly when the linear_fc1.
:class: dropdown
Let $[x \quad y]$ denote matrix concatenation. (In Megatron Bridge, this concatenation is done in an interleaved fashion, but this does not affect the proof below.)
Let $A_q = A_k = A_v = A_{qkv}$ (weight tying)
Then
$$
\begin{aligned}
[query \quad key \quad value]
&= [W_q x + B_q A_q x \quad W_k x + B_k A_k x \quad W_v x + B_v A_v x] \quad\quad \text{(canonical formulation)} \\
&= [W_q x + B_q (A_{qkv} x) \quad W_k x + B_k (A_{qkv} x) \quad W_v x + B_v (A_{qkv} x)] \\
&= [W_q \quad W_k \quad W_v] x + [B_q \quad B_k \quad B_v]A_{qkv} x \\
&= W_{qkv} x + B_{qkv} A_{qkv} x \quad\quad \text{(performant formulation)}
\end{aligned}
$$
Note: dimensions of weight matrices are as follows:
$$
\begin{align}
W_q: &\ n_q d \times h \qquad & A_q: &\ r \times h \qquad & B_q: &\ n_q d \times r \\
W_k: &\ n_{kv} d \times h \qquad & A_k: &\ r \times h \qquad & B_k: &\ n_{kv} d \times r \\
W_v: &\ n_{kv} d \times h \qquad & A_v: &\ r \times h \qquad & B_v: &\ n_{kv} d \times r \\
W_{qkv}: &\ (n_q+2n_{kv})d \times h \qquad & A_{qkv}: &\ r \times h \qquad & B_{qkv}: &\ (n_q+2n_{kv})d \times r
\end{align}
$$
Where:
- $n_q$: Number of attention heads (`num_attention_heads`).
- $n_{kv}$: Number of key value heads (`num_query_groups`). Note that if grouped query attention (GQA) is not used, $n_{kv} = n_q$.
- $h$: Transformer hidden size (`hidden_size`).
- $d$: Transformer head dimension (`kv_channels`).
- $r$: LoRA rank.
from megatron.bridge.peft.canonical_lora import CanonicalLoRA
canonical_lora_config = CanonicalLoRA(
target_modules=[
"linear_q", "linear_k", "linear_v", # Individual Q, K, V projections
"linear_proj", # Attention output projection
"linear_fc1_up", "linear_fc1_gate", # Individual up and gate projections
"linear_fc2" # Second MLP layer
],
dim=16, # Rank of adaptation
alpha=32, # Scaling parameter
dropout=0.1, # Dropout rate
)CanonicalLoRA exposes the same normalize_moe_lora and
share_expert_adapters controls as LoRA for MoE expert layers.
| Parameter | Type | Default | Description |
|---|---|---|---|
target_modules |
List[str] |
All canonical linear layers | Modules to apply canonical LoRA to |
dim |
int |
32 |
Rank of the low-rank adaptation |
alpha |
float |
32 |
Scaling parameter for LoRA |
dropout |
float |
0.0 |
Dropout rate for LoRA layers |
dropout_position |
Literal["pre", "post"] |
"pre" |
Position for applying dropout |
lora_A_init_method |
str |
"xavier" |
Initialization method for LoRA A matrix |
lora_B_init_method |
str |
"zero" |
Initialization method for LoRA B matrix |
normalize_moe_lora |
bool |
False |
Reduce expert-layer rank to dim // moe_router_topk while keeping dense layers at the full rank |
share_expert_adapters |
bool |
True |
Share one adapter across all local experts on an EP rank instead of creating one adapter per local expert |
The following table lists specific submodules within transformer architectures that are targeted for canonical LoRA:
| Module | Description |
|---|---|
linear_q |
Query projection in attention |
linear_k |
Key projection in attention |
linear_v |
Value projection in attention |
linear_proj |
Attention output projection |
linear_fc1_up |
Up projection in MLP |
linear_fc1_gate |
Gate projection in MLP |
linear_fc2 |
Second MLP layer |
Canonical LoRA does not support `linear_qkv` or `linear_fc1` targets. Use the individual component targets (`linear_q`, `linear_k`, `linear_v` for QKV and `linear_fc1_up`, `linear_fc1_gate` for FC1) instead.
DoRA decomposes the pre-trained weight into magnitude and direction. It learns a separate magnitude parameter while employing LoRA for directional updates, efficiently minimizing the number of trainable parameters. DoRA enhances both the learning capacity and training stability of LoRA, while avoiding any additional inference overhead. DoRA has been shown to consistently outperform LoRA on various downstream tasks.
In Megatron Bridge, DoRA leverages the same adapter structure as LoRA. Megatron Bridge adds support for Tensor Parallelism and Pipeline Parallelism for DoRA, enabling DoRA to be scaled to larger model variants.
from megatron.bridge.peft.dora import DoRA
dora_config = DoRA(
target_modules=["linear_qkv", "linear_proj", "linear_fc1", "linear_fc2"],
dim=16, # Rank of adaptation
alpha=32, # Scaling parameter
dropout=0.1, # Dropout rate
)The following parameters define how LoRA is applied to your model. They control which modules are targeted, the adaptation rank, scaling behavior, and dropout configuration:
| Parameter | Type | Default | Description |
|---|---|---|---|
target_modules |
List[str] |
All linear layers | Modules to apply DoRA to |
dim |
int |
32 |
Rank of the low-rank adaptation |
alpha |
float |
64 |
Scaling parameter for DoRA |
dropout |
float |
0.0 |
Dropout rate for DoRA layers |
from megatron.bridge.training.config import (
CheckpointConfig,
ConfigContainer,
SchedulerConfig,
TrainingConfig,
)
from megatron.bridge.data.builders import (
GPTSFTDatasetConfig,
HFDatasetSourceConfig,
PromptCompletionSFTPreprocessingConfig,
)
from megatron.bridge.data.packing import PackedSequenceSpecs
from megatron.bridge.peft.lora import LoRA
from megatron.core.optimizer import OptimizerConfig
# Configure PEFT fine-tuning
config = ConfigContainer(
model=model_provider,
train=TrainingConfig(
train_iters=1000,
global_batch_size=64,
micro_batch_size=1, # Required for packed sequences if used
eval_interval=100,
),
optimizer=OptimizerConfig(
optimizer="adam",
lr=1e-4, # Lower learning rate for fine-tuning
weight_decay=0.01,
bf16=True,
use_distributed_optimizer=True,
),
scheduler=SchedulerConfig(
lr_decay_style="cosine",
lr_warmup_iters=100,
lr_decay_iters=1000,
),
dataset=GPTSFTDatasetConfig(
seq_length=512,
hf_dataset=HFDatasetSourceConfig(dataset_name="squad"),
preprocessing=PromptCompletionSFTPreprocessingConfig(separator=" "),
hf_validation_proportion=0.1,
seed=5678,
do_validation=True,
do_test=False,
dataset_kwargs={"pad_to_max_length": True},
enable_offline_packing=True,
offline_packing_specs=PackedSequenceSpecs(packed_sequence_size=512),
),
checkpoint=CheckpointConfig(
pretrained_checkpoint="/checkpoints/base_model", # Required for PEFT
save="/path/to/peft/checkpoints",
save_interval=200,
),
peft=LoRA(
target_modules=["linear_qkv", "linear_proj", "linear_fc1", "linear_fc2"],
dim=16,
alpha=32,
dropout=0.1,
),
# ... other configurations
)This section describes the internal design and architecture for how PEFT is integrated into Megatron Bridge.
The PEFT framework introduces a modular design for integrating adapters into large-scale models. Its architecture consists of the following components:
- Base PEFT Class: All PEFT methods inherit from the abstract {py:class}
bridge.peft.base.PEFTbase class, which defines the core interface for module transformation. - Module Transformation: PEFT traverses the model structure to identify and transform target modules individually.
- Adapter Integration: Adapters are injected into selected modules using a pre-wrap hook during model initialization.
- Checkpoint Integration: Adapter parameters and updated MoE router expert-bias buffers are saved and loaded during checkpointing; base model weights remain frozen.
The training workflow for PEFT follows a structured sequence that ensures efficient fine-tuning with minimal overhead:
- Model Loading: The base model is initialized from a specified pretrained checkpoint.
- PEFT Application: Adapter transformations are applied after Megatron Core model initialization, but before distributed wrapping.
- Parameter Freezing: Base model parameters are frozen to reduce training complexity; only adapter parameters are updated.
- Adapter Weight Loading: When resuming training, adapter weights and saved router expert-bias buffers are restored from the checkpoint.
- Checkpoint Saving: Adapter states and router expert-bias buffers are saved, resulting in significantly smaller checkpoint files than full-model checkpoints.
PEFT offers several advantages for scalable and efficient model fine-tuning:
- Reduced Checkpoint Size: Adapter-only checkpoints are dramatically smaller than full model checkpoints.
- Memory Efficiency: Since gradients are computed only for adapter parameters, memory usage is significantly reduced.
- Resume Support: Training can be resumed seamlessly using adapter-only checkpoints, without reloading full model weights.