Skip to content
Merged
Show file tree
Hide file tree
Changes from 47 commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
16e4ed6
added mtp support
rkarimimahab Nov 23, 2025
3db9741
addressed comments
rkarimimahab Dec 1, 2025
aeecff9
fixed the duplicate
rkarimimahab Dec 3, 2025
9b4e2b5
added repeated mtp layers
rkarimimahab Dec 5, 2025
225c2dc
adds mtp-num-per-layer to fix a bug
rkarimimahab Dec 5, 2025
61eb031
added the assert
rkarimimahab Dec 5, 2025
e8304aa
added the assert for vp-stage of MTP layers
rkarimimahab Dec 9, 2025
7398d87
added the LBL fixes
Dec 18, 2025
47063da
added some fixes
Dec 18, 2025
4896a80
fixed more issues
Dec 18, 2025
d5a8dd2
updated
Dec 19, 2025
6f5e06a
added mtp support
rkarimimahab Nov 23, 2025
3b057ca
addressed comments
rkarimimahab Dec 1, 2025
7649f6e
fixed the duplicate
rkarimimahab Dec 3, 2025
07d7e6c
added repeated mtp layers
rkarimimahab Dec 5, 2025
bd1ac40
adds mtp-num-per-layer to fix a bug
rkarimimahab Dec 5, 2025
cbd19f1
added the assert
rkarimimahab Dec 5, 2025
d1ba228
added the assert for vp-stage of MTP layers
rkarimimahab Dec 9, 2025
4d0cd4d
added the LBL fixes
Dec 18, 2025
305ba71
added some fixes
Dec 18, 2025
c60715a
fixed more issues
Dec 18, 2025
cb77b0e
updated
Dec 19, 2025
d732548
fixed some lintingand small bugs
Dec 22, 2025
c66109d
solved merge conflicts
Dec 22, 2025
9d07525
fixed linting errors
Dec 22, 2025
b61e281
Add layer parsing for MTP
sancha Jan 13, 2026
c61f5bd
Shared Layer Builder + MTP Layer Construction
sancha Jan 13, 2026
c491705
MambaModel Integration + Finalization
sancha Jan 13, 2026
77ba45d
Consolidate MTP weight tying
sancha Jan 13, 2026
31f512b
consolidate shared_embedding_or_output_weight
sancha Jan 13, 2026
cf681fa
rename mtp_hybrid_override_pattern to mtp_layer_pattern
sancha Jan 13, 2026
82fab87
Remove mamba-model's re-preprocessing
sancha Jan 14, 2026
7035cd8
MLPLayer inherits from TransformerLayer
sancha Jan 14, 2026
af53f49
gpt model to not support layer patterns
sancha Jan 14, 2026
3611f2a
More thorough backward compatibility warnings for the args
sancha Jan 14, 2026
d4f6a72
Merge branch 'main' into rkarimimahab/mtp
sancha Jan 14, 2026
d81b58a
Remove addition of cp_group_size to mtp loss
sancha Jan 14, 2026
a8a8eae
Refactor arg parsing to arguments.py
sancha Jan 16, 2026
590315f
Handle mtp_standalone case, TELayerNormColumnParallel bug ..
sancha Jan 20, 2026
0520329
Address review comments
sancha Jan 21, 2026
2bfa108
standalone broken because loss not on non-mtp stage
sancha Jan 21, 2026
22de055
Merge remote-tracking branch 'gh/main' into rkarimimahab/mtp
sancha Jan 22, 2026
ae7b12a
Switch to using mamba-stack for MTP inner layers
sancha Jan 22, 2026
76c2443
Merge remote-tracking branch 'gh/main' into rkarimimahab/mtp
sancha Jan 23, 2026
6683247
Address review comments
sancha Jan 23, 2026
7bfa95f
Address review comments
sancha Jan 26, 2026
c65cbfa
Merge branch 'gh/main' into rkarimimahab/mtp
sancha Jan 28, 2026
d0773c5
Merge remote-tracking branch 'gh/main' into rkarimimahab/mtp
sancha Jan 31, 2026
d355d67
Merge branch 'main' into rkarimimahab/mtp
deepakn94 Jan 31, 2026
ea3c2bd
Fix linting errors
sancha Jan 31, 2026
19a56e5
isort then black
sancha Jan 31, 2026
e50409b
Fix pylint errors
sancha Jan 31, 2026
906e513
bigfix: test failures
sancha Jan 31, 2026
4bfc5ec
bugfix: fixing standalone mtp test failures
sancha Feb 1, 2026
9cc1668
bugfix: expected value error strings
sancha Feb 1, 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
8 changes: 6 additions & 2 deletions mamba_builders.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,18 @@
from megatron.training.arguments import core_transformer_config_from_args
from megatron.core.models.mamba.mamba_layer_specs import mamba_inference_stack_spec


