main
py 472 lines 17 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/llama/modeling_llama.py
6 # Copyright 2023 The vLLM team.
7 # Copyright 2022 EleutherAI and the HuggingFace Inc. team. All rights reserved.
8 #
9 # This code is based on EleutherAI's GPT-NeoX library and the GPT-NeoX
10 # and OPT implementations in this library. It has been modified from its
11 # original forms to accommodate minor architectural differences compared
12 # to GPT-NeoX and OPT used by the Meta AI team that trained the model.
13 #
14 # Licensed under the Apache License, Version 2.0 (the "License");
15 # you may not use this file except in compliance with the License.
16 # You may obtain a copy of the License at
17 #
18 # http://www.apache.org/licenses/LICENSE-2.0
19 #
20 # Unless required by applicable law or agreed to in writing, software
21 # distributed under the License is distributed on an "AS IS" BASIS,
22 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
23 # See the License for the specific language governing permissions and
24 # limitations under the License.
25 """Inference-only IBM Granite model compatible with HuggingFace weights.
26
27 Also serves the `granite_swa` checkpoints (`GraniteSWAForCausalLM`), supporting
28 three additional features: per-layer sliding window attention (`layer_types`), a
29 learnable per-head attention sink (`self_attn.sinks`), and a per-layer RoPE base
30 (`layer_rope_theta`, with 0 for NoPE).
31 """
32
33 from collections.abc import Iterable
34 from itertools import islice
35
36 import torch
37 from torch import nn
38 from transformers import GraniteConfig
39 from transformers.configuration_utils import PreTrainedConfig
40
41 from vllm.compilation.decorators import support_torch_compile
42 from vllm.config import CacheConfig, VllmConfig
43 from vllm.distributed import get_pp_group, get_tensor_model_parallel_world_size
44 from vllm.model_executor.layers.activation import SiluAndMul
45 from vllm.model_executor.layers.attention import Attention
46 from vllm.model_executor.layers.layernorm import RMSNorm
47 from vllm.model_executor.layers.linear import (
48 MergedColumnParallelLinear,
49 QKVParallelLinear,
50 RowParallelLinear,
51 )
52 from vllm.model_executor.layers.logits_processor import LogitsProcessor
53 from vllm.model_executor.layers.quantization import QuantizationConfig
54 from vllm.model_executor.layers.rotary_embedding import get_rope
55 from vllm.model_executor.layers.vocab_parallel_embedding import (
56 ParallelLMHead,
57 VocabParallelEmbedding,
58 )
59 from vllm.model_executor.model_loader.weight_utils import sharded_weight_loader
60 from vllm.model_executor.utils import set_weight_attrs
61 from vllm.sequence import IntermediateTensors
62
63 from .interfaces import SupportsLoRA, SupportsPP, SupportsQuant
64 from .utils import (
65 AutoWeightsLoader,
66 PPMissingLayer,
67 WeightsMapper,
68 extract_layer_index,
69 make_layers,
70 maybe_prefix,
71 )
72
73
74 def granite_layer_attn_params(
75 config: PreTrainedConfig, layer_idx: int
76 ) -> tuple[int | None, float, bool]:
77 """Resolve one layer's sliding window, RoPE base and sink usage.
78
79 Plain Granite configs carry no SWA fields and fall back to full
80 attention, global RoPE base and no sink. HF SWA checkpoints use
81 sinks without a dedicated flag, so assume true when `layer_types`
82 is used, and allow `attention_sinks` to override that decision.
83
84 Returns:
85 Sliding window size (`None` for full attention), RoPE base theta (`0`
86 for NoPE), and attention sink presence/absence.
87
88 """
89 layer_types = getattr(config, "layer_types", None)
90 sliding_window = (
91 config.sliding_window
92 if layer_types is not None and layer_types[layer_idx] == "sliding_attention"
93 else None
94 )
95
96 layer_rope_theta = getattr(config, "layer_rope_theta", None)
97 rope_theta = (
98 layer_rope_theta[layer_idx]
99 if layer_rope_theta is not None
100 else config.rope_parameters["rope_theta"]
101 )
102
103 has_sink = getattr(config, "attention_sinks", layer_types is not None)
104 return sliding_window, rope_theta, has_sink
105
106
107 class GraniteMLP(nn.Module):
108 def __init__(
109 self,
110 hidden_size: int,
111 intermediate_size: int,
112 hidden_act: str,
113 quant_config: QuantizationConfig | None = None,
114 bias: bool = False,
115 prefix: str = "",
116 ) -> None:
117 super().__init__()
118 self.gate_up_proj = MergedColumnParallelLinear(
119 input_size=hidden_size,
120 output_sizes=[intermediate_size] * 2,
121 bias=bias,
122 quant_config=quant_config,
123 prefix=f"{prefix}.gate_up_proj",
124 )
125 self.down_proj = RowParallelLinear(
126 input_size=intermediate_size,
127 output_size=hidden_size,
128 bias=bias,
129 quant_config=quant_config,
130 prefix=f"{prefix}.down_proj",
131 )
132 if hidden_act != "silu":
133 raise ValueError(
134 f"Unsupported activation: {hidden_act}. Only silu is supported for now."
135 )
136 self.act_fn = SiluAndMul()
137
138 def forward(self, x):
139 gate_up, _ = self.gate_up_proj(x)
140 x = self.act_fn(gate_up)
141 x, _ = self.down_proj(x)
142 return x
143
144
145 class GraniteAttention(nn.Module):
146 def __init__(
147 self,
148 config: GraniteConfig,
149 hidden_size: int,
150 num_heads: int,
151 num_kv_heads: int,
152 max_position_embeddings: int = 8192,
153 quant_config: QuantizationConfig | None = None,
154 bias: bool = False,
155 cache_config: CacheConfig | None = None,
156 prefix: str = "",
157 ) -> None:
158 super().__init__()
159 self.hidden_size = hidden_size
160 tp_size = get_tensor_model_parallel_world_size()
161 self.total_num_heads = num_heads
162 assert self.total_num_heads % tp_size == 0
163 self.num_heads = self.total_num_heads // tp_size
164 self.total_num_kv_heads = num_kv_heads
165 if self.total_num_kv_heads >= tp_size:
166 # Number of KV heads is greater than TP size, so we partition
167 # the KV heads across multiple tensor parallel GPUs.
168 assert self.total_num_kv_heads % tp_size == 0
169 else:
170 # Number of KV heads is less than TP size, so we replicate
171 # the KV heads across multiple tensor parallel GPUs.
172 assert tp_size % self.total_num_kv_heads == 0
173 self.num_kv_heads = max(1, self.total_num_kv_heads // tp_size)
174 # MistralConfig has an optional head_dim introduced by Mistral-Nemo
175 self.head_dim = getattr(config, "head_dim", None)
176 if self.head_dim is None:
177 self.head_dim = self.hidden_size // self.total_num_heads
178 self.q_size = self.num_heads * self.head_dim
179 self.kv_size = self.num_kv_heads * self.head_dim
180 self.scaling = config.attention_multiplier
181 self.max_position_embeddings = max_position_embeddings
182
183 self.qkv_proj = QKVParallelLinear(
184 hidden_size=hidden_size,
185 head_size=self.head_dim,
186 total_num_heads=self.total_num_heads,
187 total_num_kv_heads=self.total_num_kv_heads,
188 bias=bias,
189 quant_config=quant_config,
190 prefix=f"{prefix}.qkv_proj",
191 )
192 self.o_proj = RowParallelLinear(
193 input_size=self.total_num_heads * self.head_dim,
194 output_size=hidden_size,
195 bias=bias,
196 quant_config=quant_config,
197 prefix=f"{prefix}.o_proj",
198 )
199
200 sliding_window, rope_theta, has_sink = granite_layer_attn_params(
201 config, extract_layer_index(prefix)
202 )
203
204 self.use_rope = rope_theta != 0
205 if self.use_rope:
206 self.rotary_emb = get_rope(
207 self.head_dim,
208 max_position=max_position_embeddings,
209 rope_parameters={**config.rope_parameters, "rope_theta": rope_theta},
210 )
211
212 # Per-head sink, applied in backend as extra logit in the softmax denom
213 if has_sink:
214 self.sinks = nn.Parameter(torch.empty(self.num_heads), requires_grad=False)
215 set_weight_attrs(self.sinks, {"weight_loader": sharded_weight_loader(0)})
216 else:
217 self.sinks = None
218
219 self.attn = Attention(
220 self.num_heads,
221 self.head_dim,
222 self.scaling,
223 num_kv_heads=self.num_kv_heads,
224 cache_config=cache_config,
225 quant_config=quant_config,
226 per_layer_sliding_window=sliding_window,
227 prefix=f"{prefix}.attn",
228 sinks=self.sinks,
229 )
230
231 def forward(
232 self,
233 positions: torch.Tensor,
234 hidden_states: torch.Tensor,
235 ) -> torch.Tensor:
236 qkv, _ = self.qkv_proj(hidden_states)
237 q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1)
238 if self.use_rope:
239 q, k = self.rotary_emb(positions, q, k)
240 attn_output = self.attn(q, k, v)
241 output, _ = self.o_proj(attn_output)
242 return output
243
244
245 class GraniteDecoderLayer(nn.Module):
246 def __init__(
247 self,
248 config: GraniteConfig,
249 cache_config: CacheConfig | None = None,
250 quant_config: QuantizationConfig | None = None,
251 prefix: str = "",
252 ) -> None:
253 super().__init__()
254 self.hidden_size = config.hidden_size
255 self.residual_multiplier = config.residual_multiplier
256 max_position_embeddings = getattr(config, "max_position_embeddings", 8192)
257 # Support abacusai/Smaug-72B-v0.1 with attention_bias
258 attention_bias = getattr(config, "attention_bias", False) or getattr(
259 config, "bias", False
260 )
261 self.self_attn = GraniteAttention(
262 config=config,
263 hidden_size=self.hidden_size,
264 num_heads=config.num_attention_heads,
265 num_kv_heads=getattr(
266 config, "num_key_value_heads", config.num_attention_heads
267 ),
268 max_position_embeddings=max_position_embeddings,
269 quant_config=quant_config,
270 bias=attention_bias,
271 cache_config=cache_config,
272 prefix=f"{prefix}.self_attn",
273 )
274
275 self.mlp = GraniteMLP(
276 hidden_size=self.hidden_size,
277 intermediate_size=config.intermediate_size,
278 hidden_act=config.hidden_act,
279 quant_config=quant_config,
280 bias=getattr(config, "mlp_bias", False),
281 prefix=f"{prefix}.mlp",
282 )
283 self.input_layernorm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
284 self.post_attention_layernorm = RMSNorm(
285 config.hidden_size, eps=config.rms_norm_eps
286 )
287
288 def forward(
289 self,
290 positions: torch.Tensor,
291 hidden_states: torch.Tensor,
292 ) -> tuple[torch.Tensor, torch.Tensor]:
293 # Self Attention
294 residual = hidden_states
295 hidden_states = self.input_layernorm(hidden_states)
296 hidden_states = self.self_attn(
297 positions=positions,
298 hidden_states=hidden_states,
299 )
300 hidden_states = residual + hidden_states * self.residual_multiplier
301 # Fully Connected
302 residual = hidden_states
303 hidden_states = self.post_attention_layernorm(hidden_states)
304 hidden_states = self.mlp(hidden_states)
305 hidden_states = residual + hidden_states * self.residual_multiplier
306 return hidden_states
307
308
309 @support_torch_compile
310 class GraniteModel(nn.Module):
311 hf_to_vllm_mapper = WeightsMapper(
312 orig_to_new_stacked={
313 # weight_name: (param_name, shard_id)
314 ".q_proj": (".qkv_proj", "q"),
315 ".k_proj": (".qkv_proj", "k"),
316 ".v_proj": (".qkv_proj", "v"),
317 ".gate_proj": (".gate_up_proj", 0),
318 ".up_proj": (".gate_up_proj", 1),
319 }
320 )
321
322 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
323 super().__init__()
324
325 config = vllm_config.model_config.hf_config
326 cache_config = vllm_config.cache_config
327 quant_config = vllm_config.quant_config
328
329 self.config = config
330 self.quant_config = quant_config
331
332 if get_pp_group().is_first_rank or (
333 config.tie_word_embeddings and get_pp_group().is_last_rank
334 ):
335 self.embed_tokens = VocabParallelEmbedding(
336 config.vocab_size,
337 config.hidden_size,
338 quant_config=quant_config,
339 )
340 else:
341 self.embed_tokens = PPMissingLayer()
342 self.start_layer, self.end_layer, self.layers = make_layers(
343 config.num_hidden_layers,
344 lambda prefix: GraniteDecoderLayer(
345 config=config,
346 cache_config=cache_config,
347 quant_config=quant_config,
348 prefix=prefix,
349 ),
350 prefix=f"{prefix}.layers",
351 )
352 if get_pp_group().is_last_rank:
353 self.norm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
354 else:
355 self.norm = PPMissingLayer()
356
357 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
358 return self.embed_tokens(input_ids)
359
360 def forward(
361 self,
362 input_ids: torch.Tensor | None,
363 positions: torch.Tensor,
364 intermediate_tensors: IntermediateTensors | None,
365 inputs_embeds: torch.Tensor | None = None,
366 ) -> torch.Tensor | IntermediateTensors:
367 if get_pp_group().is_first_rank:
368 if inputs_embeds is not None:
369 hidden_states = inputs_embeds
370 else:
371 hidden_states = self.embed_input_ids(input_ids)
372
373 hidden_states *= self.config.embedding_multiplier
374 else:
375 assert intermediate_tensors is not None
376 hidden_states = intermediate_tensors["hidden_states"]
377
378 for layer in islice(self.layers, self.start_layer, self.end_layer):
379 hidden_states = layer(positions, hidden_states)
380
381 if not get_pp_group().is_last_rank:
382 return IntermediateTensors(
383 {
384 "hidden_states": hidden_states,
385 }
386 )
387
388 hidden_states = self.norm(hidden_states)
389 return hidden_states
390
391 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
392 loader = AutoWeightsLoader(self)
393 return loader.load_weights(weights, mapper=self.hf_to_vllm_mapper)
394
395
396 class GraniteForCausalLM(nn.Module, SupportsLoRA, SupportsPP, SupportsQuant):
397 hf_to_vllm_mapper = GraniteModel.hf_to_vllm_mapper
398 # LoRA specific attributes
399 packed_modules_mapping: dict[str, list[str]] = {
400 "qkv_proj": ["q_proj", "k_proj", "v_proj"],
401 "gate_up_proj": ["gate_proj", "up_proj"],
402 }
403 embedding_modules = {
404 "embed_tokens": "input_embeddings",
405 "lm_head": "output_embeddings",
406 }
407
408 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
409 super().__init__()
410 config = vllm_config.model_config.hf_config
411 quant_config = vllm_config.quant_config
412
413 self.config = config
414
415 self.quant_config = quant_config
416
417 self.model = GraniteModel(
418 vllm_config=vllm_config, prefix=maybe_prefix(prefix, "model")
419 )
420 if get_pp_group().is_last_rank:
421 self.lm_head = ParallelLMHead(
422 config.vocab_size,
423 config.hidden_size,
424 quant_config=quant_config,
425 prefix=maybe_prefix(prefix, "lm_head"),
426 )
427 if config.tie_word_embeddings:
428 self.lm_head = self.lm_head.tie_weights(self.model.embed_tokens)
429
430 logit_scale = getattr(config, "logit_scale", 1.0)
431 if hasattr(config, "logits_scaling"):
432 logit_scale /= config.logits_scaling
433
434 self.logits_processor = LogitsProcessor(
435 config.vocab_size, scale=logit_scale
436 )
437 else:
438 self.lm_head = PPMissingLayer()
439
440 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
441 return self.model.embed_input_ids(input_ids)
442
443 def forward(
444 self,
445 input_ids: torch.Tensor | None,
446 positions: torch.Tensor,
447 intermediate_tensors: IntermediateTensors | None = None,
448 inputs_embeds: torch.Tensor | None = None,
449 ) -> torch.Tensor | IntermediateTensors:
450 model_output = self.model(
451 input_ids, positions, intermediate_tensors, inputs_embeds
452 )
453 return model_output
454
455 def compute_logits(self, hidden_states: torch.Tensor) -> torch.Tensor | None:
456 logits = self.logits_processor(self.lm_head, hidden_states)
457 return logits
458
459 def make_empty_intermediate_tensors(
460 self, batch_size: int, dtype: torch.dtype, device: torch.device
461 ) -> IntermediateTensors:
462 return IntermediateTensors(
463 {
464 "hidden_states": torch.zeros(
465 (batch_size, self.config.hidden_size), dtype=dtype, device=device
466 ),
467 }
468 )
469
470 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
471 loader = AutoWeightsLoader(self)
472 return loader.load_weights(weights)