main
py 506 lines 17.5 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 from collections.abc import Iterable
4 from itertools import islice
5
6 import torch
7 import torch.nn as nn
8 from transformers import Lfm2Config
9
10 from vllm.compilation.decorators import support_torch_compile
11 from vllm.config import CacheConfig, ModelConfig, VllmConfig
12 from vllm.distributed import get_pp_group, get_tensor_model_parallel_world_size
13 from vllm.model_executor.layers.activation import SiluAndMul
14 from vllm.model_executor.layers.attention import Attention
15 from vllm.model_executor.layers.layernorm import RMSNorm
16 from vllm.model_executor.layers.linear import (
17 MergedColumnParallelLinear,
18 QKVParallelLinear,
19 RowParallelLinear,
20 )
21 from vllm.model_executor.layers.logits_processor import LogitsProcessor
22 from vllm.model_executor.layers.mamba.mamba_utils import (
23 MambaStateCopyFunc,
24 MambaStateCopyFuncCalculator,
25 MambaStateDtypeCalculator,
26 MambaStateShapeCalculator,
27 )
28 from vllm.model_executor.layers.mamba.short_conv import ShortConv
29 from vllm.model_executor.layers.quantization import QuantizationConfig
30 from vllm.model_executor.layers.rotary_embedding import get_rope
31 from vllm.model_executor.layers.vocab_parallel_embedding import (
32 ParallelLMHead,
33 VocabParallelEmbedding,
34 )
35 from vllm.sequence import IntermediateTensors
36
37 from .interfaces import HasInnerState, IsHybrid, SupportsLoRA, SupportsPP, SupportsQuant
38 from .utils import (
39 AutoWeightsLoader,
40 PPMissingLayer,
41 WeightsMapper,
42 extract_layer_index,
43 make_empty_intermediate_tensors_factory,
44 make_layers,
45 maybe_prefix,
46 )
47
48
49 class Lfm2MLP(nn.Module):
50 def __init__(
51 self,
52 dim: int,
53 intermediate_size: int,
54 multiple_of: int,
55 auto_adjust_ff_dim: bool,
56 ffn_dim_multiplier: float | None,
57 quant_config: QuantizationConfig | None = None,
58 prefix: str = "",
59 ):
60 super().__init__()
61 if auto_adjust_ff_dim:
62 intermediate_size = int(2 * intermediate_size / 3)
63 # custom dim factor multiplier
64 if ffn_dim_multiplier is not None:
65 intermediate_size = int(ffn_dim_multiplier * intermediate_size)
66 intermediate_size = multiple_of * (
67 (intermediate_size + multiple_of - 1) // multiple_of
68 )
69
70 self.w13 = MergedColumnParallelLinear(
71 input_size=dim,
72 output_sizes=[intermediate_size] * 2,
73 bias=False,
74 quant_config=quant_config,
75 prefix=f"{prefix}.w13",
76 )
77 self.w2 = RowParallelLinear(
78 input_size=intermediate_size,
79 output_size=dim,
80 bias=False,
81 quant_config=quant_config,
82 prefix=f"{prefix}.w2",
83 )
84 self.act_fn = SiluAndMul()
85
86 def forward(self, x: torch.Tensor) -> torch.Tensor:
87 gate_up, _ = self.w13(x)
88 x = self.act_fn(gate_up)
89 x, _ = self.w2(x)
90 return x
91
92
93 class Lfm2Attention(nn.Module):
94 def __init__(
95 self,
96 config: Lfm2Config,
97 layer_idx: int,
98 hidden_size: int,
99 num_heads: int,
100 num_kv_heads: int,
101 max_position_embeddings: int = 8192,
102 cache_config: CacheConfig | None = None,
103 quant_config: QuantizationConfig | None = None,
104 prefix: str = "",
105 ) -> None:
106 super().__init__()
107 self.layer_idx = layer_idx
108 self.hidden_size = hidden_size
109 self.num_kv_heads = num_kv_heads
110 tp_size = get_tensor_model_parallel_world_size()
111 self.total_num_heads = num_heads
112 assert self.total_num_heads % tp_size == 0
113 self.num_heads = self.total_num_heads // tp_size
114 self.total_num_kv_heads = num_kv_heads
115 if self.total_num_kv_heads >= tp_size:
116 # Number of KV heads is greater than TP size, so we partition
117 # the KV heads across multiple tensor parallel GPUs.
118 assert self.total_num_kv_heads % tp_size == 0
119 else:
120 # Number of KV heads is less than TP size, so we replicate
121 # the KV heads across multiple tensor parallel GPUs.
122 assert tp_size % self.total_num_kv_heads == 0
123 self.num_kv_heads = max(1, self.total_num_kv_heads // tp_size)
124 self.head_dim = self.hidden_size // self.total_num_heads
125
126 self.q_size = self.num_heads * self.head_dim
127 self.kv_size = self.num_kv_heads * self.head_dim
128 self.scaling = self.head_dim**-0.5
129 self.max_position_embeddings = max_position_embeddings
130
131 self.qkv_proj = QKVParallelLinear(
132 hidden_size=self.hidden_size,
133 head_size=self.head_dim,
134 total_num_heads=self.total_num_heads,
135 total_num_kv_heads=self.total_num_kv_heads,
136 bias=False,
137 quant_config=quant_config,
138 prefix=f"{prefix}.qkv_proj",
139 )
140 self.out_proj = RowParallelLinear(
141 input_size=self.total_num_heads * self.head_dim,
142 output_size=self.hidden_size,
143 bias=False,
144 quant_config=quant_config,
145 prefix=f"{prefix}.out_proj",
146 )
147 self.rotary_emb = get_rope(
148 self.head_dim,
149 max_position=self.max_position_embeddings,
150 rope_parameters=config.rope_parameters,
151 is_neox_style=True,
152 )
153 self.attn = Attention(
154 self.num_heads,
155 self.head_dim,
156 self.scaling,
157 num_kv_heads=self.num_kv_heads,
158 cache_config=cache_config,
159 prefix=f"{prefix}.attn",
160 )
161 self.q_layernorm = RMSNorm(self.head_dim, eps=config.norm_eps)
162 self.k_layernorm = RMSNorm(self.head_dim, eps=config.norm_eps)
163
164 def forward(
165 self,
166 positions: torch.Tensor,
167 hidden_states: torch.Tensor,
168 ) -> torch.Tensor:
169 n_tokens, _ = hidden_states.shape
170 qkv, _ = self.qkv_proj(hidden_states)
171 q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1)
172 q = q.view(n_tokens, self.num_heads, self.head_dim)
173 k = k.view(n_tokens, self.num_kv_heads, self.head_dim)
174 q = self.q_layernorm(q)
175 k = self.k_layernorm(k)
176 q, k = self.rotary_emb(positions, q, k)
177 q = q.view(n_tokens, self.num_heads * self.head_dim)
178 k = k.view(n_tokens, self.num_kv_heads * self.head_dim)
179 attn_output = self.attn(q, k, v)
180 output, _ = self.out_proj(attn_output)
181 return output
182
183
184 class Lfm2AttentionDecoderLayer(nn.Module):
185 def __init__(
186 self,
187 config: Lfm2Config,
188 layer_idx: int,
189 model_config: ModelConfig | None = None,
190 cache_config: CacheConfig | None = None,
191 quant_config: QuantizationConfig | None = None,
192 prefix: str = "",
193 ) -> None:
194 super().__init__()
195 self.prefix = prefix
196 self.config = config
197 self.layer_idx = layer_idx
198
199 max_position_embeddings = getattr(config, "max_position_embeddings", 8192)
200
201 self.self_attn = Lfm2Attention(
202 config=config,
203 layer_idx=layer_idx,
204 hidden_size=config.hidden_size,
205 num_heads=config.num_attention_heads,
206 num_kv_heads=config.num_key_value_heads,
207 max_position_embeddings=max_position_embeddings,
208 cache_config=cache_config,
209 quant_config=quant_config,
210 prefix=f"{prefix}.self_attn",
211 )
212
213 self.feed_forward = Lfm2MLP(
214 dim=config.block_dim,
215 intermediate_size=config.intermediate_size,
216 multiple_of=config.block_multiple_of,
217 auto_adjust_ff_dim=config.block_auto_adjust_ff_dim,
218 ffn_dim_multiplier=config.block_ffn_dim_multiplier,
219 quant_config=quant_config,
220 prefix=f"{prefix}.feed_forward",
221 )
222 self.operator_norm = RMSNorm(config.hidden_size, eps=config.norm_eps)
223 self.ffn_norm = RMSNorm(config.hidden_size, eps=config.norm_eps)
224
225 def forward(
226 self,
227 positions: torch.Tensor,
228 hidden_states: torch.Tensor,
229 residual: torch.Tensor | None,
230 **kwargs,
231 ) -> tuple[torch.Tensor, torch.Tensor]:
232 if residual is None:
233 residual = hidden_states
234 hidden_states = self.operator_norm(hidden_states)
235 else:
236 hidden_states, residual = self.operator_norm(hidden_states, residual)
237 hidden_states = self.self_attn(positions=positions, hidden_states=hidden_states)
238 hidden_states, residual = self.ffn_norm(hidden_states, residual)
239 return self.feed_forward(hidden_states), residual
240
241
242 class Lfm2ShortConvDecoderLayer(nn.Module):
243 def __init__(
244 self,
245 config: Lfm2Config,
246 layer_idx: int,
247 model_config: ModelConfig | None = None,
248 cache_config: CacheConfig | None = None,
249 quant_config: QuantizationConfig | None = None,
250 prefix: str = "",
251 ) -> None:
252 super().__init__()
253 self.layer_idx = layer_idx
254 self.short_conv = ShortConv(
255 config=config,
256 dim=config.conv_dim,
257 layer_idx=layer_idx,
258 model_config=model_config,
259 cache_config=cache_config,
260 quant_config=quant_config,
261 prefix=f"{prefix}.short_conv",
262 )
263
264 self.feed_forward = Lfm2MLP(
265 dim=config.block_dim,
266 intermediate_size=config.intermediate_size,
267 multiple_of=config.block_multiple_of,
268 auto_adjust_ff_dim=config.block_auto_adjust_ff_dim,
269 ffn_dim_multiplier=config.block_ffn_dim_multiplier,
270 quant_config=quant_config,
271 prefix=f"{prefix}.feed_forward",
272 )
273 self.operator_norm = RMSNorm(config.hidden_size, eps=config.norm_eps)
274 self.ffn_norm = RMSNorm(config.hidden_size, eps=config.norm_eps)
275
276 def forward(
277 self,
278 hidden_states: torch.Tensor,
279 residual: torch.Tensor | None,
280 **kwargs,
281 ):
282 if residual is None:
283 residual = hidden_states
284 hidden_states = self.operator_norm(hidden_states)
285 else:
286 hidden_states, residual = self.operator_norm(hidden_states, residual)
287 output = torch.empty_like(hidden_states)
288 self.short_conv(
289 hidden_states,
290 output,
291 )
292 hidden_states, residual = self.ffn_norm(output, residual)
293 hidden_states = self.feed_forward(hidden_states)
294 return hidden_states, residual
295
296
297 @support_torch_compile
298 class Lfm2Model(nn.Module):
299 # HF uses .conv. but vLLM uses .short_conv. to avoid LoRA regex collision
300 # with the inner .conv.conv child (ShortConv has a child self.conv, so
301 # naming the container .conv too makes _match_target_modules match both).
302 hf_to_vllm_mapper = WeightsMapper(
303 orig_to_new_substr={".conv.": ".short_conv."},
304 orig_to_new_stacked={
305 ".q_proj": (".qkv_proj", "q"),
306 ".k_proj": (".qkv_proj", "k"),
307 ".v_proj": (".qkv_proj", "v"),
308 ".w1": (".w13", 0),
309 ".w3": (".w13", 1),
310 },
311 )
312
313 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
314 super().__init__()
315
316 config = vllm_config.model_config.hf_config
317 model_config = vllm_config.model_config
318 cache_config = vllm_config.cache_config
319 quant_config = vllm_config.quant_config
320
321 self.config = config
322
323 self.vocab_size = config.vocab_size
324
325 self.embed_tokens = VocabParallelEmbedding(
326 self.vocab_size, config.hidden_size, org_num_embeddings=config.vocab_size
327 )
328
329 def get_layer(prefix: str):
330 layer_idx = extract_layer_index(prefix)
331 is_attn = self.config.layer_types[layer_idx] == "full_attention"
332 layer_class = (
333 Lfm2AttentionDecoderLayer if is_attn else Lfm2ShortConvDecoderLayer
334 )
335 return layer_class(
336 config,
337 layer_idx,
338 model_config,
339 cache_config,
340 quant_config=quant_config,
341 prefix=prefix,
342 )
343
344 self.start_layer, self.end_layer, self.layers = make_layers(
345 config.num_hidden_layers, get_layer, prefix=f"{prefix}.layers"
346 )
347 self.make_empty_intermediate_tensors = make_empty_intermediate_tensors_factory(
348 ["hidden_states", "residual"], config.hidden_size
349 )
350
351 if get_pp_group().is_last_rank:
352 self.embedding_norm = RMSNorm(config.hidden_size, eps=config.norm_eps)
353 else:
354 self.embedding_norm = PPMissingLayer()
355
356 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
357 return self.embed_tokens(input_ids)
358
359 def forward(
360 self,
361 input_ids: torch.Tensor | None,
362 positions: torch.Tensor,
363 intermediate_tensors: IntermediateTensors | None = None,
364 inputs_embeds: torch.Tensor | None = None,
365 ) -> torch.Tensor:
366 if get_pp_group().is_first_rank:
367 if inputs_embeds is not None:
368 hidden_states = inputs_embeds
369 else:
370 hidden_states = self.embed_input_ids(input_ids)
371 residual = None
372 else:
373 assert intermediate_tensors is not None
374 hidden_states = intermediate_tensors["hidden_states"]
375 residual = intermediate_tensors["residual"]
376
377 for layer in islice(self.layers, self.start_layer, self.end_layer):
378 hidden_states, residual = layer(
379 positions=positions,
380 hidden_states=hidden_states,
381 residual=residual,
382 )
383 if not get_pp_group().is_last_rank:
384 return IntermediateTensors(
385 {"hidden_states": hidden_states, "residual": residual}
386 )
387 hidden_states, _ = self.embedding_norm(hidden_states, residual)
388 return hidden_states
389
390 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
391 loader = AutoWeightsLoader(self)
392 return loader.load_weights(weights, mapper=self.hf_to_vllm_mapper)
393
394
395 class Lfm2ForCausalLM(
396 nn.Module, HasInnerState, SupportsLoRA, SupportsPP, IsHybrid, SupportsQuant
397 ):
398 packed_modules_mapping = {
399 "qkv_proj": [
400 "q_proj",
401 "k_proj",
402 "v_proj",
403 ],
404 "w13": [
405 "w1",
406 "w3",
407 ],
408 "in_proj": ["in_proj"],
409 }
410
411 # Reuse the backbone mapper so LoRA/quantization see the same name mapping.
412 hf_to_vllm_mapper = Lfm2Model.hf_to_vllm_mapper
413
414 # LoRA specific attributes
415 embedding_modules = {
416 "embed_tokens": "input_embeddings",
417 "lm_head": "output_embeddings",
418 }
419
420 @classmethod
421 def get_mamba_state_dtype_from_config(
422 cls,
423 vllm_config: "VllmConfig",
424 ) -> tuple[torch.dtype, ...]:
425 return MambaStateDtypeCalculator.short_conv_state_dtype(
426 vllm_config.model_config.dtype,
427 vllm_config.cache_config.mamba_cache_dtype,
428 )
429
430 @classmethod
431 def get_mamba_state_shape_from_config(
432 cls,
433 vllm_config: "VllmConfig",
434 ) -> tuple[tuple[int, int]]:
435 """Calculate shapes for LFM2's convolutional cache.
436
437 Args:
438 vllm_config: vLLM config
439
440 Returns:
441 Tuple containing:
442 - conv_state_shape: Shape for convolutional state cache
443
444 """
445 parallel_config = vllm_config.parallel_config
446 hf_config = vllm_config.model_config.hf_config
447
448 return MambaStateShapeCalculator.short_conv_state_shape(
449 tp_world_size=parallel_config.tensor_parallel_size,
450 intermediate_size=hf_config.conv_dim,
451 conv_kernel=hf_config.conv_L_cache,
452 )
453
454 @classmethod
455 def get_mamba_state_copy_func(cls) -> tuple[MambaStateCopyFunc]:
456 return MambaStateCopyFuncCalculator.short_conv_state_copy_func()
457
458 def __init__(self, *, vllm_config: VllmConfig, prefix: str = "") -> None:
459 config = vllm_config.model_config.hf_config
460 quant_config = vllm_config.quant_config
461 super().__init__()
462 self.config = config
463 self.model = Lfm2Model(
464 vllm_config=vllm_config, prefix=maybe_prefix(prefix, "model")
465 )
466
467 if get_pp_group().is_last_rank:
468 self.lm_head = ParallelLMHead(
469 config.vocab_size,
470 config.hidden_size,
471 quant_config=quant_config,
472 prefix=maybe_prefix(prefix, "lm_head"),
473 )
474 self.lm_head = self.lm_head.tie_weights(self.model.embed_tokens)
475 else:
476 self.lm_head = PPMissingLayer()
477
478 self.logits_processor = LogitsProcessor(config.vocab_size)
479
480 self.make_empty_intermediate_tensors = (
481 self.model.make_empty_intermediate_tensors
482 )
483
484 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
485 return self.model.embed_input_ids(input_ids)
486
487 def forward(
488 self,
489 input_ids: torch.Tensor | None,
490 positions: torch.Tensor,
491 intermediate_tensors: IntermediateTensors | None = None,
492 inputs_embeds: torch.Tensor | None = None,
493 **kwargs,
494 ) -> torch.Tensor:
495 hidden_states = self.model(
496 input_ids, positions, intermediate_tensors, inputs_embeds
497 )
498 return hidden_states
499
500 def compute_logits(self, hidden_states: torch.Tensor) -> torch.Tensor:
501 logits = self.logits_processor(self.lm_head, hidden_states)
502 return logits
503
504 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
505 loader = AutoWeightsLoader(self)
506 return loader.load_weights(weights)