def mamba_builder(args, pre_process, post_process, vp_stage=None, config=None, pg_collection=None):
print_rank_0('building MAMBA model ...')
if config is None:
config = core_transformer_config_from_args(args, TransformerConfig)
assert args.use_legacy_models is False, "Mamba only supported in Mcore!"

if config.transformer_impl == "inference_optimized":
mamba_stack_spec = mamba_inference_stack_spec
assert not config.inference_fuse_tp_communication, "inference_fuse_tp_communication is not supported for Mamba"
mamba_stack_spec = mamba_inference_stack_spec
assert (
not config.inference_fuse_tp_communication
), "inference_fuse_tp_communication is not supported for Mamba"
elif args.spec is not None:
mamba_stack_spec = import_module(args.spec)
else:
Expand All @@ -39,6 +42,7 @@ def mamba_builder(args, pre_process, post_process, vp_stage=None, config=None, p
rotary_percent=args.rotary_percent,
rotary_base=args.rotary_base,
pg_collection=pg_collection,
vp_stage=vp_stage,
)

for l in range(model.decoder.num_layers_per_pipeline_rank):
Expand Down
49 changes: 46 additions & 3 deletions megatron/core/models/common/language_module/language_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,10 @@
make_tp_sharded_tensor_for_checkpoint,
)

from megatron.core.transformer.multi_token_prediction import (
tie_output_layer_state_dict,
tie_word_embeddings_state_dict,
)

