main
py 443 lines 15.5 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 """Inference-only Bailing MoE v3 MTP model."""
4
5 from collections.abc import Callable, Iterable
6
7 import torch
8 import torch.nn as nn
9 from transformers.configuration_utils import PreTrainedConfig
10
11 from vllm.compilation.decorators import support_torch_compile
12 from vllm.config import VllmConfig
13 from vllm.distributed import get_pp_group
14 from vllm.model_executor.layers.fused_moe import (
15 fused_moe_make_expert_params_mapping,
16 )
17 from vllm.model_executor.layers.layernorm import RMSNorm
18 from vllm.model_executor.layers.linear import ReplicatedLinear
19 from vllm.model_executor.layers.logits_processor import LogitsProcessor
20 from vllm.model_executor.layers.vocab_parallel_embedding import (
21 ParallelLMHead,
22 VocabParallelEmbedding,
23 )
24 from vllm.model_executor.model_loader.weight_utils import (
25 default_weight_loader,
26 maybe_remap_kv_scale_name,
27 )
28 from vllm.model_executor.models.bailing_moe_v3 import (
29 BailingMoeV3ForCausalLM,
30 BailingMoeV3MLAAttention,
31 BailingMoeV3MoE,
32 _configure_ling_fp8_quant_config,
33 _maybe_pad_block_fp8_shared_expert_checkpoint_tensor,
34 _maybe_remap_ling_mxfp4_weight_names,
35 )
36 from vllm.model_executor.models.interfaces import (
37 MultiModalEmbeddings,
38 SupportsMultiModalEmbeddings,
39 SupportsPP,
40 )
41 from vllm.model_executor.models.utils import (
42 PPMissingLayer,
43 _merge_multimodal_embeddings,
44 is_pp_missing_parameter,
45 maybe_prefix,
46 )
47 from vllm.sequence import IntermediateTensors
48
49
50 def _get_draft_hf_config(vllm_config: VllmConfig) -> PreTrainedConfig:
51 speculative_config = vllm_config.speculative_config
52 if speculative_config is not None:
53 draft_model_config = speculative_config.draft_model_config
54 if draft_model_config is not None:
55 return draft_model_config.hf_config
56 return vllm_config.model_config.hf_config
57
58
59 class BailingMoeV3MTPSharedHead(nn.Module):
60 def __init__(
61 self,
62 config: PreTrainedConfig,
63 prefix: str,
64 vllm_config: VllmConfig,
65 ) -> None:
66 super().__init__()
67 self.head = ParallelLMHead(
68 config.vocab_size,
69 config.hidden_size,
70 quant_config=vllm_config.quant_config,
71 prefix=maybe_prefix(prefix, "head"),
72 )
73
74 def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
75 return hidden_states
76
77
78 class BailingMoeV3MultiTokenPredictorLayer(nn.Module):
79 def __init__(
80 self,
81 *,
82 vllm_config: VllmConfig,
83 prefix: str,
84 layer_id: int,
85 ) -> None:
86 super().__init__()
87 config = _get_draft_hf_config(vllm_config)
88 self.config = config
89 self.layer_id = layer_id
90 self.enorm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
91 self.hnorm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
92 self.eh_proj = ReplicatedLinear(
93 config.hidden_size * 2,
94 config.hidden_size,
95 bias=False,
96 quant_config=vllm_config.quant_config,
97 prefix=maybe_prefix(prefix, "eh_proj"),
98 return_bias=False,
99 )
100 self.input_layernorm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
101 self.self_attn = BailingMoeV3MLAAttention(
102 config,
103 quant_config=vllm_config.quant_config,
104 layer_id=layer_id,
105 prefix=maybe_prefix(prefix, "self_attn"),
106 cache_config=vllm_config.cache_config,
107 )
108 self.post_attention_layernorm = RMSNorm(
109 config.hidden_size, eps=config.rms_norm_eps
110 )
111 self.mlp = BailingMoeV3MoE(
112 config,
113 quant_config=vllm_config.quant_config,
114 layer_id=layer_id,
115 prefix=maybe_prefix(prefix, "mlp"),
116 )
117 self.final_layernorm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
118 self.shared_head = BailingMoeV3MTPSharedHead(
119 config,
120 maybe_prefix(prefix, "shared_head"),
121 vllm_config,
122 )
123
124 def forward(
125 self,
126 input_ids: torch.Tensor,
127 positions: torch.Tensor,
128 previous_hidden_states: torch.Tensor,
129 inputs_embeds: torch.Tensor | None = None,
130 spec_step_index: int = 0,
131 ) -> torch.Tensor:
132 del input_ids, spec_step_index
133 assert inputs_embeds is not None
134 inputs_embeds = self.enorm(inputs_embeds)
135 previous_hidden_states = self.hnorm(previous_hidden_states)
136
137 hidden_states = self.eh_proj(
138 torch.cat([inputs_embeds, previous_hidden_states], dim=-1)
139 )
140 residual = hidden_states
141 hidden_states = self.input_layernorm(hidden_states)
142
143 hidden_states = self.self_attn(hidden_states, positions)
144 hidden_states = residual + hidden_states
145
146 residual = hidden_states
147 hidden_states = self.post_attention_layernorm(hidden_states)
148 hidden_states = self.mlp(hidden_states)
149 hidden_states = residual + hidden_states.to(residual.device)
150 return self.final_layernorm(hidden_states)
151
152
153 class BailingMoeV3MultiTokenPredictor(nn.Module):
154 def __init__(
155 self,
156 *,
157 vllm_config: VllmConfig,
158 prefix: str = "",
159 ) -> None:
160 super().__init__()
161 config = _get_draft_hf_config(vllm_config)
162 self.mtp_start_layer_idx = config.num_hidden_layers
163 self.num_mtp_layers = config.num_nextn_predict_layers
164 self.layers = nn.ModuleDict(
165 {
166 str(idx): BailingMoeV3MultiTokenPredictorLayer(
167 vllm_config=vllm_config,
168 prefix=f"{prefix}.layers.{idx}",
169 layer_id=idx,
170 )
171 for idx in range(
172 self.mtp_start_layer_idx,
173 self.mtp_start_layer_idx + self.num_mtp_layers,
174 )
175 }
176 )
177 if get_pp_group().is_first_rank:
178 self.embed_tokens = VocabParallelEmbedding(
179 config.vocab_size,
180 config.hidden_size,
181 org_num_embeddings=config.vocab_size,
182 prefix=maybe_prefix(prefix, "embed_tokens"),
183 )
184 else:
185 self.embed_tokens = PPMissingLayer()
186 self.logits_processor = LogitsProcessor(config.vocab_size)
187
188 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
189 return self.embed_tokens(input_ids)
190
191 def forward(
192 self,
193 input_ids: torch.Tensor,
194 positions: torch.Tensor,
195 previous_hidden_states: torch.Tensor,
196 inputs_embeds: torch.Tensor | None = None,
197 spec_step_idx: int = 0,
198 ) -> torch.Tensor:
199 if inputs_embeds is None:
200 inputs_embeds = self.embed_tokens(input_ids)
201 current_step_idx = spec_step_idx % self.num_mtp_layers
202 return self.layers[str(self.mtp_start_layer_idx + current_step_idx)](
203 input_ids,
204 positions,
205 previous_hidden_states,
206 inputs_embeds,
207 current_step_idx,
208 )
209
210 def compute_logits(
211 self,
212 hidden_states: torch.Tensor,
213 spec_step_idx: int = 0,
214 lm_head: nn.Module | None = None,
215 ) -> torch.Tensor:
216 current_step_idx = spec_step_idx % self.num_mtp_layers
217 mtp_layer = self.layers[str(self.mtp_start_layer_idx + current_step_idx)]
218 head = lm_head if lm_head is not None else mtp_layer.shared_head.head
219 return self.logits_processor(head, mtp_layer.shared_head(hidden_states))
220
221
222 @support_torch_compile(
223 dynamic_arg_dims={
224 "input_ids": 0,
225 "positions": -1,
226 "hidden_states": 0,
227 "intermediate_tensors": 0,
228 "inputs_embeds": 0,
229 }
230 )
231 class BailingMoeV3MTPModel(nn.Module, SupportsPP, SupportsMultiModalEmbeddings):
232 hf_to_vllm_mapper = BailingMoeV3ForCausalLM.hf_to_vllm_mapper
233
234 packed_modules_mapping = {
235 "gate_up_proj": ["gate_proj", "up_proj"],
236 "fused_qkv_a_proj": ["q_a_proj", "kv_a_proj_with_mqa"],
237 }
238
239 def __init__(
240 self,
241 *,
242 vllm_config: VllmConfig,
243 prefix: str = "",
244 ) -> None:
245 super().__init__()
246 self.config = _get_draft_hf_config(vllm_config)
247 self.quant_config = vllm_config.quant_config
248 _configure_ling_fp8_quant_config(self.quant_config, self.config)
249 self.model = BailingMoeV3MultiTokenPredictor(
250 vllm_config=vllm_config,
251 prefix=maybe_prefix(prefix, "model"),
252 )
253 self.lm_head: nn.Module | None = None
254
255 def share_lm_head(self, lm_head: nn.Module) -> None:
256 self.lm_head = lm_head
257
258 def embed_input_ids(
259 self,
260 input_ids: torch.Tensor,
261 multimodal_embeddings: MultiModalEmbeddings | None = None,
262 *,
263 is_multimodal: torch.Tensor | None = None,
264 ) -> torch.Tensor:
265 inputs_embeds = self.model.embed_input_ids(input_ids)
266 if multimodal_embeddings is None or len(multimodal_embeddings) == 0:
267 return inputs_embeds
268 assert is_multimodal is not None
269 return _merge_multimodal_embeddings(
270 inputs_embeds=inputs_embeds,
271 multimodal_embeddings=multimodal_embeddings,
272 is_multimodal=is_multimodal,
273 )
274
275 def forward(
276 self,
277 input_ids: torch.Tensor | None,
278 positions: torch.Tensor,
279 hidden_states: torch.Tensor | None = None,
280 intermediate_tensors: IntermediateTensors | None = None,
281 inputs_embeds: torch.Tensor | None = None,
282 spec_step_idx: int = 0,
283 ) -> torch.Tensor:
284 assert hidden_states is not None
285 del intermediate_tensors
286 return self.model(
287 input_ids,
288 positions,
289 hidden_states,
290 inputs_embeds,
291 spec_step_idx,
292 )
293
294 def compute_logits(
295 self,
296 hidden_states: torch.Tensor,
297 spec_step_idx: int = 0,
298 ) -> torch.Tensor:
299 return self.model.compute_logits(hidden_states, spec_step_idx, self.lm_head)
300
301 def get_expert_mapping(self) -> list[tuple[str, str, int, str]]:
302 return fused_moe_make_expert_params_mapping(
303 self,
304 ckpt_gate_proj_name="gate_proj",
305 ckpt_down_proj_name="down_proj",
306 ckpt_up_proj_name="up_proj",
307 num_experts=self.config.num_experts,
308 )
309
310 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
311 weights = self.hf_to_vllm_mapper.apply(weights)
312 weights = _maybe_remap_ling_mxfp4_weight_names(weights, self.quant_config)
313 stacked_params_mapping = [
314 (".fused_qkv_a_proj", ".q_a_proj", 0),
315 (".fused_qkv_a_proj", ".kv_a_proj_with_mqa", 1),
316 (".gate_up_proj", ".gate_proj", 0),
317 (".gate_up_proj", ".up_proj", 1),
318 ]
319 expert_params_mapping = list(self.get_expert_mapping())
320 params_dict = dict(self.named_parameters(remove_duplicate=False))
321 loaded_params: set[str] = set()
322 loaded_mtp_layers: set[int] = set()
323
324 def load_param(
325 name: str,
326 loaded_weight: torch.Tensor,
327 shard_id=None,
328 ) -> bool:
329 remapped_name = maybe_remap_kv_scale_name(name, params_dict)
330 if remapped_name is None:
331 return False
332 name = remapped_name
333 if name not in params_dict or is_pp_missing_parameter(name, self):
334 return False
335
336 param = params_dict[name]
337 weight_loader: Callable[..., None] = getattr(
338 param, "weight_loader", default_weight_loader
339 )
340 if shard_id is None:
341 weight_loader(param, loaded_weight)
342 elif isinstance(shard_id, int):
343 weight_loader(param, loaded_weight, shard_id)
344 else:
345 weight_loader(
346 param,
347 loaded_weight,
348 name,
349 expert_id=shard_id[0],
350 shard_id=shard_id[1],
351 )
352 loaded_params.add(name)
353 return True
354
355 def get_spec_layer_idx(name: str) -> int | None:
356 if not name.startswith("model.layers."):
357 return None
358 try:
359 layer_idx = int(name.split("model.layers.", 1)[1].split(".", 1)[0])
360 except (IndexError, ValueError):
361 return None
362 mtp_idx = layer_idx - self.config.num_hidden_layers
363 if 0 <= mtp_idx < self.config.num_nextn_predict_layers:
364 return layer_idx
365 return None
366
367 def normalize_name(name: str) -> str:
368 return name.replace(
369 "mlp.gate.e_score_correction_bias",
370 "mlp.gate.expert_bias",
371 )
372
373 def load_lm_head(loaded_weight: torch.Tensor) -> None:
374 for layer_idx in range(
375 self.model.mtp_start_layer_idx,
376 self.model.mtp_start_layer_idx + self.model.num_mtp_layers,
377 ):
378 name = f"model.layers.{layer_idx}.shared_head.head.weight"
379 load_param(name, loaded_weight)
380
381 for name, loaded_weight in weights:
382 if "rotary_emb.inv_freq" in name:
383 continue
384
385 if name == "model.word_embeddings.weight":
386 load_param("model.embed_tokens.weight", loaded_weight)
387 continue
388 if name == "lm_head.weight":
389 load_lm_head(loaded_weight)
390 continue
391
392 spec_layer = get_spec_layer_idx(name)
393 if spec_layer is None:
394 continue
395 name = normalize_name(name)
396 loaded_weight = _maybe_pad_block_fp8_shared_expert_checkpoint_tensor(
397 self.quant_config,
398 self.config,
399 name,
400 loaded_weight,
401 )
402
403 loaded = False
404 for param_name, weight_name, stacked_shard_id in stacked_params_mapping:
405 if weight_name not in name:
406 continue
407 if "mlp.experts." in name and name not in params_dict:
408 continue
409 mapped_name = name.replace(weight_name, param_name)
410 if load_param(mapped_name, loaded_weight, stacked_shard_id):
411 loaded = True
412 break
413 if loaded:
414 loaded_mtp_layers.add(spec_layer)
415 continue
416
417 if "mlp.experts" in name:
418 for mapping in expert_params_mapping:
419 param_name, weight_name, expert_id, shard_id = mapping
420 if weight_name not in name:
421 continue
422 mapped_name = name.replace(weight_name, param_name)
423 if load_param(mapped_name, loaded_weight, (expert_id, shard_id)):
424 loaded = True
425 break
426 if loaded:
427 loaded_mtp_layers.add(spec_layer)
428 continue
429
430 if load_param(name, loaded_weight):
431 loaded_mtp_layers.add(spec_layer)
432
433 for layer_idx in range(
434 self.model.mtp_start_layer_idx,
435 self.model.mtp_start_layer_idx + self.model.num_mtp_layers,
436 ):
437 if layer_idx not in loaded_mtp_layers:
438 raise ValueError(
439 f"Bailing V3 MTP speculative decoding layer {layer_idx} "
440 "weights are missing from checkpoint. Use a checkpoint that "
441 "includes MTP layer weights, or disable speculative decoding."
442 )
443 return loaded_params