Skip to content

Commit 1b1eb68

Browse files
authored
update mm_gpt_model input_ids (#117)
1 parent 8bcfef2 commit 1b1eb68

4 files changed

Lines changed: 50 additions & 7 deletions

File tree

src/mcore_bridge/model/gpt_model.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -451,9 +451,6 @@ def _postprocess(self,
451451
output_weight = None
452452
if self.share_embeddings_and_output_weights:
453453
output_weight = self.shared_embedding_or_output_weight()
454-
if self.config.is_multimodal and self.config.context_parallel_size > 1 and input_ids is not None:
455-
# input_ids is required by MTP.
456-
input_ids = split_cp_inputs(input_ids, getattr(packed_seq_params, 'cu_seqlens_q', None), 1)
457454

458455
if self.mtp_process and labels is not None:
459456
hidden_states = self.mtp(

src/mcore_bridge/model/mm_gpt_model.py

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from megatron.core.transformer.spec_utils import ModuleSpec
99

1010
from mcore_bridge.config import ModelConfig
11-
from mcore_bridge.utils import split_cp_inputs
11+
from mcore_bridge.utils import reconstruct_tensor_cp, split_cp_inputs
1212

1313
from .gpt_model import GPTModel
1414

@@ -83,18 +83,24 @@ def forward(
8383
**kwargs,
8484
) -> torch.Tensor:
8585
extra_kwargs = {k: kwargs[k] for k in self.language_model.extra_forward_keys}
86+
# Compatible with legacy mcore-bridge behavior.
87+
cp_size = self.config.context_parallel_size
88+
needs_split = cp_size > 1 and input_ids is not None and position_ids.shape[-1] * cp_size == input_ids.shape[-1]
8689
if decoder_input is not None:
8790
pass
8891
elif self.pre_process:
89-
kwargs.update({'input_ids': input_ids, 'packed_seq_params': packed_seq_params})
92+
input_ids_ = input_ids if needs_split else reconstruct_tensor_cp(input_ids, packed_seq_params, dim=1)
93+
kwargs.update({'input_ids': input_ids_, 'packed_seq_params': packed_seq_params})
9094
with self._patch_word_embeddings(kwargs):
91-
decoder_input = self.language_model.embedding(input_ids=input_ids, position_ids=position_ids)
95+
decoder_input = self.language_model.embedding(input_ids=input_ids_, position_ids=position_ids)
9296
else:
9397
# intermediate stage of pipeline
9498
# decoder will get hidden_states from encoder.input_tensor
9599
decoder_input = None
96100
kwargs = {}
97101
kwargs.update(extra_kwargs)
102+
if needs_split:
103+
input_ids = split_cp_inputs(input_ids, getattr(packed_seq_params, 'cu_seqlens_q', None), dim=1)
98104
return self.language_model(
99105
input_ids=input_ids,
100106
position_ids=position_ids,

src/mcore_bridge/utils/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
from .env import get_dist_setting, get_node_setting, is_dist, is_last_rank, is_local_master, is_master
44
from .import_utils import _LazyModule, is_flash_attn_3_available
55
from .logger import get_logger
6-
from .megatron_utils import get_local_layer_specs, roll_tensor, set_random_seed, split_cp_inputs, unwrap_model
6+
from .megatron_utils import (get_local_layer_specs, reconstruct_tensor_cp, roll_tensor, set_random_seed,
7+
split_cp_inputs, unwrap_model)
78
from .safetensors import SafetensorLazyLoader, StreamingSafetensorSaver
89
from .torch_utils import gc_collect, get_current_device, safe_ddp_context, to_device
910
from .utils import deep_getattr, get_env_args, json_parse_to_dict, patch_deepcopy

src/mcore_bridge/utils/megatron_utils.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import torch
55
from megatron.core import mpu, tensor_parallel
66
from megatron.core.distributed import DistributedDataParallel as DDP
7+
from megatron.core.ssm.mamba_context_parallel import _undo_attention_load_balancing
78
from megatron.core.transformer.module import Float16Module
89
from megatron.core.transformer.multi_token_prediction import roll_tensor as mcore_roll_tensor
910
from megatron.core.transformer.transformer_block import get_num_layers_to_build
@@ -67,6 +68,44 @@ def split_cp_inputs(inputs: torch.Tensor, cu_seqlens: Optional[torch.Tensor], di
6768
return torch.cat(new_inputs, dim=dim)
6869

6970

71+
def reconstruct_tensor_cp(tensor, packed_seq_params, dim: int) -> torch.Tensor:
72+
"""In CP mode, all-gather and undo the load-balanced (zigzag) chunking
73+
produced by ``split_cp_inputs``, restoring the full sequence in original
74+
token order along ``dim``.
75+
76+
Args:
77+
tensor: CP-sharded local tensor whose sequence dim is at ``dim``.
78+
packed_seq_params: ``PackedSeqParams`` for THD inputs, or ``None`` for
79+
regular ``[B, S, ...]`` inputs.
80+
dim: Sequence dimension index of ``tensor`` (default: 1).
81+
82+
Returns:
83+
torch.Tensor: Full-sequence tensor with the same shape as ``tensor``
84+
except the size at ``dim`` is multiplied by ``cp_size``.
85+
"""
86+
87+
cp_size = mpu.get_context_parallel_world_size()
88+
if cp_size <= 1:
89+
return tensor
90+
91+
cp_rank = mpu.get_context_parallel_rank()
92+
cp_group = mpu.get_context_parallel_group()
93+
94+
# All-gather across CP ranks (preserve local autograd graph for `tensor`).
95+
output_list = [torch.empty_like(tensor) for _ in range(cp_size)]
96+
torch.distributed.all_gather(output_list, tensor.contiguous(), group=cp_group)
97+
output_list[cp_rank] = tensor
98+
gathered = torch.cat(output_list, dim=dim)
99+
100+
# `_undo_attention_load_balancing` assumes sequence dim is 0; transpose if needed.
101+
if dim != 0:
102+
gathered = gathered.transpose(0, dim).contiguous()
103+
out = _undo_attention_load_balancing(gathered, cp_size, packed_seq_params)
104+
if dim != 0:
105+
out = out.transpose(0, dim).contiguous()
106+
return out
107+
108+
70109
def get_local_layer_specs(config, layer_specs, vp_stage=None):
71110
num_layers_to_build = get_num_layers_to_build(config, vp_stage=vp_stage)
72111

0 commit comments

Comments
 (0)