class LanguageModule(MegatronModule):
"""Base language module that has common helper functions used across GPT, BERT etc.
Expand Down Expand Up @@ -255,12 +259,20 @@ def setup_embeddings_and_output_layer(self) -> None:
LanguageModule.embedding_warning_printed = True

def shared_embedding_or_output_weight(self) -> Tensor:
"""Gets the emedding weight or output logit weights when share embedding and output weights set to True.
"""Gets the embedding weight or output logit weights when share embedding and output weights set to True
or when use Multi-Token Prediction (MTP).

Returns:
Tensor: During pre processing it returns the input embeddings weight while during post processing it returns the final output layers weight
Tensor: During pre processing or MTP process it returns the input embeddings weight while during post processing it returns the final output layers weight
"""
if self.pre_process:
if self.pre_process or getattr(self, 'mtp_process', False):
# Multi-Token Prediction (MTP) need both embedding layer and output layer.
# So there will be both embedding layer and output layer in the mtp process stage.
# When share_embeddings_and_output_weights is True, the embedding weight is the
# canonical shared weight and is passed to the output layer during forward.
assert hasattr(
self, 'embedding'
), f"embedding is needed in this pipeline stage, but it is not initialized."
return self.embedding.word_embeddings.weight
elif self.post_process:
return self.output_layer.weight
Expand Down Expand Up @@ -293,6 +305,37 @@ def sharded_state_dict(
output_layer_weight_key = f'{prefix}output_layer.weight'
output_layer_bias_key = f'{prefix}output_layer.bias'

# Multi-Token Prediction (MTP) need both embedding layer and output layer in
# mtp process stage.
# If MTP is not placed in the pre processing stage, we need to maintain a copy of
# embedding layer in the mtp process stage and tie it to the embedding in the pre
# processing stage.
# Also, if MTP is not placed in the post processing stage, we need to maintain a copy
# of output layer in the mtp process stage and tie it to the output layer in the post
# processing stage.
if self.mtp_process and not self.pre_process:
emb_weight = self.embedding.word_embeddings.weight
tie_word_embeddings_state_dict(
sharded_state_dict,
emb_weight,
first_stage_word_emb_key,
tp_group=self.tp_group,
dp_cp_group=metadata['dp_cp_group'],
)

if self.mtp_process and not self.post_process:
# We only need to tie the output layer weight if share_embeddings_and_output_weights
# is False. Because if share_embeddings_and_output_weights is True, the shared weight
# will be stored in embedding layer, and output layer will not have any weight.
if not self.share_embeddings_and_output_weights:
output_layer_weight = self.output_layer.weight
tie_output_layer_state_dict(
sharded_state_dict,
output_layer_weight,
output_layer_weight_key,
tp_group=self.tp_group,
dp_cp_group=metadata['dp_cp_group'],
)
if self.share_embeddings_and_output_weights:
self.tie_embeddings_and_output_weights_state_dict(
sharded_state_dict, output_layer_weight_key, first_stage_word_emb_key, metadata
Expand Down
2 changes: 1 addition & 1 deletion megatron/core/models/gpt/gpt_layer_specs.py
Original file line number Diff line number Diff line change
Expand Up @@ -707,7 +707,7 @@ def get_gpt_mtp_block_spec_for_backend(
raise ValueError(f"Invalid spec: {spec}")

mtp_layer_spec = get_mtp_layer_spec_for_backend(
transformer_layer_spec=transformer_layer_spec, backend=backend
mtp_model_layer_spec=transformer_layer_spec, backend=backend
)
mtp_num_layers = config.mtp_num_layers if config.mtp_num_layers else 0
mtp_layer_specs = [mtp_layer_spec] * mtp_num_layers
Expand Down
110 changes: 18 additions & 92 deletions megatron/core/models/gpt/gpt_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,9 @@
from megatron.core.tensor_parallel import gather_from_sequence_parallel_region
from megatron.core.transformer.enums import CudaGraphScope, ModelType
from megatron.core.transformer.multi_token_prediction import (
MTPLossAutoScaler,
MTPLossLoggingHelper,
MultiTokenPredictionBlock,
roll_tensor,
tie_word_embeddings_state_dict,
mtp_on_this_rank,
process_mtp_loss,
)
from megatron.core.transformer.spec_utils import ModuleSpec
from megatron.core.transformer.transformer_block import TransformerBlock
Expand Down Expand Up @@ -144,7 +142,9 @@ def __init__(
self.rotary_base = rotary_base
self.rotary_scaling = rope_scaling
self.mtp_block_spec = mtp_block_spec
self.mtp_process = mtp_block_spec is not None
self.mtp_process = mtp_block_spec is not None and mtp_on_this_rank(
self.config, ignore_virtual=False, vp_stage=vp_stage
)

if self.pre_process or self.mtp_process:
self.embedding = LanguageModelEmbedding(
Expand Down Expand Up @@ -609,56 +609,19 @@ def _postprocess(
return hidden_states

if self.config.mtp_num_layers is not None:
mtp_labels = labels.clone()
hidden_states_list = torch.chunk(hidden_states, 1 + self.config.mtp_num_layers, dim=0)
hidden_states = hidden_states_list[0]
if loss_mask is None:
# if loss_mask is not provided, use all ones as loss_mask
loss_mask = torch.ones_like(mtp_labels)
for mtp_layer_number in range(self.config.mtp_num_layers):
# output
mtp_logits, _ = self.output_layer(
hidden_states_list[mtp_layer_number + 1],
weight=output_weight,
runtime_gather_output=runtime_gather_output,
)
# Calc loss for the current Multi-Token Prediction (MTP) layers.
mtp_labels, _ = roll_tensor(
mtp_labels,
shifts=-1,
dims=-1,
cp_group=self.cp_group,
packed_seq_params=packed_seq_params,
)
loss_mask, num_tokens = roll_tensor(
loss_mask,
shifts=-1,
dims=-1,
cp_group=self.cp_group,
packed_seq_params=packed_seq_params,
)
mtp_loss = self.compute_language_model_loss(mtp_labels, mtp_logits)
mtp_loss = loss_mask * mtp_loss
if self.training:
# TODO(shifangx): remove the use of parallel_state here
# after moving loss logging to loss_func in pretrain_gpt.py
MTPLossLoggingHelper.save_loss_to_tracker(
torch.sum(mtp_loss) / num_tokens,
mtp_layer_number,
self.config.mtp_num_layers,
avg_group=parallel_state.get_data_parallel_group(
with_context_parallel=True
),
)
mtp_loss_scale = self.config.mtp_loss_scaling_factor / self.config.mtp_num_layers
if self.config.calculate_per_token_loss:
hidden_states = MTPLossAutoScaler.apply(
hidden_states, mtp_loss_scale * mtp_loss
)
else:
hidden_states = MTPLossAutoScaler.apply(
hidden_states, mtp_loss_scale * mtp_loss / num_tokens
)
hidden_states = process_mtp_loss(
hidden_states=hidden_states,
labels=labels,
loss_mask=loss_mask,
output_layer=self.output_layer,
output_weight=output_weight,
runtime_gather_output=runtime_gather_output,
is_training=self.training,
compute_language_model_loss=self.compute_language_model_loss,
config=self.config,
cp_group=self.pg_collection.cp,
packed_seq_params=packed_seq_params,
)
sequence_parallel_override = False

if in_inference_mode and inference_context.materialize_only_last_token_logits:
Expand Down Expand Up @@ -715,27 +678,6 @@ def _postprocess(

return loss

def shared_embedding_or_output_weight(self) -> Tensor:
"""Gets the embedding weight or output logit weights when share input embedding and
output weights set to True or when use Multi-Token Prediction (MTP) feature.

Returns:
Tensor: During pre processing or MTP process it returns the input embeddings weight.
Otherwise, during post processing it returns the final output layers weight.
"""
if self.pre_process or self.mtp_process:
# Multi-Token Prediction (MTP) need both embedding layer and output layer.
# So there will be both embedding layer and output layer in the mtp process stage.
# In this case, if share_embeddings_and_output_weights is True, the shared weights
# will be stored in embedding layer, and output layer will not have any weight.
assert hasattr(
self, 'embedding'
), f"embedding is needed in this pipeline stage, but it is not initialized."
return self.embedding.word_embeddings.weight
elif self.post_process:
return self.output_layer.weight
return None

def build_schedule_plan(
self,
input_ids: Tensor,
Expand Down Expand Up @@ -826,20 +768,4 @@ def sharded_state_dict(
output_extra_state and output_extra_state.data
), f'Expected output layer extra state to be empty, got: {output_extra_state}'

# Multi-Token Prediction (MTP) need embedding layer in mtp process stage.
# If MTP is not placed in the pre processing stage, we need to maintain a copy of
# embedding layer in the mtp process stage and tie it to the embedding in the pre
# processing stage.
# Now MTP loss is computed in post processing stage, so the output_layer is not needed.
if self.mtp_process and not self.pre_process:
emb_weight_key = f'{prefix}embedding.word_embeddings.weight'
emb_weight = self.embedding.word_embeddings.weight
tie_word_embeddings_state_dict(
sharded_state_dict,
emb_weight,
emb_weight_key,
tp_group=self.tp_group,
dp_cp_group=metadata['dp_cp_group'],
)

return sharded_state_dict
35 changes: 34 additions & 1 deletion megatron/core/models/mamba/mamba_layer_specs.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
TELayerNormColumnParallelLinear,
TENorm,
TERowParallelLinear,
TEColumnParallelLinear,
)
from megatron.core.fusions.fused_bias_dropout import get_bias_dropout_add
from megatron.core.models.gpt.moe_module_specs import get_moe_module_spec
Expand All @@ -19,20 +20,49 @@
from megatron.core.transformer.attention import SelfAttention, SelfAttentionSubmodules
from megatron.core.transformer.enums import AttnMaskType
from megatron.core.transformer.mlp import MLP, MLPSubmodules
from megatron.core.transformer.spec_utils import ModuleSpec
from megatron.core.transformer.transformer_layer import (
MoETransformerLayer,
TransformerLayer,
TransformerLayerSubmodules,
)
from megatron.core.transformer.spec_utils import ModuleSpec
from megatron.core.transformer.multi_token_prediction import (
MultiTokenPredictionBlock,
MultiTokenPredictionBlockSubmodules,
MultiTokenPredictionLayer,
MultiTokenPredictionLayerSubmodules,
)

# This should be private and should not be used outside of this file.
moe = get_moe_module_spec(
use_te=True,
num_experts=8, # Can be any positive integer (must not be None).
moe_grouped_gemm=True,
moe_use_legacy_grouped_gemm=False,
)


# MTP block spec for Mamba - provides norms and projection only.
# Inner layers are built by MultiTokenPredictionLayer using nested MambaStack
_mamba_mtp_block_spec = ModuleSpec(
module=MultiTokenPredictionBlock,
submodules=MultiTokenPredictionBlockSubmodules(
layer_specs=[
ModuleSpec(
module=MultiTokenPredictionLayer,
submodules=MultiTokenPredictionLayerSubmodules(
enorm=TENorm,
hnorm=TENorm,
eh_proj=TEColumnParallelLinear,
mtp_model_layer=None, # Built via pattern + mamba_submodules
layer_norm=TENorm,
),
)
]
),
)


mamba_stack_spec = ModuleSpec(
module=MambaStack,
submodules=MambaStackSubmodules(
Expand Down Expand Up @@ -87,9 +117,11 @@
pre_mlp_layernorm=TENorm, mlp=moe, mlp_bda=get_bias_dropout_add
),
),
mtp_block_spec=_mamba_mtp_block_spec,
),
)


mamba_inference_stack_spec = ModuleSpec(
module=MambaStack,
submodules=MambaStackSubmodules(
Expand Down Expand Up @@ -147,5 +179,6 @@
pre_mlp_layernorm=TENorm, mlp=moe, mlp_bda=get_bias_dropout_add
),
),
mtp_block_spec=_mamba_mtp_block_spec,
),
)
Loading