main
py 302 lines 10.9 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 # Copyright 2025 The Zhipu AI 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-0414 model compatible with HuggingFace weights."""
25
26 from collections.abc import Iterable
27
28 import torch
29 from torch import nn
30 from transformers import Glm4Config
31
32 from vllm.compilation.decorators import support_torch_compile
33 from vllm.config import CacheConfig, VllmConfig
34 from vllm.distributed import get_pp_group, get_tensor_model_parallel_world_size
35 from vllm.model_executor.layers.attention import Attention
36 from vllm.model_executor.layers.layernorm import RMSNorm
37 from vllm.model_executor.layers.linear import QKVParallelLinear, RowParallelLinear
38 from vllm.model_executor.layers.logits_processor import LogitsProcessor
39 from vllm.model_executor.layers.quantization import QuantizationConfig
40 from vllm.model_executor.layers.rotary_embedding import get_rope
41 from vllm.model_executor.layers.vocab_parallel_embedding import ParallelLMHead
42 from vllm.sequence import IntermediateTensors
43 from vllm.v1.attention.backend import AttentionType
44
45 from .interfaces import SupportsLoRA, SupportsPP
46 from .llama import LlamaMLP as Glm4MLP
47 from .llama import LlamaModel
48 from .utils import AutoWeightsLoader, PPMissingLayer, WeightsMapper, maybe_prefix
49
50
51 class Glm4Attention(nn.Module):
52 def __init__(
53 self,
54 config: Glm4Config,
55 hidden_size: int,
56 num_heads: int,
57 num_kv_heads: int,
58 max_position: int = 4096 * 32,
59 head_dim: int | None = None,
60 qkv_bias: bool = False,
61 cache_config: CacheConfig | None = None,
62 quant_config: QuantizationConfig | None = None,
63 prefix: str = "",
64 attn_type: str = AttentionType.DECODER,
65 ) -> None:
66 super().__init__()
67 self.hidden_size = hidden_size
68 tp_size = get_tensor_model_parallel_world_size()
69 self.total_num_heads = num_heads
70 assert self.total_num_heads % tp_size == 0
71 self.num_heads = self.total_num_heads // tp_size
72 self.total_num_kv_heads = num_kv_heads
73 if self.total_num_kv_heads >= tp_size:
74 # Number of KV heads is greater than TP size, so we partition
75 # the KV heads across multiple tensor parallel GPUs.
76 assert self.total_num_kv_heads % tp_size == 0
77 else:
78 # Number of KV heads is less than TP size, so we replicate
79 # the KV heads across multiple tensor parallel GPUs.
80 assert tp_size % self.total_num_kv_heads == 0
81
82 rope_params = getattr(config, "rope_parameters", None)
83 if isinstance(rope_params, dict) and "partial_rotary_factor" in rope_params:
84 config.rope_parameters.setdefault(
85 "partial_rotary_factor", rope_params["partial_rotary_factor"]
86 )
87 else:
88 config.rope_parameters.setdefault("partial_rotary_factor", 0.5)
89
90 self.num_kv_heads = max(1, self.total_num_kv_heads // tp_size)
91 self.head_dim = head_dim or hidden_size // self.total_num_heads
92 self.q_size = self.num_heads * self.head_dim
93 self.kv_size = self.num_kv_heads * self.head_dim
94 self.scaling = self.head_dim**-0.5
95 self.qkv_proj = QKVParallelLinear(
96 hidden_size,
97 self.head_dim,
98 self.total_num_heads,
99 self.total_num_kv_heads,
100 bias=qkv_bias,
101 quant_config=quant_config,
102 prefix=f"{prefix}.qkv_proj",
103 )
104 self.o_proj = RowParallelLinear(
105 self.total_num_heads * self.head_dim,
106 hidden_size,
107 bias=False,
108 quant_config=quant_config,
109 prefix=f"{prefix}.o_proj",
110 )
111 self.rotary_emb = get_rope(
112 self.head_dim,
113 max_position=max_position,
114 rope_parameters=config.rope_parameters,
115 is_neox_style=False,
116 )
117 self.attn = Attention(
118 self.num_heads,
119 self.head_dim,
120 self.scaling,
121 num_kv_heads=self.num_kv_heads,
122 cache_config=cache_config,
123 quant_config=quant_config,
124 prefix=f"{prefix}.attn",
125 attn_type=attn_type,
126 )
127
128 def forward(
129 self,
130 positions: torch.Tensor,
131 hidden_states: torch.Tensor,
132 ) -> torch.Tensor:
133 qkv, _ = self.qkv_proj(hidden_states)
134 q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1)
135 q, k = self.rotary_emb(positions, q, k)
136 attn_output = self.attn(q, k, v)
137 output, _ = self.o_proj(attn_output)
138 return output
139
140
141 class Glm4DecoderLayer(nn.Module):
142 def __init__(
143 self,
144 vllm_config: VllmConfig,
145 prefix: str = "",
146 config: Glm4Config | None = None,
147 ) -> None:
148 super().__init__()
149
150 config = config or vllm_config.model_config.hf_config
151 cache_config = vllm_config.cache_config
152 quant_config = vllm_config.quant_config
153
154 self.hidden_size = config.hidden_size
155
156 self.self_attn = Glm4Attention(
157 config=config,
158 hidden_size=self.hidden_size,
159 num_heads=config.num_attention_heads,
160 max_position=config.max_position_embeddings,
161 num_kv_heads=config.num_key_value_heads,
162 qkv_bias=getattr(config, "attention_bias", False),
163 head_dim=getattr(config, "head_dim", None),
164 cache_config=cache_config,
165 quant_config=quant_config,
166 prefix=f"{prefix}.self_attn",
167 attn_type=AttentionType.DECODER,
168 )
169 self.mlp = Glm4MLP(
170 hidden_size=self.hidden_size,
171 intermediate_size=config.intermediate_size,
172 hidden_act=config.hidden_act,
173 quant_config=quant_config,
174 prefix=f"{prefix}.mlp",
175 )
176 self.input_layernorm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
177 self.post_attention_layernorm = RMSNorm(
178 config.hidden_size, eps=config.rms_norm_eps
179 )
180 self.post_self_attn_layernorm = RMSNorm(
181 config.hidden_size, eps=config.rms_norm_eps
182 )
183 self.post_mlp_layernorm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
184
185 def forward(
186 self,
187 positions: torch.Tensor,
188 hidden_states: torch.Tensor,
189 residual: torch.Tensor | None,
190 ) -> tuple[torch.Tensor, torch.Tensor]:
191 # Self Attention
192 if residual is None:
193 residual = hidden_states
194 hidden_states = self.input_layernorm(hidden_states)
195 else:
196 hidden_states, residual = self.input_layernorm(hidden_states, residual)
197 hidden_states = self.self_attn(
198 positions=positions,
199 hidden_states=hidden_states,
200 )
201
202 hidden_states = self.post_self_attn_layernorm(hidden_states)
203
204 # Fully Connected
205 hidden_states, residual = self.post_attention_layernorm(hidden_states, residual)
206 hidden_states = self.mlp(hidden_states)
207 hidden_states = self.post_mlp_layernorm(hidden_states)
208
209 return hidden_states, residual
210
211
212 ALL_DECODER_LAYER_TYPES = {
213 "attention": Glm4DecoderLayer,
214 }
215
216
217 @support_torch_compile(
218 dynamic_arg_dims={
219 "input_ids": 0,
220 "positions": -1,
221 "intermediate_tensors": 0,
222 "inputs_embeds": 0,
223 }
224 )
225 class Glm4Model(LlamaModel):
226 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
227 super().__init__(
228 vllm_config=vllm_config, prefix=prefix, layer_type=Glm4DecoderLayer
229 )
230
231
232 class Glm4ForCausalLM(nn.Module, SupportsLoRA, SupportsPP):
233 packed_modules_mapping = {
234 "qkv_proj": ["q_proj", "k_proj", "v_proj"],
235 "gate_up_proj": ["gate_proj", "up_proj"],
236 }
237
238 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
239 super().__init__()
240 config = vllm_config.model_config.hf_config
241 quant_config = vllm_config.quant_config
242
243 self.config = config
244
245 self.quant_config = quant_config
246 self.model = Glm4Model(
247 vllm_config=vllm_config, prefix=maybe_prefix(prefix, "model")
248 )
249
250 if get_pp_group().is_last_rank:
251 self.lm_head = ParallelLMHead(
252 config.vocab_size,
253 config.hidden_size,
254 quant_config=quant_config,
255 prefix=maybe_prefix(prefix, "lm_head"),
256 )
257 if config.tie_word_embeddings:
258 self.lm_head = self.lm_head.tie_weights(self.model.embed_tokens)
259 else:
260 self.lm_head = PPMissingLayer()
261
262 self.logits_processor = LogitsProcessor(config.vocab_size)
263
264 self.make_empty_intermediate_tensors = (
265 self.model.make_empty_intermediate_tensors
266 )
267
268 # Drop the speculative (MTP) layers, which are loaded by the draft
269 # model instead. They are appended after the main decoder layers.
270 num_nextn_layers = getattr(config, "num_nextn_predict_layers", 0)
271 self.hf_to_vllm_mapper = WeightsMapper(
272 orig_to_new_prefix={
273 f"model.layers.{config.num_hidden_layers + i}.": None
274 for i in range(num_nextn_layers)
275 }
276 )
277
278 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
279 return self.model.embed_input_ids(input_ids)
280
281 def forward(
282 self,
283 input_ids: torch.Tensor | None,
284 positions: torch.Tensor,
285 intermediate_tensors: IntermediateTensors | None = None,
286 inputs_embeds: torch.Tensor | None = None,
287 ) -> torch.Tensor | IntermediateTensors:
288 hidden_states = self.model(
289 input_ids, positions, intermediate_tensors, inputs_embeds
290 )
291 return hidden_states
292
293 def compute_logits(
294 self,
295 hidden_states: torch.Tensor,
296 ) -> torch.Tensor | None:
297 logits = self.logits_processor(self.lm_head, hidden_states)
298 return logits
299
300 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
301 loader = AutoWeightsLoader(self)
302 return loader.load_weights(weights, mapper=self.hf_to_vllm_mapper)