Files
ModelHub XC d4e0a1af66 初始化项目,由ModelHub XC社区提供模型
Model: ayh015/myLightningOPD
Source: Original Platform
2026-08-27 23:50:14 +08:00

239 lines
10 KiB
Python

# SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
import inspect
import re
from argparse import Namespace
from collections.abc import Iterator, Sequence
import torch
import torch.distributed as dist
from megatron.core import mpu
from megatron.core.transformer.transformer_layer import get_transformer_layer_offset
from slime.backends.megatron_utils.misc_utils import strip_param_name_prefix
from slime.utils.types import ParamInfo
def all_gather_param(name: str, param: torch.nn.Parameter) -> torch.Tensor:
"""
All-gather TP-sharded param to full tensor. expert_bias→param, non-TP/duplicated→param.data.
Uses expert-TP for ".experts.", else regular-TP. linear_fc1 rechunked (GLU), linear_fc2 dim fix.
"""
if "expert_bias" in name:
return param
assert hasattr(param, "tensor_model_parallel"), f"{name} does not have tensor_model_parallel attribute"
if not param.tensor_model_parallel or getattr(param, "parallel_mode", None) == "duplicated":
return param.data
if ".experts." in name:
tp_size = mpu.get_expert_tensor_parallel_world_size()
tp_group = mpu.get_expert_tensor_parallel_group()
else:
tp_size = mpu.get_tensor_model_parallel_world_size()
tp_group = mpu.get_tensor_model_parallel_group()
param_partitions = [torch.empty_like(param.data) for _ in range(tp_size)]
dist.all_gather(param_partitions, param.data, group=tp_group)
partition_dim = param.partition_dim
assert param.partition_stride == 1, "partition_stride != 1 is not supported"
# TODO: here we did an extra copy during concat, maybe merge this with convert_to_hf is better?
# TODO: check only GLU is used.
if "linear_fc1.weight" in name:
param_partitions = [p.chunk(2, dim=0) for p in param_partitions]
param_partitions = [p[0] for p in param_partitions] + [p[1] for p in param_partitions]
# this is bug in megatron's grouped moe.
if "linear_fc2.weight" in name:
if partition_dim == 0:
partition_dim = 1
param = torch.cat(param_partitions, dim=partition_dim)
return param
def all_gather_params_async(
param_infos_and_params: list[tuple[ParamInfo, torch.Tensor]],
) -> list[torch.Tensor]:
"""
Parallel TP all-gather for multiple params. Loop 1: for each TP param, allocate buffers +
dist.all_gather(async_op=True) on expert-TP/regular-TP group (skip expert_bias/non-TP/duplicated).
Loop 2: wait all NCCL handles (enables overlap). Loop 3: concat partitions + apply GLU rechunk/MoE dim fix.
"""
# Phase 1: Start all async all_gather operations
gather_tasks = []
handles = []
for info, param in param_infos_and_params:
# Prepare async all_gather
if "expert_bias" in info.name:
gather_tasks.append((info, param, None, None, None))
handles.append(None)
elif not param.tensor_model_parallel or getattr(param, "parallel_mode", None) == "duplicated":
gather_tasks.append((info, param.data, None, None, None))
handles.append(None)
else:
# Start async all_gather
if ".experts." in info.name:
tp_size = mpu.get_expert_tensor_parallel_world_size()
tp_group = mpu.get_expert_tensor_parallel_group()
else:
tp_size = mpu.get_tensor_model_parallel_world_size()
tp_group = mpu.get_tensor_model_parallel_group()
param_partitions = [torch.empty_like(param.data) for _ in range(tp_size)]
handle = dist.all_gather(param_partitions, param.data, group=tp_group, async_op=True)
gather_tasks.append((info, None, handle, param_partitions, param.partition_dim))
handles.append(handle)
# Phase 2: Wait for ALL async operations to complete at once
# This ensures maximum parallelism by not blocking on individual operations
for handle in handles:
if handle is not None:
handle.wait()
# Phase 3: Process all results after all communications are done
gathered_params = []
for info, direct_param, handle, param_partitions, partition_dim in gather_tasks:
if handle is None:
# No all_gather needed
param = direct_param
else:
# Process the gathered partitions (same logic as original all_gather_param)
assert partition_dim is not None, "partition_stride != 1 is not supported"
# TODO: here we did an extra copy during concat, maybe merge this with convert_to_hf is better?
# TODO: check only GLU is used.
if "linear_fc1.weight" in info.name:
param_partitions = [p.chunk(2, dim=0) for p in param_partitions]
param_partitions = [p[0] for p in param_partitions] + [p[1] for p in param_partitions]
# this is bug in megatron's grouped moe.
if "linear_fc2.weight" in info.name:
if partition_dim == 0:
partition_dim = 1
param = torch.cat(param_partitions, dim=partition_dim)
gathered_params.append(param)
return gathered_params
def named_params_and_buffers(
args: Namespace,
model: Sequence[torch.nn.Module],
convert_to_global_name: bool = True,
translate_gpu_to_cpu: bool = False,
) -> Iterator[tuple[str, torch.Tensor]]:
if convert_to_global_name:
ans = _named_params_and_buffers_global(args, model)
else:
ans = _named_params_and_buffers_vanilla(model)
if translate_gpu_to_cpu:
ans = ((name, _maybe_get_cpu_backup(tensor)) for name, tensor in ans)
return ans
def _maybe_get_cpu_backup(x: torch.Tensor):
from torch_memory_saver import torch_memory_saver
if (cpu_tensor := torch_memory_saver.get_cpu_backup(x)) is not None:
return cpu_tensor
return x
def _named_params_and_buffers_vanilla(model: Sequence[torch.nn.Module]) -> Iterator[tuple[str, torch.Tensor]]:
for vp_stage, model_module in enumerate(model):
def _compute_fqn(name, vp_stage=vp_stage):
return f"vp_stages.{vp_stage}.{strip_param_name_prefix(name)}"
for name, param in model_module.named_parameters():
yield _compute_fqn(name), param
for name, buffer in model_module.named_buffers():
# TODO shall we handle (almost) all buffers like Megatron Bridge
if "expert_bias" not in name:
continue
yield _compute_fqn(name), buffer
def _named_params_and_buffers_global(
args: Namespace, model: Sequence[torch.nn.Module]
) -> Iterator[tuple[str, torch.Tensor]]:
"""
Yield (global_name, param/buffer) with consistent names across PP/EP. Adjusts indices for
virtual PP + EP offsets. Handles decoder.layers, mtp.layers (Multi-Token Prediction), expert_bias.
"""
ep_size = mpu.get_expert_model_parallel_world_size()
ep_rank = mpu.get_expert_model_parallel_rank()
if args.num_experts:
expert_offset = ep_rank * args.num_experts // ep_size
sig = inspect.signature(get_transformer_layer_offset)
need_vp_stage = "vp_stage" in sig.parameters
for vp_stage, model_module in enumerate(model):
if need_vp_stage:
layer_offset = get_transformer_layer_offset(model_module.config, vp_stage)
else:
layer_offset = get_transformer_layer_offset(model_module.config)
for name, param in model_module.named_parameters():
# for model without ddp wrap
if not name.startswith("module.module."):
name = "module." + name
decoder_layers_pattern = r"module\.module\.decoder\.layers\.(\d+)\.(.+)"
match = re.match(decoder_layers_pattern, name)
if not match:
# MTP (Multi-Token Prediction) layers for speculative decoding
mtp_layers_pattern = r"module\.module\.mtp\.layers\.(\d+)\.(.+)"
match = re.match(mtp_layers_pattern, name)
if not match:
yield name, param
continue
# MTP layer indices start from 0
layer_idx, rest = match.groups()
expert_pattern = r"transformer_layer.mlp.experts\.(.+)\.weight(\d+)"
match = re.match(expert_pattern, rest)
if not match:
yield name, param
continue
rest, expert_idx = match.groups()
expert_idx = int(expert_idx) + expert_offset
yield f"module.module.mtp.layers.{layer_idx}.transformer_layer.mlp.experts.{rest}.weight{expert_idx}", param
continue
layer_idx, rest = match.groups()
layer_idx = int(layer_idx) + layer_offset
# this is hardcoded for te grouped matmul
expert_pattern = r"mlp.experts\.(.+)\.weight(\d+)"
match = re.match(expert_pattern, rest)
if match:
rest, expert_idx = match.groups()
expert_idx = int(expert_idx) + expert_offset
yield f"module.module.decoder.layers.{layer_idx}.mlp.experts.{rest}.weight{expert_idx}", param
else:
yield f"module.module.decoder.layers.{layer_idx}.{rest}", param
# treat expert bias as normal parameters
for name, buffer in model_module.named_buffers():
# TODO shall we handle (almost) all buffers like Megatron Bridge
if "expert_bias" not in name:
continue
# for model without ddp wrap
if not name.startswith("module.module."):
name = "module." + name
decoder_layers_pattern = r"module\.module\.decoder\.layers\.(\d+)\.(.+)"
match = re.match(decoder_layers_pattern, name)
if not match:
yield name, buffer
else:
layer_idx, rest = match.groups()
layer_idx = int(layer_idx) + layer_offset
yield f"module.module.decoder.layers.{layer_idx}.{rest}", buffer