main
py 615 lines 22.4 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 # Copyright 2025 The ZhipuAI 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 GLM-4.5, GLM-4.6, GLM-4.7 model
25 compatible with HuggingFace weights."""
26
27 from collections.abc import Iterable
28 from itertools import islice
29
30 import torch
31 from torch import nn
32 from transformers.models.glm4_moe import Glm4MoeConfig
33
34 from vllm._aiter_ops import rocm_aiter_ops
35 from vllm.compilation.decorators import support_torch_compile
36 from vllm.config import CacheConfig, VllmConfig, get_current_vllm_config
37 from vllm.distributed import (
38 get_ep_group,
39 get_pp_group,
40 get_tensor_model_parallel_world_size,
41 )
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 )
48 from vllm.model_executor.layers.fused_moe.utils import (
49 is_model_fused_shared_expert_compatible,
50 resolve_layer_fused_shared_expert,
51 )
52 from vllm.model_executor.layers.layernorm import RMSNorm
53 from vllm.model_executor.layers.linear import (
54 MergedColumnParallelLinear,
55 QKVParallelLinear,
56 RowParallelLinear,
57 )
58 from vllm.model_executor.layers.logits_processor import LogitsProcessor
59 from vllm.model_executor.layers.quantization import QuantizationConfig
60 from vllm.model_executor.layers.rotary_embedding import get_rope
61 from vllm.model_executor.layers.vocab_parallel_embedding import (
62 ParallelLMHead,
63 VocabParallelEmbedding,
64 )
65 from vllm.sequence import IntermediateTensors
66
67 from .interfaces import MixtureOfExperts, SupportsLoRA, SupportsPP
68 from .utils import (
69 AutoWeightsLoader,
70 PPMissingLayer,
71 WeightsMapper,
72 make_empty_intermediate_tensors_factory,
73 make_layers,
74 maybe_fuse_shared_experts,
75 maybe_prefix,
76 skip_spec_layers,
77 )
78
79
80 class Glm4MoeMLP(nn.Module):
81 def __init__(
82 self,
83 hidden_size: int,
84 intermediate_size: int,
85 hidden_act: str,
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=False,
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=False,
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 Glm4MoE(nn.Module):
120 def __init__(
121 self,
122 config: Glm4MoeConfig,
123 quant_config: QuantizationConfig | None = None,
124 prefix: str = "",
125 enable_eplb: bool = False,
126 ):
127 super().__init__()
128 self.tp_size = get_tensor_model_parallel_world_size()
129 self.routed_scaling_factor = config.routed_scaling_factor
130
131 self.ep_group = get_ep_group().device_group
132 self.ep_size = self.ep_group.size()
133 self.n_routed_experts: int = config.n_routed_experts
134 self.n_shared_experts: int = config.n_shared_experts
135
136 if config.hidden_act != "silu":
137 raise ValueError(
138 f"Unsupported activation: {config.hidden_act}. "
139 "Only silu is supported for now."
140 )
141 self.gate = GateLinear(
142 config.hidden_size,
143 config.n_routed_experts,
144 out_dtype=torch.float32,
145 params_dtype=torch.float32,
146 prefix=f"{prefix}.gate",
147 )
148 self.gate.e_score_correction_bias = nn.Parameter(
149 torch.empty(config.n_routed_experts, dtype=torch.float32)
150 )
151
152 # Load balancing settings.
153 vllm_config = get_current_vllm_config()
154 eplb_config = vllm_config.parallel_config.eplb_config
155 self.enable_eplb = enable_eplb
156
157 self.n_redundant_experts = eplb_config.num_redundant_experts
158 self.n_logical_experts = self.n_routed_experts
159 self.n_physical_experts = self.n_logical_experts + self.n_redundant_experts
160 self.n_local_physical_experts = self.n_physical_experts // self.ep_size
161
162 self.is_rocm_aiter_moe_enabled = rocm_aiter_ops.is_fused_moe_enabled()
163
164 self.is_fused_shared_expert_enabled = False
165 if config.n_shared_experts is not None:
166 self.is_fused_shared_expert_enabled = resolve_layer_fused_shared_expert(
167 quant_config, prefix
168 )
169
170 if config.n_shared_experts is None or self.is_fused_shared_expert_enabled:
171 self.shared_experts = None
172 else:
173 intermediate_size = config.moe_intermediate_size * config.n_shared_experts
174 self.shared_experts = Glm4MoeMLP(
175 hidden_size=config.hidden_size,
176 intermediate_size=intermediate_size,
177 hidden_act=config.hidden_act,
178 quant_config=quant_config,
179 reduce_results=False,
180 prefix=f"{prefix}.shared_experts",
181 )
182
183 self.experts = FusedMoEFactory(
184 shared_experts=self.shared_experts,
185 num_experts=config.n_routed_experts,
186 top_k=config.num_experts_per_tok,
187 hidden_size=config.hidden_size,
188 intermediate_size=config.moe_intermediate_size,
189 renormalize=config.norm_topk_prob,
190 quant_config=quant_config,
191 use_grouped_topk=True,
192 num_expert_group=config.n_group,
193 topk_group=config.topk_group,
194 prefix=f"{prefix}.experts",
195 scoring_func="sigmoid",
196 # aiter applies routed_scaling_factor internally; see deepseek_v2.py.
197 routed_scaling_factor=self.routed_scaling_factor,
198 apply_routed_scale_to_output=not self.is_rocm_aiter_moe_enabled,
199 e_score_correction_bias=self.gate.e_score_correction_bias,
200 enable_eplb=self.enable_eplb,
201 num_redundant_experts=self.n_redundant_experts,
202 router_logits_dtype=torch.float32,
203 n_shared_experts=(
204 config.n_shared_experts if self.is_fused_shared_expert_enabled else None
205 ),
206 fuse_shared_experts=self.is_fused_shared_expert_enabled,
207 )
208
209 def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
210 num_tokens, hidden_dim = hidden_states.shape
211 hidden_states = hidden_states.view(-1, hidden_dim)
212
213 # router_logits: (num_tokens, n_experts)
214 router_logits, _ = self.gate(hidden_states)
215
216 final_hidden_states = self.experts(
217 hidden_states=hidden_states, router_logits=router_logits
218 )
219 return final_hidden_states.view(num_tokens, hidden_dim)
220
221
222 class Glm4MoeAttention(nn.Module):
223 def __init__(
224 self,
225 config: Glm4MoeConfig,
226 hidden_size: int,
227 num_heads: int,
228 num_kv_heads: int,
229 max_position_embeddings: int = 131072,
230 head_dim: int | None = None,
231 rms_norm_eps: float = 1e-05,
232 qkv_bias: bool = False,
233 use_qk_norm: bool = False,
234 cache_config: CacheConfig | None = None,
235 quant_config: QuantizationConfig | None = None,
236 prefix: str = "",
237 ) -> None:
238 super().__init__()
239 self.hidden_size = hidden_size
240 tp_size = get_tensor_model_parallel_world_size()
241 self.total_num_heads = num_heads
242 assert self.total_num_heads % tp_size == 0
243 self.num_heads = self.total_num_heads // tp_size
244 self.total_num_kv_heads = num_kv_heads
245 if self.total_num_kv_heads >= tp_size:
246 # Number of KV heads is greater than TP size, so we partition
247 # the KV heads across multiple tensor parallel GPUs.
248 assert self.total_num_kv_heads % tp_size == 0
249 else:
250 # Number of KV heads is less than TP size, so we replicate
251 # the KV heads across multiple tensor parallel GPUs.
252 assert tp_size % self.total_num_kv_heads == 0
253 self.num_kv_heads = max(1, self.total_num_kv_heads // tp_size)
254 self.head_dim = head_dim or (hidden_size // self.total_num_heads)
255 self.q_size = self.num_heads * self.head_dim
256 self.kv_size = self.num_kv_heads * self.head_dim
257 self.scaling = self.head_dim**-0.5
258 self.max_position_embeddings = max_position_embeddings
259 self.use_qk_norm = use_qk_norm
260
261 self.qkv_proj = QKVParallelLinear(
262 hidden_size,
263 self.head_dim,
264 self.total_num_heads,
265 self.total_num_kv_heads,
266 bias=qkv_bias,
267 quant_config=quant_config,
268 prefix=f"{prefix}.qkv_proj",
269 )
270
271 self.o_proj = RowParallelLinear(
272 self.total_num_heads * self.head_dim,
273 hidden_size,
274 bias=False,
275 quant_config=quant_config,
276 prefix=f"{prefix}.o_proj",
277 )
278
279 config.rope_parameters.setdefault("partial_rotary_factor", 0.5)
280 self.rotary_emb = get_rope(
281 self.head_dim,
282 max_position=max_position_embeddings,
283 rope_parameters=config.rope_parameters,
284 )
285 self.attn = Attention(
286 self.num_heads,
287 self.head_dim,
288 self.scaling,
289 num_kv_heads=self.num_kv_heads,
290 cache_config=cache_config,
291 quant_config=quant_config,
292 prefix=f"{prefix}.attn",
293 )
294
295 if self.use_qk_norm:
296 self.q_norm = RMSNorm(self.head_dim, eps=rms_norm_eps)
297 self.k_norm = RMSNorm(self.head_dim, eps=rms_norm_eps)
298
299 def forward(
300 self,
301 positions: torch.Tensor,
302 hidden_states: torch.Tensor,
303 ) -> torch.Tensor:
304 qkv, _ = self.qkv_proj(hidden_states)
305 q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1)
306 if self.use_qk_norm:
307 q = self.q_norm(q.reshape(-1, self.num_heads, self.head_dim)).reshape(
308 q.shape
309 )
310 k = self.k_norm(k.reshape(-1, self.num_kv_heads, self.head_dim)).reshape(
311 k.shape
312 )
313
314 q, k = self.rotary_emb(positions, q, k)
315 attn_output = self.attn(q, k, v)
316 output, _ = self.o_proj(attn_output)
317 return output
318
319
320 class Glm4MoeDecoderLayer(nn.Module):
321 def __init__(
322 self,
323 config: Glm4MoeConfig,
324 cache_config: CacheConfig | None = None,
325 quant_config: QuantizationConfig | None = None,
326 prefix: str = "",
327 enable_eplb: bool = False,
328 ) -> None:
329 super().__init__()
330 self.hidden_size = config.hidden_size
331 max_position_embeddings = getattr(config, "max_position_embeddings", 131072)
332 # DecoderLayers are created with `make_layers` which passes the prefix
333 # with the layer's index.
334 layer_idx = int(prefix.split(sep=".")[-1])
335 self.layer_idx = layer_idx
336
337 self.self_attn = Glm4MoeAttention(
338 config=config,
339 hidden_size=self.hidden_size,
340 num_heads=config.num_attention_heads,
341 num_kv_heads=config.num_key_value_heads,
342 max_position_embeddings=max_position_embeddings,
343 head_dim=config.head_dim,
344 rms_norm_eps=config.rms_norm_eps,
345 qkv_bias=config.attention_bias,
346 cache_config=cache_config,
347 quant_config=quant_config,
348 prefix=f"{prefix}.self_attn",
349 use_qk_norm=config.use_qk_norm,
350 )
351
352 if (
353 config.n_routed_experts is not None
354 and layer_idx >= config.first_k_dense_replace
355 ):
356 self.mlp = Glm4MoE(
357 config=config,
358 quant_config=quant_config,
359 prefix=f"{prefix}.mlp",
360 enable_eplb=enable_eplb,
361 )
362 else:
363 self.mlp = Glm4MoeMLP(
364 hidden_size=config.hidden_size,
365 intermediate_size=config.intermediate_size,
366 hidden_act=config.hidden_act,
367 quant_config=quant_config,
368 prefix=f"{prefix}.mlp",
369 )
370
371 self.input_layernorm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
372 self.post_attention_layernorm = RMSNorm(
373 config.hidden_size, eps=config.rms_norm_eps
374 )
375 self.routed_scaling_factor = config.routed_scaling_factor
376
377 def forward(
378 self,
379 positions: torch.Tensor,
380 hidden_states: torch.Tensor,
381 residual: torch.Tensor | None,
382 ) -> tuple[torch.Tensor, torch.Tensor]:
383 if residual is None:
384 residual = hidden_states
385 hidden_states = self.input_layernorm(hidden_states)
386 else:
387 hidden_states, residual = self.input_layernorm(hidden_states, residual)
388 hidden_states = self.self_attn(positions=positions, hidden_states=hidden_states)
389 hidden_states, residual = self.post_attention_layernorm(hidden_states, residual)
390 hidden_states = self.mlp(hidden_states)
391 return hidden_states, residual
392
393
394 @support_torch_compile(
395 dynamic_arg_dims={
396 "input_ids": 0,
397 "positions": -1,
398 "intermediate_tensors": 0,
399 "inputs_embeds": 0,
400 }
401 )
402 class Glm4MoeModel(nn.Module):
403 hf_to_vllm_mapper = WeightsMapper(
404 orig_to_new_stacked={
405 ".q_proj": (".qkv_proj", "q"),
406 ".k_proj": (".qkv_proj", "k"),
407 ".v_proj": (".qkv_proj", "v"),
408 # .experts.gate_up_proj must be handled by MoERunner.load_weights for EP
409 ".mlp.gate_proj": (".mlp.gate_up_proj", 0),
410 ".mlp.up_proj": (".mlp.gate_up_proj", 1),
411 ".shared_experts.gate_proj": (".shared_experts.gate_up_proj", 0),
412 ".shared_experts.up_proj": (".shared_experts.gate_up_proj", 1),
413 }
414 )
415
416 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
417 super().__init__()
418
419 config = vllm_config.model_config.hf_config
420 cache_config = vllm_config.cache_config
421 quant_config = vllm_config.quant_config
422 enable_eplb = vllm_config.parallel_config.enable_eplb
423 self.config = config
424
425 self.vocab_size = config.vocab_size
426
427 if get_pp_group().is_first_rank:
428 self.embed_tokens = VocabParallelEmbedding(
429 config.vocab_size, config.hidden_size, prefix=f"{prefix}.embed_tokens"
430 )
431 else:
432 self.embed_tokens = PPMissingLayer()
433
434 self.start_layer, self.end_layer, self.layers = make_layers(
435 config.num_hidden_layers,
436 lambda prefix: Glm4MoeDecoderLayer(
437 config=config,
438 cache_config=cache_config,
439 quant_config=quant_config,
440 prefix=prefix,
441 enable_eplb=enable_eplb,
442 ),
443 prefix=f"{prefix}.layers",
444 )
445
446 self.is_fused_shared_expert_enabled = is_model_fused_shared_expert_compatible(
447 self.layers,
448 Glm4MoE,
449 "mlp",
450 )
451
452 if get_pp_group().is_last_rank:
453 self.norm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
454 else:
455 self.norm = PPMissingLayer()
456 self.make_empty_intermediate_tensors = make_empty_intermediate_tensors_factory(
457 ["hidden_states", "residual"], config.hidden_size
458 )
459
460 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
461 return self.embed_tokens(input_ids)
462
463 def forward(
464 self,
465 input_ids: torch.Tensor | None,
466 positions: torch.Tensor,
467 intermediate_tensors: IntermediateTensors | None = None,
468 inputs_embeds: torch.Tensor | None = None,
469 ) -> torch.Tensor | IntermediateTensors:
470 if get_pp_group().is_first_rank:
471 if inputs_embeds is not None:
472 hidden_states = inputs_embeds
473 else:
474 hidden_states = self.embed_input_ids(input_ids)
475 residual = None
476 else:
477 assert intermediate_tensors is not None
478 hidden_states = intermediate_tensors["hidden_states"]
479 residual = intermediate_tensors["residual"]
480
481 for layer in islice(self.layers, self.start_layer, self.end_layer):
482 hidden_states, residual = layer(positions, hidden_states, residual)
483
484 if not get_pp_group().is_last_rank:
485 return IntermediateTensors(
486 {"hidden_states": hidden_states, "residual": residual}
487 )
488
489 hidden_states, _ = self.norm(hidden_states, residual)
490 return hidden_states
491
492 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
493 weights = maybe_fuse_shared_experts(
494 skip_spec_layers(weights, self.config),
495 n_routed_experts=self.config.n_routed_experts,
496 n_shared_experts=self.config.n_shared_experts or 1,
497 enabled=self.is_fused_shared_expert_enabled,
498 )
499 loader = AutoWeightsLoader(self)
500 return loader.load_weights(weights, mapper=self.hf_to_vllm_mapper)
501
502
503 class Glm4MixtureOfExperts(MixtureOfExperts):
504 moe_mlp_layers: list[Glm4MoE]
505
506 def extract_moe_parameters(self, example_moe: Glm4MoE | None) -> None:
507 if example_moe is None:
508 raise RuntimeError("No Glm4MoE layer found in model.layers.")
509 else:
510 self.num_logical_experts = example_moe.n_logical_experts
511 self.num_physical_experts = example_moe.n_physical_experts
512 self.num_local_physical_experts = example_moe.n_local_physical_experts
513 self.num_routed_experts = example_moe.n_routed_experts
514 self.num_shared_experts = example_moe.n_shared_experts
515 self.num_redundant_experts = example_moe.n_redundant_experts
516
517 def update_physical_experts_metadata(
518 self,
519 num_physical_experts: int,
520 num_local_physical_experts: int,
521 ) -> None:
522 assert self.num_local_physical_experts == num_local_physical_experts
523 self.num_physical_experts = num_physical_experts
524 self.num_local_physical_experts = num_local_physical_experts
525 self.num_redundant_experts = num_physical_experts - self.num_logical_experts
526 for moe in self.moe_mlp_layers:
527 moe.n_local_physical_experts = num_local_physical_experts
528 moe.n_physical_experts = num_physical_experts
529 moe.n_redundant_experts = self.num_redundant_experts
530 moe.experts.update_expert_map()
531
532
533 class Glm4MoeForCausalLM(nn.Module, SupportsPP, SupportsLoRA, Glm4MixtureOfExperts):
534 packed_modules_mapping = {
535 "qkv_proj": [
536 "q_proj",
537 "k_proj",
538 "v_proj",
539 ],
540 "gate_up_proj": [
541 "gate_proj",
542 "up_proj",
543 ],
544 }
545
546 fall_back_to_pt_during_load = False
547
548 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
549 super().__init__()
550 config = vllm_config.model_config.hf_config
551 quant_config = vllm_config.quant_config
552 self.config = config
553 self.quant_config = quant_config
554 self.model = Glm4MoeModel(
555 vllm_config=vllm_config, prefix=maybe_prefix(prefix, "model")
556 )
557 if get_pp_group().is_last_rank:
558 self.lm_head = ParallelLMHead(
559 config.vocab_size,
560 config.hidden_size,
561 quant_config=quant_config,
562 prefix=maybe_prefix(prefix, "lm_head"),
563 )
564 else:
565 self.lm_head = PPMissingLayer()
566 self.logits_processor = LogitsProcessor(config.vocab_size)
567 self.make_empty_intermediate_tensors = (
568 self.model.make_empty_intermediate_tensors
569 )
570 # Set MoE hyperparameters
571 self.num_moe_layers = config.num_hidden_layers - config.first_k_dense_replace
572 self.num_expert_groups = config.n_group
573
574 self.moe_layers = []
575 self.moe_mlp_layers: list[Glm4MoE] = []
576
577 example_moe = None
578 for layer in self.model.layers:
579 if isinstance(layer, PPMissingLayer):
580 continue
581
582 assert isinstance(layer, Glm4MoeDecoderLayer)
583 if isinstance(layer.mlp, Glm4MoE):
584 # Pick last one layer since the first ones may be dense layers.
585 example_moe = layer.mlp
586 self.moe_mlp_layers.append(layer.mlp)
587 self.moe_layers.append(layer.mlp.experts)
588
589 self.extract_moe_parameters(example_moe)
590
591 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
592 return self.model.embed_input_ids(input_ids)
593
594 def forward(
595 self,
596 input_ids: torch.Tensor | None,
597 positions: torch.Tensor,
598 intermediate_tensors: IntermediateTensors | None = None,
599 inputs_embeds: torch.Tensor | None = None,
600 ) -> torch.Tensor | IntermediateTensors:
601 hidden_states = self.model(
602 input_ids, positions, intermediate_tensors, inputs_embeds
603 )
604 return hidden_states
605
606 def compute_logits(
607 self,
608 hidden_states: torch.Tensor,
609 ) -> torch.Tensor | None:
610 logits = self.logits_processor(self.lm_head, hidden_states)
611 return logits
612
613 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
614 loader = AutoWeightsLoader(self)
615 return loader.load_weights(weights)