main
py 668 lines 23.1 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
9 from vllm.compilation.decorators import support_torch_compile
10 from vllm.config import CacheConfig, ModelConfig, VllmConfig, get_current_vllm_config
11 from vllm.distributed import (
12 get_ep_group,
13 get_pp_group,
14 get_tensor_model_parallel_world_size,
15 )
16 from vllm.model_executor.layers.activation import SiluAndMul
17 from vllm.model_executor.layers.attention import Attention
18 from vllm.model_executor.layers.fused_moe import (
19 FusedMoEFactory,
20 GateLinear,
21 )
22 from vllm.model_executor.layers.layernorm import RMSNorm
23 from vllm.model_executor.layers.linear import (
24 MergedColumnParallelLinear,
25 QKVParallelLinear,
26 RowParallelLinear,
27 )
28 from vllm.model_executor.layers.logits_processor import LogitsProcessor
29 from vllm.model_executor.layers.mamba.mamba_utils import (
30 MambaStateCopyFunc,
31 MambaStateCopyFuncCalculator,
32 MambaStateDtypeCalculator,
33 MambaStateShapeCalculator,
34 )
35 from vllm.model_executor.layers.mamba.short_conv import ShortConv
36 from vllm.model_executor.layers.quantization import QuantizationConfig
37 from vllm.model_executor.layers.rotary_embedding import get_rope
38 from vllm.model_executor.layers.vocab_parallel_embedding import (
39 ParallelLMHead,
40 VocabParallelEmbedding,
41 )
42 from vllm.sequence import IntermediateTensors
43 from vllm.transformers_utils.configs.lfm2_moe import Lfm2MoeConfig
44
45 from .interfaces import (
46 HasInnerState,
47 IsHybrid,
48 MixtureOfExperts,
49 SupportsLoRA,
50 SupportsPP,
51 SupportsQuant,
52 )
53 from .utils import (
54 AutoWeightsLoader,
55 PPMissingLayer,
56 WeightsMapper,
57 extract_layer_index,
58 make_empty_intermediate_tensors_factory,
59 make_layers,
60 maybe_prefix,
61 )
62
63
64 class Lfm2MoeMlp(nn.Module):
65 def __init__(
66 self,
67 dim: int,
68 ff_dim: int,
69 quant_config: QuantizationConfig | None = None,
70 prefix: str = "",
71 ):
72 super().__init__()
73 self.w13 = MergedColumnParallelLinear(
74 input_size=dim,
75 output_sizes=[ff_dim] * 2,
76 bias=False,
77 quant_config=quant_config,
78 prefix=f"{prefix}.w13",
79 )
80 self.w2 = RowParallelLinear(
81 input_size=ff_dim,
82 output_size=dim,
83 bias=False,
84 quant_config=quant_config,
85 prefix=f"{prefix}.w2",
86 )
87 self.act_fn = SiluAndMul()
88
89 def forward(self, x: torch.Tensor) -> torch.Tensor:
90 gate_up, _ = self.w13(x)
91 x = self.act_fn(gate_up)
92 x, _ = self.w2(x)
93 return x
94
95
96 class Lfm2MoeSparseMoeBlock(nn.Module):
97 def __init__(
98 self,
99 config: Lfm2MoeConfig,
100 quant_config: QuantizationConfig | None = None,
101 prefix: str = "",
102 enable_eplb: bool = False,
103 ):
104 super().__init__()
105 self.tp_size = get_tensor_model_parallel_world_size()
106 self.routed_scaling_factor = config.routed_scaling_factor
107
108 self.ep_group = get_ep_group().device_group
109 self.ep_size = self.ep_group.size()
110 self.n_routed_experts = config.num_experts
111
112 if self.tp_size > self.n_routed_experts:
113 raise ValueError(
114 f"Tensor parallel size {self.tp_size} is greater than "
115 f"the number of experts {self.n_routed_experts}."
116 )
117
118 # Load balancing settings.
119 vllm_config = get_current_vllm_config()
120 eplb_config = vllm_config.parallel_config.eplb_config
121 self.enable_eplb = enable_eplb
122
123 self.n_logical_experts = self.n_routed_experts
124 self.n_redundant_experts = eplb_config.num_redundant_experts
125 self.n_physical_experts = self.n_logical_experts + self.n_redundant_experts
126 self.n_local_physical_experts = self.n_physical_experts // self.ep_size
127
128 self.gate = GateLinear(
129 config.hidden_size,
130 config.num_experts,
131 quant_config=quant_config,
132 prefix=f"{prefix}.gate",
133 )
134 if config.use_expert_bias:
135 self.gate.e_score_correction_bias = nn.Parameter(
136 torch.empty(self.n_routed_experts, dtype=torch.float32)
137 )
138 else:
139 self.gate.e_score_correction_bias = None
140
141 self.experts = FusedMoEFactory(
142 num_experts=self.n_routed_experts,
143 top_k=config.num_experts_per_tok,
144 hidden_size=config.hidden_size,
145 intermediate_size=config.moe_intermediate_size,
146 renormalize=config.norm_topk_prob,
147 quant_config=quant_config,
148 use_grouped_topk=True, # needed for softmax score func
149 num_expert_group=1,
150 topk_group=1,
151 prefix=f"{prefix}.experts",
152 enable_eplb=self.enable_eplb,
153 num_redundant_experts=self.n_redundant_experts,
154 scoring_func="sigmoid",
155 e_score_correction_bias=self.gate.e_score_correction_bias,
156 routed_scaling_factor=self.routed_scaling_factor,
157 ckpt_names=("w1", "w2", "w3"),
158 )
159
160 def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
161 orig_shape = hidden_states.shape
162 hidden_dim = hidden_states.shape[-1]
163 hidden_states = hidden_states.view(-1, hidden_dim)
164
165 # router_logits: (num_tokens, n_experts)
166 router_logits, _ = self.gate(hidden_states)
167 final_hidden_states = self.experts(
168 hidden_states=hidden_states, router_logits=router_logits
169 )
170
171 return final_hidden_states.view(orig_shape)
172
173
174 class Lfm2MoeAttention(nn.Module):
175 def __init__(
176 self,
177 config: Lfm2MoeConfig,
178 layer_idx: int,
179 hidden_size: int,
180 num_heads: int,
181 num_kv_heads: int,
182 max_position_embeddings: int = 8192,
183 cache_config: CacheConfig | None = None,
184 quant_config: QuantizationConfig | None = None,
185 prefix: str = "",
186 ) -> None:
187 super().__init__()
188 self.layer_idx = layer_idx
189 self.hidden_size = hidden_size
190 self.num_kv_heads = num_kv_heads
191 tp_size = get_tensor_model_parallel_world_size()
192 self.total_num_heads = num_heads
193 assert self.total_num_heads % tp_size == 0
194 self.num_heads = self.total_num_heads // tp_size
195 self.total_num_kv_heads = num_kv_heads
196 if self.total_num_kv_heads >= tp_size:
197 # Number of KV heads is greater than TP size, so we partition
198 # the KV heads across multiple tensor parallel GPUs.
199 assert self.total_num_kv_heads % tp_size == 0
200 else:
201 # Number of KV heads is less than TP size, so we replicate
202 # the KV heads across multiple tensor parallel GPUs.
203 assert tp_size % self.total_num_kv_heads == 0
204 self.num_kv_heads = max(1, self.total_num_kv_heads // tp_size)
205 self.head_dim = self.hidden_size // self.total_num_heads
206
207 self.q_size = self.num_heads * self.head_dim
208 self.kv_size = self.num_kv_heads * self.head_dim
209 self.scaling = self.head_dim**-0.5
210 self.max_position_embeddings = max_position_embeddings
211
212 self.qkv_proj = QKVParallelLinear(
213 hidden_size=self.hidden_size,
214 head_size=self.head_dim,
215 total_num_heads=self.total_num_heads,
216 total_num_kv_heads=self.total_num_kv_heads,
217 bias=False,
218 quant_config=quant_config,
219 prefix=f"{prefix}.qkv_proj",
220 )
221 self.out_proj = RowParallelLinear(
222 input_size=self.total_num_heads * self.head_dim,
223 output_size=self.hidden_size,
224 bias=False,
225 quant_config=quant_config,
226 prefix=f"{prefix}.out_proj",
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=True,
233 )
234 self.attn = Attention(
235 self.num_heads,
236 self.head_dim,
237 self.scaling,
238 num_kv_heads=self.num_kv_heads,
239 cache_config=cache_config,
240 prefix=f"{prefix}.attn",
241 )
242 self.q_layernorm = RMSNorm(self.head_dim, eps=config.norm_eps)
243 self.k_layernorm = RMSNorm(self.head_dim, eps=config.norm_eps)
244
245 def forward(
246 self,
247 positions: torch.Tensor,
248 hidden_states: torch.Tensor,
249 ) -> torch.Tensor:
250 n_tokens, _ = hidden_states.shape
251 qkv, _ = self.qkv_proj(hidden_states)
252 q, k, v = qkv.split([self.q_size, self.kv_size, self.kv_size], dim=-1)
253 q = q.view(n_tokens, self.num_heads, self.head_dim)
254 k = k.view(n_tokens, self.num_kv_heads, self.head_dim)
255 q = self.q_layernorm(q)
256 k = self.k_layernorm(k)
257 q, k = self.rotary_emb(positions, q, k)
258 q = q.view(n_tokens, self.num_heads * self.head_dim)
259 k = k.view(n_tokens, self.num_kv_heads * self.head_dim)
260 attn_output = self.attn(q, k, v)
261 output, _ = self.out_proj(attn_output)
262 return output
263
264
265 class Lfm2MoeAttentionDecoderLayer(nn.Module):
266 def __init__(
267 self,
268 config: Lfm2MoeConfig,
269 layer_idx: int,
270 model_config: ModelConfig | None = None,
271 cache_config: CacheConfig | None = None,
272 quant_config: QuantizationConfig | None = None,
273 prefix: str = "",
274 enable_eplb: bool = False,
275 ) -> None:
276 super().__init__()
277 self.prefix = prefix
278 self.config = config
279 self.layer_idx = layer_idx
280
281 max_position_embeddings = getattr(config, "max_position_embeddings", 8192)
282
283 self.self_attn = Lfm2MoeAttention(
284 config=config,
285 layer_idx=layer_idx,
286 hidden_size=config.hidden_size,
287 num_heads=config.num_attention_heads,
288 num_kv_heads=config.num_key_value_heads,
289 max_position_embeddings=max_position_embeddings,
290 cache_config=cache_config,
291 quant_config=quant_config,
292 prefix=f"{prefix}.self_attn",
293 )
294
295 if layer_idx < config.num_dense_layers:
296 self.feed_forward = Lfm2MoeMlp(
297 dim=config.hidden_size,
298 ff_dim=config.intermediate_size,
299 quant_config=quant_config,
300 prefix=f"{prefix}.feed_forward",
301 )
302 else:
303 self.feed_forward = Lfm2MoeSparseMoeBlock(
304 config=config,
305 quant_config=quant_config,
306 prefix=f"{prefix}.feed_forward",
307 enable_eplb=enable_eplb,
308 )
309
310 self.operator_norm = RMSNorm(config.hidden_size, eps=config.norm_eps)
311 self.ffn_norm = RMSNorm(config.hidden_size, eps=config.norm_eps)
312
313 def forward(
314 self,
315 positions: torch.Tensor,
316 hidden_states: torch.Tensor,
317 residual: torch.Tensor | None,
318 **kwargs,
319 ) -> tuple[torch.Tensor, torch.Tensor]:
320 if residual is None:
321 residual = hidden_states
322 hidden_states = self.operator_norm(hidden_states)
323 else:
324 hidden_states, residual = self.operator_norm(hidden_states, residual)
325 hidden_states = self.self_attn(positions=positions, hidden_states=hidden_states)
326 hidden_states, residual = self.ffn_norm(hidden_states, residual)
327 return self.feed_forward(hidden_states), residual
328
329
330 class Lfm2MoeShortConvDecoderLayer(nn.Module):
331 def __init__(
332 self,
333 config: Lfm2MoeConfig,
334 layer_idx: int,
335 model_config: ModelConfig | None = None,
336 cache_config: CacheConfig | None = None,
337 quant_config: QuantizationConfig | None = None,
338 prefix: str = "",
339 enable_eplb: bool = False,
340 ) -> None:
341 super().__init__()
342 self.layer_idx = layer_idx
343 self.short_conv = ShortConv(
344 config=config,
345 dim=config.hidden_size,
346 layer_idx=layer_idx,
347 model_config=model_config,
348 cache_config=cache_config,
349 quant_config=quant_config,
350 prefix=f"{prefix}.short_conv",
351 )
352
353 if layer_idx < config.num_dense_layers:
354 self.feed_forward = Lfm2MoeMlp(
355 dim=config.hidden_size,
356 ff_dim=config.intermediate_size,
357 quant_config=quant_config,
358 prefix=f"{prefix}.feed_forward",
359 )
360 else:
361 self.feed_forward = Lfm2MoeSparseMoeBlock(
362 config=config,
363 quant_config=quant_config,
364 prefix=f"{prefix}.feed_forward",
365 enable_eplb=enable_eplb,
366 )
367
368 self.operator_norm = RMSNorm(config.hidden_size, eps=config.norm_eps)
369 self.ffn_norm = RMSNorm(config.hidden_size, eps=config.norm_eps)
370
371 def forward(
372 self,
373 hidden_states: torch.Tensor,
374 residual: torch.Tensor | None,
375 **kwargs,
376 ):
377 if residual is None:
378 residual = hidden_states
379 hidden_states = self.operator_norm(hidden_states)
380 else:
381 hidden_states, residual = self.operator_norm(hidden_states, residual)
382 output = torch.empty_like(hidden_states)
383 self.short_conv(
384 hidden_states,
385 output,
386 )
387 hidden_states, residual = self.ffn_norm(output, residual)
388 hidden_states = self.feed_forward(hidden_states)
389 return hidden_states, residual
390
391
392 @support_torch_compile
393 class Lfm2MoeModel(nn.Module):
394 hf_to_vllm_mapper = WeightsMapper(
395 orig_to_new_substr={
396 ".conv.": ".short_conv.",
397 "expert_bias": "gate.e_score_correction_bias",
398 },
399 orig_to_new_stacked={
400 ".q_proj": (".qkv_proj", "q"),
401 ".k_proj": (".qkv_proj", "k"),
402 ".v_proj": (".qkv_proj", "v"),
403 # Scoped (with trailing dots) to the dense MLP so routed experts.*.w1/w3
404 # (loaded by FusedMoE) are left untouched and .w1 does not match inside .w13
405 ".feed_forward.w1.": (".feed_forward.w13.", 0),
406 ".feed_forward.w3.": (".feed_forward.w13.", 1),
407 },
408 )
409
410 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
411 super().__init__()
412
413 config = vllm_config.model_config.hf_config
414 model_config = vllm_config.model_config
415 cache_config = vllm_config.cache_config
416 quant_config = vllm_config.quant_config
417
418 parallel_config = vllm_config.parallel_config
419 enable_eplb = parallel_config.enable_eplb
420 eplb_config = parallel_config.eplb_config
421 self.num_redundant_experts = eplb_config.num_redundant_experts
422
423 self.config = config
424
425 self.vocab_size = config.vocab_size
426
427 self.embed_tokens = VocabParallelEmbedding(
428 self.vocab_size, config.hidden_size, org_num_embeddings=config.vocab_size
429 )
430
431 def get_layer(prefix: str):
432 layer_idx = extract_layer_index(prefix)
433 is_attn = self.config.layer_types[layer_idx] == "full_attention"
434 layer_class = (
435 Lfm2MoeAttentionDecoderLayer
436 if is_attn
437 else Lfm2MoeShortConvDecoderLayer
438 )
439 return layer_class(
440 config,
441 layer_idx,
442 model_config,
443 cache_config,
444 quant_config=quant_config,
445 prefix=prefix,
446 enable_eplb=enable_eplb,
447 )
448
449 self.start_layer, self.end_layer, self.layers = make_layers(
450 config.num_hidden_layers, get_layer, prefix=f"{prefix}.layers"
451 )
452 self.make_empty_intermediate_tensors = make_empty_intermediate_tensors_factory(
453 ["hidden_states", "residual"], config.hidden_size
454 )
455
456 if get_pp_group().is_last_rank:
457 self.embedding_norm = RMSNorm(config.hidden_size, eps=config.norm_eps)
458 else:
459 self.embedding_norm = PPMissingLayer()
460
461 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
462 return self.embed_tokens(input_ids)
463
464 def forward(
465 self,
466 input_ids: torch.Tensor | None,
467 positions: torch.Tensor,
468 intermediate_tensors: IntermediateTensors | None = None,
469 inputs_embeds: torch.Tensor | None = None,
470 ) -> torch.Tensor:
471 if get_pp_group().is_first_rank:
472 if inputs_embeds is not None:
473 hidden_states = inputs_embeds
474 else:
475 hidden_states = self.embed_input_ids(input_ids)
476 residual = None
477 else:
478 assert intermediate_tensors is not None
479 hidden_states = intermediate_tensors["hidden_states"]
480 residual = intermediate_tensors["residual"]
481
482 for layer in islice(self.layers, self.start_layer, self.end_layer):
483 hidden_states, residual = layer(
484 positions=positions,
485 hidden_states=hidden_states,
486 residual=residual,
487 )
488 if not get_pp_group().is_last_rank:
489 return IntermediateTensors(
490 {"hidden_states": hidden_states, "residual": residual}
491 )
492 hidden_states, _ = self.embedding_norm(hidden_states, residual)
493 return hidden_states
494
495 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
496 loader = AutoWeightsLoader(self, ignore_unexpected_suffixes=[".bias", "_bias"])
497 return loader.load_weights(weights, mapper=self.hf_to_vllm_mapper)
498
499
500 class Lfm2MoeForCausalLM(
501 nn.Module,
502 HasInnerState,
503 SupportsLoRA,
504 SupportsPP,
505 IsHybrid,
506 SupportsQuant,
507 MixtureOfExperts,
508 ):
509 packed_modules_mapping = {
510 "qkv_proj": [
511 "q_proj",
512 "k_proj",
513 "v_proj",
514 ],
515 "w13": [
516 "w1",
517 "w3",
518 ],
519 "in_proj": ["in_proj"],
520 }
521
522 # HF uses .conv. but vLLM uses .short_conv. to avoid LoRA regex collision
523 # with the inner .conv.conv child (ShortConv has a child self.conv, so
524 # naming the container .conv too makes _match_target_modules match both)
525 hf_to_vllm_mapper = WeightsMapper(
526 orig_to_new_substr={".conv.": ".short_conv."},
527 )
528
529 # LoRA specific attributes
530 embedding_modules = {
531 "embed_tokens": "input_embeddings",
532 "lm_head": "output_embeddings",
533 }
534
535 @classmethod
536 def get_mamba_state_dtype_from_config(
537 cls,
538 vllm_config: "VllmConfig",
539 ) -> tuple[torch.dtype, ...]:
540 return MambaStateDtypeCalculator.short_conv_state_dtype(
541 vllm_config.model_config.dtype,
542 vllm_config.cache_config.mamba_cache_dtype,
543 )
544
545 @classmethod
546 def get_mamba_state_shape_from_config(
547 cls,
548 vllm_config: "VllmConfig",
549 ) -> tuple[tuple[int, int]]:
550 """Calculate shapes for LFM2's convolutional cache.
551
552 Args:
553 vllm_config: vLLM config
554
555 Returns:
556 Tuple containing:
557 - conv_state_shape: Shape for convolutional state cache
558
559 """
560 parallel_config = vllm_config.parallel_config
561 hf_config = vllm_config.model_config.hf_config
562
563 return MambaStateShapeCalculator.short_conv_state_shape(
564 tp_world_size=parallel_config.tensor_parallel_size,
565 intermediate_size=hf_config.hidden_size,
566 conv_kernel=hf_config.conv_L_cache,
567 )
568
569 @classmethod
570 def get_mamba_state_copy_func(cls) -> tuple[MambaStateCopyFunc]:
571 return MambaStateCopyFuncCalculator.short_conv_state_copy_func()
572
573 def __init__(self, *, vllm_config: VllmConfig, prefix: str = "") -> None:
574 config = vllm_config.model_config.hf_config
575 quant_config = vllm_config.quant_config
576
577 super().__init__()
578 self.config = config
579 self.model = Lfm2MoeModel(
580 vllm_config=vllm_config, prefix=maybe_prefix(prefix, "model")
581 )
582
583 if get_pp_group().is_last_rank:
584 self.lm_head = ParallelLMHead(
585 config.vocab_size,
586 config.hidden_size,
587 quant_config=quant_config,
588 prefix=maybe_prefix(prefix, "lm_head"),
589 )
590 self.lm_head = self.lm_head.tie_weights(self.model.embed_tokens)
591 else:
592 self.lm_head = PPMissingLayer()
593
594 self.logits_processor = LogitsProcessor(config.vocab_size)
595
596 self.make_empty_intermediate_tensors = (
597 self.model.make_empty_intermediate_tensors
598 )
599
600 # Set MoE hyperparameters
601
602 self.moe_layers = []
603 example_layer = None
604 for layer in self.model.layers:
605 if isinstance(layer, PPMissingLayer):
606 continue
607
608 assert isinstance(
609 layer, (Lfm2MoeAttentionDecoderLayer, Lfm2MoeShortConvDecoderLayer)
610 )
611 if isinstance(layer.feed_forward, Lfm2MoeSparseMoeBlock):
612 example_layer = layer.feed_forward
613 self.moe_layers.append(layer.feed_forward.experts)
614
615 if example_layer is None:
616 raise RuntimeError(
617 "No Lfm2MoeSparseMoeBlock layer found in the model.layers."
618 )
619
620 self.num_moe_layers = len(self.moe_layers)
621 self.num_expert_groups = 1
622 self.num_shared_experts = 0
623 self.num_logical_experts = example_layer.n_logical_experts
624 self.num_physical_experts = example_layer.n_physical_experts
625 self.num_local_physical_experts = example_layer.n_local_physical_experts
626 self.num_routed_experts = example_layer.n_routed_experts
627 self.num_redundant_experts = example_layer.n_redundant_experts
628
629 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
630 return self.model.embed_input_ids(input_ids)
631
632 def update_physical_experts_metadata(
633 self,
634 num_physical_experts: int,
635 num_local_physical_experts: int,
636 ) -> None:
637 assert self.num_local_physical_experts == num_local_physical_experts
638 self.num_physical_experts = num_physical_experts
639 self.num_local_physical_experts = num_local_physical_experts
640 self.num_redundant_experts = num_physical_experts - self.num_logical_experts
641 for layer in self.model.layers:
642 if isinstance(layer.feed_forward, Lfm2MoeSparseMoeBlock):
643 moe = layer.feed_forward
644 moe.n_local_physical_experts = num_local_physical_experts
645 moe.n_physical_experts = num_physical_experts
646 moe.n_redundant_experts = self.num_redundant_experts
647 moe.experts.update_expert_map()
648
649 def forward(
650 self,
651 input_ids: torch.Tensor | None,
652 positions: torch.Tensor,
653 intermediate_tensors: IntermediateTensors | None = None,
654 inputs_embeds: torch.Tensor | None = None,
655 **kwargs,
656 ) -> torch.Tensor:
657 hidden_states = self.model(
658 input_ids, positions, intermediate_tensors, inputs_embeds
659 )
660 return hidden_states
661
662 def compute_logits(self, hidden_states: torch.Tensor) -> torch.Tensor:
663 logits = self.logits_processor(self.lm_head, hidden_states)
664 return logits
665
666 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
667 loader = AutoWeightsLoader(self)
668 return loader.load_weights(weights)