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