main
py 454 lines 18.1 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 import typing
4 from collections.abc import Callable, Iterable
5 from itertools import islice
6
7 import torch
8
9 import vllm.envs as envs
10 from vllm.config import VllmConfig
11 from vllm.distributed import get_pp_group
12 from vllm.forward_context import get_forward_context, is_forward_context_available
13 from vllm.model_executor.layers.fused_embed_norm import (
14 fused_embed_norm,
15 has_full_vocab_on_rank,
16 make_input_embedding,
17 )
18 from vllm.model_executor.layers.fused_moe import (
19 fused_moe_make_expert_params_mapping,
20 )
21 from vllm.model_executor.layers.layernorm import RMSNorm
22 from vllm.model_executor.model_loader.weight_utils import (
23 default_weight_loader,
24 maybe_remap_kv_scale_name,
25 )
26 from vllm.model_executor.models.deepseek_v2 import (
27 DeepseekV2ForCausalLM,
28 DeepseekV2MLP,
29 DeepseekV2MoE,
30 _try_load_fp8_indexer_wk,
31 get_spec_layer_idx_from_weight_name,
32 )
33 from vllm.model_executor.models.utils import (
34 PPMissingLayer,
35 get_pp_missing_layer_names,
36 is_pp_missing_parameter,
37 make_empty_intermediate_tensors_factory,
38 make_layers,
39 )
40 from vllm.models.common.ops.fused_allreduce_rms_norm import fused_allreduce_rms_norm
41 from vllm.models.common.ops.sequence_parallel import (
42 sp_all_gather,
43 sp_padding_mask,
44 sp_reduce_scatter,
45 sp_shard,
46 )
47 from vllm.models.deepseek_v32.attention import DeepseekV32Attention
48 from vllm.sequence import IntermediateTensors
49 from vllm.v1.attention.backends.mla.index_group import (
50 SparseMLAIndexGroupBuilder,
51 get_sparse_mla_index_group_max_rows,
52 )
53
54 from .glm52_low_latency_gemm import enable_glm52_low_latency_gemm
55
56
57 class DeepseekV32DecoderLayer(torch.nn.Module):
58 def __init__(
59 self,
60 vllm_config: VllmConfig,
61 prefix: str,
62 config=None,
63 topk_indices_buffer: torch.Tensor | None = None,
64 index_group_builder: SparseMLAIndexGroupBuilder | None = None,
65 ) -> None:
66 super().__init__()
67
68 if config is None:
69 config = vllm_config.model_config.hf_config
70 quant_config = vllm_config.quant_config
71 parallel_config = vllm_config.parallel_config
72
73 self.hidden_size = config.hidden_size
74 moe_layer_freq = getattr(config, "moe_layer_freq", 1)
75 layer_idx = int(prefix.split(sep=".")[-1])
76 self.layer_idx = layer_idx
77 self.use_mha = False
78 self.use_sequence_parallel = (
79 parallel_config.use_sequence_parallel_moe
80 and parallel_config.pipeline_parallel_size == 1
81 )
82
83 self.self_attn = DeepseekV32Attention(
84 vllm_config=vllm_config,
85 config=config,
86 prefix=f"{prefix}.self_attn",
87 topk_indices_buffer=topk_indices_buffer,
88 index_group_builder=index_group_builder,
89 )
90
91 if (
92 config.n_routed_experts is not None
93 and layer_idx >= config.first_k_dense_replace
94 and layer_idx % moe_layer_freq == 0
95 ):
96 # Keep the MoE output un-reduced. Non-SP fuses its all-reduce into
97 # the next layer norm; SP keeps the token shard local.
98 self.mlp = DeepseekV2MoE(
99 config=config,
100 parallel_config=parallel_config,
101 quant_config=quant_config,
102 reduce_results=False,
103 prefix=f"{prefix}.mlp",
104 apply_routed_scale_to_output=False,
105 )
106 else:
107 self.mlp = DeepseekV2MLP(
108 hidden_size=config.hidden_size,
109 intermediate_size=config.intermediate_size,
110 hidden_act=config.hidden_act,
111 quant_config=quant_config,
112 prefix=f"{prefix}.mlp",
113 reduce_results=False,
114 is_sequence_parallel=self.use_sequence_parallel,
115 )
116 self.input_layernorm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
117 self.post_attention_layernorm = RMSNorm(
118 config.hidden_size, eps=config.rms_norm_eps
119 )
120 self.routed_scaling_factor = getattr(config, "routed_scaling_factor", 1.0)
121
122 def forward(
123 self,
124 positions: torch.Tensor,
125 hidden_states: torch.Tensor,
126 residual: torch.Tensor | None,
127 attn_in: torch.Tensor | None = None,
128 ) -> tuple[torch.Tensor, torch.Tensor]:
129 full_num_tokens = positions.shape[0]
130
131 if residual is None:
132 # First layer: hidden_states is the embedding (the residual).
133 residual = hidden_states
134 # ``attn_in`` is input_layernorm(embedding) already computed fused
135 # with the embedding gather; otherwise apply it here.
136 hidden_states = (
137 attn_in if attn_in is not None else self.input_layernorm(hidden_states)
138 )
139 elif self.use_sequence_parallel:
140 hidden_states, residual = self.input_layernorm(hidden_states, residual)
141 else:
142 # The previous layer's MLP/MoE output is left un-reduced; fuse its
143 # all-reduce into this input_layernorm.
144 hidden_states, residual = fused_allreduce_rms_norm(
145 hidden_states, residual, self.input_layernorm
146 )
147 if self.use_sequence_parallel:
148 hidden_states = sp_all_gather(hidden_states)[:full_num_tokens]
149
150 # self_attn's o_proj runs reduce_results=False; reduce before RMSNorm.
151 hidden_states = self.self_attn(positions=positions, hidden_states=hidden_states)
152 if self.use_sequence_parallel:
153 hidden_states = sp_reduce_scatter(hidden_states)
154 hidden_states, residual = self.post_attention_layernorm(
155 hidden_states, residual
156 )
157 else:
158 hidden_states, residual = fused_allreduce_rms_norm(
159 hidden_states, residual, self.post_attention_layernorm
160 )
161
162 if self.use_sequence_parallel and isinstance(self.mlp, DeepseekV2MoE):
163 hidden_states = self.mlp(hidden_states, already_sequence_parallel=True)
164 else:
165 hidden_states = self.mlp(hidden_states)
166 return hidden_states, residual
167
168
169 class DeepseekV32Model(torch.nn.Module):
170 fall_back_to_pt_during_load = False
171
172 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
173 super().__init__()
174
175 config = vllm_config.model_config.hf_config
176 quant_config = vllm_config.quant_config
177 self.config = config
178 from vllm.platforms import current_platform
179
180 self.device = current_platform.device_type
181
182 self.vocab_size = config.vocab_size
183 parallel_config = vllm_config.parallel_config
184 self.use_sequence_parallel = (
185 parallel_config.use_sequence_parallel_moe
186 and parallel_config.pipeline_parallel_size == 1
187 )
188 # DSA is always sparse (has index_topk); allocate the shared top-k
189 # buffer the indexer writes and the sparse MLA backend reads.
190 self.is_v32 = True
191 # On the model, not a local: the MTP proposer shares it with the draft.
192 self.topk_indices_buffer = torch.empty(
193 vllm_config.scheduler_config.max_num_batched_tokens,
194 config.index_topk,
195 dtype=torch.int32,
196 device=self.device,
197 )
198 index_group_builder = SparseMLAIndexGroupBuilder(
199 self.topk_indices_buffer,
200 get_sparse_mla_index_group_max_rows(vllm_config),
201 )
202
203 if get_pp_group().is_first_rank:
204 self.embed_tokens = make_input_embedding(
205 config.vocab_size,
206 config.hidden_size,
207 quant_config=quant_config,
208 prefix=f"{prefix}.embed_tokens",
209 tie_word_embeddings=getattr(config, "tie_word_embeddings", False),
210 )
211 else:
212 self.embed_tokens = PPMissingLayer()
213 # The fused embed+norm gather needs the full table on-rank.
214 self.replicated_embed = has_full_vocab_on_rank(self.embed_tokens)
215
216 self.start_layer, self.end_layer, self.layers = make_layers(
217 config.num_hidden_layers,
218 lambda prefix: DeepseekV32DecoderLayer(
219 vllm_config=vllm_config,
220 prefix=prefix,
221 topk_indices_buffer=self.topk_indices_buffer,
222 index_group_builder=index_group_builder,
223 ),
224 prefix=f"{prefix}.layers",
225 )
226
227 if get_pp_group().is_last_rank:
228 self.norm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
229 else:
230 self.norm = PPMissingLayer()
231 self.make_empty_intermediate_tensors = make_empty_intermediate_tensors_factory(
232 ["hidden_states", "residual"], config.hidden_size
233 )
234
235 self.aux_hidden_state_layers = tuple[int, ...]()
236 self.num_redundant_experts = parallel_config.eplb_config.num_redundant_experts
237
238 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
239 return self.embed_tokens(input_ids)
240
241 def forward(
242 self,
243 input_ids: torch.Tensor | None,
244 positions: torch.Tensor,
245 intermediate_tensors: IntermediateTensors | None = None,
246 inputs_embeds: torch.Tensor | None = None,
247 ) -> torch.Tensor | IntermediateTensors:
248 attn_in = None
249 if get_pp_group().is_first_rank:
250 if inputs_embeds is not None:
251 hidden_states = inputs_embeds
252 elif self.replicated_embed:
253 assert input_ids is not None
254 # Full table on-rank: gather the embedding and the first layer's
255 # input_layernorm in one launch. ``attn_in`` is the pre-normed
256 # attention input; ``hidden_states`` is the (residual) embedding.
257 hidden_states, attn_in = fused_embed_norm(
258 input_ids,
259 self.embed_tokens.weight,
260 chain_weight=self.layers[self.start_layer].input_layernorm.weight,
261 eps=self.config.rms_norm_eps,
262 )
263 else:
264 assert input_ids is not None
265 hidden_states = self.embed_input_ids(input_ids)
266 residual = None
267 else:
268 assert intermediate_tensors is not None
269 hidden_states = intermediate_tensors["hidden_states"]
270 residual = intermediate_tensors["residual"]
271
272 full_num_tokens = positions.shape[0]
273 if self.use_sequence_parallel:
274 if envs.VLLM_MOE_SKIP_PADDING and is_forward_context_available():
275 forward_context = get_forward_context()
276 forward_context.is_padding = sp_padding_mask(
277 forward_context.is_padding, hidden_states
278 )
279 hidden_states = sp_shard(hidden_states)
280 if attn_in is not None:
281 attn_in = sp_shard(attn_in)
282 assert residual is None, "Currently, SP is not supported with PP"
283
284 aux_hidden_states = []
285 for idx, layer in enumerate(
286 islice(self.layers, self.start_layer, self.end_layer),
287 start=self.start_layer,
288 ):
289 if idx in self.aux_hidden_state_layers:
290 aux_hidden_states.append(
291 hidden_states if residual is None else hidden_states + residual
292 )
293 hidden_states, residual = layer(positions, hidden_states, residual, attn_in)
294 attn_in = None
295
296 if not get_pp_group().is_last_rank:
297 assert not self.use_sequence_parallel, (
298 "Currently, SP is not supported with PP"
299 )
300 return IntermediateTensors(
301 {"hidden_states": hidden_states, "residual": residual}
302 )
303
304 if self.use_sequence_parallel:
305 hidden_states, _ = self.norm(hidden_states, residual)
306 if aux_hidden_states:
307 hidden_size = hidden_states.shape[-1]
308 packed_hidden_states = torch.cat(
309 [hidden_states, *aux_hidden_states], dim=-1
310 )
311 packed_hidden_states = sp_all_gather(packed_hidden_states)
312 packed_hidden_states = packed_hidden_states[:full_num_tokens]
313 hidden_states, *aux_hidden_states = packed_hidden_states.split(
314 hidden_size, dim=-1
315 )
316 else:
317 hidden_states = sp_all_gather(hidden_states)[:full_num_tokens]
318 else:
319 hidden_states, _ = fused_allreduce_rms_norm(
320 hidden_states, residual, self.norm
321 )
322 if len(aux_hidden_states) > 0:
323 return hidden_states, aux_hidden_states
324 return hidden_states
325
326 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
327 # DSA-only: MLA (fused_qkv_a_proj) + the fused indexer wk/weights_proj +
328 # routed experts. No MHA (qkv_proj) or ROCm shared-expert-fusion paths.
329 stacked_params_mapping = [
330 ("gate_up_proj", "gate_proj", 0),
331 ("gate_up_proj", "up_proj", 1),
332 ("fused_qkv_a_proj", "q_a_proj", 0),
333 ("fused_qkv_a_proj", "kv_a_proj_with_mqa", 1),
334 ("wk_weights_proj", "wk", 0),
335 ("wk_weights_proj", "weights_proj", 1),
336 ]
337 expert_params_mapping = fused_moe_make_expert_params_mapping(
338 self,
339 ckpt_gate_proj_name="gate_proj",
340 ckpt_down_proj_name="down_proj",
341 ckpt_up_proj_name="up_proj",
342 num_experts=self.config.n_routed_experts,
343 num_redundant_experts=self.num_redundant_experts,
344 )
345
346 pp_missing_layer_names = get_pp_missing_layer_names(self)
347 params_dict = dict(self.named_parameters())
348 loaded_params: set[str] = set()
349 _pending_wk_fp8: dict = {}
350 for name, loaded_weight in weights:
351 if "rotary_emb.inv_freq" in name:
352 continue
353 # MTP / nextn layers are loaded by the MTP model, not here.
354 if get_spec_layer_idx_from_weight_name(self.config, name) is not None:
355 continue
356 if _try_load_fp8_indexer_wk(
357 name,
358 loaded_weight,
359 _pending_wk_fp8,
360 params_dict,
361 loaded_params,
362 pp_missing_layer_names,
363 ):
364 continue
365
366 for param_name, weight_name, shard_id in stacked_params_mapping:
367 if weight_name not in name:
368 continue
369 # Experts are handled below; skip here before the name rewrite.
370 if ("mlp.experts." in name) and name not in params_dict:
371 continue
372 name_mapped = name.replace(weight_name, param_name)
373 if (
374 param_name == "fused_qkv_a_proj"
375 ) and name_mapped not in params_dict:
376 continue
377 name = name_mapped
378 if name.endswith(".bias") and name not in params_dict:
379 continue
380 if is_pp_missing_parameter(name, self):
381 continue
382 param = params_dict[name]
383 param.weight_loader(param, loaded_weight, shard_id)
384 break
385 else:
386 is_expert_weight = False
387 for mapping in expert_params_mapping:
388 param_name, weight_name, expert_id, shard_id = mapping # type: ignore[assignment]
389 if weight_name not in name:
390 continue
391 is_expert_weight = True
392 name_mapped = name.replace(weight_name, param_name)
393 if is_pp_missing_parameter(name_mapped, self):
394 continue
395 param = params_dict[name_mapped]
396 weight_loader = typing.cast(
397 Callable[..., bool], param.weight_loader
398 )
399 success = weight_loader(
400 param,
401 loaded_weight,
402 name_mapped,
403 shard_id=shard_id,
404 expert_id=expert_id,
405 return_success=True,
406 )
407 if success:
408 name = name_mapped
409 break
410 else:
411 if is_expert_weight:
412 continue
413 if name.endswith(".bias") and name not in params_dict:
414 continue
415 name = maybe_remap_kv_scale_name(name, params_dict) # type: ignore[assignment]
416 if name is None:
417 continue
418 if is_pp_missing_parameter(name, self):
419 continue
420 param = params_dict[name]
421 loader = getattr(param, "weight_loader", default_weight_loader)
422 loader(param, loaded_weight)
423 loaded_params.add(name)
424 return loaded_params
425
426
427 class DeepseekV32ForCausalLM(DeepseekV2ForCausalLM):
428 """DSA causal LM — DeepSeek V2/V3 orchestration with the DSA backbone.
429
430 Serves DeepSeek V3.2 and any architecture reusing DSA (e.g. GLM-5.2).
431 """
432
433 model_cls = DeepseekV32Model
434
435 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
436 super().__init__(vllm_config=vllm_config, prefix=prefix)
437 if self.config.model_type == "glm_moe_dsa":
438 enable_glm52_low_latency_gemm(self, vllm_config.model_config.dtype)
439
440 def set_moe_parameters(self):
441 # Same as the base, but keyed on the MoE block type rather than the
442 # decoder-layer type (DeepseekV32DecoderLayer is a plain nn.Module).
443 self.num_expert_groups = getattr(self.config, "n_group", 1)
444 self.moe_layers = []
445 self.moe_mlp_layers = []
446 example_moe = None
447 for layer in self.model.layers:
448 if isinstance(layer, PPMissingLayer):
449 continue
450 if isinstance(layer.mlp, DeepseekV2MoE):
451 example_moe = layer.mlp
452 self.moe_mlp_layers.append(layer.mlp)
453 self.moe_layers.append(layer.mlp.experts)
454 self.extract_moe_parameters(example_moe)