Skip to content
Merged
Show file tree
Hide file tree
Changes from 39 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
dbbafca
Add refactored recipe for qwen2, qwen3
athitten Jan 23, 2026
6478f92
Add refactored recipe for qwen3_moe, qwen3_next
athitten Jan 23, 2026
a1db4a9
Add refactored recipe for llama2, llama3
athitten Jan 24, 2026
b6f209a
Add deepseek_v2, deepseek_v3 refactored recipe
athitten Jan 24, 2026
4b7f37b
Add refactored recipe for gemma2, gemma3
athitten Jan 24, 2026
3398c09
Add refactored recipe for glm45
athitten Jan 25, 2026
19ac6fc
Add GPT OSS refactored recipe
athitten Jan 25, 2026
0166966
Add kimi k2 refactored recipe
athitten Jan 25, 2026
b94bd4e
Add refactored recipe for moonlight_16b
athitten Jan 26, 2026
5e6a1ab
Add refactored recipe nemotron nano v2
athitten Jan 26, 2026
9005e80
Add refactored recipe for Nemotron-H
athitten Jan 26, 2026
4685af1
Add OLMoE 7B refactored config
athitten Jan 26, 2026
9079c62
Add refactored recipe for GPt3 175B
athitten Jan 26, 2026
b9c7ba0
Add new pretrain_configs to the recipes and remove *_new.py recipes
athitten Jan 27, 2026
f914d6b
Add missing _model_config and remove comments
athitten Jan 28, 2026
8584789
Add refactored recipe for nemotron nanov3 and update test file
athitten Jan 28, 2026
cc4feaa
fix lint check error
athitten Jan 28, 2026
07b671e
Add refactored SFT recipes for qwen2, qwen3
athitten Jan 31, 2026
7e7084f
Add SFT refactored recipe for qwen3_moe and qwen3_next
athitten Jan 31, 2026
5859eec
Add recfactored sft configs for gemma2, gemma3
athitten Feb 2, 2026
61f0ae9
Add refactored sft recipe for GLM45
athitten Feb 2, 2026
676fbef
Add refactored SFT recipe for gpt_oss
athitten Feb 2, 2026
940bcfb
Fix DDP configs and add Llama3 refactored SFT recipe
athitten Feb 2, 2026
ed8e9f1
Add SFT refactored recipe for moonlight_16b
athitten Feb 2, 2026
ddb87f6
Add refactored SFT recipe for nemotron
athitten Feb 3, 2026
c4372f6
Add refactored SFT recipe for olmoe_7b
athitten Feb 4, 2026
1b12226
Add new _sft_config, _peft_config to the original recipes
athitten Feb 4, 2026
d26339e
Delete _new.py recipe files created for sft, peft
athitten Feb 6, 2026
5aa0c12
Fix lint check errors
athitten Feb 7, 2026
1406b31
Add new config names in __init__.py for all models
athitten Feb 7, 2026
1e71781
Fix tests for the new SFT, PEFT API and add pretrained ckpt option
athitten Feb 7, 2026
b0726bd
Update functional tests
athitten Feb 7, 2026
528c210
Remove unused import
athitten Feb 9, 2026
21ed8d9
Fix gpt-oss, nemotronh functional tests
athitten Feb 9, 2026
a365d5c
Replace union with pipe syntax
athitten Feb 10, 2026
a06578b
Add ValidationConfig related changes and update to latest main
athitten Feb 10, 2026
0d5f555
Add apply_flex_dispatcher_backend to qwen3_moe,qwen3_next and fix mer…
athitten Feb 12, 2026
767707b
Fix formatting
athitten Feb 12, 2026
34d94ea
Merge branch 'main' into athitten/sft_recipe_refactor
yaoyu-33 Feb 20, 2026
31cb09a
Merge branch 'main' into athitten/sft_recipe_refactor
yaoyu-33 Feb 25, 2026
599a25b
Merge branch 'main' into athitten/sft_recipe_refactor
yaoyu-33 Feb 25, 2026
18dab36
Use seq_length var in all sft, peft configs
athitten Feb 26, 2026
2a99c10
async_tensor_model_parallel_allreduce=True from Nemotron3NanoProvider
athitten Feb 26, 2026
271c57d
Merge branch 'main' into athitten/sft_recipe_refactor
athitten Feb 26, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 45 additions & 49 deletions scripts/performance/configs/llama/llama3_llm_finetune.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@
from utils.precision import get_precision_config
from utils.utils import get_workload_base_config

