main
py 388 lines 14.9 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 import typing
5 from collections.abc import Callable, Iterable
6 from itertools import islice
7
8 import torch
9
10 from vllm.config import VllmConfig
11 from vllm.distributed import get_pp_group
12 from vllm.model_executor.layers.fused_moe import (
13 fused_moe_make_expert_params_mapping,
14 )
15 from vllm.model_executor.layers.fused_moe.utils import (
16 is_model_fused_shared_expert_compatible,
17 )
18 from vllm.model_executor.layers.layernorm import RMSNorm
19 from vllm.model_executor.layers.vocab_parallel_embedding import (
20 VocabParallelEmbedding,
21 )
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.sequence import IntermediateTensors
42
43 from .rocm import DeepseekV32MLAAttention
44
45
46 class DeepseekV32DecoderLayer(torch.nn.Module):
47 def __init__(
48 self,
49 vllm_config: VllmConfig,
50 prefix: str,
51 config=None,
52 topk_indices_buffer: torch.Tensor | None = None,
53 ) -> None:
54 super().__init__()
55
56 if config is None:
57 config = vllm_config.model_config.hf_config
58 quant_config = vllm_config.quant_config
59 parallel_config = vllm_config.parallel_config
60
61 self.hidden_size = config.hidden_size
62 moe_layer_freq = getattr(config, "moe_layer_freq", 1)
63 layer_idx = int(prefix.split(sep=".")[-1])
64 self.layer_idx = layer_idx
65 self.use_mha = False
66
67 self.self_attn = DeepseekV32MLAAttention(
68 vllm_config=vllm_config,
69 config=config,
70 prefix=f"{prefix}.self_attn",
71 topk_indices_buffer=topk_indices_buffer,
72 )
73
74 if (
75 config.n_routed_experts is not None
76 and layer_idx >= config.first_k_dense_replace
77 and layer_idx % moe_layer_freq == 0
78 ):
79 self.mlp = DeepseekV2MoE(
80 config=config,
81 parallel_config=parallel_config,
82 quant_config=quant_config,
83 prefix=f"{prefix}.mlp",
84 )
85 self.mlp.experts.moe_config.skip_final_all_reduce = True
86 else:
87 self.mlp = DeepseekV2MLP(
88 hidden_size=config.hidden_size,
89 intermediate_size=config.intermediate_size,
90 hidden_act=config.hidden_act,
91 quant_config=quant_config,
92 prefix=f"{prefix}.mlp",
93 reduce_results=False,
94 )
95 self.input_layernorm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
96 self.post_attention_layernorm = RMSNorm(
97 config.hidden_size, eps=config.rms_norm_eps
98 )
99 self.routed_scaling_factor = getattr(config, "routed_scaling_factor", 1.0)
100
101 def forward(
102 self,
103 positions: torch.Tensor,
104 hidden_states: torch.Tensor,
105 residual: torch.Tensor | None,
106 ) -> tuple[torch.Tensor, torch.Tensor]:
107 if residual is None:
108 residual = hidden_states
109 hidden_states = self.input_layernorm(hidden_states)
110 else:
111 hidden_states, residual = fused_allreduce_rms_norm(
112 hidden_states, residual, self.input_layernorm
113 )
114 hidden_states = self.self_attn(positions=positions, hidden_states=hidden_states)
115 hidden_states, residual = fused_allreduce_rms_norm(
116 hidden_states, residual, self.post_attention_layernorm
117 )
118 hidden_states = self.mlp(hidden_states)
119 return hidden_states, residual
120
121
122 class DeepseekV32Model(torch.nn.Module):
123 fall_back_to_pt_during_load = False
124
125 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
126 super().__init__()
127
128 config = vllm_config.model_config.hf_config
129 quant_config = vllm_config.quant_config
130 self.config = config
131 from vllm.platforms import current_platform
132
133 self.device = current_platform.device_type
134
135 self.vocab_size = config.vocab_size
136 self.is_v32 = True
137 topk_indices_buffer = torch.empty(
138 vllm_config.scheduler_config.max_num_batched_tokens,
139 config.index_topk,
140 dtype=torch.int32,
141 device=self.device,
142 )
143
144 if get_pp_group().is_first_rank:
145 self.embed_tokens = VocabParallelEmbedding(
146 config.vocab_size,
147 config.hidden_size,
148 quant_config=quant_config,
149 prefix=f"{prefix}.embed_tokens",
150 )
151 else:
152 self.embed_tokens = PPMissingLayer()
153
154 self.start_layer, self.end_layer, self.layers = make_layers(
155 config.num_hidden_layers,
156 lambda prefix: DeepseekV32DecoderLayer(
157 vllm_config=vllm_config,
158 prefix=prefix,
159 topk_indices_buffer=topk_indices_buffer,
160 ),
161 prefix=f"{prefix}.layers",
162 )
163
164 self.is_fused_shared_expert_enabled = is_model_fused_shared_expert_compatible(
165 self.layers,
166 DeepseekV2MoE,
167 "mlp",
168 )
169
170 if get_pp_group().is_last_rank:
171 self.norm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
172 else:
173 self.norm = PPMissingLayer()
174 self.make_empty_intermediate_tensors = make_empty_intermediate_tensors_factory(
175 ["hidden_states", "residual"], config.hidden_size
176 )
177
178 self.aux_hidden_state_layers = tuple[int, ...]()
179 self.num_redundant_experts = (
180 vllm_config.parallel_config.eplb_config.num_redundant_experts
181 )
182
183 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
184 return self.embed_tokens(input_ids)
185
186 def forward(
187 self,
188 input_ids: torch.Tensor | None,
189 positions: torch.Tensor,
190 intermediate_tensors: IntermediateTensors | None = None,
191 inputs_embeds: torch.Tensor | None = None,
192 ) -> torch.Tensor | IntermediateTensors:
193 if get_pp_group().is_first_rank:
194 if inputs_embeds is not None:
195 hidden_states = inputs_embeds
196 else:
197 assert input_ids is not None
198 hidden_states = self.embed_input_ids(input_ids)
199 residual = None
200 else:
201 assert intermediate_tensors is not None
202 hidden_states = intermediate_tensors["hidden_states"]
203 residual = intermediate_tensors["residual"]
204
205 aux_hidden_states = []
206 for idx, layer in enumerate(
207 islice(self.layers, self.start_layer, self.end_layer),
208 start=self.start_layer,
209 ):
210 if idx in self.aux_hidden_state_layers:
211 aux_hidden_states.append(hidden_states + residual)
212 hidden_states, residual = layer(positions, hidden_states, residual)
213
214 if not get_pp_group().is_last_rank:
215 return IntermediateTensors(
216 {"hidden_states": hidden_states, "residual": residual}
217 )
218
219 hidden_states, _ = fused_allreduce_rms_norm(hidden_states, residual, self.norm)
220 if len(aux_hidden_states) > 0:
221 return hidden_states, aux_hidden_states
222 return hidden_states
223
224 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
225 stacked_params_mapping = [
226 ("gate_up_proj", "gate_proj", 0),
227 ("gate_up_proj", "up_proj", 1),
228 ("fused_qkv_a_proj", "q_a_proj", 0),
229 ("fused_qkv_a_proj", "kv_a_proj_with_mqa", 1),
230 ("wk_weights_proj", "wk", 0),
231 ("wk_weights_proj", "weights_proj", 1),
232 ]
233 expert_params_mapping = fused_moe_make_expert_params_mapping(
234 self,
235 ckpt_gate_proj_name="gate_proj",
236 ckpt_down_proj_name="down_proj",
237 ckpt_up_proj_name="up_proj",
238 num_experts=self.config.n_routed_experts
239 + (
240 self.config.n_shared_experts
241 if self.is_fused_shared_expert_enabled
242 else 0
243 ),
244 num_redundant_experts=self.num_redundant_experts,
245 )
246
247 pp_missing_layer_names = get_pp_missing_layer_names(self)
248 params_dict = dict(self.named_parameters())
249 loaded_params: set[str] = set()
250 _pending_wk_fp8: dict = {}
251 for name, loaded_weight in weights:
252 if "rotary_emb.inv_freq" in name:
253 continue
254 if get_spec_layer_idx_from_weight_name(self.config, name) is not None:
255 continue
256 is_fusion_moe_shared_experts_layer = (
257 self.is_fused_shared_expert_enabled and ("mlp.shared_experts" in name)
258 )
259 if _try_load_fp8_indexer_wk(
260 name,
261 loaded_weight,
262 _pending_wk_fp8,
263 params_dict,
264 loaded_params,
265 pp_missing_layer_names,
266 ):
267 continue
268
269 for param_name, weight_name, shard_id in stacked_params_mapping:
270 if weight_name not in name:
271 continue
272 if ("mlp.experts." in name) and name not in params_dict:
273 continue
274 if is_fusion_moe_shared_experts_layer:
275 continue
276 name_mapped = name.replace(weight_name, param_name)
277 if (
278 param_name == "fused_qkv_a_proj"
279 ) and name_mapped not in params_dict:
280 continue
281 name = name_mapped
282 if name.endswith(".bias") and name not in params_dict:
283 continue
284 if is_pp_missing_parameter(name, self):
285 continue
286 param = params_dict[name]
287 param.weight_loader(param, loaded_weight, shard_id)
288 break
289 else:
290 is_expert_weight = False
291
292 # When AITER fusion_shared_experts is enabled the checkpoint
293 # provides a single widened shared_experts tensor per layer with
294 # no expert index. Split it evenly and route the slices into the
295 # expert slots appended after the routed experts.
296 num_chunks = 1
297 if is_fusion_moe_shared_experts_layer:
298 num_chunks = getattr(self.config, "n_shared_experts", 1) or 1
299 # gate/up are ColumnParallel (dim 0), down is RowParallel (dim 1).
300 split_dim = (
301 1
302 if ("down_proj.weight" in name and loaded_weight.ndim > 1)
303 else 0
304 )
305 total = loaded_weight.shape[split_dim]
306 assert total % num_chunks == 0, (
307 f"Shared expert weight dim {total} "
308 f"not divisible by num_chunks {num_chunks}"
309 )
310 chunk_size = total // num_chunks
311
312 for j in range(num_chunks):
313 chunk_name = name
314 weight_to_load = loaded_weight
315
316 if is_fusion_moe_shared_experts_layer:
317 chunk_slice = slice(j * chunk_size, (j + 1) * chunk_size)
318 if loaded_weight.ndim == 1:
319 weight_to_load = loaded_weight[chunk_slice]
320 elif split_dim == 0:
321 weight_to_load = loaded_weight[chunk_slice, :]
322 else:
323 weight_to_load = loaded_weight[:, chunk_slice]
324 chunk_name = name.replace(
325 "mlp.shared_experts",
326 f"mlp.experts.{self.config.n_routed_experts + j}",
327 )
328
329 for mapping in expert_params_mapping:
330 param_name, weight_name, expert_id, shard_id = mapping # type: ignore[assignment]
331 if weight_name not in chunk_name:
332 continue
333 is_expert_weight = True
334 name_mapped = chunk_name.replace(weight_name, param_name)
335 if is_pp_missing_parameter(name_mapped, self):
336 continue
337 param = params_dict[name_mapped]
338 weight_loader = typing.cast(
339 Callable[..., bool], param.weight_loader
340 )
341 success = weight_loader(
342 param,
343 weight_to_load,
344 name_mapped,
345 shard_id=shard_id,
346 expert_id=expert_id,
347 return_success=True,
348 )
349 if success:
350 if not is_fusion_moe_shared_experts_layer:
351 name = name_mapped
352 else:
353 loaded_params.add(name_mapped)
354 break
355 else:
356 if is_expert_weight:
357 continue
358 if name.endswith(".bias") and name not in params_dict:
359 continue
360 name = maybe_remap_kv_scale_name(name, params_dict) # type: ignore[assignment]
361 if name is None:
362 continue
363 if is_pp_missing_parameter(name, self):
364 continue
365 param = params_dict[name]
366 loader = getattr(param, "weight_loader", default_weight_loader)
367 loader(param, loaded_weight)
368 if name is not None and not is_fusion_moe_shared_experts_layer:
369 loaded_params.add(name)
370 return loaded_params
371
372
373 class DeepseekV32ForCausalLM(DeepseekV2ForCausalLM):
374 model_cls = DeepseekV32Model
375
376 def set_moe_parameters(self):
377 self.num_expert_groups = getattr(self.config, "n_group", 1)
378 self.moe_layers = []
379 self.moe_mlp_layers = []
380 example_moe = None
381 for layer in self.model.layers:
382 if isinstance(layer, PPMissingLayer):
383 continue
384 if isinstance(layer.mlp, DeepseekV2MoE):
385 example_moe = layer.mlp
386 self.moe_mlp_layers.append(layer.mlp)
387 self.moe_layers.append(layer.mlp.experts)
388 self.extract_moe_parameters(example_moe)