main
py 1,232 lines 45.7 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 # Copyright (c) 2025 Huawei Technologies Co., Ltd. All Rights Reserved.
4 # Copyright 2023 The vLLM team.
5 # Copyright 2022 EleutherAI and the HuggingFace Inc. team. All rights reserved.
6 #
7 # This code is based on EleutherAI's GPT-NeoX library and the GPT-NeoX
8 # and OPT implementations in this library. It has been modified from its
9 # original forms to accommodate minor architectural differences compared
10 # to GPT-NeoX and OPT used by the Meta AI team that trained the model.
11 #
12 # Licensed under the Apache License, Version 2.0 (the "License");
13 # you may not use this file except in compliance with the License.
14 # You may obtain a copy of the License at
15 #
16 # http://www.apache.org/licenses/LICENSE-2.0
17 #
18 # Unless required by applicable law or agreed to in writing, software
19 # distributed under the License is distributed on an "AS IS" BASIS,
20 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21 # See the License for the specific language governing permissions and
22 # limitations under the License.
23
24 from collections.abc import Iterable
25 from typing import Any
26
27 import torch
28 from torch import nn
29 from transformers import PreTrainedConfig
30
31 from vllm.compilation.decorators import support_torch_compile
32 from vllm.config import CacheConfig, ParallelConfig, VllmConfig
33 from vllm.distributed import (
34 get_ep_group,
35 get_pp_group,
36 get_tensor_model_parallel_rank,
37 get_tensor_model_parallel_world_size,
38 get_tp_group,
39 tensor_model_parallel_all_gather,
40 )
41 from vllm.model_executor.layers.activation import SiluAndMul
42 from vllm.model_executor.layers.attention import (
43 Attention,
44 StaticSinkAttention,
45 )
46 from vllm.model_executor.layers.fused_moe import (
47 FusedMoEFactory,
48 GateLinear,
49 )
50 from vllm.model_executor.layers.layernorm import RMSNorm
51 from vllm.model_executor.layers.linear import (
52 ColumnParallelLinear,
53 MergedColumnParallelLinear,
54 QKVParallelLinear,
55 ReplicatedLinear,
56 RowParallelLinear,
57 )
58 from vllm.model_executor.layers.logits_processor import LogitsProcessor
59 from vllm.model_executor.layers.mla import MLAModules, MultiHeadLatentAttentionWrapper
60 from vllm.model_executor.layers.quantization import QuantizationConfig
61 from vllm.model_executor.layers.rotary_embedding import get_rope
62 from vllm.model_executor.layers.vocab_parallel_embedding import (
63 ParallelLMHead,
64 VocabParallelEmbedding,
65 )
66 from vllm.model_executor.models.interfaces import (
67 MixtureOfExperts,
68 SupportsLoRA,
69 SupportsPP,
70 )
71 from vllm.model_executor.models.utils import (
72 AutoWeightsLoader,
73 PPMissingLayer,
74 WeightsMapper,
75 extract_layer_index,
76 make_empty_intermediate_tensors_factory,
77 make_layers,
78 maybe_prefix,
79 sequence_parallel_chunk,
80 )
81 from vllm.model_executor.utils import set_weight_attrs
82 from vllm.platforms import current_platform
83 from vllm.sequence import IntermediateTensors
84 from vllm.transformers_utils.config import set_default_rope_theta
85 from vllm.v1.attention.backend import AttentionType
86 from vllm.v1.attention.backends.flash_attn_diffkv import FlashAttentionDiffKVBackend
87
88
89 def check_ffn_act_fn(act_fn: str):
90 if act_fn != "silu":
91 raise ValueError(
92 f"Unsupported activation: {act_fn}. Only silu is supported for now."
93 )
94
95
96 class OpenPanguMLP(nn.Module):
97 def __init__(
98 self,
99 hidden_size: int,
100 intermediate_size: int,
101 hidden_act: str,
102 quant_config: QuantizationConfig | None = None,
103 bias: bool = False,
104 reduce_results: bool = True,
105 is_sequence_parallel=False,
106 prefix: str = "",
107 ) -> None:
108 super().__init__()
109 self.gate_up_proj = MergedColumnParallelLinear(
110 hidden_size,
111 [intermediate_size] * 2,
112 bias=bias,
113 quant_config=quant_config,
114 disable_tp=is_sequence_parallel,
115 prefix=f"{prefix}.gate_up_proj",
116 )
117 self.down_proj = RowParallelLinear(
118 intermediate_size,
119 hidden_size,
120 bias=bias,
121 quant_config=quant_config,
122 reduce_results=reduce_results,
123 disable_tp=is_sequence_parallel,
124 prefix=f"{prefix}.down_proj",
125 )
126
127 check_ffn_act_fn(hidden_act)
128 self.act_fn = SiluAndMul()
129
130 def forward(self, x: torch.Tensor) -> torch.Tensor:
131 return self.down_proj(self.act_fn(self.gate_up_proj(x)[0]))[0]
132
133
134 class OpenPanguMoE(nn.Module):
135 def __init__(
136 self,
137 config: PreTrainedConfig,
138 parallel_config: ParallelConfig,
139 quant_config: QuantizationConfig | None = None,
140 prefix: str = "",
141 ):
142 super().__init__()
143 self.tp_size = get_tensor_model_parallel_world_size()
144 self.tp_rank = get_tp_group().rank_in_group
145
146 self.routed_scaling_factor = config.routed_scaling_factor
147 self.ep_group = get_ep_group().device_group
148 self.ep_size = self.ep_group.size()
149 self.n_routed_experts: int = config.n_routed_experts
150 self.n_shared_experts: int = config.n_shared_experts
151
152 self.is_sequence_parallel = parallel_config.use_sequence_parallel_moe
153 check_ffn_act_fn(config.hidden_act)
154
155 self.gate = GateLinear(
156 config.hidden_size,
157 config.n_routed_experts,
158 prefix=f"{prefix}.gate",
159 )
160 if (
161 hasattr(config, "router_enable_expert_bias")
162 and config.router_enable_expert_bias
163 ):
164 self.gate.e_score_correction_bias = nn.Parameter(
165 torch.empty(self.n_routed_experts, dtype=torch.float32)
166 )
167 else:
168 self.gate.e_score_correction_bias = None
169
170 # Load balancing settings.
171 eplb_config = parallel_config.eplb_config
172 self.enable_eplb = parallel_config.enable_eplb
173
174 self.n_redundant_experts = eplb_config.num_redundant_experts
175 self.n_logical_experts = self.n_routed_experts
176 self.n_physical_experts = self.n_logical_experts + self.n_redundant_experts
177 self.n_local_physical_experts = self.n_physical_experts // self.ep_size
178
179 self.shared_experts: OpenPanguMLP | None
180 if config.n_shared_experts is not None:
181 intermediate_size = config.moe_intermediate_size * config.n_shared_experts
182 self.shared_experts = OpenPanguMLP(
183 hidden_size=config.hidden_size,
184 intermediate_size=intermediate_size,
185 hidden_act=config.hidden_act,
186 quant_config=quant_config,
187 is_sequence_parallel=self.is_sequence_parallel,
188 reduce_results=False,
189 prefix=f"{prefix}.shared_experts",
190 )
191 else:
192 self.shared_experts = None
193
194 self.experts = FusedMoEFactory(
195 shared_experts=self.shared_experts,
196 num_experts=config.n_routed_experts,
197 top_k=config.num_experts_per_tok,
198 hidden_size=config.hidden_size,
199 intermediate_size=config.moe_intermediate_size,
200 renormalize=config.norm_topk_prob,
201 quant_config=quant_config,
202 use_grouped_topk=True,
203 num_expert_group=1,
204 topk_group=1,
205 prefix=f"{prefix}.experts",
206 scoring_func="sigmoid",
207 routed_scaling_factor=self.routed_scaling_factor,
208 apply_routed_scale_to_output=True,
209 e_score_correction_bias=self.gate.e_score_correction_bias,
210 enable_eplb=self.enable_eplb,
211 num_redundant_experts=self.n_redundant_experts,
212 is_sequence_parallel=self.is_sequence_parallel,
213 )
214
215 def forward(
216 self,
217 hidden_states: torch.Tensor,
218 ) -> torch.Tensor:
219 num_tokens, hidden_dim = hidden_states.shape
220 hidden_states = hidden_states.view(-1, hidden_dim)
221
222 if self.is_sequence_parallel:
223 hidden_states = sequence_parallel_chunk(hidden_states)
224
225 router_logits, _ = self.gate(hidden_states)
226
227 final_hidden_states = self.experts(
228 hidden_states=hidden_states, router_logits=router_logits
229 )
230
231 if self.is_sequence_parallel:
232 final_hidden_states = tensor_model_parallel_all_gather(
233 final_hidden_states, 0
234 )
235 final_hidden_states = final_hidden_states[:num_tokens]
236
237 return final_hidden_states.view(num_tokens, hidden_dim)
238
239
240 class OpenPanguMLAAttention(nn.Module):
241 def __init__(
242 self,
243 config: PreTrainedConfig,
244 hidden_size: int,
245 num_heads: int,
246 qk_nope_head_dim: int,
247 qk_rope_head_dim: int,
248 v_head_dim: int,
249 q_lora_rank: int | None,
250 kv_lora_rank: int,
251 max_position_embeddings: int = 8192,
252 cache_config: CacheConfig | None = None,
253 quant_config: QuantizationConfig | None = None,
254 prefix: str = "",
255 ) -> None:
256 super().__init__()
257 self.hidden_size = hidden_size
258 self.num_heads = num_heads
259 self.qk_nope_head_dim = qk_nope_head_dim
260 self.qk_rope_head_dim = qk_rope_head_dim
261 self.qk_head_dim = qk_nope_head_dim + qk_rope_head_dim
262 self.v_head_dim = v_head_dim
263 self.q_lora_rank = q_lora_rank
264 self.kv_lora_rank = kv_lora_rank
265 self.tp_size = get_tensor_model_parallel_world_size()
266 if num_heads % self.tp_size != 0:
267 raise ValueError(
268 f"num_heads {num_heads} is not divisible by tp_size {self.tp_size}."
269 )
270 self.num_local_heads = num_heads // self.tp_size
271
272 self.scaling = self.qk_head_dim**-0.5
273 self.max_position_embeddings = max_position_embeddings
274
275 self.prefix = prefix
276
277 if self.q_lora_rank is not None:
278 self.fused_qkv_a_proj = MergedColumnParallelLinear(
279 self.hidden_size,
280 [self.q_lora_rank, self.kv_lora_rank + self.qk_rope_head_dim],
281 bias=False,
282 quant_config=quant_config,
283 prefix=f"{prefix}.fused_qkv_a_proj",
284 disable_tp=True,
285 )
286 self.q_a_layernorm = RMSNorm(self.q_lora_rank, eps=config.rms_norm_eps)
287 self.q_b_proj = ColumnParallelLinear(
288 self.q_lora_rank,
289 self.num_heads * self.qk_head_dim,
290 bias=False,
291 quant_config=quant_config,
292 prefix=f"{prefix}.q_b_proj",
293 )
294 else:
295 self.q_proj = ColumnParallelLinear(
296 self.hidden_size,
297 self.num_heads * self.qk_head_dim,
298 bias=False,
299 quant_config=quant_config,
300 prefix=f"{prefix}.q_proj",
301 )
302 self.kv_a_proj_with_mqa = ReplicatedLinear(
303 self.hidden_size,
304 self.kv_lora_rank + self.qk_rope_head_dim,
305 bias=False,
306 quant_config=quant_config,
307 prefix=f"{prefix}.kv_a_proj_with_mqa",
308 )
309
310 self.kv_a_layernorm = RMSNorm(self.kv_lora_rank, eps=config.rms_norm_eps)
311 self.kv_b_proj = ColumnParallelLinear(
312 self.kv_lora_rank,
313 self.num_heads * (self.qk_nope_head_dim + self.v_head_dim),
314 bias=False,
315 quant_config=quant_config,
316 prefix=f"{prefix}.kv_b_proj",
317 )
318
319 self.o_proj = RowParallelLinear(
320 self.num_heads * self.v_head_dim,
321 self.hidden_size,
322 bias=False,
323 quant_config=quant_config,
324 prefix=f"{prefix}.o_proj",
325 )
326
327 # TODO: remove hard coding
328 set_default_rope_theta(config, default_theta=10000)
329 rope_parameters = {
330 "rope_theta": config.rope_parameters["rope_theta"],
331 "beta_fast": 32,
332 "beta_slow": 1,
333 "factor": 1,
334 "mscale": 1.0,
335 "mscale_all_dim": 1.0,
336 "original_max_position_embeddings": max_position_embeddings,
337 "type": "yarn",
338 "rope_type": "deepseek_yarn",
339 }
340 self.rotary_emb = get_rope(
341 qk_rope_head_dim,
342 max_position=max_position_embeddings,
343 rope_parameters=rope_parameters,
344 is_neox_style=False,
345 )
346
347 mla_modules = MLAModules(
348 kv_a_layernorm=self.kv_a_layernorm,
349 kv_b_proj=self.kv_b_proj,
350 rotary_emb=self.rotary_emb,
351 o_proj=self.o_proj,
352 fused_qkv_a_proj=self.fused_qkv_a_proj
353 if self.q_lora_rank is not None
354 else None,
355 kv_a_proj_with_mqa=self.kv_a_proj_with_mqa
356 if self.q_lora_rank is None
357 else None,
358 q_a_layernorm=self.q_a_layernorm if self.q_lora_rank is not None else None,
359 q_b_proj=self.q_b_proj if self.q_lora_rank is not None else None,
360 q_proj=self.q_proj if self.q_lora_rank is None else None,
361 indexer=None,
362 is_sparse=False,
363 topk_indices_buffer=None,
364 )
365
366 self.mla_attn = MultiHeadLatentAttentionWrapper(
367 self.hidden_size,
368 self.num_local_heads,
369 self.scaling,
370 self.qk_nope_head_dim,
371 self.qk_rope_head_dim,
372 self.v_head_dim,
373 self.q_lora_rank,
374 self.kv_lora_rank,
375 mla_modules,
376 cache_config,
377 quant_config,
378 prefix,
379 )
380
381 def forward(
382 self,
383 positions: torch.Tensor,
384 hidden_states: torch.Tensor,
385 ) -> torch.Tensor:
386 return self.mla_attn(positions, hidden_states)
387
388
389 class OpenPanguEmbeddedAttention(nn.Module):
390 def __init__(
391 self,
392 config: PreTrainedConfig,
393 hidden_size: int,
394 num_heads: int,
395 num_kv_heads: int,
396 max_position_embeddings: int = 8192,
397 quant_config: QuantizationConfig | None = None,
398 bias: bool = False,
399 bias_o_proj: bool = False,
400 cache_config: CacheConfig | None = None,
401 prefix: str = "",
402 attn_type: str = AttentionType.DECODER,
403 ) -> None:
404 super().__init__()
405 layer_idx = extract_layer_index(prefix)
406 self.hidden_size = hidden_size
407 tp_size = get_tensor_model_parallel_world_size()
408 self.total_num_heads = num_heads
409 if self.total_num_heads % tp_size != 0:
410 raise ValueError(
411 f"total_num_heads {self.total_num_heads} "
412 f"is not divisible by tp_size {tp_size}."
413 )
414 self.num_heads = self.total_num_heads // tp_size
415 self.total_num_kv_heads = num_kv_heads
416 if self.total_num_kv_heads > tp_size and self.total_num_kv_heads % tp_size != 0:
417 # Number of KV heads is greater than TP size, so we partition
418 # the KV heads across multiple tensor parallel ranks.
419 raise ValueError(
420 "Number of KV heads is greater than TP size, "
421 f"but total_num_kv_heads {self.total_num_kv_heads} "
422 f"is not divisible by tp_size {tp_size}."
423 )
424 elif (
425 self.total_num_kv_heads < tp_size and tp_size % self.total_num_kv_heads != 0
426 ):
427 # Number of KV heads is less than TP size, so we replicate
428 # the KV heads across multiple tensor parallel ranks.
429 raise ValueError(
430 f"Number of KV heads is less than TP size, but tp_size {tp_size} "
431 f"is not divisible by total_num_kv_heads {self.total_num_kv_heads}."
432 )
433 self.num_kv_heads = max(1, self.total_num_kv_heads // tp_size)
434 head_dim = getattr(config, "head_dim", None)
435 if head_dim is None:
436 head_dim = self.hidden_size // self.total_num_heads
437 self.head_dim = head_dim
438 self.q_size = self.num_heads * self.head_dim
439 self.kv_size = self.num_kv_heads * self.head_dim
440 self.scaling = self.head_dim**-0.5
441 self.max_position_embeddings = max_position_embeddings
442
443 self.qkv_proj = QKVParallelLinear(
444 hidden_size=hidden_size,
445 head_size=self.head_dim,
446 total_num_heads=self.total_num_heads,
447 total_num_kv_heads=self.total_num_kv_heads,
448 bias=bias,
449 quant_config=quant_config,
450 prefix=f"{prefix}.qkv_proj",
451 )
452
453 self.o_proj = RowParallelLinear(
454 input_size=self.total_num_heads * self.head_dim,
455 output_size=hidden_size,
456 bias=bias_o_proj,
457 quant_config=quant_config,
458 prefix=f"{prefix}.o_proj",
459 )
460
461 self._init_rotary_emb(config, quant_config=quant_config)
462
463 if hasattr(config, "interleaved_sliding_window"):
464 interleaved_sliding_window = config.interleaved_sliding_window
465 if isinstance(interleaved_sliding_window, int):
466 sliding_window = interleaved_sliding_window
467 elif isinstance(interleaved_sliding_window, list):
468 sw_idx = layer_idx % len(interleaved_sliding_window)
469 sliding_window = interleaved_sliding_window[sw_idx]
470 else:
471 raise ValueError(
472 f"{type(interleaved_sliding_window)} "
473 "for interleaved_sliding_window is not supported."
474 )
475 else:
476 sliding_window = None
477
478 self.attn = Attention(
479 self.num_heads,
480 self.head_dim,
481 self.scaling,
482 num_kv_heads=self.num_kv_heads,
483 cache_config=cache_config,
484 quant_config=quant_config,
485 per_layer_sliding_window=sliding_window,
486 attn_type=attn_type,
487 prefix=f"{prefix}.attn",
488 )
489
490 def forward(
491 self,
492 positions: torch.Tensor,
493 hidden_states: torch.Tensor,
494 ) -> torch.Tensor:
495 qkv, _ = self.qkv_proj(hidden_states)
496 q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1)
497 q, k = self.rotary_emb(positions, q, k)
498 attn_output = self.attn(q, k, v)
499 output, _ = self.o_proj(attn_output)
500 return output
501
502 def _init_rotary_emb(
503 self,
504 config: PreTrainedConfig,
505 quant_config: QuantizationConfig | None,
506 ) -> None:
507 is_neox_style = True
508 rope_parameters = config.rope_parameters or {}
509 if rope_parameters is not None and rope_parameters.get(
510 "mrope_interleaved", False
511 ):
512 rope_parameters["rope_type"] = "openpangu"
513
514 self.rotary_emb = get_rope(
515 self.head_dim,
516 max_position=self.max_position_embeddings,
517 rope_parameters=rope_parameters,
518 is_neox_style=is_neox_style,
519 )
520
521
522 class OpenPanguSinkAttention(nn.Module):
523 def __init__(
524 self,
525 config: PreTrainedConfig,
526 hidden_size: int,
527 num_heads: int,
528 num_kv_heads: int,
529 rope_parameters: dict[str, Any] | None = None,
530 max_position_embeddings: int = 8192,
531 quant_config: QuantizationConfig | None = None,
532 bias: bool = False,
533 bias_o_proj: bool = False,
534 cache_config: CacheConfig | None = None,
535 prefix: str = "",
536 attn_type: str = AttentionType.DECODER,
537 ) -> None:
538 super().__init__()
539 layer_idx = extract_layer_index(prefix)
540 self.hidden_size = hidden_size
541 self.tp_size = get_tensor_model_parallel_world_size()
542 self.tp_rank = get_tensor_model_parallel_rank()
543 self.total_num_heads = num_heads
544 if self.total_num_heads % self.tp_size != 0:
545 raise ValueError(
546 f"total_num_heads {self.total_num_heads} "
547 f"is not divisible by tp_size {self.tp_size}."
548 )
549 self.num_heads = self.total_num_heads // self.tp_size
550 self.total_num_kv_heads = num_kv_heads
551 if (
552 self.total_num_kv_heads > self.tp_size
553 and self.total_num_kv_heads % self.tp_size != 0
554 ):
555 # Number of KV heads is greater than TP size, so we partition
556 # the KV heads across multiple tensor parallel ranks.
557 raise ValueError(
558 "Number of KV heads is greater than TP size, "
559 f"but total_num_kv_heads {self.total_num_kv_heads} "
560 f"is not divisible by tp_size {self.tp_size}."
561 )
562 elif self.total_num_kv_heads < self.tp_size:
563 # TODO: Number of KV heads is less than TP size, so we replicate
564 # the KV heads across multiple tensor parallel ranks.
565 raise ValueError(
566 f"Number of KV heads {self.total_num_kv_heads} is less than "
567 f"TP size {self.tp_size}, KV heads replication is not support yet."
568 )
569 self.num_kv_heads = max(1, self.total_num_kv_heads // self.tp_size)
570 self.qk_nope_dim = getattr(config, "qk_nope_dim", None)
571 self.qk_rope_dim = getattr(config, "qk_rope_dim", None)
572 self.v_channels = getattr(config, "v_channels", None)
573 assert self.qk_nope_dim is not None
574 assert self.qk_rope_dim is not None
575 assert self.v_channels is not None
576 self.head_dim = self.qk_rope_dim + self.qk_nope_dim
577 self.q_size = self.num_heads * self.head_dim
578 self.k_size = self.num_kv_heads * self.head_dim
579 self.v_size = self.num_kv_heads * self.v_channels
580 self.scaling = self.head_dim**-0.5
581 self.max_position_embeddings = max_position_embeddings
582
583 self.param_sink_number = getattr(config, "param_sink_number", 0)
584 self.param_sink_with_value = getattr(config, "param_sink_with_value", False)
585 self.param_sink_scalar = getattr(config, "param_sink_scalar", None)
586 self.param_sink_of_head_num = getattr(config, "param_sink_of_head_dim", False)
587
588 self.qkv_proj = MergedColumnParallelLinear(
589 input_size=hidden_size,
590 output_sizes=[
591 self.q_size * self.tp_size,
592 self.k_size * self.tp_size,
593 self.v_size * self.tp_size,
594 ],
595 bias=bias,
596 quant_config=quant_config,
597 prefix=f"{prefix}.qkv_proj",
598 )
599
600 self.o_proj = RowParallelLinear(
601 input_size=self.total_num_heads * self.v_channels,
602 output_size=hidden_size,
603 bias=bias_o_proj,
604 quant_config=quant_config,
605 prefix=f"{prefix}.o_proj",
606 )
607
608 self.k_layernorm = RMSNorm(self.head_dim, eps=config.rms_norm_eps)
609
610 self._init_rotary_emb(
611 config, rope_parameters=rope_parameters, quant_config=quant_config
612 )
613
614 if hasattr(config, "interleaved_sliding_window"):
615 interleaved_sliding_window = config.interleaved_sliding_window
616 if isinstance(interleaved_sliding_window, int):
617 sliding_window = interleaved_sliding_window
618 elif isinstance(interleaved_sliding_window, list):
619 sw_idx = layer_idx % len(interleaved_sliding_window)
620 sliding_window = interleaved_sliding_window[sw_idx]
621 else:
622 raise ValueError(
623 f"{type(interleaved_sliding_window)} "
624 "for interleaved_sliding_window is not supported."
625 )
626 else:
627 sliding_window = None
628
629 FlashAttentionDiffKVBackend.set_head_size_v(self.v_channels)
630 self.attn = StaticSinkAttention(
631 self.num_heads,
632 self.head_dim,
633 self.scaling,
634 sink_len=self.param_sink_number,
635 num_kv_heads=self.num_kv_heads,
636 cache_config=cache_config,
637 quant_config=quant_config,
638 per_layer_sliding_window=sliding_window,
639 attn_type=attn_type,
640 prefix=f"{prefix}.attn",
641 attn_backend=FlashAttentionDiffKVBackend,
642 head_size_v=self.v_channels,
643 )
644
645 if self.param_sink_number > 0:
646 self.param_sink_key = torch.nn.Parameter(
647 torch.empty(
648 (
649 self.param_sink_number,
650 self.num_kv_heads,
651 self.head_dim,
652 ),
653 device=current_platform.current_device(),
654 dtype=config.torch_dtype,
655 )
656 )
657 set_weight_attrs(
658 self.param_sink_key,
659 {
660 "output_dim": 1,
661 "weight_loader": self.weight_loader,
662 },
663 )
664
665 if self.param_sink_with_value:
666 self.param_sink_value = torch.nn.Parameter(
667 torch.empty(
668 (
669 self.param_sink_number,
670 self.num_kv_heads,
671 self.v_channels,
672 ),
673 device=current_platform.current_device(),
674 dtype=config.torch_dtype,
675 )
676 )
677 set_weight_attrs(
678 self.param_sink_value,
679 {
680 "output_dim": 1,
681 "weight_loader": self.weight_loader,
682 },
683 )
684 else:
685 self.register_buffer(
686 "param_sink_value",
687 torch.zeros(
688 (
689 self.param_sink_number,
690 self.num_kv_heads,
691 self.v_channels,
692 ),
693 device=current_platform.current_device(),
694 dtype=config.torch_dtype,
695 ),
696 persistent=False,
697 )
698 # To enable dummy run with out weight
699 self.post_weight_load()
700
701 def weight_loader(self, param: nn.Parameter, loaded_weight: torch.Tensor):
702 output_dim = getattr(param, "output_dim", None)
703
704 is_sharded_weight = getattr(param, "is_sharded_weight", False)
705
706 param_data = param.data
707 if output_dim is not None and not is_sharded_weight:
708 shard_size = param_data.shape[output_dim]
709 start_idx = self.tp_rank * shard_size
710 loaded_weight = loaded_weight.narrow(output_dim, start_idx, shard_size)
711
712 # Special case for loading scales off disk, which often do not
713 # have a shape (such as in the case of AutoFP8).
714 if len(loaded_weight.shape) == 0:
715 loaded_weight = loaded_weight.reshape(1)
716
717 assert param_data.shape == loaded_weight.shape
718 param_data.copy_(loaded_weight)
719
720 def forward(
721 self,
722 positions: torch.Tensor,
723 hidden_states: torch.Tensor,
724 ) -> torch.Tensor:
725 qkv, _ = self.qkv_proj(hidden_states)
726 q, k, v = qkv.split([self.q_size, self.k_size, self.v_size], dim=-1)
727 k = self.k_layernorm(k.view(-1, self.num_kv_heads, self.head_dim))
728 q, k = self.rotary_emb(positions, q, k)
729
730 q = q.view(-1, self.q_size)
731 k = k.view(-1, self.k_size)
732
733 attn_output = self.attn(
734 q,
735 k,
736 v,
737 output_shape=torch.Size(
738 [q.shape[0], q.shape[1] // self.head_dim * self.v_channels]
739 ),
740 )
741 output, _ = self.o_proj(attn_output)
742 return output
743
744 def _init_rotary_emb(
745 self,
746 config: PreTrainedConfig,
747 rope_parameters: dict[str, Any] | None,
748 quant_config: QuantizationConfig | None,
749 ) -> None:
750 is_neox_style = False
751 rope_parameters = {"partial_rotary_factor": self.qk_rope_dim / self.head_dim}
752
753 self.rotary_emb = get_rope(
754 self.head_dim,
755 max_position=self.max_position_embeddings,
756 rope_parameters=rope_parameters,
757 is_neox_style=is_neox_style,
758 )
759
760 def post_weight_load(self) -> None:
761 if hasattr(self, "k_layernorm") and self.k_layernorm is not None:
762 param_sink_key = self.k_layernorm(self.param_sink_key)
763 else:
764 param_sink_key = self.param_sink_key
765
766 self.attn.update_sink_kv(param_sink_key, self.param_sink_value)
767
768
769 class OpenPanguDecoderLayer(nn.Module):
770 def __init__(
771 self,
772 config: PreTrainedConfig,
773 prefix: str,
774 vllm_config: VllmConfig,
775 ) -> None:
776 super().__init__()
777
778 if config is None:
779 config = vllm_config.model_config.hf_config
780 cache_config = vllm_config.cache_config
781 quant_config = vllm_config.quant_config
782 parallel_config = vllm_config.parallel_config
783
784 self.hidden_size = config.hidden_size
785 max_position_embeddings = getattr(config, "max_position_embeddings", 8192)
786
787 layer_idx = int(prefix.split(sep=".")[-1])
788 self.layer_idx = layer_idx
789
790 self.use_mla = (
791 hasattr(config, "qk_nope_head_dim")
792 and hasattr(config, "qk_rope_head_dim")
793 and hasattr(config, "v_head_dim")
794 and hasattr(config, "kv_lora_rank")
795 )
796 self.use_sink_attention = (
797 hasattr(config, "param_sink_number") and config.param_sink_number > 0
798 )
799 if self.use_mla:
800 self.self_attn = OpenPanguMLAAttention(
801 config=config,
802 hidden_size=self.hidden_size,
803 num_heads=config.num_attention_heads,
804 qk_nope_head_dim=config.qk_nope_head_dim,
805 qk_rope_head_dim=config.qk_rope_head_dim,
806 v_head_dim=config.v_head_dim,
807 q_lora_rank=(
808 config.q_lora_rank if hasattr(config, "q_lora_rank") else None
809 ),
810 kv_lora_rank=config.kv_lora_rank,
811 max_position_embeddings=max_position_embeddings,
812 cache_config=cache_config,
813 quant_config=quant_config,
814 prefix=f"{prefix}.self_attn",
815 )
816 elif self.use_sink_attention:
817 attention_bias = getattr(config, "attention_bias", False) or getattr(
818 config, "bias", False
819 )
820 bias_o_proj = attention_bias
821 if hasattr(config, "qkv_bias"):
822 attention_bias = config.qkv_bias
823 if getattr(config, "is_causal", True):
824 attn_type = AttentionType.DECODER
825 else:
826 raise ValueError(
827 f"is_causal={config.is_causal} is not support "
828 "for attention with sink"
829 )
830 rope_parameters = getattr(config, "rope_scaling", None)
831 if rope_parameters is None:
832 rope_parameters = {
833 "rope_type": "default",
834 "rope_theta": config.rope_theta,
835 }
836 self.self_attn = OpenPanguSinkAttention(
837 config=config,
838 hidden_size=self.hidden_size,
839 num_heads=config.num_attention_heads,
840 num_kv_heads=getattr(
841 config, "num_key_value_heads", config.num_attention_heads
842 ),
843 rope_parameters=rope_parameters,
844 max_position_embeddings=max_position_embeddings,
845 quant_config=quant_config,
846 bias=attention_bias,
847 bias_o_proj=bias_o_proj,
848 cache_config=cache_config,
849 prefix=f"{prefix}.self_attn",
850 attn_type=attn_type,
851 )
852 else:
853 attention_bias = getattr(config, "attention_bias", False) or getattr(
854 config, "bias", False
855 )
856 bias_o_proj = attention_bias
857 if hasattr(config, "qkv_bias"):
858 attention_bias = config.qkv_bias
859 # By default, PanguEmbedded uses causal attention
860 # as it is a decoder-only model.
861 # You can override the HF config with `is_causal=False` to enable
862 # bidirectional attention, which is used in some embedding models
863 if getattr(config, "is_causal", True):
864 attn_type = AttentionType.DECODER
865 else:
866 attn_type = AttentionType.ENCODER_ONLY
867 self.self_attn = OpenPanguEmbeddedAttention(
868 config=config,
869 hidden_size=self.hidden_size,
870 num_heads=config.num_attention_heads,
871 num_kv_heads=getattr(
872 config, "num_key_value_heads", config.num_attention_heads
873 ),
874 max_position_embeddings=max_position_embeddings,
875 quant_config=quant_config,
876 bias=attention_bias,
877 bias_o_proj=bias_o_proj,
878 cache_config=cache_config,
879 prefix=f"{prefix}.self_attn",
880 attn_type=attn_type,
881 )
882
883 if (
884 getattr(config, "n_routed_experts", None) is not None
885 and layer_idx >= config.first_k_dense_replace
886 ):
887 self.mlp = OpenPanguMoE(
888 config=config,
889 parallel_config=parallel_config,
890 quant_config=quant_config,
891 prefix=f"{prefix}.mlp",
892 )
893 else:
894 self.mlp = OpenPanguMLP(
895 hidden_size=self.hidden_size,
896 intermediate_size=config.intermediate_size,
897 hidden_act=config.hidden_act,
898 quant_config=quant_config,
899 bias=getattr(config, "mlp_bias", False),
900 prefix=f"{prefix}.mlp",
901 )
902 self.routed_scaling_factor = getattr(config, "routed_scaling_factor", 1.0)
903 self.num_hidden_layers = config.num_hidden_layers
904 self.first_k_dense_replace = getattr(
905 config, "first_k_dense_replace", self.num_hidden_layers
906 )
907
908 self.input_layernorm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
909 self.post_attention_layernorm = RMSNorm(
910 config.hidden_size, eps=config.rms_norm_eps
911 )
912 self.tp_group = get_tp_group().device_group
913 self.sandwich_norm = getattr(config, "sandwich_norm", False)
914 if self.sandwich_norm:
915 self.pre_mlp_layernorm = RMSNorm(
916 config.hidden_size, eps=config.rms_norm_eps
917 )
918 self.post_mlp_layernorm = RMSNorm(
919 config.hidden_size, eps=config.rms_norm_eps
920 )
921
922 def forward(
923 self,
924 positions: torch.Tensor,
925 hidden_states: torch.Tensor,
926 residual: torch.Tensor | None,
927 ) -> torch.Tensor:
928 if residual is None:
929 residual = hidden_states
930 hidden_states = self.input_layernorm(hidden_states)
931 else:
932 hidden_states, residual = self.input_layernorm(hidden_states, residual)
933
934 hidden_states = self.self_attn(
935 positions=positions,
936 hidden_states=hidden_states,
937 )
938
939 if (
940 self.routed_scaling_factor is not None
941 and hidden_states.dtype == torch.float16
942 ):
943 # Fix FP16 overflow
944 # We scale both hidden_states and residual before
945 # rmsnorm, and rmsnorm result would not affect by scale.
946 hidden_states *= 1.0 / self.routed_scaling_factor
947 if self.layer_idx == 0:
948 # The residual is shared by all layers, we only scale it on
949 # first layer.
950 residual *= 1.0 / self.routed_scaling_factor
951
952 if self.sandwich_norm:
953 hidden_states = self.post_attention_layernorm(hidden_states)
954 hidden_states, residual = self.pre_mlp_layernorm(hidden_states, residual)
955 else:
956 hidden_states, residual = self.post_attention_layernorm(
957 hidden_states, residual
958 )
959
960 # Fully Connected
961 hidden_states = self.mlp(hidden_states)
962
963 if (
964 self.routed_scaling_factor is not None
965 and isinstance(self.mlp, OpenPanguMLP)
966 and hidden_states.dtype == torch.float16
967 ):
968 hidden_states *= 1.0 / self.routed_scaling_factor
969
970 if self.sandwich_norm:
971 hidden_states = self.post_mlp_layernorm(hidden_states)
972
973 return hidden_states, residual
974
975
976 @support_torch_compile
977 class OpenPanguModel(nn.Module):
978 fall_back_to_pt_during_load = False
979
980 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
981 super().__init__()
982
983 config = vllm_config.model_config.hf_config
984 quant_config = vllm_config.quant_config
985 eplb_config = vllm_config.parallel_config.eplb_config
986 self.config = config
987 self.num_redundant_experts = eplb_config.num_redundant_experts
988
989 self.vocab_size = config.vocab_size
990
991 if get_pp_group().is_first_rank or (
992 config.tie_word_embeddings and get_pp_group().is_last_rank
993 ):
994 self.embed_tokens = VocabParallelEmbedding(
995 config.vocab_size,
996 config.hidden_size,
997 quant_config=quant_config,
998 prefix=f"{prefix}.embed_tokens",
999 )
1000 else:
1001 self.embed_tokens = PPMissingLayer()
1002
1003 self.start_layer, self.end_layer, self.layers = make_layers(
1004 config.num_hidden_layers,
1005 lambda prefix: OpenPanguDecoderLayer(config, prefix, vllm_config),
1006 prefix=f"{prefix}.layers",
1007 )
1008
1009 if get_pp_group().is_last_rank:
1010 self.norm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
1011 else:
1012 self.norm = PPMissingLayer()
1013 self.make_empty_intermediate_tensors = make_empty_intermediate_tensors_factory(
1014 ["hidden_states", "residual"], config.hidden_size
1015 )
1016
1017 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
1018 return self.embed_tokens(input_ids)
1019
1020 def forward(
1021 self,
1022 input_ids: torch.Tensor | None,
1023 positions: torch.Tensor,
1024 intermediate_tensors: IntermediateTensors | None,
1025 inputs_embeds: torch.Tensor | None = None,
1026 ) -> torch.Tensor | IntermediateTensors:
1027 if get_pp_group().is_first_rank:
1028 if inputs_embeds is not None:
1029 hidden_states = inputs_embeds
1030 else:
1031 hidden_states = self.embed_input_ids(input_ids)
1032 residual = None
1033 else:
1034 assert intermediate_tensors is not None
1035 hidden_states = intermediate_tensors["hidden_states"]
1036 residual = intermediate_tensors["residual"]
1037
1038 for i in range(self.start_layer, self.end_layer):
1039 layer = self.layers[i]
1040 hidden_states, residual = layer(positions, hidden_states, residual)
1041
1042 if not get_pp_group().is_last_rank:
1043 return IntermediateTensors(
1044 {"hidden_states": hidden_states, "residual": residual}
1045 )
1046
1047 hidden_states, _ = self.norm(hidden_states, residual)
1048 return hidden_states
1049
1050 def _filter_spec_layers(
1051 self, weights: Iterable[tuple[str, torch.Tensor]]
1052 ) -> Iterable[tuple[str, torch.Tensor]]:
1053 # Skip the MTP (spec-decode) layers that follow the main model's layers.
1054 num_mtp = getattr(self.config, "num_nextn_predict_layers", 0)
1055 for name, loaded_weight in weights:
1056 if "layers" in name and num_mtp > 0:
1057 layer_idx = int(name.split("layers.")[-1].split(".")[0])
1058 mtp_idx = layer_idx - self.config.num_hidden_layers
1059 if 0 <= mtp_idx < num_mtp:
1060 continue
1061 yield name, loaded_weight
1062
1063 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
1064 # .experts.gate_up_proj must be handled by MoERunner.load_weights for EP
1065 stacked: dict[str, tuple[str, int | str]] = {
1066 ".mlp.gate_proj": (".mlp.gate_up_proj", 0),
1067 ".mlp.up_proj": (".mlp.gate_up_proj", 1),
1068 ".shared_experts.gate_proj": (".shared_experts.gate_up_proj", 0),
1069 ".shared_experts.up_proj": (".shared_experts.gate_up_proj", 1),
1070 }
1071 # The attention layout is config-dependent: MLA with a fused low-rank
1072 # projection, MLA without it, or standard qkv. Only add the stacked
1073 # entries whose fused target actually exists as a parameter.
1074 param_names = list(dict(self.named_parameters()))
1075 if any(".fused_qkv_a_proj." in n for n in param_names):
1076 stacked[".q_a_proj"] = (".fused_qkv_a_proj", 0)
1077 stacked[".kv_a_proj_with_mqa"] = (".fused_qkv_a_proj", 1)
1078 if any(".qkv_proj." in n for n in param_names):
1079 stacked[".q_proj"] = (".qkv_proj", "q")
1080 stacked[".k_proj"] = (".qkv_proj", "k")
1081 stacked[".v_proj"] = (".qkv_proj", "v")
1082 mapper = WeightsMapper(
1083 orig_to_new_substr={
1084 "e_score_correction_bias": "gate.e_score_correction_bias",
1085 },
1086 orig_to_new_stacked=stacked,
1087 )
1088 loader = AutoWeightsLoader(self)
1089 loaded = loader.load_weights(self._filter_spec_layers(weights), mapper=mapper)
1090 self.post_weight_load()
1091 return loaded
1092
1093 def post_weight_load(self) -> None:
1094 for name, module in self.named_modules():
1095 if module is self:
1096 continue
1097 if hasattr(module, "post_weight_load"):
1098 module.post_weight_load()
1099
1100
1101 class OpenPanguModelBase(nn.Module, SupportsPP, SupportsLoRA):
1102 packed_modules_mapping = {
1103 "qkv_proj": ["q_proj", "k_proj", "v_proj"],
1104 "gate_up_proj": ["gate_proj", "up_proj"],
1105 }
1106
1107 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
1108 super().__init__()
1109 config = vllm_config.model_config.hf_config
1110 quant_config = vllm_config.quant_config
1111 self.config = config
1112 self.quant_config = quant_config
1113
1114 self.fuse_qkv_a_proj = (
1115 hasattr(config, "q_lora_rank") and config.q_lora_rank is not None
1116 )
1117 if self.fuse_qkv_a_proj:
1118 self.packed_modules_mapping["fused_qkv_a_proj"] = [
1119 "q_a_proj",
1120 "kv_a_proj_with_mqa",
1121 ]
1122
1123 self.model = OpenPanguModel(
1124 vllm_config=vllm_config, prefix=maybe_prefix(prefix, "model")
1125 )
1126 if get_pp_group().is_last_rank:
1127 self.lm_head = ParallelLMHead(
1128 config.vocab_size,
1129 config.hidden_size,
1130 quant_config=quant_config,
1131 prefix=maybe_prefix(prefix, "lm_head"),
1132 )
1133 if config.tie_word_embeddings:
1134 self.lm_head = self.lm_head.tie_weights(self.model.embed_tokens)
1135 else:
1136 self.lm_head = PPMissingLayer()
1137 self.logits_processor = LogitsProcessor(config.vocab_size)
1138 self.make_empty_intermediate_tensors = (
1139 self.model.make_empty_intermediate_tensors
1140 )
1141
1142 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
1143 return self.model.embed_input_ids(input_ids)
1144
1145 def forward(
1146 self,
1147 input_ids: torch.Tensor | None,
1148 positions: torch.Tensor,
1149 intermediate_tensors: IntermediateTensors | None = None,
1150 inputs_embeds: torch.Tensor | None = None,
1151 ) -> torch.Tensor | IntermediateTensors:
1152 hidden_states = self.model(
1153 input_ids, positions, intermediate_tensors, inputs_embeds
1154 )
1155 return hidden_states
1156
1157 def compute_logits(
1158 self,
1159 hidden_states: torch.Tensor,
1160 ) -> torch.Tensor | None:
1161 logits = self.logits_processor(self.lm_head, hidden_states)
1162 return logits
1163
1164 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
1165 loader = AutoWeightsLoader(self)
1166 return loader.load_weights(weights)
1167
1168
1169 class OpenPanguMoEModel(OpenPanguModelBase, MixtureOfExperts):
1170 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
1171 super().__init__(vllm_config=vllm_config, prefix=prefix)
1172 config = vllm_config.model_config.hf_config
1173
1174 # Set MoE hyperparameters
1175 self.num_moe_layers = config.num_hidden_layers - config.first_k_dense_replace
1176 self.num_expert_groups = 1
1177
1178 self.moe_layers = []
1179 example_moe = None
1180 for layer in self.model.layers:
1181 if isinstance(layer, PPMissingLayer):
1182 continue
1183
1184 assert isinstance(layer, OpenPanguDecoderLayer)
1185 if isinstance(layer.mlp, OpenPanguMoE):
1186 # Pick last one layer since the first ones may be dense layers.
1187 example_moe = layer.mlp
1188 self.moe_layers.append(layer.mlp.experts)
1189
1190 if example_moe is None:
1191 raise RuntimeError("No MOE layer found in model.layers.")
1192
1193 self.num_logical_experts = example_moe.n_logical_experts
1194 self.num_physical_experts = example_moe.n_physical_experts
1195 self.num_local_physical_experts = example_moe.n_local_physical_experts
1196 self.n_routed_experts = example_moe.n_routed_experts
1197 self.n_shared_experts = example_moe.n_shared_experts
1198 self.num_redundant_experts = example_moe.n_redundant_experts
1199
1200 def update_physical_experts_metadata(
1201 self,
1202 num_physical_experts: int,
1203 num_local_physical_experts: int,
1204 ) -> None:
1205 assert self.num_local_physical_experts == num_local_physical_experts
1206 self.num_physical_experts = num_physical_experts
1207 self.num_local_physical_experts = num_local_physical_experts
1208 self.num_redundant_experts = num_physical_experts - self.num_logical_experts
1209 for layer in self.model.layers:
1210 if isinstance(layer.mlp, OpenPanguMoE):
1211 moe = layer.mlp
1212 moe.n_local_physical_experts = num_local_physical_experts
1213 moe.n_physical_experts = num_physical_experts
1214 moe.n_redundant_experts = self.num_redundant_experts
1215 moe.experts.update_expert_map()
1216
1217
1218 class OpenPanguEmbeddedModel(OpenPanguModelBase):
1219 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
1220 super().__init__(vllm_config=vllm_config, prefix=prefix)
1221
1222
1223 class PanguEmbeddedForCausalLM(OpenPanguEmbeddedModel):
1224 pass
1225
1226
1227 class PanguUltraMoEForCausalLM(OpenPanguMoEModel):
1228 pass
1229
1230
1231 class PanguProMoEV2ForCausalLM(OpenPanguMoEModel):
1232 pass