from megatron.bridge.recipes.llama import llama3_8b_finetune_config, llama3_70b_finetune_config
from megatron.bridge.recipes.llama import (
llama3_8b_sft_config,
llama3_70b_peft_config,
llama3_70b_sft_config,
)
from megatron.bridge.training.comm_overlap import (
CommOverlapConfig,
)
Expand Down Expand Up @@ -57,12 +61,11 @@ def llama3_8b_sft_config_gb200(precision: str = "bf16", config_variant: str = "v
)
precision_config = get_precision_config(precision)

cfg = llama3_8b_finetune_config(
peft="none",
precision_config=precision_config,
packed_sequence=True,
seq_length=16384,
)
cfg = llama3_8b_sft_config()
cfg.mixed_precision = precision_config
cfg.model.seq_length = 16384

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we keep this as a separate var and use it for model.seq_length, dataset.seq_length, dataset.packed_sequence_specs.packed_sequence_size?

only one change to make if and when we change the value

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes let me push a commit for this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 18dab36

cfg.dataset.seq_length = 16384
cfg.dataset.packed_sequence_specs.packed_sequence_size = 16384
set_llama3_common_peft_configs(cfg)
set_workload_base_configs(cfg, base_cfg)

Expand All @@ -81,12 +84,11 @@ def llama3_8b_sft_config_h100(precision: str = "bf16", config_variant: str = "v1
)
precision_config = get_precision_config(precision)

cfg = llama3_8b_finetune_config(
peft="none",
precision_config=precision_config,
packed_sequence=True,
seq_length=4096,
)
cfg = llama3_8b_sft_config()
cfg.mixed_precision = precision_config
cfg.model.seq_length = 4096
cfg.dataset.seq_length = 4096
cfg.dataset.packed_sequence_specs.packed_sequence_size = 4096
set_llama3_common_peft_configs(cfg)
set_workload_base_configs(cfg, base_cfg)

Expand All @@ -105,12 +107,11 @@ def llama3_70b_sft_config_gb300(precision: str = "bf16", config_variant: str = "
)
precision_config = get_precision_config(precision)

cfg = llama3_70b_finetune_config(
peft="none",
precision_config=precision_config,
packed_sequence=True,
seq_length=4096,
)
cfg = llama3_70b_sft_config()
cfg.mixed_precision = precision_config
cfg.model.seq_length = 4096
cfg.dataset.seq_length = 4096
cfg.dataset.packed_sequence_specs.packed_sequence_size = 4096
set_llama3_common_peft_configs(cfg)
set_workload_base_configs(cfg, base_cfg)

Expand Down Expand Up @@ -141,12 +142,11 @@ def llama3_70b_sft_config_gb200(precision: str = "bf16", config_variant: str = "
)
precision_config = get_precision_config(precision)

cfg = llama3_70b_finetune_config(
peft="none",
precision_config=precision_config,
packed_sequence=True,
seq_length=4096,
)
cfg = llama3_70b_sft_config()
cfg.mixed_precision = precision_config
cfg.model.seq_length = 4096
cfg.dataset.seq_length = 4096
cfg.dataset.packed_sequence_specs.packed_sequence_size = 4096
set_llama3_common_peft_configs(cfg)
set_workload_base_configs(cfg, base_cfg)

Expand All @@ -171,12 +171,11 @@ def llama3_70b_sft_config_h100(precision: str = "bf16", config_variant: str = "v
)
precision_config = get_precision_config(precision)

