main
py 521 lines 18.3 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 # Adapted from
5 # https://github.com/huggingface/transformers/blob/v4.28.0/src/transformers/models/qwen2/modeling_qwen2.py
6 # Copyright 2024 The Qwen team.
7 # Copyright 2023 The vLLM team.
8 # Copyright 2022 EleutherAI and the HuggingFace Inc. team. All rights reserved.
9 #
10 # This code is based on EleutherAI's GPT-NeoX library and the GPT-NeoX
11 # and OPT implementations in this library. It has been modified from its
12 # original forms to accommodate minor architectural differences compared
13 # to GPT-NeoX and OPT used by the Meta AI team that trained the model.
14 #
15 # Licensed under the Apache License, Version 2.0 (the "License");
16 # you may not use this file except in compliance with the License.
17 # You may obtain a copy of the License at
18 #
19 # http://www.apache.org/licenses/LICENSE-2.0
20 #
21 # Unless required by applicable law or agreed to in writing, software
22 # distributed under the License is distributed on an "AS IS" BASIS,
23 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24 # See the License for the specific language governing permissions and
25 # limitations under the License.
26 """Inference-only Qwen2 model compatible with HuggingFace weights."""
27
28 from collections.abc import Iterable
29 from itertools import islice
30 from typing import Any
31
32 import torch
33 from torch import nn
34 from transformers import Qwen2Config
35
36 from vllm.compilation.decorators import support_torch_compile
37 from vllm.config import CacheConfig, VllmConfig
38 from vllm.distributed import get_pp_group, get_tensor_model_parallel_world_size
39 from vllm.model_executor.layers.activation import SiluAndMul
40 from vllm.model_executor.layers.attention import (
41 Attention,
42 EncoderOnlyAttention,
43 )
44 from vllm.model_executor.layers.layernorm import RMSNorm
45 from vllm.model_executor.layers.linear import (
46 MergedColumnParallelLinear,
47 QKVParallelLinear,
48 RowParallelLinear,
49 )
50 from vllm.model_executor.layers.logits_processor import LogitsProcessor
51 from vllm.model_executor.layers.quantization import QuantizationConfig
52 from vllm.model_executor.layers.rotary_embedding import get_rope
53 from vllm.model_executor.layers.vocab_parallel_embedding import (
54 ParallelLMHead,
55 VocabParallelEmbedding,
56 )
57 from vllm.sequence import IntermediateTensors
58 from vllm.transformers_utils.config import set_default_rope_theta
59 from vllm.v1.attention.backend import AttentionType
60
61 from .interfaces import (
62 EagleModelMixin,
63 SupportsEagle,
64 SupportsEagle3,
65 SupportsLoRA,
66 SupportsPP,
67 SupportsQuant,
68 )
69 from .utils import (
70 AutoWeightsLoader,
71 PPMissingLayer,
72 WeightsMapper,
73 extract_layer_index,
74 make_empty_intermediate_tensors_factory,
75 make_layers,
76 maybe_prefix,
77 spec_decode_needs_target_embed,
78 )
79
80
81 class Qwen2MLP(nn.Module):
82 def __init__(
83 self,
84 hidden_size: int,
85 intermediate_size: int,
86 hidden_act: str,
87 quant_config: QuantizationConfig | None = None,
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 prefix=f"{prefix}.down_proj",
104 )
105 if hidden_act != "silu":
106 raise ValueError(
107 f"Unsupported activation: {hidden_act}. Only silu is supported for now."
108 )
109 self.act_fn = SiluAndMul()
110
111 def forward(self, x):
112 gate_up, _ = self.gate_up_proj(x)
113 x = self.act_fn(gate_up)
114 x, _ = self.down_proj(x)
115 return x
116
117
118 class Qwen2Attention(nn.Module):
119 def __init__(
120 self,
121 hidden_size: int,
122 num_heads: int,
123 num_kv_heads: int,
124 rope_parameters: dict[str, Any],
125 max_position: int = 4096 * 32,
126 cache_config: CacheConfig | None = None,
127 quant_config: QuantizationConfig | None = None,
128 prefix: str = "",
129 attn_type: str = AttentionType.DECODER,
130 dual_chunk_attention_config: dict[str, Any] | None = None,
131 qk_norm: bool = False,
132 rms_norm_eps: float = 1e-6,
133 ) -> None:
134 super().__init__()
135 self.hidden_size = hidden_size
136 tp_size = get_tensor_model_parallel_world_size()
137 self.total_num_heads = num_heads
138 assert self.total_num_heads % tp_size == 0
139 self.num_heads = self.total_num_heads // tp_size
140 self.total_num_kv_heads = num_kv_heads
141 if self.total_num_kv_heads >= tp_size:
142 # Number of KV heads is greater than TP size, so we partition
143 # the KV heads across multiple tensor parallel GPUs.
144 assert self.total_num_kv_heads % tp_size == 0
145 else:
146 # Number of KV heads is less than TP size, so we replicate
147 # the KV heads across multiple tensor parallel GPUs.
148 assert tp_size % self.total_num_kv_heads == 0
149 self.num_kv_heads = max(1, self.total_num_kv_heads // tp_size)
150 self.head_dim = hidden_size // self.total_num_heads
151 self.q_size = self.num_heads * self.head_dim
152 self.kv_size = self.num_kv_heads * self.head_dim
153 self.scaling = self.head_dim**-0.5
154 self.dual_chunk_attention_config = dual_chunk_attention_config
155 self.qk_norm = qk_norm
156
157 self.qkv_proj = QKVParallelLinear(
158 hidden_size,
159 self.head_dim,
160 self.total_num_heads,
161 self.total_num_kv_heads,
162 bias=True,
163 quant_config=quant_config,
164 prefix=f"{prefix}.qkv_proj",
165 )
166 self.o_proj = RowParallelLinear(
167 self.total_num_heads * self.head_dim,
168 hidden_size,
169 bias=False,
170 quant_config=quant_config,
171 prefix=f"{prefix}.o_proj",
172 )
173
174 # QK Normalization support (used in BAGEL and some other models)
175 if self.qk_norm:
176 self.q_norm = RMSNorm(self.head_dim, eps=rms_norm_eps)
177 self.k_norm = RMSNorm(self.head_dim, eps=rms_norm_eps)
178
179 self.rotary_emb = get_rope(
180 self.head_dim,
181 max_position=max_position,
182 rope_parameters=rope_parameters,
183 dual_chunk_attention_config=dual_chunk_attention_config,
184 )
185 attn_cls = (
186 EncoderOnlyAttention
187 if attn_type == AttentionType.ENCODER_ONLY
188 else Attention
189 )
190 attention_kwargs: dict[str, Any] = {}
191 if dual_chunk_attention_config:
192 attention_kwargs = {
193 "layer_idx": extract_layer_index(prefix),
194 "dual_chunk_attention_config": dual_chunk_attention_config,
195 }
196 self.attn = attn_cls(
197 self.num_heads,
198 self.head_dim,
199 self.scaling,
200 num_kv_heads=self.num_kv_heads,
201 cache_config=cache_config,
202 quant_config=quant_config,
203 attn_type=attn_type,
204 prefix=f"{prefix}.attn",
205 **attention_kwargs,
206 )
207
208 def forward(
209 self,
210 positions: torch.Tensor,
211 hidden_states: torch.Tensor,
212 ) -> torch.Tensor:
213 qkv, _ = self.qkv_proj(hidden_states)
214 q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1)
215
216 # Apply QK normalization if enabled (before RoPE)
217 if self.qk_norm:
218 # Reshape to apply per-head normalization
219 # q shape: (total_tokens, q_size) -> (total_tokens, num_heads, head_dim)
220 total_tokens = q.shape[0]
221 q = q.view(total_tokens, self.num_heads, self.head_dim)
222 k = k.view(total_tokens, self.num_kv_heads, self.head_dim)
223
224 # Apply normalization
225 q = self.q_norm(q)
226 k = self.k_norm(k)
227
228 # Reshape back
229 q = q.view(total_tokens, self.q_size)
230 k = k.view(total_tokens, self.kv_size)
231
232 q, k = self.rotary_emb(positions, q, k)
233 attn_output = self.attn(q, k, v)
234 output, _ = self.o_proj(attn_output)
235 return output
236
237
238 class Qwen2DecoderLayer(nn.Module):
239 def __init__(
240 self,
241 config: Qwen2Config,
242 cache_config: CacheConfig | None = None,
243 quant_config: QuantizationConfig | None = None,
244 prefix: str = "",
245 ) -> None:
246 super().__init__()
247 self.hidden_size = config.hidden_size
248 set_default_rope_theta(config, default_theta=1000000)
249 dual_chunk_attention_config = getattr(
250 config, "dual_chunk_attention_config", None
251 )
252
253 # By default, Qwen2 uses causal attention as it is a decoder-only model.
254 # You can override the HF config with `is_causal=False` to enable
255 # bidirectional attention, which is used in some embedding models
256 # (e.g. Alibaba-NLP/gte-Qwen2-7B-instruct)
257 if getattr(config, "is_causal", True):
258 attn_type = AttentionType.DECODER
259 else:
260 attn_type = AttentionType.ENCODER_ONLY
261
262 # Check if QK normalization is enabled (used in BAGEL and some other models)
263 qk_norm = getattr(config, "qk_norm", False)
264
265 self.self_attn = Qwen2Attention(
266 hidden_size=self.hidden_size,
267 num_heads=config.num_attention_heads,
268 max_position=config.max_position_embeddings,
269 num_kv_heads=config.num_key_value_heads,
270 cache_config=cache_config,
271 quant_config=quant_config,
272 rope_parameters=config.rope_parameters,
273 prefix=f"{prefix}.self_attn",
274 attn_type=attn_type,
275 dual_chunk_attention_config=dual_chunk_attention_config,
276 qk_norm=qk_norm,
277 rms_norm_eps=config.rms_norm_eps,
278 )
279 self.mlp = Qwen2MLP(
280 hidden_size=self.hidden_size,
281 intermediate_size=config.intermediate_size,
282 hidden_act=config.hidden_act,
283 quant_config=quant_config,
284 prefix=f"{prefix}.mlp",
285 )
286 self.input_layernorm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
287 self.post_attention_layernorm = RMSNorm(
288 config.hidden_size, eps=config.rms_norm_eps
289 )
290
291 def forward(
292 self,
293 positions: torch.Tensor,
294 hidden_states: torch.Tensor,
295 residual: torch.Tensor | None,
296 ) -> tuple[torch.Tensor, torch.Tensor]:
297 # Self Attention
298 if residual is None:
299 residual = hidden_states
300 hidden_states = self.input_layernorm(hidden_states)
301 else:
302 hidden_states, residual = self.input_layernorm(hidden_states, residual)
303 hidden_states = self.self_attn(
304 positions=positions,
305 hidden_states=hidden_states,
306 )
307
308 # Fully Connected
309 hidden_states, residual = self.post_attention_layernorm(hidden_states, residual)
310 hidden_states = self.mlp(hidden_states)
311 return hidden_states, residual
312
313
314 @support_torch_compile(
315 dynamic_arg_dims={
316 "input_ids": {0: "b"},
317 # positions is of shape (3, seq_len) if mrope is enabled for qwen2-vl,
318 # otherwise (seq_len, ).
319 "positions": {-1: "b"},
320 "intermediate_tensors": {0: "b"},
321 "inputs_embeds": {0: "b"},
322 }
323 )
324 class Qwen2Model(nn.Module, EagleModelMixin):
325 hf_to_vllm_mapper = WeightsMapper(
326 orig_to_new_stacked={
327 # weight_name: (param_name, shard_id)
328 ".q_proj": (".qkv_proj", "q"),
329 ".k_proj": (".qkv_proj", "k"),
330 ".v_proj": (".qkv_proj", "v"),
331 ".gate_proj": (".gate_up_proj", 0),
332 ".up_proj": (".gate_up_proj", 1),
333 }
334 )
335 supports_aux_hidden_states_over_pp = True
336
337 def __init__(
338 self,
339 *,
340 vllm_config: VllmConfig,
341 prefix: str = "",
342 decoder_layer_type: type[nn.Module] = Qwen2DecoderLayer,
343 ):
344 super().__init__()
345
346 config = vllm_config.model_config.hf_config.get_text_config()
347 cache_config = vllm_config.cache_config
348 quant_config = vllm_config.quant_config
349
350 # TODO (@robertgshaw2): see if this can be moved out
351 if len(set(getattr(config, "layer_types", []))) > 1:
352 assert config.max_window_layers == config.num_hidden_layers, (
353 "Sliding window for some but all layers is not supported. "
354 "This model uses sliding window but `max_window_layers` = {} "
355 "is less than `num_hidden_layers` = {}. Please open an issue "
356 "to discuss this feature.".format(
357 config.max_window_layers,
358 config.num_hidden_layers,
359 )
360 )
361
362 self.config = config
363 self.quant_config = quant_config
364 self.vocab_size = config.vocab_size
365
366 if (
367 get_pp_group().is_first_rank
368 or (config.tie_word_embeddings and get_pp_group().is_last_rank)
369 or spec_decode_needs_target_embed(vllm_config)
370 ):
371 self.embed_tokens = VocabParallelEmbedding(
372 config.vocab_size,
373 config.hidden_size,
374 quant_config=quant_config,
375 prefix=f"{prefix}.embed_tokens",
376 )
377 else:
378 self.embed_tokens = PPMissingLayer()
379
380 self.start_layer, self.end_layer, self.layers = make_layers(
381 config.num_hidden_layers,
382 lambda prefix: decoder_layer_type(
383 config=config,
384 cache_config=cache_config,
385 quant_config=quant_config,
386 prefix=prefix,
387 ),
388 prefix=f"{prefix}.layers",
389 )
390
391 self.make_empty_intermediate_tensors = make_empty_intermediate_tensors_factory(
392 ["hidden_states", "residual"], config.hidden_size
393 )
394 if get_pp_group().is_last_rank:
395 self.norm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
396 else:
397 self.norm = PPMissingLayer()
398
399 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
400 return self.embed_tokens(input_ids)
401
402 def forward(
403 self,
404 input_ids: torch.Tensor | None,
405 positions: torch.Tensor,
406 intermediate_tensors: IntermediateTensors | None = None,
407 inputs_embeds: torch.Tensor | None = None,
408 ) -> torch.Tensor | IntermediateTensors:
409 if get_pp_group().is_first_rank:
410 if inputs_embeds is not None:
411 hidden_states = inputs_embeds
412 else:
413 hidden_states = self.embed_input_ids(input_ids)
414 residual = None
415 else:
416 assert intermediate_tensors is not None
417 hidden_states = intermediate_tensors["hidden_states"]
418 residual = intermediate_tensors["residual"]
419
420 remote_aux = self.collect_remote_aux_hidden_states(intermediate_tensors)
421
422 aux_hidden_states: list[torch.Tensor] = []
423 if get_pp_group().is_first_rank:
424 self._maybe_add_hidden_state(
425 aux_hidden_states, self.start_layer, hidden_states, residual
426 )
427 for idx, layer in enumerate(
428 islice(self.layers, self.start_layer, self.end_layer),
429 start=self.start_layer,
430 ):
431 hidden_states, residual = layer(positions, hidden_states, residual)
432 self._maybe_add_hidden_state(
433 aux_hidden_states, idx + 1, hidden_states, residual
434 )
435
436 if not get_pp_group().is_last_rank:
437 return IntermediateTensors(
438 {
439 "hidden_states": hidden_states,
440 "residual": residual,
441 **self.pack_local_aux_hidden_states(aux_hidden_states),
442 }
443 )
444
445 hidden_states, _ = self.norm(hidden_states, residual)
446
447 aux_hidden_states = remote_aux + aux_hidden_states
448 if len(aux_hidden_states) > 0:
449 return hidden_states, aux_hidden_states
450
451 return hidden_states
452
453 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
454 loader = AutoWeightsLoader(self)
455 return loader.load_weights(weights, mapper=self.hf_to_vllm_mapper)
456
457
458 class Qwen2ForCausalLM(
459 nn.Module, SupportsLoRA, SupportsPP, SupportsEagle, SupportsEagle3, SupportsQuant
460 ):
461 hf_to_vllm_mapper = Qwen2Model.hf_to_vllm_mapper
462 packed_modules_mapping = {
463 "qkv_proj": ["q_proj", "k_proj", "v_proj"],
464 "gate_up_proj": ["gate_proj", "up_proj"],
465 }
466
467 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
468 super().__init__()
469 config = vllm_config.model_config.hf_config.get_text_config()
470 quant_config = vllm_config.quant_config
471
472 self.config = config
473
474 self.quant_config = quant_config
475 self.model = Qwen2Model(
476 vllm_config=vllm_config, prefix=maybe_prefix(prefix, "model")
477 )
478
479 if get_pp_group().is_last_rank:
480 self.lm_head = ParallelLMHead(
481 config.vocab_size,
482 config.hidden_size,
483 quant_config=quant_config,
484 prefix=maybe_prefix(prefix, "lm_head"),
485 )
486 if config.tie_word_embeddings:
487 self.lm_head = self.lm_head.tie_weights(self.model.embed_tokens)
488 else:
489 self.lm_head = PPMissingLayer()
490
491 self.logits_processor = LogitsProcessor(config.vocab_size)
492
493 self.make_empty_intermediate_tensors = (
494 self.model.make_empty_intermediate_tensors
495 )
496
497 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
498 return self.model.embed_input_ids(input_ids)
499
500 def forward(
501 self,
502 input_ids: torch.Tensor | None,
503 positions: torch.Tensor,
504 intermediate_tensors: IntermediateTensors | None = None,
505 inputs_embeds: torch.Tensor | None = None,
506 ) -> torch.Tensor | IntermediateTensors:
507 hidden_states = self.model(
508 input_ids, positions, intermediate_tensors, inputs_embeds
509 )
510 return hidden_states
511
512 def compute_logits(
513 self,
514 hidden_states: torch.Tensor,
515 ) -> torch.Tensor | None:
516 logits = self.logits_processor(self.lm_head, hidden_states)
517 return logits
518
519 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
520 loader = AutoWeightsLoader(self)
521 return loader.load_weights(weights)