main
py 494 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 2025 The Swiss AI Initiative.
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 the architectural differences made by
13 # the Swiss AI Initiative 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 Apertus model compatible with HuggingFace weights."""
27
28 from collections.abc import Iterable
29 from itertools import islice
30
31 import torch
32 from torch import nn
33 from transformers import ApertusConfig
34
35 from vllm.compilation.decorators import support_torch_compile
36 from vllm.config import CacheConfig, VllmConfig
37 from vllm.distributed import get_pp_group, get_tensor_model_parallel_world_size
38 from vllm.model_executor.layers.activation import XIELU
39 from vllm.model_executor.layers.attention import (
40 Attention,
41 EncoderOnlyAttention,
42 )
43 from vllm.model_executor.layers.layernorm import RMSNorm
44 from vllm.model_executor.layers.linear import (
45 ColumnParallelLinear,
46 QKVParallelLinear,
47 RowParallelLinear,
48 )
49 from vllm.model_executor.layers.logits_processor import LogitsProcessor
50 from vllm.model_executor.layers.quantization import QuantizationConfig
51 from vllm.model_executor.layers.rotary_embedding import get_rope
52 from vllm.model_executor.layers.vocab_parallel_embedding import (
53 ParallelLMHead,
54 VocabParallelEmbedding,
55 )
56 from vllm.sequence import IntermediateTensors
57 from vllm.v1.attention.backend import AttentionType
58
59 from .interfaces import (
60 EagleModelMixin,
61 SupportsEagle,
62 SupportsEagle3,
63 SupportsLoRA,
64 SupportsPP,
65 )
66 from .utils import (
67 AutoWeightsLoader,
68 PPMissingLayer,
69 WeightsMapper,
70 extract_layer_index,
71 make_empty_intermediate_tensors_factory,
72 make_layers,
73 maybe_prefix,
74 )
75
76
77 class ApertusMLP(nn.Module):
78 def __init__(
79 self,
80 hidden_size: int,
81 intermediate_size: int,
82 hidden_act: str,
83 quant_config: QuantizationConfig | None = None,
84 bias: bool = False,
85 prefix: str = "",
86 reduce_results: bool = True,
87 ) -> None:
88 super().__init__()
89 self.up_proj = ColumnParallelLinear(
90 input_size=hidden_size,
91 output_size=intermediate_size,
92 bias=bias,
93 quant_config=quant_config,
94 prefix=f"{prefix}.up_proj",
95 )
96 self.down_proj = RowParallelLinear(
97 input_size=intermediate_size,
98 output_size=hidden_size,
99 bias=bias,
100 quant_config=quant_config,
101 reduce_results=reduce_results,
102 prefix=f"{prefix}.down_proj",
103 )
104 if hidden_act != "xielu":
105 raise ValueError(
106 f"Unsupported activation: {hidden_act}. "
107 "Only xIELU is supported for now."
108 )
109 self.act_fn = XIELU()
110
111 def forward(self, x):
112 x, _ = self.up_proj(x)
113 x = self.act_fn(x)
114 x, _ = self.down_proj(x)
115 return x
116
117
118 class ApertusAttention(nn.Module):
119 def __init__(
120 self,
121 config: ApertusConfig,
122 hidden_size: int,
123 num_heads: int,
124 num_kv_heads: int,
125 max_position_embeddings: int = 8192,
126 quant_config: QuantizationConfig | None = None,
127 bias: bool = False,
128 bias_o_proj: bool = False,
129 cache_config: CacheConfig | None = None,
130 prefix: str = "",
131 attn_type: str = AttentionType.DECODER,
132 ) -> None:
133 super().__init__()
134 layer_idx = extract_layer_index(prefix)
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 # MistralConfig has an optional head_dim introduced by Mistral-Nemo
151 head_dim = getattr(config, "head_dim", None)
152 if head_dim is None:
153 head_dim = self.hidden_size // self.total_num_heads
154 self.head_dim = head_dim
155 self.q_size = self.num_heads * self.head_dim
156 self.kv_size = self.num_kv_heads * self.head_dim
157 self.scaling = self.head_dim**-0.5
158 self.max_position_embeddings = max_position_embeddings
159
160 self.qkv_proj = QKVParallelLinear(
161 hidden_size=hidden_size,
162 head_size=self.head_dim,
163 total_num_heads=self.total_num_heads,
164 total_num_kv_heads=self.total_num_kv_heads,
165 bias=bias,
166 quant_config=quant_config,
167 prefix=f"{prefix}.qkv_proj",
168 )
169
170 self.o_proj = RowParallelLinear(
171 input_size=self.total_num_heads * self.head_dim,
172 output_size=hidden_size,
173 bias=bias_o_proj,
174 quant_config=quant_config,
175 prefix=f"{prefix}.o_proj",
176 )
177
178 self._init_rotary_emb(config, quant_config=quant_config)
179
180 sliding_window = None
181 if layer_types := getattr(config, "layer_types", None):
182 is_sliding = layer_types[layer_idx] == "sliding_attention"
183 if is_sliding:
184 sliding_window = config.sliding_window
185
186 attn_cls = (
187 EncoderOnlyAttention
188 if attn_type == AttentionType.ENCODER_ONLY
189 else Attention
190 )
191
192 self.attn = attn_cls(
193 self.num_heads,
194 self.head_dim,
195 self.scaling,
196 num_kv_heads=self.num_kv_heads,
197 cache_config=cache_config,
198 quant_config=quant_config,
199 per_layer_sliding_window=sliding_window,
200 attn_type=attn_type,
201 prefix=f"{prefix}.attn",
202 )
203
204 self.q_norm = RMSNorm(self.head_dim, eps=config.rms_norm_eps)
205 self.k_norm = RMSNorm(self.head_dim, eps=config.rms_norm_eps)
206
207 def forward(
208 self,
209 positions: torch.Tensor,
210 hidden_states: torch.Tensor,
211 ) -> torch.Tensor:
212 qkv, _ = self.qkv_proj(hidden_states)
213 q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1)
214 q = self.q_norm(q.view(-1, self.num_heads, self.head_dim)).view_as(q)
215 k = self.k_norm(k.view(-1, self.num_kv_heads, self.head_dim)).view_as(k)
216 q, k = self.rotary_emb(positions, q, k)
217 attn_output = self.attn(q, k, v)
218 output, _ = self.o_proj(attn_output)
219 return output
220
221 def _init_rotary_emb(
222 self,
223 config: ApertusConfig,
224 quant_config: QuantizationConfig | None,
225 ) -> None:
226 is_neox_style = True
227
228 self.rotary_emb = get_rope(
229 self.head_dim,
230 max_position=self.max_position_embeddings,
231 rope_parameters=config.rope_parameters,
232 is_neox_style=is_neox_style,
233 )
234
235
236 class ApertusDecoderLayer(nn.Module):
237 def __init__(
238 self,
239 config: ApertusConfig,
240 cache_config: CacheConfig | None = None,
241 quant_config: QuantizationConfig | None = None,
242 prefix: str = "",
243 ) -> None:
244 super().__init__()
245 self.hidden_size = config.hidden_size
246 max_position_embeddings = getattr(config, "max_position_embeddings", 8192)
247 # Support abacusai/Smaug-72B-v0.1 with attention_bias
248 attention_bias = getattr(config, "attention_bias", False) or getattr(
249 config, "bias", False
250 )
251 bias_o_proj = attention_bias
252 # support internlm/internlm3-8b with qkv_bias
253 if hasattr(config, "qkv_bias"):
254 attention_bias = config.qkv_bias
255
256 # Apertus defaults to causal attention as it is a decoder-only model.
257 # You can override the HF config with `is_causal=False` to enable
258 # bidirectional attention, which is used in some embedding models.
259 if getattr(config, "is_causal", True):
260 attn_type = AttentionType.DECODER
261 else:
262 attn_type = AttentionType.ENCODER_ONLY
263
264 self.self_attn = ApertusAttention(
265 config=config,
266 hidden_size=self.hidden_size,
267 num_heads=config.num_attention_heads,
268 num_kv_heads=getattr(
269 config, "num_key_value_heads", config.num_attention_heads
270 ),
271 max_position_embeddings=max_position_embeddings,
272 quant_config=quant_config,
273 bias=attention_bias,
274 bias_o_proj=bias_o_proj,
275 cache_config=cache_config,
276 prefix=f"{prefix}.self_attn",
277 attn_type=attn_type,
278 )
279 self.mlp = ApertusMLP(
280 hidden_size=self.hidden_size,
281 intermediate_size=config.intermediate_size,
282 hidden_act=config.hidden_act,
283 quant_config=quant_config,
284 bias=getattr(config, "mlp_bias", False),
285 prefix=f"{prefix}.mlp",
286 )
287 self.attention_layernorm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
288 self.feedforward_layernorm = RMSNorm(
289 config.hidden_size, eps=config.rms_norm_eps
290 )
291
292 def forward(
293 self,
294 positions: torch.Tensor,
295 hidden_states: torch.Tensor,
296 residual: torch.Tensor | None,
297 ) -> tuple[torch.Tensor, torch.Tensor]:
298 # Self Attention
299 if residual is None:
300 residual = hidden_states
301 hidden_states = self.attention_layernorm(hidden_states)
302 else:
303 hidden_states, residual = self.attention_layernorm(hidden_states, residual)
304 hidden_states = self.self_attn(positions=positions, hidden_states=hidden_states)
305
306 # Fully Connected
307 hidden_states, residual = self.feedforward_layernorm(hidden_states, residual)
308 hidden_states = self.mlp(hidden_states)
309 return hidden_states, residual
310
311
312 @support_torch_compile
313 class ApertusModel(nn.Module, EagleModelMixin):
314 def __init__(
315 self,
316 *,
317 vllm_config: VllmConfig,
318 prefix: str = "",
319 layer_type: type[nn.Module] = ApertusDecoderLayer,
320 ):
321 super().__init__()
322
323 config = vllm_config.model_config.hf_config
324 cache_config = vllm_config.cache_config
325 quant_config = vllm_config.quant_config
326
327 self.config = config
328 self.quant_config = quant_config
329
330 self.vocab_size = config.vocab_size
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 self.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: layer_type(
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 self.make_empty_intermediate_tensors = make_empty_intermediate_tensors_factory(
358 ["hidden_states", "residual"], config.hidden_size
359 )
360
361 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
362 return self.embed_tokens(input_ids)
363
364 def forward(
365 self,
366 input_ids: torch.Tensor | None,
367 positions: torch.Tensor,
368 intermediate_tensors: IntermediateTensors | None,
369 inputs_embeds: torch.Tensor | None = None,
370 ) -> torch.Tensor | IntermediateTensors | tuple[torch.Tensor, list[torch.Tensor]]:
371 if get_pp_group().is_first_rank:
372 if inputs_embeds is not None:
373 hidden_states = inputs_embeds
374 else:
375 hidden_states = self.embed_input_ids(input_ids)
376 residual = None
377 else:
378 assert intermediate_tensors is not None
379 hidden_states = intermediate_tensors["hidden_states"]
380 residual = intermediate_tensors["residual"]
381
382 aux_hidden_states = self._maybe_add_hidden_state([], 0, hidden_states, residual)
383 for idx, layer in enumerate(
384 islice(self.layers, self.start_layer, self.end_layer)
385 ):
386 hidden_states, residual = layer(positions, hidden_states, residual)
387 self._maybe_add_hidden_state(
388 aux_hidden_states, idx + 1, hidden_states, residual
389 )
390
391 if not get_pp_group().is_last_rank:
392 return IntermediateTensors(
393 {"hidden_states": hidden_states, "residual": residual}
394 )
395
396 hidden_states, _ = self.norm(hidden_states, residual)
397
398 if len(aux_hidden_states) > 0:
399 return hidden_states, aux_hidden_states
400 return hidden_states
401
402
403 class ApertusForCausalLM(
404 nn.Module, SupportsLoRA, SupportsPP, SupportsEagle, SupportsEagle3
405 ):
406 hf_to_vllm_mapper = WeightsMapper(
407 orig_to_new_stacked={
408 ".q_proj": (".qkv_proj", "q"),
409 ".k_proj": (".qkv_proj", "k"),
410 ".v_proj": (".qkv_proj", "v"),
411 }
412 )
413 packed_modules_mapping = {"qkv_proj": ["q_proj", "k_proj", "v_proj"]}
414
415 # LoRA specific attributes
416 embedding_modules = {
417 "embed_tokens": "input_embeddings",
418 "lm_head": "output_embeddings",
419 }
420
421 def __init__(
422 self,
423 *,
424 vllm_config: VllmConfig,
425 prefix: str = "",
426 layer_type: type[nn.Module] = ApertusDecoderLayer,
427 ):
428 super().__init__()
429 config = vllm_config.model_config.hf_config
430 quant_config = vllm_config.quant_config
431 self.config = config
432
433 self.model = self._init_model(
434 vllm_config=vllm_config,
435 prefix=maybe_prefix(prefix, "model"),
436 layer_type=layer_type,
437 )
438
439 if get_pp_group().is_last_rank:
440 self.lm_head = ParallelLMHead(
441 config.vocab_size,
442 config.hidden_size,
443 quant_config=quant_config,
444 prefix=maybe_prefix(prefix, "lm_head"),
445 )
446 if config.tie_word_embeddings:
447 self.lm_head = self.lm_head.tie_weights(self.model.embed_tokens)
448
449 logit_scale = getattr(config, "logit_scale", 1.0)
450 self.logits_processor = LogitsProcessor(
451 config.vocab_size, scale=logit_scale
452 )
453 else:
454 self.lm_head = PPMissingLayer()
455
456 self.make_empty_intermediate_tensors = (
457 self.model.make_empty_intermediate_tensors
458 )
459
460 def _init_model(
461 self,
462 vllm_config: VllmConfig,
463 prefix: str = "",
464 layer_type: type[nn.Module] = ApertusDecoderLayer,
465 ):
466 return ApertusModel(
467 vllm_config=vllm_config, prefix=prefix, layer_type=layer_type
468 )
469
470 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
471 return self.model.embed_input_ids(input_ids)
472
473 def forward(
474 self,
475 input_ids: torch.Tensor | None,
476 positions: torch.Tensor,
477 intermediate_tensors: IntermediateTensors | None = None,
478 inputs_embeds: torch.Tensor | None = None,
479 ) -> torch.Tensor | IntermediateTensors:
480 model_output = self.model(
481 input_ids, positions, intermediate_tensors, inputs_embeds
482 )
483 return model_output
484
485 def compute_logits(
486 self,
487 hidden_states: torch.Tensor,
488 ) -> torch.Tensor | None:
489 logits = self.logits_processor(self.lm_head, hidden_states)
490 return logits
491
492 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
493 loader = AutoWeightsLoader(self)
494 return loader.load_weights(weights, mapper=self.hf_to_vllm_mapper)