cfg = llama3_70b_finetune_config(
peft="none",
precision_config=precision_config,
packed_sequence=True,
seq_length=4096,
)
cfg = llama3_70b_sft_config()
cfg.mixed_precision = precision_config
cfg.model.seq_length = 4096
cfg.dataset.seq_length = 4096
cfg.dataset.packed_sequence_specs.packed_sequence_size = 4096
set_llama3_common_peft_configs(cfg)
set_workload_base_configs(cfg, base_cfg)

Expand All @@ -201,12 +200,11 @@ def llama3_70b_lora_config_gb300(precision: str = "bf16", config_variant: str =
)
precision_config = get_precision_config(precision)

cfg = llama3_70b_finetune_config(
peft="lora",
precision_config=precision_config,
packed_sequence=True,
seq_length=4096,
)
cfg = llama3_70b_peft_config(peft_scheme="lora")
cfg.mixed_precision = precision_config
cfg.model.seq_length = 4096
cfg.dataset.seq_length = 4096
cfg.dataset.packed_sequence_specs.packed_sequence_size = 4096
set_llama3_common_peft_configs(cfg)
set_workload_base_configs(cfg, base_cfg)
cfg.comm_overlap = CommOverlapConfig(tp_comm_overlap=bool(cfg.model.tensor_model_parallel_size > 1))
Expand Down Expand Up @@ -238,12 +236,11 @@ def llama3_70b_lora_config_gb200(precision: str = "bf16", config_variant: str =
# BF16 uses seq_length=2048, FP8 variants use seq_length=4096
seq_length = 2048 if precision.lower() == "bf16" else 4096

cfg = llama3_70b_finetune_config(
peft="lora",
precision_config=precision_config,
packed_sequence=True,
seq_length=seq_length,
)
cfg = llama3_70b_peft_config(peft_scheme="lora")
cfg.mixed_precision = precision_config
cfg.model.seq_length = seq_length
cfg.dataset.seq_length = seq_length
cfg.dataset.packed_sequence_specs.packed_sequence_size = seq_length
set_llama3_common_peft_configs(cfg)
set_workload_base_configs(cfg, base_cfg)
# Enable pad_cu_seqlens for CUDA graphs compatibility with packed sequences.
Expand Down Expand Up @@ -271,12 +268,11 @@ def llama3_70b_lora_config_h100(precision: str = "bf16", config_variant: str = "
)
precision_config = get_precision_config(precision)

cfg = llama3_70b_finetune_config(
peft="lora",
precision_config=precision_config,
packed_sequence=True,
seq_length=4096,
)
cfg = llama3_70b_peft_config(peft_scheme="lora")
cfg.mixed_precision = precision_config
cfg.model.seq_length = 4096
cfg.dataset.seq_length = 4096
cfg.dataset.packed_sequence_specs.packed_sequence_size = 4096
set_llama3_common_peft_configs(cfg)
set_workload_base_configs(cfg, base_cfg)
cfg.comm_overlap = CommOverlapConfig(tp_comm_overlap=bool(cfg.model.tensor_model_parallel_size > 1))
Expand Down
209 changes: 209 additions & 0 deletions src/megatron/bridge/recipes/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

from megatron.core.distributed import DistributedDataParallelConfig

from megatron.bridge.peft.lora import LoRA
from megatron.bridge.recipes.utils.finetune_utils import default_squad_config
from megatron.bridge.recipes.utils.optimizer_utils import distributed_fused_adam_with_cosine_annealing
from megatron.bridge.training.config import (
CheckpointConfig,
Expand Down Expand Up @@ -126,3 +128,210 @@ def _pretrain_common() -> ConfigContainer:
)

return cfg


def _sft_common() -> ConfigContainer:
"""Create a base SFT (Supervised Fine-Tuning) ConfigContainer with common defaults.

This function returns a ConfigContainer template with sensible defaults for full SFT
(not LoRA/DoRA). The caller MUST set `cfg.model` and `cfg.tokenizer.tokenizer_model`
before use.

Key differences from pre-training:
- Uses HFDatasetConfig with SQuAD as default dataset
- Lower learning rate (5e-6) suitable for full fine-tuning
- Fewer training iterations (1000)
- Smaller batch sizes
- Supports pretrained_checkpoint loading
- No PEFT (full parameter training)

Returns:
ConfigContainer: Base configuration template for full SFT.
"""
# Default output directories
base_output_dir = os.path.join(os.getcwd(), "nemo_experiments")
run_output_dir = os.path.join(base_output_dir, "default")
checkpoint_dir = os.path.join(run_output_dir, "checkpoints")
tensorboard_dir = os.path.join(run_output_dir, "tb_logs")

# Default sequence length for SFT
seq_length = 2048

# Packed sequence is enabled by default for training efficiency
# pad_seq_to_mult should be set to context_parallel_size * 2 if CP > 1
packed_sequence = True
pad_seq_to_mult = 1 # Override in model config if context_parallel_size > 1

# Optimizer and scheduler with lower LR for full SFT
opt_cfg, scheduler_cfg = distributed_fused_adam_with_cosine_annealing(
lr_warmup_iters=50,
lr_decay_iters=None, # Defaults to train_iters during validation
max_lr=5e-6, # Lower LR for full fine-tuning
min_lr=0.0,
adam_beta2=0.98, # Common for fine-tuning
)

cfg = ConfigContainer(
# Model - MUST be set by each recipe before use
model=None, # type: ignore[arg-type]
# Training config - shorter training for SFT
train=TrainingConfig(
train_iters=1000,
global_batch_size=128,
micro_batch_size=1,
),
validation=ValidationConfig(
eval_interval=100,
eval_iters=32,
),
# Optimizer and scheduler
optimizer=opt_cfg,
scheduler=scheduler_cfg,
# DDP config - minimal settings, model-specific configs can override
ddp=DistributedDataParallelConfig(
check_for_nan_in_grad=True,
grad_reduce_in_fp32=True,
),
# Dataset config - uses SQuAD with packed sequences by default
dataset=default_squad_config(
seq_length=seq_length, packed_sequence=packed_sequence, pad_seq_to_mult=pad_seq_to_mult
),
# Logger config
logger=LoggerConfig(
log_interval=1,
tensorboard_dir=tensorboard_dir,
log_timers_to_tensorboard=True,
),
# Tokenizer - placeholder, each recipe should set tokenizer_model
tokenizer=TokenizerConfig(
tokenizer_type="HuggingFaceTokenizer",
tokenizer_model=None, # Must be set by each recipe
),
# Checkpoint config with pretrained_checkpoint support
checkpoint=CheckpointConfig(
save_interval=100,
save=checkpoint_dir,
load=checkpoint_dir,
pretrained_checkpoint=None, # Set to load from pretrained weights
ckpt_format="torch_dist",
fully_parallel_save=True,
),
# RNG config - different seed from pretrain
rng=RNGConfig(seed=5678),
# Distributed init config
dist=DistributedInitConfig(),
comm_overlap=None,
# Mixed precision - bf16 by default
mixed_precision="bf16_mixed",
# No PEFT for full SFT
peft=None,
)

return cfg


def _peft_common() -> ConfigContainer:
"""Create a base PEFT (Parameter-Efficient Fine-Tuning) ConfigContainer with LoRA defaults.

This function returns a ConfigContainer template with sensible defaults for PEFT
using LoRA. The caller MUST set `cfg.model` and `cfg.tokenizer.tokenizer_model`
before use.

Key differences from full SFT:
- Higher learning rate (1e-4) suitable for adapter training
- LoRA enabled by default with standard settings (dim=32, alpha=32)
- Targets all linear layers: linear_qkv, linear_proj, linear_fc1, linear_fc2

Returns:
ConfigContainer: Base configuration template for PEFT with LoRA.
"""
# Default output directories
base_output_dir = os.path.join(os.getcwd(), "nemo_experiments")
run_output_dir = os.path.join(base_output_dir, "default")
checkpoint_dir = os.path.join(run_output_dir, "checkpoints")
tensorboard_dir = os.path.join(run_output_dir, "tb_logs")

# Default sequence length for PEFT
seq_length = 2048

# Packed sequence is enabled by default for training efficiency
# pad_seq_to_mult should be set to context_parallel_size * 2 if CP > 1
packed_sequence = True
pad_seq_to_mult = 1 # Override in model config if context_parallel_size > 1

# Optimizer and scheduler with higher LR for PEFT (only training adapters)
opt_cfg, scheduler_cfg = distributed_fused_adam_with_cosine_annealing(
lr_warmup_iters=50,
lr_decay_iters=None, # Defaults to train_iters during validation
max_lr=1e-4, # Higher LR for adapter training
min_lr=0.0,
adam_beta2=0.98, # Common for fine-tuning
)

cfg = ConfigContainer(
# Model - MUST be set by each recipe before use
model=None, # type: ignore[arg-type]
# Training config - shorter training for PEFT
train=TrainingConfig(
train_iters=1000,
global_batch_size=128,
micro_batch_size=1,
),
validation=ValidationConfig(
eval_interval=100,
eval_iters=32,
),
# Optimizer and scheduler
optimizer=opt_cfg,
scheduler=scheduler_cfg,
# DDP config - minimal settings for PEFT
ddp=DistributedDataParallelConfig(
check_for_nan_in_grad=True,
grad_reduce_in_fp32=True,
),
# Dataset config - uses SQuAD with packed sequences by default
dataset=default_squad_config(
seq_length=seq_length, packed_sequence=packed_sequence, pad_seq_to_mult=pad_seq_to_mult
),
# Logger config
logger=LoggerConfig(
log_interval=1,
tensorboard_dir=tensorboard_dir,
log_timers_to_tensorboard=True,
),
# Tokenizer - placeholder, each recipe should set tokenizer_model
tokenizer=TokenizerConfig(
tokenizer_type="HuggingFaceTokenizer",
tokenizer_model=None, # Must be set by each recipe
),
# Checkpoint config with pretrained_checkpoint support
checkpoint=CheckpointConfig(
save_interval=100,
save=checkpoint_dir,
load=checkpoint_dir,
pretrained_checkpoint=None, # Set to load from pretrained weights
ckpt_format="torch_dist",
fully_parallel_save=True,
),
Comment on lines +308 to +315

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

PEFT base config fails validation without a pretrained checkpoint.

ConfigContainer.validate() asserts checkpoint.pretrained_checkpoint when peft is set; leaving it None makes the parameterless PEFT configs error unless callers remember to patch it. Consider defaulting it to a placeholder (e.g., checkpoint_dir or checkpoint.load) or explicitly wiring it via the helper.

🛠️ Suggested fix
         checkpoint=CheckpointConfig(
             save_interval=100,
             save=checkpoint_dir,
             load=checkpoint_dir,
-            pretrained_checkpoint=None,  # Set to load from pretrained weights
+            pretrained_checkpoint=checkpoint_dir,  # Override with actual pretrained weights path
             ckpt_format="torch_dist",
             fully_parallel_save=True,
         ),
🤖 Prompt for AI Agents
In `@src/megatron/bridge/recipes/common.py` around lines 297 - 304, The PEFT base
config validation fails because CheckpointConfig.pretrained_checkpoint is set to
None while ConfigContainer.validate() requires it when peft is enabled; update
the CheckpointConfig initialization in the recipes/common.py snippet (the
checkpoint=CheckpointConfig(...) block) to default pretrained_checkpoint to a
non-null placeholder (for example use the same checkpoint_dir or checkpoint.load
value) or ensure the config helper explicitly sets
checkpoint.pretrained_checkpoint when peft is set so ConfigContainer.validate()
passes.

# RNG config - different seed from pretrain
rng=RNGConfig(seed=5678),
# Distributed init config
dist=DistributedInitConfig(),
comm_overlap=None,
# Mixed precision - bf16 by default
mixed_precision="bf16_mixed",
# LoRA config with standard defaults
peft=LoRA(
target_modules=["linear_qkv", "linear_proj", "linear_fc1", "linear_fc2"],
dim=32,
alpha=32,
dropout=0.0,
dropout_position="pre",
lora_A_init_method="xavier",
lora_B_init_method="zero",
a2a_experimental=False,
lora_dtype=None, # Uses model's dtype
),
)

return cfg
Loading
Loading