main
py 340 lines 12.4 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 # Copyright 2024 The Qwen 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 Qwen3 model compatible with HuggingFace weights."""
25
26 from collections.abc import Iterable
27 from typing import Any
28
29 import torch
30 from torch import nn
31 from transformers import Qwen3Config
32
33 from vllm.compilation.decorators import support_torch_compile
34 from vllm.config import CacheConfig, VllmConfig
35 from vllm.distributed import get_pp_group, get_tensor_model_parallel_world_size
36 from vllm.logger import init_logger
37 from vllm.model_executor.layers.attention.encoder_only_attention import (
38 Attention,
39 EncoderOnlyAttention,
40 )
41 from vllm.model_executor.layers.layernorm import RMSNorm
42 from vllm.model_executor.layers.linear import QKVParallelLinear, RowParallelLinear
43 from vllm.model_executor.layers.logits_processor import LogitsProcessor
44 from vllm.model_executor.layers.quantization import QuantizationConfig
45 from vllm.model_executor.layers.rotary_embedding import get_rope
46 from vllm.model_executor.layers.vocab_parallel_embedding import ParallelLMHead
47 from vllm.sequence import IntermediateTensors
48 from vllm.transformers_utils.config import set_default_rope_theta
49 from vllm.v1.attention.backend import AttentionType
50
51 from .interfaces import (
52 LocalArgmaxMixin,
53 SupportsEagle,
54 SupportsEagle3,
55 SupportsLoRA,
56 SupportsPP,
57 )
58 from .qwen2 import Qwen2MLP as Qwen3MLP
59 from .qwen2 import Qwen2Model
60 from .utils import AutoWeightsLoader, PPMissingLayer, extract_layer_index, maybe_prefix
61
62 logger = init_logger(__name__)
63
64
65 class Qwen3Attention(nn.Module):
66 def __init__(
67 self,
68 hidden_size: int,
69 num_heads: int,
70 num_kv_heads: int,
71 rope_parameters: dict,
72 max_position: int = 4096 * 32,
73 head_dim: int | None = None,
74 rms_norm_eps: float = 1e-06,
75 qkv_bias: bool = False,
76 cache_config: CacheConfig | None = None,
77 quant_config: QuantizationConfig | None = None,
78 prefix: str = "",
79 attn_type: str = AttentionType.DECODER,
80 dual_chunk_attention_config: dict[str, Any] | None = None,
81 per_layer_sliding_window: int | None = None,
82 ) -> None:
83 super().__init__()
84 self.hidden_size = hidden_size
85 tp_size = get_tensor_model_parallel_world_size()
86 self.total_num_heads = num_heads
87 assert self.total_num_heads % tp_size == 0
88 self.num_heads = self.total_num_heads // tp_size
89 self.total_num_kv_heads = num_kv_heads
90 if self.total_num_kv_heads >= tp_size:
91 # Number of KV heads is greater than TP size, so we partition
92 # the KV heads across multiple tensor parallel GPUs.
93 assert self.total_num_kv_heads % tp_size == 0
94 else:
95 # Number of KV heads is less than TP size, so we replicate
96 # the KV heads across multiple tensor parallel GPUs.
97 assert tp_size % self.total_num_kv_heads == 0
98 self.num_kv_heads = max(1, self.total_num_kv_heads // tp_size)
99 self.head_dim = head_dim or hidden_size // self.total_num_heads
100 self.q_size = self.num_heads * self.head_dim
101 self.kv_size = self.num_kv_heads * self.head_dim
102 self.scaling = self.head_dim**-0.5
103 self.dual_chunk_attention_config = dual_chunk_attention_config
104
105 self.qkv_proj = QKVParallelLinear(
106 hidden_size,
107 self.head_dim,
108 self.total_num_heads,
109 self.total_num_kv_heads,
110 bias=qkv_bias,
111 quant_config=quant_config,
112 prefix=f"{prefix}.qkv_proj",
113 )
114 self.o_proj = RowParallelLinear(
115 self.total_num_heads * self.head_dim,
116 hidden_size,
117 bias=False,
118 quant_config=quant_config,
119 prefix=f"{prefix}.o_proj",
120 )
121
122 self.rotary_emb = get_rope(
123 self.head_dim,
124 max_position=max_position,
125 rope_parameters=rope_parameters,
126 dual_chunk_attention_config=dual_chunk_attention_config,
127 )
128 attn_cls = (
129 EncoderOnlyAttention
130 if attn_type == AttentionType.ENCODER_ONLY
131 else Attention
132 )
133 attention_kwargs: dict[str, Any] = {}
134 if dual_chunk_attention_config:
135 attention_kwargs = {
136 "layer_idx": extract_layer_index(prefix),
137 "dual_chunk_attention_config": dual_chunk_attention_config,
138 }
139 self.attn = attn_cls(
140 self.num_heads,
141 self.head_dim,
142 self.scaling,
143 num_kv_heads=self.num_kv_heads,
144 cache_config=cache_config,
145 quant_config=quant_config,
146 per_layer_sliding_window=per_layer_sliding_window,
147 prefix=f"{prefix}.attn",
148 attn_type=attn_type,
149 **attention_kwargs,
150 )
151 self.q_norm = RMSNorm(self.head_dim, eps=rms_norm_eps)
152 self.k_norm = RMSNorm(self.head_dim, eps=rms_norm_eps)
153
154 def forward(
155 self,
156 positions: torch.Tensor,
157 hidden_states: torch.Tensor,
158 ) -> torch.Tensor:
159 qkv, _ = self.qkv_proj(hidden_states)
160 q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1)
161 # Add qk-norm
162 q_by_head = q.view(*q.shape[:-1], q.shape[-1] // self.head_dim, self.head_dim)
163 q_by_head = self.q_norm(q_by_head)
164 q = q_by_head.view(q.shape)
165 k_by_head = k.view(*k.shape[:-1], k.shape[-1] // self.head_dim, self.head_dim)
166 k_by_head = self.k_norm(k_by_head)
167 k = k_by_head.view(k.shape)
168 q, k = self.rotary_emb(positions, q, k)
169 attn_output = self.attn(q, k, v)
170 output, _ = self.o_proj(attn_output)
171 return output
172
173
174 class Qwen3DecoderLayer(nn.Module):
175 def __init__(
176 self,
177 config: Qwen3Config,
178 cache_config: CacheConfig | None = None,
179 quant_config: QuantizationConfig | None = None,
180 prefix: str = "",
181 per_layer_sliding_window: int | None = None,
182 ) -> None:
183 super().__init__()
184 self.hidden_size = config.hidden_size
185 set_default_rope_theta(config, default_theta=1000000)
186 dual_chunk_attention_config = getattr(
187 config, "dual_chunk_attention_config", None
188 )
189
190 # By default, Qwen3 uses causal attention as it is a decoder-only model.
191 # You can override the HF config with `is_causal=False` to enable
192 # bidirectional attention, which is used in some embedding models
193 # (e.g. Alibaba-NLP/gte-Qwen3-7B-instruct)
194 if getattr(config, "is_causal", True):
195 attn_type = AttentionType.DECODER
196 else:
197 attn_type = AttentionType.ENCODER_ONLY
198
199 self.self_attn = Qwen3Attention(
200 hidden_size=self.hidden_size,
201 num_heads=config.num_attention_heads,
202 max_position=config.max_position_embeddings,
203 num_kv_heads=config.num_key_value_heads,
204 rms_norm_eps=config.rms_norm_eps,
205 qkv_bias=getattr(config, "attention_bias", False),
206 head_dim=getattr(config, "head_dim", None),
207 cache_config=cache_config,
208 quant_config=quant_config,
209 rope_parameters=config.rope_parameters,
210 prefix=f"{prefix}.self_attn",
211 attn_type=attn_type,
212 dual_chunk_attention_config=dual_chunk_attention_config,
213 per_layer_sliding_window=per_layer_sliding_window,
214 )
215 self.mlp = Qwen3MLP(
216 hidden_size=self.hidden_size,
217 intermediate_size=config.intermediate_size,
218 hidden_act=config.hidden_act,
219 quant_config=quant_config,
220 prefix=f"{prefix}.mlp",
221 )
222 self.input_layernorm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
223 self.post_attention_layernorm = RMSNorm(
224 config.hidden_size, eps=config.rms_norm_eps
225 )
226
227 def forward(
228 self,
229 positions: torch.Tensor,
230 hidden_states: torch.Tensor,
231 residual: torch.Tensor | None,
232 ) -> tuple[torch.Tensor, torch.Tensor]:
233 # Self Attention
234 if residual is None:
235 residual = hidden_states
236 hidden_states = self.input_layernorm(hidden_states)
237 else:
238 hidden_states, residual = self.input_layernorm(hidden_states, residual)
239 hidden_states = self.self_attn(
240 positions=positions,
241 hidden_states=hidden_states,
242 )
243
244 # Fully Connected
245 hidden_states, residual = self.post_attention_layernorm(hidden_states, residual)
246 hidden_states = self.mlp(hidden_states)
247 return hidden_states, residual
248
249
250 ALL_DECODER_LAYER_TYPES = {
251 "attention": Qwen3DecoderLayer,
252 }
253
254
255 @support_torch_compile(
256 dynamic_arg_dims={
257 "input_ids": 0,
258 # positions is of shape (3, seq_len) if mrope is enabled for qwen2-vl,
259 # otherwise (seq_len, ).
260 "positions": -1,
261 "intermediate_tensors": 0,
262 "inputs_embeds": 0,
263 }
264 )
265 class Qwen3Model(Qwen2Model):
266 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
267 super().__init__(
268 vllm_config=vllm_config, prefix=prefix, decoder_layer_type=Qwen3DecoderLayer
269 )
270
271
272 class Qwen3ForCausalLM(
273 LocalArgmaxMixin, nn.Module, SupportsLoRA, SupportsPP, SupportsEagle, SupportsEagle3
274 ):
275 hf_to_vllm_mapper = Qwen3Model.hf_to_vllm_mapper
276 packed_modules_mapping = {
277 "qkv_proj": ["q_proj", "k_proj", "v_proj"],
278 "gate_up_proj": ["gate_proj", "up_proj"],
279 }
280 embedding_modules = {
281 "embed_tokens": "input_embeddings",
282 "lm_head": "output_embeddings",
283 }
284
285 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
286 super().__init__()
287 config = vllm_config.model_config.hf_config
288 quant_config = vllm_config.quant_config
289
290 self.config = config
291
292 self.vllm_config = vllm_config
293 self.quant_config = quant_config
294 self.model = Qwen3Model(
295 vllm_config=vllm_config, prefix=maybe_prefix(prefix, "model")
296 )
297
298 if get_pp_group().is_last_rank:
299 self.lm_head = ParallelLMHead(
300 config.vocab_size,
301 config.hidden_size,
302 quant_config=quant_config,
303 prefix=maybe_prefix(prefix, "lm_head"),
304 )
305 if config.tie_word_embeddings:
306 self.lm_head = self.lm_head.tie_weights(self.model.embed_tokens)
307 else:
308 self.lm_head = PPMissingLayer()
309
310 self.logits_processor = LogitsProcessor(config.vocab_size)
311
312 self.make_empty_intermediate_tensors = (
313 self.model.make_empty_intermediate_tensors
314 )
315
316 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
317 return self.model.embed_input_ids(input_ids)
318
319 def forward(
320 self,
321 input_ids: torch.Tensor | None,
322 positions: torch.Tensor,
323 intermediate_tensors: IntermediateTensors | None = None,
324 inputs_embeds: torch.Tensor | None = None,
325 ) -> torch.Tensor | IntermediateTensors:
326 hidden_states = self.model(
327 input_ids, positions, intermediate_tensors, inputs_embeds
328 )
329 return hidden_states
330
331 def compute_logits(
332 self,
333 hidden_states: torch.Tensor,
334 ) -> torch.Tensor | None:
335 logits = self.logits_processor(self.lm_head, hidden_states)
336 return logits
337
338 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
339 loader = AutoWeightsLoader(self)
340 return loader.load_weights(weights)