main
py 446 lines 15.7 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 # Copyright 2025 The vLLM team.
4 # Copyright 2025 Google Inc. HuggingFace Inc. team. All rights reserved.
5 #
6 #
7 # Licensed under the Apache License, Version 2.0 (the "License");
8 # you may not use this file except in compliance with the License.
9 # You may obtain a copy of the License at
10 #
11 # http://www.apache.org/licenses/LICENSE-2.0
12 #
13 # Unless required by applicable law or agreed to in writing, software
14 # distributed under the License is distributed on an "AS IS" BASIS,
15 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 # See the License for the specific language governing permissions and
17 # limitations under the License.
18 from collections.abc import Iterable
19 from itertools import islice
20
21 import torch
22 from torch import nn
23 from transformers import Gemma3TextConfig
24
25 from vllm.compilation.decorators import support_torch_compile
26 from vllm.config import CacheConfig, VllmConfig
27 from vllm.distributed import get_pp_group, get_tensor_model_parallel_world_size
28 from vllm.logger import init_logger
29 from vllm.model_executor.layers.activation import get_act_and_mul_fn
30 from vllm.model_executor.layers.attention import (
31 Attention,
32 EncoderOnlyAttention,
33 )
34 from vllm.model_executor.layers.layernorm import GemmaRMSNorm
35 from vllm.model_executor.layers.linear import (
36 MergedColumnParallelLinear,
37 QKVParallelLinear,
38 RowParallelLinear,
39 )
40 from vllm.model_executor.layers.logits_processor import LogitsProcessor
41 from vllm.model_executor.layers.quantization import QuantizationConfig
42 from vllm.model_executor.layers.rotary_embedding import get_rope
43 from vllm.model_executor.layers.vocab_parallel_embedding import (
44 ParallelLMHead,
45 VocabParallelEmbedding,
46 )
47 from vllm.sequence import IntermediateTensors
48 from vllm.v1.attention.backend import AttentionType
49
50 from .interfaces import SupportsLoRA, SupportsPP
51 from .utils import (
52 AutoWeightsLoader,
53 WeightsMapper,
54 extract_layer_index,
55 make_empty_intermediate_tensors_factory,
56 make_layers,
57 maybe_prefix,
58 )
59
60 logger = init_logger(__name__)
61
62
63 class Gemma3MLP(nn.Module):
64 def __init__(
65 self,
66 hidden_size: int,
67 intermediate_size: int,
68 hidden_activation: str,
69 quant_config: QuantizationConfig | None = None,
70 prefix: str = "",
71 ) -> None:
72 super().__init__()
73 self.gate_up_proj = MergedColumnParallelLinear(
74 hidden_size,
75 [intermediate_size] * 2,
76 bias=False,
77 quant_config=quant_config,
78 prefix=f"{prefix}.gate_up_proj",
79 )
80 self.down_proj = RowParallelLinear(
81 intermediate_size,
82 hidden_size,
83 bias=False,
84 quant_config=quant_config,
85 prefix=f"{prefix}.down_proj",
86 )
87 self.act_fn = get_act_and_mul_fn(hidden_activation)
88
89 def forward(self, x: torch.Tensor) -> torch.Tensor:
90 gate_up, _ = self.gate_up_proj(x)
91 x = self.act_fn(gate_up)
92 x, _ = self.down_proj(x)
93 return x
94
95
96 class Gemma3Attention(nn.Module):
97 def __init__(
98 self,
99 config: Gemma3TextConfig,
100 hidden_size: int,
101 num_heads: int,
102 num_kv_heads: int,
103 head_dim: int,
104 max_position_embeddings: int,
105 cache_config: CacheConfig | None = None,
106 quant_config: QuantizationConfig | None = None,
107 attn_logits_soft_cap: float | None = None,
108 prefix: str = "",
109 ) -> None:
110 super().__init__()
111 self.config = config
112 self.hidden_size = hidden_size
113 tp_size = get_tensor_model_parallel_world_size()
114 self.total_num_heads = num_heads
115 assert self.total_num_heads % tp_size == 0
116 self.num_heads = self.total_num_heads // tp_size
117 self.total_num_kv_heads = num_kv_heads
118 if self.total_num_kv_heads >= tp_size:
119 # Number of KV heads is greater than TP size, so we partition
120 # the KV heads across multiple tensor parallel GPUs.
121 assert self.total_num_kv_heads % tp_size == 0
122 else:
123 # Number of KV heads is less than TP size, so we replicate
124 # the KV heads across multiple tensor parallel GPUs.
125 assert tp_size % self.total_num_kv_heads == 0
126 self.num_kv_heads = max(1, self.total_num_kv_heads // tp_size)
127 self.head_dim = head_dim
128 self.q_size = self.num_heads * self.head_dim
129 self.kv_size = self.num_kv_heads * self.head_dim
130 self.scaling = config.query_pre_attn_scalar**-0.5
131
132 self.qkv_proj = QKVParallelLinear(
133 hidden_size,
134 self.head_dim,
135 self.total_num_heads,
136 self.total_num_kv_heads,
137 bias=config.attention_bias,
138 quant_config=quant_config,
139 prefix=f"{prefix}.qkv_proj",
140 )
141 self.o_proj = RowParallelLinear(
142 self.total_num_heads * self.head_dim,
143 hidden_size,
144 bias=config.attention_bias,
145 quant_config=quant_config,
146 prefix=f"{prefix}.o_proj",
147 )
148
149 self.q_norm = GemmaRMSNorm(self.head_dim, eps=config.rms_norm_eps)
150 self.k_norm = GemmaRMSNorm(self.head_dim, eps=config.rms_norm_eps)
151
152 layer_idx = extract_layer_index(prefix)
153 layer_type = config.layer_types[layer_idx]
154 self.is_sliding = layer_type == "sliding_attention"
155 sliding_window = config.sliding_window if self.is_sliding else None
156
157 # Initialize the rotary embedding.
158 if layer_type in config.rope_parameters:
159 # Transformers v5 rope config.
160 rope_parameters = config.rope_parameters[layer_type]
161 else:
162 # Transformers v4 rope config.
163 # Global attention. Use the values in config.json.
164 rope_parameters = config.rope_parameters
165 # Local attention. Override the values in config.json.
166 if self.is_sliding:
167 rope_parameters = dict(
168 rope_type="default", rope_theta=config.rope_local_base_freq
169 )
170
171 self.rotary_emb = get_rope(
172 self.head_dim,
173 max_position=max_position_embeddings,
174 rope_parameters=rope_parameters,
175 is_neox_style=True,
176 )
177
178 if getattr(config, "is_causal", True):
179 attn_type = AttentionType.DECODER
180 else:
181 attn_type = AttentionType.ENCODER_ONLY
182
183 attn_cls = (
184 EncoderOnlyAttention
185 if attn_type == AttentionType.ENCODER_ONLY
186 else Attention
187 )
188
189 self.attn = attn_cls(
190 self.num_heads,
191 self.head_dim,
192 self.scaling,
193 num_kv_heads=self.num_kv_heads,
194 cache_config=cache_config,
195 quant_config=quant_config,
196 attn_type=attn_type,
197 logits_soft_cap=attn_logits_soft_cap,
198 per_layer_sliding_window=sliding_window,
199 prefix=f"{prefix}.attn",
200 )
201
202 def forward(
203 self,
204 positions: torch.Tensor,
205 hidden_states: torch.Tensor,
206 **kwargs,
207 ) -> torch.Tensor:
208 qkv, _ = self.qkv_proj(hidden_states)
209 q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1)
210
211 q = q.unflatten(-1, (self.num_heads, self.head_dim))
212 q = self.q_norm(q)
213 q = q.flatten(-2, -1)
214 k = k.unflatten(-1, (self.num_kv_heads, self.head_dim))
215 k = self.k_norm(k)
216 k = k.flatten(-2, -1)
217
218 q, k = self.rotary_emb(positions, q, k)
219 attn_output = self.attn(q, k, v)
220 output, _ = self.o_proj(attn_output)
221 return output
222
223
224 class Gemma3DecoderLayer(nn.Module):
225 def __init__(
226 self,
227 config: Gemma3TextConfig,
228 cache_config: CacheConfig | None = None,
229 quant_config: QuantizationConfig | None = None,
230 prefix: str = "",
231 ) -> None:
232 super().__init__()
233 self.hidden_size = config.hidden_size
234 self.self_attn = Gemma3Attention(
235 config=config,
236 hidden_size=self.hidden_size,
237 num_heads=config.num_attention_heads,
238 num_kv_heads=config.num_key_value_heads,
239 head_dim=config.head_dim,
240 max_position_embeddings=config.max_position_embeddings,
241 cache_config=cache_config,
242 quant_config=quant_config,
243 attn_logits_soft_cap=None,
244 prefix=f"{prefix}.self_attn",
245 )
246 self.hidden_size = config.hidden_size
247 self.mlp = Gemma3MLP(
248 hidden_size=self.hidden_size,
249 intermediate_size=config.intermediate_size,
250 hidden_activation=config.hidden_activation,
251 quant_config=quant_config,
252 prefix=f"{prefix}.mlp",
253 )
254 self.input_layernorm = GemmaRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
255 self.post_attention_layernorm = GemmaRMSNorm(
256 config.hidden_size, eps=config.rms_norm_eps
257 )
258 self.pre_feedforward_layernorm = GemmaRMSNorm(
259 config.hidden_size, eps=config.rms_norm_eps
260 )
261 self.post_feedforward_layernorm = GemmaRMSNorm(
262 config.hidden_size, eps=config.rms_norm_eps
263 )
264
265 def forward(
266 self,
267 positions: torch.Tensor,
268 hidden_states: torch.Tensor,
269 residual: torch.Tensor | None,
270 **kwargs,
271 ) -> tuple[torch.Tensor, torch.Tensor]:
272 if residual is None:
273 residual = hidden_states
274 hidden_states = self.input_layernorm(hidden_states)
275 else:
276 hidden_states, residual = self.input_layernorm(hidden_states, residual)
277 hidden_states = self.self_attn(
278 positions=positions,
279 hidden_states=hidden_states,
280 **kwargs,
281 )
282 hidden_states = self.post_attention_layernorm(hidden_states)
283
284 hidden_states, residual = self.pre_feedforward_layernorm(
285 hidden_states, residual
286 )
287 hidden_states = self.mlp(hidden_states)
288 hidden_states = self.post_feedforward_layernorm(hidden_states)
289 return hidden_states, residual
290
291
292 @support_torch_compile
293 class Gemma3Model(nn.Module):
294 hf_to_vllm_mapper = WeightsMapper(
295 orig_to_new_stacked={
296 # weight_name: (param_name, shard_id)
297 ".q_proj": (".qkv_proj", "q"),
298 ".k_proj": (".qkv_proj", "k"),
299 ".v_proj": (".qkv_proj", "v"),
300 ".gate_proj": (".gate_up_proj", 0),
301 ".up_proj": (".gate_up_proj", 1),
302 }
303 )
304
305 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
306 super().__init__()
307 config = vllm_config.model_config.hf_config
308 cache_config = vllm_config.cache_config
309 quant_config = vllm_config.quant_config
310 self.config = config
311 self.quant_config = quant_config
312
313 self.embed_tokens = VocabParallelEmbedding(
314 config.vocab_size,
315 config.hidden_size,
316 quant_config=quant_config,
317 prefix=f"{prefix}.embed_tokens",
318 )
319 self.start_layer, self.end_layer, self.layers = make_layers(
320 config.num_hidden_layers,
321 lambda prefix: Gemma3DecoderLayer(
322 config, cache_config, quant_config, prefix=prefix
323 ),
324 prefix=f"{prefix}.layers",
325 )
326 self.norm = GemmaRMSNorm(config.hidden_size, eps=config.rms_norm_eps)
327
328 # Normalize the embedding by sqrt(hidden_size)
329 # The normalizer's data type should be downcasted to the model's
330 # data type such as bfloat16, not float32.
331 # See https://github.com/huggingface/transformers/pull/29402
332 normalizer = self.config.hidden_size**0.5
333 self.register_buffer("normalizer", torch.tensor(normalizer), persistent=False)
334 self.make_empty_intermediate_tensors = make_empty_intermediate_tensors_factory(
335 ["hidden_states", "residual"], config.hidden_size
336 )
337
338 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
339 # NOTE(woosuk): Only apply the normalizer to the output of
340 # vocab embedding. Don't apply it to the vision embedding.
341 return self.embed_tokens(input_ids) * self.normalizer
342
343 def forward(
344 self,
345 input_ids: torch.Tensor | None,
346 positions: torch.Tensor,
347 intermediate_tensors: IntermediateTensors | None = None,
348 inputs_embeds: torch.Tensor | None = None,
349 **kwargs,
350 ) -> torch.Tensor | IntermediateTensors:
351 if get_pp_group().is_first_rank:
352 if inputs_embeds is not None:
353 hidden_states = inputs_embeds
354 else:
355 hidden_states = self.embed_input_ids(input_ids)
356 residual = None
357 else:
358 assert intermediate_tensors is not None
359 hidden_states = intermediate_tensors["hidden_states"]
360 residual = intermediate_tensors["residual"]
361 for layer in islice(self.layers, self.start_layer, self.end_layer):
362 hidden_states, residual = layer(
363 positions,
364 hidden_states,
365 residual,
366 **kwargs,
367 )
368 if not get_pp_group().is_last_rank:
369 return IntermediateTensors(
370 {"hidden_states": hidden_states, "residual": residual}
371 )
372 hidden_states, _ = self.norm(hidden_states, residual)
373 return hidden_states
374
375 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
376 loader = AutoWeightsLoader(self)
377 return loader.load_weights(weights, mapper=self.hf_to_vllm_mapper)
378
379
380 class Gemma3ForCausalLM(nn.Module, SupportsLoRA, SupportsPP):
381 hf_to_vllm_mapper = Gemma3Model.hf_to_vllm_mapper
382 packed_modules_mapping = {
383 "qkv_proj": [
384 "q_proj",
385 "k_proj",
386 "v_proj",
387 ],
388 "gate_up_proj": [
389 "gate_proj",
390 "up_proj",
391 ],
392 }
393
394 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
395 config = vllm_config.model_config.hf_config
396 quant_config = vllm_config.quant_config
397
398 super().__init__()
399 self.config = config
400 self.quant_config = quant_config
401 self.model = Gemma3Model(
402 vllm_config=vllm_config, prefix=maybe_prefix(prefix, "model")
403 )
404
405 self.lm_head = ParallelLMHead(
406 config.vocab_size,
407 config.hidden_size,
408 quant_config=quant_config,
409 prefix=maybe_prefix(prefix, "lm_head"),
410 )
411 if config.tie_word_embeddings:
412 self.lm_head = self.lm_head.tie_weights(self.model.embed_tokens)
413
414 self.logits_processor = LogitsProcessor(
415 config.vocab_size, soft_cap=config.final_logit_softcapping
416 )
417 self.make_empty_intermediate_tensors = (
418 self.model.make_empty_intermediate_tensors
419 )
420
421 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
422 return self.model.embed_input_ids(input_ids)
423
424 def forward(
425 self,
426 input_ids: torch.Tensor | None,
427 positions: torch.Tensor,
428 intermediate_tensors: IntermediateTensors | None = None,
429 inputs_embeds: torch.Tensor | None = None,
430 **kwargs,
431 ) -> torch.Tensor | IntermediateTensors:
432 hidden_states = self.model(
433 input_ids, positions, intermediate_tensors, inputs_embeds, **kwargs
434 )
435 return hidden_states
436
437 def compute_logits(
438 self,
439 hidden_states: torch.Tensor,
440 ) -> torch.Tensor | None:
441 logits = self.logits_processor(self.lm_head, hidden_states)
442 return logits
443
444 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
445 loader = AutoWeightsLoader(self)
446 return loader.load_weights(weights, mapper=self.hf_to_vllm_mapper)