Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
26 changes: 14 additions & 12 deletions gpt_builders.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
# Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
# Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.

from megatron.core.models.gpt import GPTModel
from megatron.core.models.gpt.gpt_layer_specs import (
get_gpt_decoder_block_spec,
get_gpt_layer_local_spec,
get_gpt_layer_with_transformer_engine_spec,
get_gpt_mtp_block_spec,
get_gpt_decoder_layer_specs,
)
from megatron.core.models.gpt.heterogeneous.heterogeneous_layer_specs import (
get_gpt_heterogeneous_layer_spec,
Expand Down Expand Up @@ -57,18 +58,19 @@ def gpt_builder(args, pre_process, post_process, vp_stage=None, config=None):
transformer_layer_spec = _get_transformer_layer_spec(use_te, config)
mtp_block_spec = None
if args.mtp_num_layers is not None:
if (
hasattr(transformer_layer_spec, 'layer_specs')
and len(transformer_layer_spec.layer_specs) == 0
):
# Get the decoder layer spec explicitly if no decoder layer in the last stage,
# Only happens with block spec (TransformerBlockSubmodules) when using MoE.
transformer_layer_spec_for_mtp = _get_transformer_layer_spec(use_te, config)
# Get GPT decoder layer specs for the model.
if args.spec is not None:
mtp_transformer_layer_spec = import_module(args.spec)
else:
transformer_layer_spec_for_mtp = transformer_layer_spec
# Define the decoder block spec
decoder_layer_specs = get_gpt_decoder_layer_specs(
config, use_transformer_engine=use_te, normalization=args.normalization, qk_l2_norm=args.qk_l2_norm, vp_stage=vp_stage
)
mtp_transformer_layer_spec = decoder_layer_specs[-1]
# Use spec of the last layer in decoder block as spec of the transformer layer in MTP
mtp_block_spec = get_gpt_mtp_block_spec(
config,
transformer_layer_spec_for_mtp,
mtp_transformer_layer_spec,
use_transformer_engine=use_te,
vp_stage=vp_stage,
)
Expand Down Expand Up @@ -96,12 +98,12 @@ def gpt_builder(args, pre_process, post_process, vp_stage=None, config=None):

def _get_transformer_layer_spec(use_te, config):
"""Get transformer layer specification based on configuration.

Args:
use_te (bool): Whether to use Transformer Engine
args: Training arguments
config: Model configuration

Returns:
transformer_layer_spec: The transformer layer specification
"""
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2022, NVIDIA CORPORATION. All rights reserved.
# Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.

import logging
import math
Expand Down Expand Up @@ -47,11 +47,13 @@ def __init__(
sizes: List[int],
is_built_on_rank: Callable,
config: BlendedMegatronDatasetConfig,
vp_stage: Optional[int] = None,
):
self.cls = cls
self.sizes = sizes
self.is_built_on_rank = is_built_on_rank
self.config = config
self.vp_stage = vp_stage

log_single_rank(
logger,
Expand Down
12 changes: 10 additions & 2 deletions megatron/core/distributed/finalize_model_grads.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved.
# Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.

from functools import partial
from typing import Callable, List, Optional, Union
Expand Down Expand Up @@ -193,7 +193,11 @@ def _allreduce_word_embedding_grads(
pp_group = parallel_state.get_pipeline_model_parallel_group()

_allreduce_embedding_grad(
model, embd_group, pp_group, partial(_get_shared_word_embedding_weight, config=config)
model,
embd_group,
pp_group,
partial(_get_shared_word_embedding_weight, config=config),
config=config,
)


Expand All @@ -203,6 +207,7 @@ def _allreduce_embedding_grad(
pp_group: torch.distributed.ProcessGroup,
weight_getter: Callable[[torch.nn.Module], Optional[torch.nn.Parameter]],
skip_if_none: bool = True,
config: TransformerConfig = None,
):
"""Unified helper to all-reduce embedding parameters across pipeline stages.

Expand All @@ -229,6 +234,9 @@ def _allreduce_embedding_grad(
model_module = model[0]
elif is_pp_last_stage(pp_group):
model_module = model[-1]
elif getattr(config, 'mtp_num_layers', None) is not None and config.mtp_num_layers > 0:
# Embedding for MTP layers is in the last virtual pipeline model parallel stage.
model_module = model[-1]
else: # We do not support an interleaved schedule for models with encoders yet.
model_module = model[0]

Expand Down
6 changes: 5 additions & 1 deletion megatron/core/model_parallel_config.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved.
# Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.

from dataclasses import dataclass
from typing import Callable, ContextManager, Optional
Expand Down Expand Up @@ -315,6 +315,10 @@ class ModelParallelConfig:
rank 1 | 0 1 2 0 1 2 3 4 3 4
"""

mtp_standalone: bool = False
"""This will be set automatically according to the pipeline layout,
and will be set to True if MTP is in a separate vpp stage."""

###################
# CPU Offloading
###################
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2024, NVIDIA CORPORATION. All rights reserved.
# Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
import logging
import os
from typing import Optional, Tuple
Expand Down Expand Up @@ -62,6 +62,8 @@ def _is_in_embd_group(self):
if torch.distributed.get_rank() in torch.distributed.get_process_group_ranks(
self.embd_group
):
if getattr(self, 'mtp_process', False):
return True
if (
torch.distributed.get_rank()
== torch.distributed.get_process_group_ranks(self.embd_group)[0]
Expand Down Expand Up @@ -201,7 +203,10 @@ def setup_embeddings_and_output_layer(self) -> None:
):
self.shared_embedding_or_output_weight().shared_embedding = True

if (self.post_process or getattr(self, 'mtp_process', False)) and not self.pre_process:
if (
(self.post_process and self.share_embeddings_and_output_weights)
or getattr(self, 'mtp_process', False)
) and not self.pre_process:
assert not (
is_vp_first_stage(self.vp_stage, self.vp_size) and is_pp_first_stage(self.pp_group)
)
Expand Down
44 changes: 23 additions & 21 deletions megatron/core/models/common/model_chunk_schedule_plan.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
get_comm_stream,
get_comp_stream,
)
from megatron.core.transformer.multi_token_prediction import get_mtp_num_layers_to_build


class ModelChunkState:
Expand Down Expand Up @@ -319,37 +318,40 @@ def __init__(
self._model_chunk_state.context_mask = None
self._model_chunk_state.attention_bias = None

transformer_num_layers = model.decoder.num_layers_per_pipeline_rank
mtp_num_layers = get_mtp_num_layers_to_build(model.config, vp_stage=self.vp_stage)

# build preprocess
self.pre_process = PreProcessNode(model, self._model_chunk_state, self._event, comp_stream)
# build layer schedule plan for each layer
for layer_idx in range(transformer_num_layers):
layer = model.decoder._get_layer(layer_idx)
layer_plan = TransformerLayerSchedulePlan(
layer, self._event, self._model_chunk_state, comp_stream, comm_stream

# build layer schedule plan for each layer.
# The methods to obtain layers are different for MTP so we need the other build plan for
# MTP. Also, this can help annotate MTP layer so that it can know where MTP is.
self._build_layer_schedule_plan(model.decoder, comp_stream, comm_stream)
self._build_layer_schedule_plan(getattr(model, "mtp", None), comp_stream, comm_stream)

# build post process
if model.post_process:
self.post_process = PostProcessNode(
model, self._model_chunk_state, self._event, comp_stream
)
self._transformer_layers.append(layer_plan)

# build mtp layers
for layer_idx in range(mtp_num_layers):
def _build_layer_schedule_plan(self, module, comp_stream, comm_stream):
if module is None:
return
num_layers = len(module.layers)
for layer_idx in range(num_layers):
extra_args = {
"is_first_layer": layer_idx == 0,
"is_last_layer": layer_idx == mtp_num_layers - 1,
"is_last_layer": layer_idx == num_layers - 1,
}
layer = model.mtp.layers[layer_idx]
layer_plan = TransformerLayerSchedulePlan(
layer, self.event, self.state, comp_stream, comm_stream, extra_args
module.layers[layer_idx],
self.event,
self.state,
comp_stream,
comm_stream,
extra_args,
)
self._transformer_layers.append(layer_plan)

# build post process
if model.post_process:
self.post_process = PostProcessNode(
model, self._model_chunk_state, self._event, comp_stream
)

@property
def event(self):
"""Gets the CUDA event for synchronization."""
Expand Down
36 changes: 14 additions & 22 deletions megatron/core/models/gpt/fine_grained_callables.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,26 +158,19 @@ def forward_impl(self, hidden_states):
"""Implements the forward pass for postprocessing.

This method handles:
1. Final layer normalization
2. Output layer computation
3. Loss computation if labels are provided
1. Output layer computation
2. Loss computation if labels are provided

Args:
hidden_states: The hidden states from the transformer layers.

Returns:
The logits or loss depending on whether labels are provided.
"""
# Final layer norm from Decoder
if self.gpt_model.decoder.final_layernorm and not self.gpt_model.mtp_process:
hidden_states = self.gpt_model.decoder.final_layernorm(hidden_states)
# TENorm produces a "viewed" tensor. This will result in schedule.py's
# deallocate_output_tensor() throwing an error, so a viewless tensor is
# created to prevent this.
hidden_states = make_viewless_tensor(
inp=hidden_states, requires_grad=True, keep_graph=True
)

Note:
Final layernorm now has been moved from the post-process stage to the
last decoder layer, so we don't need to run the final layer norm here.
"""
# Run GPTModel._postprocess
loss = self.gpt_model._postprocess(
hidden_states=hidden_states,
Expand Down Expand Up @@ -251,6 +244,7 @@ def __init__(
self.submodule = submodule
self.detached = tuple()
self.before_detached = tuple()
self.is_mtp = extra_args.get("is_mtp", False)

# Create flags to indicate first and last layer
self.is_first_layer = extra_args.get("is_first_layer", False)
Expand Down Expand Up @@ -459,6 +453,12 @@ def submodule_combine_forward(

# release tensor reference after use
node.layer_state.residual = None

# final layer norm from decoder
final_layernorm = node.chunk_state.model.decoder.final_layernorm
if not node.is_mtp and final_layernorm and node.is_last_layer:
output = final_layernorm(output)
output = make_viewless_tensor(inp=output, requires_grad=True, keep_graph=True)
return output

def mlp_wrapper(node: ScheduleNode, *args, **kwargs):
Expand Down Expand Up @@ -498,15 +498,7 @@ def build_mtp_layer_callables(layer):
def submodule_mtp_attn_forward(node, hidden_states):
# MTP Block Preprocess
if node.is_first_layer:
# Final layer norm from Decoder
final_layernorm = node.chunk_state.model.decoder.final_layernorm
if final_layernorm:
hidden_states = final_layernorm(hidden_states)
hidden_states = make_viewless_tensor(
inp=hidden_states, requires_grad=True, keep_graph=True
)
hidden_states = node.detach(hidden_states)
offset = get_mtp_layer_offset(layer.config)
offset = get_mtp_layer_offset(layer.config, node.chunk_state.model.vp_stage)
node.chunk_state.mtp_hidden_states = list(torch.chunk(hidden_states, 1 + offset, dim=0))
hidden_states = node.chunk_state.mtp_hidden_states[offset]

Expand Down
28 changes: 22 additions & 6 deletions megatron/core/models/gpt/gpt_layer_specs.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved.
# Copyright (c) 2025 NVIDIA CORPORATION & AFFILIATES. All rights reserved.

import warnings
from typing import Optional, Union
Expand Down Expand Up @@ -477,7 +477,7 @@ def get_mlp_module_spec_for_backend(
)


def get_gpt_decoder_block_spec(
def get_gpt_decoder_layer_specs(
config: TransformerConfig,
use_transformer_engine: bool,
normalization: Optional[str] = None,
Expand Down Expand Up @@ -600,6 +600,21 @@ def get_gpt_decoder_block_spec(
raise ValueError(f"Invalid layer spec key: {layer_spec_key}")
layer_specs.append(layer_spec_dict[layer_spec_key])

return layer_specs


def get_gpt_decoder_block_spec(
config: TransformerConfig,
use_transformer_engine: bool,
normalization: Optional[str] = None,
qk_l2_norm: Optional[bool] = False,
vp_stage: Optional[int] = None,
pp_rank: Optional[int] = None,
) -> TransformerBlockSubmodules:
"""GPT block spec."""
layer_specs = get_gpt_decoder_layer_specs(
config, use_transformer_engine, normalization, qk_l2_norm
)
# Slice the layer specs to only include the layers that are built in this pipeline stage.
# Note: MCore layer_number starts at 1
num_layers_to_build = get_num_layers_to_build(config, vp_stage=vp_stage, pp_rank=pp_rank)
Expand All @@ -615,6 +630,10 @@ def get_gpt_decoder_block_spec(
offset = get_transformer_layer_offset(config, vp_stage=vp_stage, pp_rank=pp_rank)
local_layer_specs = layer_specs[offset : offset + num_layers_to_build]

if use_transformer_engine:
layer_norm_impl = TENorm
else:
layer_norm_impl = LNImpl
# Block spec.
block_spec = TransformerBlockSubmodules(
layer_specs=local_layer_specs, layer_norm=layer_norm_impl
Expand Down Expand Up @@ -674,13 +693,10 @@ def get_gpt_mtp_block_spec_for_backend(
mtp_num_layers = config.mtp_num_layers if config.mtp_num_layers else 0
mtp_layer_specs = [mtp_layer_spec] * mtp_num_layers

offset = get_mtp_layer_offset(config)
offset = get_mtp_layer_offset(config, vp_stage=vp_stage)
# split the mtp layer specs to only include the layers that are built in this pipeline stage.
mtp_layer_specs = mtp_layer_specs[offset : offset + num_layers_to_build]
if len(mtp_layer_specs) > 0:
assert (
len(mtp_layer_specs) == config.mtp_num_layers
), +f"currently all of the mtp layers must stage in the same pipeline stage."
mtp_block_spec = MultiTokenPredictionBlockSubmodules(layer_specs=mtp_layer_specs)
else:
mtp_block_spec = None
Expand Down
Loading
Loading