[PD] Support PD disaggregation with Prefill PP (#8846)
Signed-off-by: Shangming Cai <caishangming@linux.alibaba.com> Signed-off-by: Shangming Cai <csmthu@gmail.com> Co-authored-by: root <huzhiyuan@xiaohongshu.com> Co-authored-by: Ying Sheng <sqy1415@gmail.com> Co-authored-by: Francis <38564764+ssssnow@users.noreply.github.com> Co-authored-by: zitto <zhjc1124@gmail.com>
This commit is contained in:
@@ -20,7 +20,7 @@ import torch
|
||||
from torch import nn
|
||||
from transformers import PretrainedConfig
|
||||
|
||||
from sglang.srt.distributed import get_tensor_model_parallel_world_size
|
||||
from sglang.srt.distributed import get_pp_group, get_tensor_model_parallel_world_size
|
||||
from sglang.srt.eplb.expert_distribution import get_global_expert_distribution_recorder
|
||||
from sglang.srt.layers.dp_attention import is_dp_attention_enabled
|
||||
from sglang.srt.layers.layernorm import RMSNorm
|
||||
@@ -135,6 +135,8 @@ class DeepseekV3ForCausalLMNextN(DeepseekV3ForCausalLM):
|
||||
self.config = config
|
||||
self.tp_size = get_tensor_model_parallel_world_size()
|
||||
self.quant_config = quant_config
|
||||
# if not set, model load will be broken in DeepseekV3ForCausalLM load_weights()
|
||||
self.pp_group = get_pp_group()
|
||||
self.determine_num_fused_shared_experts("DeepseekV3ForCausalLMNextN")
|
||||
|
||||
self.model = DeepseekModelNextN(
|
||||
|
||||
@@ -20,7 +20,7 @@ import concurrent.futures
|
||||
import logging
|
||||
import os
|
||||
from enum import IntEnum, auto
|
||||
from typing import Any, Dict, Iterable, Optional, Tuple
|
||||
from typing import Any, Dict, Iterable, Optional, Tuple, Union
|
||||
|
||||
import torch
|
||||
import torch.nn.functional as F
|
||||
@@ -30,6 +30,7 @@ from transformers import PretrainedConfig
|
||||
|
||||
from sglang.srt.distributed import (
|
||||
get_moe_expert_parallel_world_size,
|
||||
get_pp_group,
|
||||
get_tensor_model_parallel_world_size,
|
||||
parallel_state,
|
||||
tensor_model_parallel_all_reduce,
|
||||
@@ -87,13 +88,13 @@ from sglang.srt.layers.quantization.int8_utils import (
|
||||
)
|
||||
from sglang.srt.layers.radix_attention import RadixAttention
|
||||
from sglang.srt.layers.rotary_embedding import get_rope, get_rope_wrapper
|
||||
from sglang.srt.layers.utils import is_sm100_supported
|
||||
from sglang.srt.layers.utils import PPMissingLayer, get_layer_id, is_sm100_supported
|
||||
from sglang.srt.layers.vocab_parallel_embedding import (
|
||||
ParallelLMHead,
|
||||
VocabParallelEmbedding,
|
||||
)
|
||||
from sglang.srt.managers.schedule_batch import global_server_args_dict
|
||||
from sglang.srt.model_executor.forward_batch_info import ForwardBatch
|
||||
from sglang.srt.model_executor.forward_batch_info import ForwardBatch, PPProxyTensors
|
||||
from sglang.srt.model_loader.weight_utils import default_weight_loader
|
||||
from sglang.srt.two_batch_overlap import (
|
||||
MaybeTboDeepEPDispatcher,
|
||||
@@ -114,6 +115,7 @@ from sglang.srt.utils import (
|
||||
is_hip,
|
||||
is_non_idle_and_non_empty,
|
||||
log_info_on_rank0,
|
||||
make_layers,
|
||||
use_intel_amx_backend,
|
||||
)
|
||||
|
||||
@@ -2029,26 +2031,35 @@ class DeepseekV2Model(nn.Module):
|
||||
self.padding_id = config.pad_token_id
|
||||
self.vocab_size = config.vocab_size
|
||||
self.first_k_dense_replace = config.first_k_dense_replace
|
||||
self.pp_group = get_pp_group()
|
||||
|
||||
if self.pp_group.is_first_rank:
|
||||
self.embed_tokens = VocabParallelEmbedding(
|
||||
config.vocab_size,
|
||||
config.hidden_size,
|
||||
enable_tp=not is_dp_attention_enabled(),
|
||||
)
|
||||
else:
|
||||
self.embed_tokens = PPMissingLayer()
|
||||
|
||||
self.embed_tokens = VocabParallelEmbedding(
|
||||
config.vocab_size,
|
||||
config.hidden_size,
|
||||
enable_tp=not is_dp_attention_enabled(),
|
||||
)
|
||||
self.alt_stream = torch.cuda.Stream() if _is_cuda else None
|
||||
self.layers = nn.ModuleList(
|
||||
[
|
||||
DeepseekV2DecoderLayer(
|
||||
config,
|
||||
layer_id,
|
||||
quant_config=quant_config,
|
||||
prefix=add_prefix(f"layers.{layer_id}", prefix),
|
||||
alt_stream=self.alt_stream,
|
||||
)
|
||||
for layer_id in range(config.num_hidden_layers)
|
||||
]
|
||||
self.layers, self.start_layer, self.end_layer = make_layers(
|
||||
config.num_hidden_layers,
|
||||
lambda idx, prefix: DeepseekV2DecoderLayer(
|
||||
config=config,
|
||||
layer_id=idx,
|
||||
quant_config=quant_config,
|
||||
prefix=prefix,
|
||||
alt_stream=self.alt_stream,
|
||||
),
|
||||
pp_rank=self.pp_group.rank_in_group,
|
||||
pp_size=self.pp_group.world_size,
|
||||
prefix=add_prefix("layers", prefix),
|
||||
)
|
||||
self.norm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
|
||||
if self.pp_group.is_last_rank:
|
||||
self.norm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
|
||||
else:
|
||||
self.norm = PPMissingLayer(return_tuple=True)
|
||||
|
||||
def get_input_embeddings(self) -> torch.Tensor:
|
||||
return self.embed_tokens
|
||||
@@ -2059,8 +2070,9 @@ class DeepseekV2Model(nn.Module):
|
||||
positions: torch.Tensor,
|
||||
forward_batch: ForwardBatch,
|
||||
input_embeds: torch.Tensor = None,
|
||||
) -> torch.Tensor:
|
||||
total_num_layers = len(self.layers)
|
||||
pp_proxy_tensors: Optional[PPProxyTensors] = None,
|
||||
) -> Union[torch.Tensor, PPProxyTensors]:
|
||||
total_num_layers = self.end_layer - self.start_layer
|
||||
device = input_embeds.device if input_embeds is not None else input_ids.device
|
||||
zero_allocator = BumpAllocator(
|
||||
buffer_size=total_num_layers * 2 * (2 if forward_batch.can_run_tbo else 1),
|
||||
@@ -2068,44 +2080,62 @@ class DeepseekV2Model(nn.Module):
|
||||
device=device,
|
||||
)
|
||||
|
||||
if input_embeds is None:
|
||||
hidden_states = self.embed_tokens(input_ids)
|
||||
if self.pp_group.is_first_rank:
|
||||
if input_embeds is None:
|
||||
hidden_states = self.embed_tokens(input_ids)
|
||||
else:
|
||||
hidden_states = input_embeds
|
||||
residual = None
|
||||
else:
|
||||
hidden_states = input_embeds
|
||||
assert pp_proxy_tensors is not None
|
||||
hidden_states = pp_proxy_tensors["hidden_states"]
|
||||
residual = pp_proxy_tensors["residual"]
|
||||
|
||||
residual = None
|
||||
normal_start_layer = self.start_layer
|
||||
normal_end_layer = self.end_layer
|
||||
if forward_batch.can_run_tbo:
|
||||
if (
|
||||
self.first_k_dense_replace > normal_start_layer
|
||||
and self.first_k_dense_replace < normal_end_layer
|
||||
):
|
||||
normal_end_layer = self.first_k_dense_replace
|
||||
elif self.first_k_dense_replace < normal_start_layer:
|
||||
normal_end_layer = normal_start_layer = 0
|
||||
|
||||
normal_num_layers = (
|
||||
self.first_k_dense_replace
|
||||
if forward_batch.can_run_tbo
|
||||
else total_num_layers
|
||||
)
|
||||
for i in range(normal_num_layers):
|
||||
for i in range(normal_start_layer, normal_end_layer):
|
||||
with get_global_expert_distribution_recorder().with_current_layer(i):
|
||||
layer = self.layers[i]
|
||||
hidden_states, residual = layer(
|
||||
positions, hidden_states, forward_batch, residual, zero_allocator
|
||||
)
|
||||
|
||||
if normal_num_layers != total_num_layers:
|
||||
if normal_end_layer != self.end_layer:
|
||||
hidden_states, residual = model_forward_maybe_tbo(
|
||||
layers=self.layers[normal_num_layers:],
|
||||
layers=self.layers[normal_end_layer : self.end_layer],
|
||||
enable_tbo=True,
|
||||
positions=positions,
|
||||
forward_batch=forward_batch,
|
||||
hidden_states=hidden_states,
|
||||
residual=residual,
|
||||
input_data_scatter_mode=self.layers[
|
||||
normal_num_layers - 1
|
||||
normal_end_layer - 1
|
||||
].layer_scatter_modes.layer_output_mode,
|
||||
zero_allocator=zero_allocator,
|
||||
)
|
||||
|
||||
if not forward_batch.forward_mode.is_idle():
|
||||
if residual is None:
|
||||
hidden_states = self.norm(hidden_states)
|
||||
else:
|
||||
hidden_states, _ = self.norm(hidden_states, residual)
|
||||
if not self.pp_group.is_last_rank:
|
||||
return PPProxyTensors(
|
||||
{
|
||||
"hidden_states": hidden_states,
|
||||
"residual": residual,
|
||||
}
|
||||
)
|
||||
else:
|
||||
if not forward_batch.forward_mode.is_idle():
|
||||
if residual is None:
|
||||
hidden_states = self.norm(hidden_states)
|
||||
else:
|
||||
hidden_states, _ = self.norm(hidden_states, residual)
|
||||
return hidden_states
|
||||
|
||||
|
||||
@@ -2132,6 +2162,7 @@ class DeepseekV2ForCausalLM(nn.Module):
|
||||
"kv_a_proj_with_mqa",
|
||||
]
|
||||
|
||||
self.pp_group = get_pp_group()
|
||||
self.config = config
|
||||
self.tp_size = get_tensor_model_parallel_world_size()
|
||||
self.quant_config = quant_config
|
||||
@@ -2201,13 +2232,27 @@ class DeepseekV2ForCausalLM(nn.Module):
|
||||
positions: torch.Tensor,
|
||||
forward_batch: ForwardBatch,
|
||||
input_embeds: torch.Tensor = None,
|
||||
pp_proxy_tensors: Optional[PPProxyTensors] = None,
|
||||
) -> torch.Tensor:
|
||||
hidden_states = self.model(input_ids, positions, forward_batch, input_embeds)
|
||||
|
||||
return self.logits_processor(
|
||||
input_ids, hidden_states, self.lm_head, forward_batch
|
||||
hidden_states = self.model(
|
||||
input_ids, positions, forward_batch, input_embeds, pp_proxy_tensors
|
||||
)
|
||||
|
||||
if self.pp_group.is_last_rank:
|
||||
return self.logits_processor(
|
||||
input_ids, hidden_states, self.lm_head, forward_batch
|
||||
)
|
||||
else:
|
||||
return hidden_states
|
||||
|
||||
@property
|
||||
def start_layer(self):
|
||||
return self.model.start_layer
|
||||
|
||||
@property
|
||||
def end_layer(self):
|
||||
return self.model.end_layer
|
||||
|
||||
def post_load_weights(self, is_nextn=False, weight_names=None):
|
||||
|
||||
# Perform post-processing after loading weights
|
||||
@@ -2215,7 +2260,7 @@ class DeepseekV2ForCausalLM(nn.Module):
|
||||
layer_ids = [self.config.num_hidden_layers]
|
||||
else:
|
||||
if weight_names is None:
|
||||
layer_ids = range(self.config.num_hidden_layers)
|
||||
layer_ids = range(self.model.start_layer, self.model.end_layer)
|
||||
else:
|
||||
layer_ids = set()
|
||||
for name in weight_names:
|
||||
@@ -2497,6 +2542,16 @@ class DeepseekV2ForCausalLM(nn.Module):
|
||||
params_dict = dict(self.named_parameters())
|
||||
weight_names = []
|
||||
for name, loaded_weight in weights:
|
||||
layer_id = get_layer_id(name)
|
||||
if (
|
||||
layer_id is not None
|
||||
and hasattr(self.model, "start_layer")
|
||||
and (
|
||||
layer_id < self.model.start_layer
|
||||
or layer_id >= self.model.end_layer
|
||||
)
|
||||
):
|
||||
continue
|
||||
if self.num_fused_shared_experts > 0 and "mlp.shared_experts" in name:
|
||||
name = name.replace(
|
||||
"mlp.shared_experts",
|
||||
@@ -2581,6 +2636,12 @@ class DeepseekV2ForCausalLM(nn.Module):
|
||||
# Skip loading extra bias for GPTQ models.
|
||||
if name.endswith(".bias") and name not in params_dict:
|
||||
continue
|
||||
# Skip loading embed_tokens if not first rank in pipeline parallelism
|
||||
if ".embed_tokens." in name and not self.pp_group.is_first_rank:
|
||||
continue
|
||||
# Skip loading norm if not last rank in pipeline parallelism
|
||||
if ".norm." in name and not self.pp_group.is_last_rank:
|
||||
continue
|
||||
if fuse_qkv_a_proj and (
|
||||
"q_a_proj" in name or "kv_a_proj_with_mqa" in name
|
||||
):
|
||||
|
||||
Reference in New Issue
Block a user