main
py 633 lines 22.5 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 # Copyright 2025 The Baidu team.
5 # Copyright 2023 The vLLM team.
6 # Copyright 2022 EleutherAI and the HuggingFace Inc. team. All rights reserved.
7 #
8 # This code is based on EleutherAI's GPT-NeoX library and the GPT-NeoX
9 # and OPT implementations in this library. It has been modified from its
10 # original forms to accommodate minor architectural differences compared
11 # to GPT-NeoX and OPT used by the Meta AI team that trained the model.
12 #
13 # Licensed under the Apache License, Version 2.0 (the "License");
14 # you may not use this file except in compliance with the License.
15 # You may obtain a copy of the License at
16 #
17 # http://www.apache.org/licenses/LICENSE-2.0
18 #
19 # Unless required by applicable law or agreed to in writing, software
20 # distributed under the License is distributed on an "AS IS" BASIS,
21 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 # See the License for the specific language governing permissions and
23 # limitations under the License.
24 """Inference-only ErineMoE model compatible with HuggingFace weights."""
25
26 from collections.abc import Iterable
27 from itertools import islice
28 from typing import Any
29
30 import torch
31 from torch import nn
32 from transformers import PreTrainedConfig
33
34 from vllm.compilation.decorators import support_torch_compile
35 from vllm.config import CacheConfig, VllmConfig, get_current_vllm_config
36 from vllm.distributed import (
37 get_ep_group,
38 get_pp_group,
39 get_tensor_model_parallel_world_size,
40 )
41 from vllm.logger import init_logger
42 from vllm.model_executor.layers.activation import SiluAndMul
43 from vllm.model_executor.layers.attention import Attention
44 from vllm.model_executor.layers.fused_moe import (
45 FusedMoEFactory,
46 GateLinear,
47 MoERunner,
48 )
49 from vllm.model_executor.layers.layernorm import RMSNorm
50 from vllm.model_executor.layers.linear import (
51 MergedColumnParallelLinear,
52 QKVParallelLinear,
53 RowParallelLinear,
54 )
55 from vllm.model_executor.layers.logits_processor import LogitsProcessor
56 from vllm.model_executor.layers.quantization import QuantizationConfig
57 from vllm.model_executor.layers.rotary_embedding import get_rope
58 from vllm.model_executor.layers.vocab_parallel_embedding import (
59 ParallelLMHead,
60 VocabParallelEmbedding,
61 )
62 from vllm.sequence import IntermediateTensors
63 from vllm.transformers_utils.config import set_default_rope_theta
64
65 from .interfaces import MixtureOfExperts, SupportsLoRA, SupportsPP
66 from .utils import (
67 AutoWeightsLoader,
68 PPMissingLayer,
69 WeightsMapper,
70 extract_layer_index,
71 make_empty_intermediate_tensors_factory,
72 make_layers,
73 maybe_prefix,
74 )
75
76 logger = init_logger(__name__)
77
78
79 class Ernie4_5_MoeMLP(nn.Module):
80 def __init__(
81 self,
82 hidden_size: int,
83 intermediate_size: int,
84 hidden_act: str,
85 use_bias: bool = False,
86 quant_config: QuantizationConfig | None = None,
87 reduce_results: bool = True,
88 prefix: str = "",
89 ) -> None:
90 super().__init__()
91 self.gate_up_proj = MergedColumnParallelLinear(
92 hidden_size,
93 [intermediate_size] * 2,
94 bias=use_bias,
95 quant_config=quant_config,
96 prefix=f"{prefix}.gate_up_proj",
97 )
98 self.down_proj = RowParallelLinear(
99 intermediate_size,
100 hidden_size,
101 bias=use_bias,
102 quant_config=quant_config,
103 reduce_results=reduce_results,
104 prefix=f"{prefix}.down_proj",
105 )
106 if hidden_act != "silu":
107 raise ValueError(
108 f"Unsupported activation: {hidden_act}. Only silu is supported for now."
109 )
110 self.act_fn = SiluAndMul()
111
112 def forward(self, x):
113 gate_up, _ = self.gate_up_proj(x)
114 x = self.act_fn(gate_up)
115 x, _ = self.down_proj(x)
116 return x
117
118
119 class Ernie4_5_MoeMoE(nn.Module):
120 def __init__(
121 self,
122 config: PreTrainedConfig,
123 quant_config: QuantizationConfig | None = None,
124 prefix: str = "",
125 enable_eplb: bool = False,
126 ):
127 super().__init__()
128
129 layer_idx = extract_layer_index(prefix)
130 self.layer_idx = layer_idx
131 self.tp_size = get_tensor_model_parallel_world_size()
132
133 self.moe_num_shared_experts = getattr(config, "moe_num_shared_experts", 0)
134 self.ep_group = get_ep_group().device_group
135 self.ep_size = self.ep_group.size()
136 self.n_routed_experts: int = config.moe_num_experts
137 self.n_shared_experts: int = self.moe_num_shared_experts
138
139 # Load balancing settings.
140 vllm_config = get_current_vllm_config()
141 eplb_config = vllm_config.parallel_config.eplb_config
142 self.enable_eplb = enable_eplb
143
144 self.n_redundant_experts = eplb_config.num_redundant_experts
145 self.n_logical_experts = self.n_routed_experts
146 self.n_physical_experts = self.n_logical_experts + self.n_redundant_experts
147 self.n_local_physical_experts = self.n_physical_experts // self.ep_size
148 self.has_shared_experts = getattr(config, "moe_num_shared_experts", 0) > 0
149
150 if self.tp_size > config.moe_num_experts:
151 raise ValueError(
152 f"Tensor parallel size {self.tp_size} is greater than "
153 f"the number of experts {config.moe_num_experts}."
154 )
155
156 self.gate = GateLinear(
157 config.hidden_size,
158 config.moe_num_experts,
159 out_dtype=torch.float32,
160 params_dtype=torch.float32,
161 prefix=f"{prefix}.gate",
162 )
163
164 self.gate.e_score_correction_bias = nn.Parameter(
165 torch.empty(config.moe_num_experts, dtype=torch.float32)
166 )
167
168 self.shared_experts: Ernie4_5_MoeMLP | None
169 if self.has_shared_experts:
170 intermediate_size = (
171 config.moe_intermediate_size * config.moe_num_shared_experts
172 )
173 self.shared_experts = Ernie4_5_MoeMLP(
174 hidden_size=config.hidden_size,
175 intermediate_size=intermediate_size,
176 hidden_act=config.hidden_act,
177 quant_config=quant_config,
178 prefix=f"{prefix}.shared_experts",
179 reduce_results=False,
180 )
181 else:
182 self.shared_experts = None
183
184 self.experts = FusedMoEFactory(
185 shared_experts=self.shared_experts,
186 num_experts=config.moe_num_experts,
187 top_k=config.moe_k,
188 hidden_size=config.hidden_size,
189 intermediate_size=config.moe_intermediate_size,
190 renormalize=True,
191 quant_config=quant_config,
192 prefix=f"{prefix}.experts",
193 e_score_correction_bias=self.gate.e_score_correction_bias,
194 enable_eplb=self.enable_eplb,
195 num_redundant_experts=self.n_redundant_experts,
196 router_logits_dtype=torch.float32,
197 )
198
199 def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
200 orig_shape = hidden_states.shape
201 hidden_dim = hidden_states.shape[-1]
202 hidden_states = hidden_states.view(-1, hidden_dim)
203
204 router_logits, _ = self.gate(hidden_states)
205
206 final_hidden_states = self.experts(
207 hidden_states=hidden_states, router_logits=router_logits
208 )
209
210 return final_hidden_states.view(orig_shape)
211
212
213 class Ernie4_5_MoeAttention(nn.Module):
214 def __init__(
215 self,
216 hidden_size: int,
217 num_heads: int,
218 num_kv_heads: int,
219 rope_parameters: dict[str, Any],
220 head_dim: int | None = None,
221 max_position_embeddings: int = 131072,
222 rms_norm_eps: float = 1e-05,
223 qkv_bias: bool = False,
224 cache_config: CacheConfig | None = None,
225 quant_config: QuantizationConfig | None = None,
226 prefix: str = "",
227 ) -> None:
228 super().__init__()
229 layer_idx = extract_layer_index(prefix) if len(prefix) > 0 else 0
230 self.layer_idx = layer_idx
231 self.hidden_size = hidden_size
232 tp_size = get_tensor_model_parallel_world_size()
233 self.total_num_heads = num_heads
234 assert self.total_num_heads % tp_size == 0
235 self.num_heads = self.total_num_heads // tp_size
236
237 self.total_num_kv_heads = num_kv_heads
238 if self.total_num_kv_heads >= tp_size:
239 # Number of KV heads is greater than TP size, so we partition
240 # the KV heads across multiple tensor parallel GPUs.
241 assert self.total_num_kv_heads % tp_size == 0
242 else:
243 # Number of KV heads is less than TP size, so we replicate
244 # the KV heads across multiple tensor parallel GPUs.
245 assert tp_size % self.total_num_kv_heads == 0
246 self.num_kv_heads = max(1, self.total_num_kv_heads // tp_size)
247 self.head_dim = head_dim or (hidden_size // self.total_num_heads)
248
249 self.q_size = self.num_heads * self.head_dim
250 self.kv_size = self.num_kv_heads * self.head_dim
251 self.scaling = self.head_dim**-0.5
252 self.max_position_embeddings = max_position_embeddings
253
254 self.qkv_proj = QKVParallelLinear(
255 hidden_size,
256 self.head_dim,
257 self.total_num_heads,
258 self.total_num_kv_heads,
259 bias=qkv_bias,
260 quant_config=quant_config,
261 prefix=f"{prefix}.qkv_proj",
262 )
263
264 self.o_proj = RowParallelLinear(
265 self.total_num_heads * self.head_dim,
266 hidden_size,
267 bias=False,
268 quant_config=quant_config,
269 prefix=f"{prefix}.o_proj",
270 )
271
272 self.rotary_emb = get_rope(
273 self.head_dim,
274 max_position=max_position_embeddings,
275 rope_parameters=rope_parameters,
276 is_neox_style=False,
277 )
278 self.attn = Attention(
279 self.num_heads,
280 self.head_dim,
281 self.scaling,
282 num_kv_heads=self.num_kv_heads,
283 cache_config=cache_config,
284 quant_config=quant_config,
285 prefix=f"{prefix}.attn",
286 )
287
288 def forward(
289 self,
290 positions: torch.Tensor,
291 hidden_states: torch.Tensor,
292 ) -> torch.Tensor:
293 qkv, _ = self.qkv_proj(hidden_states)
294
295 q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1)
296 q, k = self.rotary_emb(positions, q, k)
297
298 # Attention
299 attn_output = self.attn(q, k, v)
300 # Output projection
301 output, _ = self.o_proj(attn_output)
302 return output
303
304
305 class Ernie4_5_MoeDecoderLayer(nn.Module):
306 def __init__(
307 self,
308 config: PreTrainedConfig,
309 cache_config: CacheConfig | None = None,
310 quant_config: QuantizationConfig | None = None,
311 prefix: str = "",
312 enable_eplb: bool = False,
313 ) -> None:
314 super().__init__()
315 self.hidden_size = config.hidden_size
316 set_default_rope_theta(config, default_theta=500000)
317 max_position_embeddings = getattr(config, "max_position_embeddings", 131072)
318 self.self_attn = Ernie4_5_MoeAttention(
319 hidden_size=self.hidden_size,
320 num_heads=config.num_attention_heads,
321 num_kv_heads=config.num_key_value_heads,
322 head_dim=getattr(config, "head_dim", None),
323 rope_parameters=config.rope_parameters,
324 max_position_embeddings=max_position_embeddings,
325 rms_norm_eps=config.rms_norm_eps,
326 qkv_bias=getattr(config, "use_bias", False),
327 cache_config=cache_config,
328 quant_config=quant_config,
329 prefix=f"{prefix}.self_attn",
330 )
331
332 layer_idx = extract_layer_index(prefix)
333 self.layer_idx = layer_idx
334
335 # MoE
336 moe_num_experts = getattr(config, "moe_num_experts", 0)
337 moe_layer_start_index = getattr(config, "moe_layer_start_index", 0)
338 moe_layer_end_index = getattr(
339 config, "moe_layer_end_index", config.num_hidden_layers - 1
340 )
341 moe_layer_interval = getattr(config, "moe_layer_interval", 1)
342 use_moe = getattr(config, "use_moe", moe_num_experts > 0)
343
344 if (
345 use_moe
346 and ((layer_idx + 1) % moe_layer_interval == 0)
347 and layer_idx >= moe_layer_start_index
348 and layer_idx <= moe_layer_end_index
349 ):
350 self.mlp = Ernie4_5_MoeMoE(
351 config=config,
352 quant_config=quant_config,
353 prefix=f"{prefix}.mlp",
354 enable_eplb=enable_eplb,
355 )
356 else:
357 self.mlp = Ernie4_5_MoeMLP(
358 hidden_size=config.hidden_size,
359 intermediate_size=config.intermediate_size,
360 hidden_act=config.hidden_act,
361 use_bias=getattr(config, "use_bias", False),
362 quant_config=quant_config,
363 prefix=f"{prefix}.mlp",
364 )
365
366 self.input_layernorm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
367 self.post_attention_layernorm = RMSNorm(
368 config.hidden_size, eps=config.rms_norm_eps
369 )
370
371 def forward(
372 self,
373 positions: torch.Tensor,
374 hidden_states: torch.Tensor,
375 residual: torch.Tensor | None,
376 ) -> torch.Tensor:
377 # Self Attention
378 if residual is None:
379 residual = hidden_states
380 hidden_states = self.input_layernorm(hidden_states)
381 else:
382 hidden_states, residual = self.input_layernorm(hidden_states, residual)
383
384 hidden_states = self.self_attn(
385 positions=positions,
386 hidden_states=hidden_states,
387 )
388
389 # Fully Connected
390 hidden_states, residual = self.post_attention_layernorm(hidden_states, residual)
391
392 hidden_states = self.mlp(hidden_states)
393
394 return hidden_states, residual
395
396
397 @support_torch_compile
398 class Ernie4_5_MoeModel(nn.Module):
399 hf_to_vllm_mapper = WeightsMapper(
400 orig_to_new_stacked={
401 ".q_proj": (".qkv_proj", "q"),
402 ".k_proj": (".qkv_proj", "k"),
403 ".v_proj": (".qkv_proj", "v"),
404 # .experts.gate_up_proj must be handled by MoERunner.load_weights for EP
405 ".mlp.gate_proj": (".mlp.gate_up_proj", 0),
406 ".mlp.up_proj": (".mlp.gate_up_proj", 1),
407 ".shared_experts.gate_proj": (".shared_experts.gate_up_proj", 0),
408 ".shared_experts.up_proj": (".shared_experts.gate_up_proj", 1),
409 },
410 orig_to_new_substr={"mtp": None},
411 )
412
413 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
414 super().__init__()
415
416 config = vllm_config.model_config.hf_config
417 cache_config = vllm_config.cache_config
418 quant_config = vllm_config.quant_config
419
420 self.vocab_size = config.vocab_size
421 self.config = config
422 parallel_config = vllm_config.parallel_config
423 eplb_config = parallel_config.eplb_config
424 enable_eplb = parallel_config.enable_eplb
425
426 self.num_redundant_experts = eplb_config.num_redundant_experts
427
428 if get_pp_group().is_first_rank:
429 self.embed_tokens = VocabParallelEmbedding(
430 config.vocab_size,
431 config.hidden_size,
432 quant_config=quant_config,
433 prefix=f"{prefix}.embed_tokens",
434 )
435 else:
436 self.embed_tokens = PPMissingLayer()
437
438 self.start_layer, self.end_layer, self.layers = make_layers(
439 config.num_hidden_layers,
440 lambda prefix: Ernie4_5_MoeDecoderLayer(
441 config=config,
442 cache_config=cache_config,
443 quant_config=quant_config,
444 prefix=prefix,
445 enable_eplb=enable_eplb,
446 ),
447 prefix=f"{prefix}.layers",
448 )
449
450 if get_pp_group().is_last_rank:
451 self.norm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
452 else:
453 self.norm = PPMissingLayer()
454
455 self.make_empty_intermediate_tensors = make_empty_intermediate_tensors_factory(
456 ["hidden_states", "residual"], config.hidden_size
457 )
458
459 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
460 return self.embed_tokens(input_ids)
461
462 def forward(
463 self,
464 input_ids: torch.Tensor | None,
465 positions: torch.Tensor,
466 intermediate_tensors: IntermediateTensors | None = None,
467 inputs_embeds: torch.Tensor | None = None,
468 ) -> torch.Tensor | IntermediateTensors:
469 if get_pp_group().is_first_rank:
470 if inputs_embeds is not None:
471 hidden_states = inputs_embeds
472 else:
473 hidden_states = self.embed_input_ids(input_ids)
474 residual = None
475 else:
476 assert intermediate_tensors is not None
477 hidden_states = intermediate_tensors["hidden_states"]
478 residual = intermediate_tensors["residual"]
479
480 for layer in islice(self.layers, self.start_layer, self.end_layer):
481 hidden_states, residual = layer(positions, hidden_states, residual)
482
483 if not get_pp_group().is_last_rank:
484 return IntermediateTensors(
485 {"hidden_states": hidden_states, "residual": residual}
486 )
487
488 hidden_states, _ = self.norm(hidden_states, residual)
489
490 return hidden_states
491
492 def _preprocess(
493 self, weights: Iterable[tuple[str, torch.Tensor]]
494 ) -> Iterable[tuple[str, torch.Tensor]]:
495 for name, loaded_weight in weights:
496 # moe_statics.e_score_correction_bias is stored with a leading
497 # singleton dim and under a different module name.
498 if "e_score_correction_bias" in name:
499 name = name.replace("moe_statics", "gate")
500 loaded_weight = loaded_weight.squeeze(0)
501 yield name, loaded_weight
502
503 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
504 loader = AutoWeightsLoader(self)
505 return loader.load_weights(
506 self._preprocess(weights), mapper=self.hf_to_vllm_mapper
507 )
508
509
510 class Ernie4_5_MoeForCausalLM(nn.Module, SupportsPP, SupportsLoRA, MixtureOfExperts):
511 packed_modules_mapping = {
512 "qkv_proj": [
513 "q_proj",
514 "k_proj",
515 "v_proj",
516 ],
517 "gate_up_proj": [
518 "gate_proj",
519 "up_proj",
520 ],
521 }
522
523 fall_back_to_pt_during_load = False
524
525 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
526 super().__init__()
527 config = vllm_config.model_config.hf_config
528 quant_config = vllm_config.quant_config
529 self.config = config
530 self.quant_config = quant_config
531 self.model = Ernie4_5_MoeModel(
532 vllm_config=vllm_config, prefix=maybe_prefix(prefix, "model")
533 )
534
535 if get_pp_group().is_last_rank:
536 self.lm_head = ParallelLMHead(
537 config.vocab_size,
538 config.hidden_size,
539 quant_config=quant_config,
540 prefix=maybe_prefix(prefix, "lm_head"),
541 )
542 else:
543 self.lm_head = PPMissingLayer()
544
545 if self.config.tie_word_embeddings:
546 self.lm_head = self.lm_head.tie_weights(self.model.embed_tokens)
547 self.logits_processor = LogitsProcessor(config.vocab_size)
548 self.make_empty_intermediate_tensors = (
549 self.model.make_empty_intermediate_tensors
550 )
551
552 # Set MoE hyperparameters
553 moe_layers_indices = [
554 i
555 for i in range(config.num_hidden_layers)
556 if (
557 i >= config.moe_layer_start_index
558 and i <= config.moe_layer_end_index
559 and (i + 1) % config.moe_layer_interval == 0
560 )
561 ]
562 self.num_moe_layers = len(moe_layers_indices)
563 self.num_expert_groups = 1
564
565 self.moe_layers: list[MoERunner] = []
566 example_moe = None
567 for layer in self.model.layers:
568 if isinstance(layer, PPMissingLayer):
569 continue
570
571 assert isinstance(layer, Ernie4_5_MoeDecoderLayer)
572 if isinstance(layer.mlp, Ernie4_5_MoeMoE):
573 example_moe = layer.mlp
574 self.moe_layers.append(layer.mlp.experts)
575
576 if example_moe is None:
577 logger.warning("No Ernie4_5_MoeMoE layer found in model.layers.")
578 self.num_logical_experts = 0
579 self.num_physical_experts = 0
580 self.num_local_physical_experts = 0
581 self.num_routed_experts = 0
582 self.num_shared_experts = 0
583 self.num_redundant_experts = 0
584 else:
585 self.num_logical_experts = example_moe.n_logical_experts
586 self.num_physical_experts = example_moe.n_physical_experts
587 self.num_local_physical_experts = example_moe.n_local_physical_experts
588 self.num_routed_experts = example_moe.n_routed_experts
589 self.num_shared_experts = example_moe.n_shared_experts
590 self.num_redundant_experts = example_moe.n_redundant_experts
591
592 def update_physical_experts_metadata(
593 self,
594 num_physical_experts: int,
595 num_local_physical_experts: int,
596 ) -> None:
597 assert self.num_local_physical_experts == num_local_physical_experts
598 self.num_physical_experts = num_physical_experts
599 self.num_local_physical_experts = num_local_physical_experts
600 self.num_redundant_experts = num_physical_experts - self.num_logical_experts
601 for layer in self.model.layers:
602 if isinstance(layer.mlp, Ernie4_5_MoeMoE):
603 moe = layer.mlp
604 moe.n_local_physical_experts = num_local_physical_experts
605 moe.n_physical_experts = num_physical_experts
606 moe.n_redundant_experts = self.num_redundant_experts
607 moe.experts.update_expert_map()
608
609 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
610 return self.model.embed_input_ids(input_ids)
611
612 def forward(
613 self,
614 input_ids: torch.Tensor | None,
615 positions: torch.Tensor,
616 intermediate_tensors: IntermediateTensors | None = None,
617 inputs_embeds: torch.Tensor | None = None,
618 ) -> torch.Tensor | IntermediateTensors:
619 hidden_states = self.model(
620 input_ids, positions, intermediate_tensors, inputs_embeds
621 )
622 return hidden_states
623
624 def compute_logits(
625 self,
626 hidden_states: torch.Tensor,
627 ) -> torch.Tensor | None:
628 logits = self.logits_processor(self.lm_head, hidden_states)
629 return logits
630
631 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
632 loader = AutoWeightsLoader(self)
633 return loader.load_weights(weights)