main
py 1,094 lines 40.7 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 from collections.abc import Iterable
5 from typing import Any
6
7 import torch
8 from torch import nn
9
10 from vllm.config import CacheConfig, VllmConfig
11 from vllm.distributed import (
12 get_pp_group,
13 get_tensor_model_parallel_world_size,
14 )
15 from vllm.logger import init_logger
16 from vllm.model_executor.layers.activation import SiluAndMul, SituAndMul
17 from vllm.model_executor.layers.fused_moe import (
18 FusedMoEFactory,
19 fused_moe_make_expert_params_mapping,
20 )
21 from vllm.model_executor.layers.fused_moe.router.gate_linear import GateLinear
22 from vllm.model_executor.layers.layernorm import RMSNorm
23 from vllm.model_executor.layers.linear import (
24 ColumnParallelLinear,
25 MergedColumnParallelLinear,
26 ReplicatedLinear,
27 RowParallelLinear,
28 )
29 from vllm.model_executor.layers.logits_processor import LogitsProcessor
30 from vllm.model_executor.layers.mamba.gdn.kimi_gdn_linear_attn import (
31 KimiGatedDeltaNetAttention as KimiLinearGatedDeltaNetAttention,
32 )
33 from vllm.model_executor.layers.mamba.mamba_utils import (
34 MambaStateCopyFunc,
35 MambaStateCopyFuncCalculator,
36 MambaStateDtypeCalculator,
37 MambaStateShapeCalculator,
38 )
39 from vllm.model_executor.layers.mla import MLAModules
40 from vllm.model_executor.layers.quantization.base_config import QuantizationConfig
41 from vllm.model_executor.layers.vocab_parallel_embedding import (
42 ParallelLMHead,
43 VocabParallelEmbedding,
44 )
45 from vllm.model_executor.model_loader.weight_utils import (
46 default_weight_loader,
47 maybe_remap_kv_scale_name,
48 )
49 from vllm.model_executor.models.interfaces import (
50 EagleModelMixin,
51 HasInnerState,
52 IsHybrid,
53 MixtureOfExperts,
54 SupportsPP,
55 )
56 from vllm.model_executor.models.utils import (
57 AutoWeightsLoader,
58 PPMissingLayer,
59 get_spec_layer_idx_from_weight_name,
60 is_pp_missing_parameter,
61 make_layers,
62 maybe_prefix,
63 )
64 from vllm.models.kimi_k3.amd.kda import KimiK3DeltaAttention
65 from vllm.models.kimi_k3.amd.latent_moe_runner import ROCmLatentMoERunner
66 from vllm.models.kimi_k3.amd.mla import KimiK3MultiHeadLatentAttentionWrapper
67 from vllm.models.kimi_k3.amd.ops.attn_res import attn_res
68 from vllm.sequence import IntermediateTensors
69 from vllm.transformers_utils.configs.kimi_linear import KimiLinearConfig
70 from vllm.utils.math_utils import cdiv
71
72 logger = init_logger(__name__)
73
74
75 class KimiMLP(nn.Module):
76 def __init__(
77 self,
78 hidden_size: int,
79 intermediate_size: int,
80 hidden_act: str,
81 quant_config: QuantizationConfig | None = None,
82 reduce_results: bool = True,
83 prefix: str = "",
84 activation_situ_beta: float | None = None,
85 activation_situ_linear_beta: float | None = None,
86 ) -> None:
87 super().__init__()
88
89 self.gate_up_proj = MergedColumnParallelLinear(
90 hidden_size,
91 [intermediate_size] * 2,
92 bias=False,
93 quant_config=quant_config,
94 prefix=f"{prefix}.gate_up_proj",
95 )
96 self.down_proj = RowParallelLinear(
97 intermediate_size,
98 hidden_size,
99 bias=False,
100 quant_config=quant_config,
101 reduce_results=reduce_results,
102 prefix=f"{prefix}.down_proj",
103 )
104 if hidden_act == "silu":
105 self.act_fn = SiluAndMul()
106 elif hidden_act == "situ":
107 self.act_fn = SituAndMul(
108 beta=activation_situ_beta or 1.0,
109 linear_beta=activation_situ_linear_beta,
110 )
111 else:
112 raise ValueError(
113 f"Unsupported activation: {hidden_act}. "
114 "Only silu and situ are supported."
115 )
116
117 def forward(self, x):
118 gate_up, _ = self.gate_up_proj(x)
119 x = self.act_fn(gate_up)
120 x, _ = self.down_proj(x)
121 return x
122
123
124 class KimiRoutedOutputTransform(nn.Module):
125 def __init__(
126 self,
127 norm: RMSNorm | None,
128 up_proj: ReplicatedLinear,
129 ) -> None:
130 super().__init__()
131 self.norm = norm
132 self.up_proj = up_proj
133
134 def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
135 if self.norm is not None:
136 hidden_states = self.norm(hidden_states)
137 hidden_states, _ = self.up_proj(hidden_states)
138 return hidden_states
139
140
141 def _apply_attn_res(
142 prefix_sum: torch.Tensor,
143 block_residual: torch.Tensor,
144 proj: ReplicatedLinear,
145 norm: RMSNorm,
146 num_valid_blocks: int,
147 *,
148 delta: torch.Tensor | None = None,
149 output_norm: RMSNorm | None = None,
150 block_write_idx: int = -1,
151 ) -> torch.Tensor:
152 return attn_res(
153 prefix_sum,
154 delta,
155 block_residual,
156 norm.weight,
157 proj.weight.squeeze(0),
158 None if output_norm is None else output_norm.weight,
159 num_valid_blocks,
160 block_write_idx,
161 norm.variance_epsilon,
162 0.0 if output_norm is None else output_norm.variance_epsilon,
163 )
164
165
166 class KimiMoE(nn.Module):
167 def __init__(
168 self,
169 config: KimiLinearConfig,
170 quant_config: QuantizationConfig | None = None,
171 prefix: str = "",
172 layer_idx: int = 0,
173 ):
174 super().__init__()
175 hidden_size = config.hidden_size
176 moe_intermediate_size = config.moe_intermediate_size
177 num_experts = config.num_experts
178 num_experts_per_token = config.num_experts_per_token
179 assert moe_intermediate_size is not None
180 assert num_experts is not None
181 assert num_experts_per_token is not None
182 moe_renormalize = config.moe_renormalize
183 routed_expert_hidden_size = config.routed_expert_hidden_size
184 self.use_latent_moe = routed_expert_hidden_size is not None
185 self.moe_hidden_size = (
186 routed_expert_hidden_size
187 if routed_expert_hidden_size is not None
188 else hidden_size
189 )
190 self.latent_moe_use_norm = config.latent_moe_use_norm
191 self.tp_size = get_tensor_model_parallel_world_size()
192 self.routed_scaling_factor = config.routed_scaling_factor
193 self.num_shared_experts = config.num_shared_experts
194 self.layer_idx = layer_idx
195 self.padded_moe_intermediate_size = moe_intermediate_size
196 min_moe_intermediate_per_partition = getattr(
197 config, "min_moe_intermediate_per_partition", 256
198 )
199 if self.tp_size > 1:
200 moe_intermediate_per_partition = moe_intermediate_size // self.tp_size
201 if moe_intermediate_per_partition < min_moe_intermediate_per_partition:
202 self.padded_moe_intermediate_size = (
203 min_moe_intermediate_per_partition * self.tp_size
204 )
205 activation_situ_beta = (
206 config.activation_situ_beta if config.hidden_act == "situ" else None
207 )
208 activation_situ_linear_beta = (
209 config.activation_situ_linear_beta if config.hidden_act == "situ" else None
210 )
211
212 # Route with fp32 logits for numerically stable expert selection.
213 self.gate = GateLinear(
214 input_size=hidden_size,
215 output_size=num_experts,
216 bias=False,
217 out_dtype=torch.float32,
218 prefix=f"{prefix}.gate",
219 )
220
221 # Preserve FP32 checkpoint values and match FP32 router logits.
222 self.gate.e_score_correction_bias = nn.Parameter(
223 torch.empty(num_experts, dtype=torch.float32)
224 )
225
226 if self.num_shared_experts is not None:
227 shared_intermediate_size = moe_intermediate_size * self.num_shared_experts
228 self.shared_experts = KimiMLP(
229 hidden_size=config.hidden_size,
230 intermediate_size=shared_intermediate_size,
231 hidden_act=config.hidden_act,
232 quant_config=quant_config,
233 reduce_results=False,
234 prefix=f"{prefix}.shared_experts",
235 activation_situ_beta=activation_situ_beta,
236 activation_situ_linear_beta=activation_situ_linear_beta,
237 )
238 else:
239 self.shared_experts = None
240
241 self.routed_expert_down_proj: ReplicatedLinear | None
242 self.routed_expert_norm: RMSNorm | None
243 self.routed_expert_up_proj: ReplicatedLinear | None
244 self.routed_output_transform: KimiRoutedOutputTransform | None
245 if self.use_latent_moe:
246 self.routed_expert_down_proj = ReplicatedLinear(
247 hidden_size,
248 self.moe_hidden_size,
249 bias=False,
250 quant_config=None,
251 prefix=f"{prefix}.routed_expert_down_proj",
252 )
253 self.routed_expert_norm = (
254 RMSNorm(self.moe_hidden_size, eps=config.rms_norm_eps)
255 if self.latent_moe_use_norm
256 else None
257 )
258 self.routed_expert_up_proj = ReplicatedLinear(
259 self.moe_hidden_size,
260 hidden_size,
261 bias=False,
262 quant_config=None,
263 prefix=f"{prefix}.routed_expert_up_proj",
264 )
265 self.routed_output_transform = KimiRoutedOutputTransform(
266 self.routed_expert_norm, self.routed_expert_up_proj
267 )
268 else:
269 self.routed_expert_down_proj = None
270 self.routed_expert_norm = None
271 self.routed_expert_up_proj = None
272 self.routed_output_transform = None
273
274 self.experts = FusedMoEFactory(
275 shared_experts=self.shared_experts,
276 num_experts=num_experts,
277 top_k=num_experts_per_token,
278 hidden_size=self.moe_hidden_size,
279 intermediate_size=self.padded_moe_intermediate_size,
280 activation=config.hidden_act,
281 activation_situ_beta=activation_situ_beta,
282 activation_situ_linear_beta=activation_situ_linear_beta,
283 renormalize=moe_renormalize,
284 quant_config=quant_config,
285 use_grouped_topk=config.use_grouped_topk,
286 num_expert_group=config.num_expert_group,
287 topk_group=config.topk_group,
288 prefix=f"{prefix}.experts",
289 scoring_func=config.moe_router_activation_func,
290 e_score_correction_bias=self.gate.e_score_correction_bias,
291 routed_scaling_factor=self.routed_scaling_factor,
292 routed_input_transform=self.routed_expert_down_proj,
293 routed_output_transform=self.routed_output_transform,
294 runner_cls=ROCmLatentMoERunner if self.use_latent_moe else None,
295 )
296 if self.padded_moe_intermediate_size != moe_intermediate_size:
297 w13_weight = getattr(self.experts, "w13_weight", None)
298 if w13_weight is None:
299 w13_weight = self.experts.w13_weight_packed
300 w2_weight = getattr(self.experts, "w2_weight", None)
301 if w2_weight is None:
302 w2_weight = self.experts.w2_weight_packed
303 w13_weight.data.zero_()
304 w2_weight.data.zero_()
305 self.experts.moe_config.intermediate_size_per_partition_unpadded = (
306 moe_intermediate_size // self.tp_size
307 )
308
309 def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
310 num_tokens, hidden_size = hidden_states.shape
311 hidden_states = hidden_states.view(-1, hidden_size)
312 router_logits, _ = self.gate(hidden_states)
313 final_hidden_states = self.experts(
314 hidden_states=hidden_states, router_logits=router_logits
315 )
316 return final_hidden_states.view(num_tokens, hidden_size)
317
318
319 class KimiMLAAttention(nn.Module):
320 """Main reference: DeepseekV2 vllm Implementation."""
321
322 def __init__(
323 self,
324 config: KimiLinearConfig,
325 hidden_size: int,
326 num_heads: int,
327 qk_nope_head_dim: int,
328 qk_rope_head_dim: int,
329 v_head_dim: int,
330 q_lora_rank: int | None,
331 kv_lora_rank: int,
332 use_nope: bool = False,
333 cache_config: CacheConfig | None = None,
334 quant_config: QuantizationConfig | None = None,
335 prefix: str = "",
336 **kwargs,
337 ) -> None:
338 super().__init__()
339 self.hidden_size = hidden_size
340 self.qk_nope_head_dim = qk_nope_head_dim
341 self.qk_rope_head_dim = qk_rope_head_dim
342 self.qk_head_dim = qk_nope_head_dim + qk_rope_head_dim
343 self.v_head_dim = v_head_dim
344 self.q_lora_rank = q_lora_rank
345 self.kv_lora_rank = kv_lora_rank
346 self.num_heads = num_heads
347 tp_size = get_tensor_model_parallel_world_size()
348 self.num_local_heads = num_heads // tp_size
349 self.scaling = self.qk_head_dim**-0.5
350 self.use_nope = use_nope
351 assert self.use_nope is True
352 assert num_heads % tp_size == 0
353 if self.q_lora_rank is not None:
354 self.fused_qkv_a_proj = MergedColumnParallelLinear(
355 self.hidden_size,
356 [self.q_lora_rank, self.kv_lora_rank + self.qk_rope_head_dim],
357 bias=False,
358 quant_config=quant_config,
359 prefix=f"{prefix}.fused_qkv_a_proj",
360 disable_tp=True,
361 )
362 else:
363 self.kv_a_proj_with_mqa = ReplicatedLinear(
364 self.hidden_size,
365 self.kv_lora_rank + self.qk_rope_head_dim,
366 bias=False,
367 quant_config=quant_config,
368 prefix=f"{prefix}.kv_a_proj_with_mqa",
369 )
370 if self.q_lora_rank is not None:
371 self.q_a_layernorm = RMSNorm(
372 self.q_lora_rank,
373 eps=config.rms_norm_eps,
374 )
375 self.q_b_proj = ColumnParallelLinear(
376 self.q_lora_rank,
377 self.num_heads * self.qk_head_dim,
378 bias=False,
379 quant_config=quant_config,
380 prefix=f"{prefix}.q_b_proj",
381 )
382 else:
383 self.q_proj = ColumnParallelLinear(
384 self.hidden_size,
385 self.num_heads * self.qk_head_dim,
386 bias=False,
387 quant_config=quant_config,
388 prefix=f"{prefix}.q_proj",
389 )
390 self.kv_a_layernorm = RMSNorm(
391 self.kv_lora_rank,
392 eps=config.rms_norm_eps,
393 )
394 self.kv_b_proj = ColumnParallelLinear(
395 self.kv_lora_rank,
396 self.num_heads * (self.qk_nope_head_dim + self.v_head_dim),
397 bias=False,
398 quant_config=quant_config,
399 prefix=f"{prefix}.kv_b_proj",
400 )
401 self.o_proj = RowParallelLinear(
402 self.num_heads * self.v_head_dim,
403 self.hidden_size,
404 bias=False,
405 quant_config=quant_config,
406 prefix=f"{prefix}.o_proj",
407 )
408
409 self.use_output_gate = config.mla_use_output_gate
410 if self.use_output_gate:
411 projection_size = self.num_heads * self.v_head_dim
412 self.g_proj = ColumnParallelLinear(
413 self.hidden_size,
414 projection_size,
415 bias=False,
416 quant_config=quant_config,
417 prefix=f"{prefix}.g_proj",
418 )
419
420 # TODO: Remove this mypy workaround once the K3 PR is fully merged.
421 mla_modules = MLAModules( # type: ignore[call-arg]
422 kv_a_layernorm=self.kv_a_layernorm,
423 kv_b_proj=self.kv_b_proj,
424 rotary_emb=None,
425 o_proj=self.o_proj,
426 fused_qkv_a_proj=self.fused_qkv_a_proj
427 if self.q_lora_rank is not None
428 else None,
429 kv_a_proj_with_mqa=self.kv_a_proj_with_mqa
430 if self.q_lora_rank is None
431 else None,
432 q_a_layernorm=self.q_a_layernorm if self.q_lora_rank is not None else None,
433 q_b_proj=self.q_b_proj if self.q_lora_rank is not None else None,
434 q_proj=self.q_proj if self.q_lora_rank is None else None,
435 indexer=None,
436 is_sparse=False,
437 topk_indices_buffer=None,
438 g_proj=getattr(self, "g_proj", None),
439 )
440 self.mla_attn = KimiK3MultiHeadLatentAttentionWrapper(
441 self.hidden_size,
442 self.num_local_heads,
443 self.scaling,
444 self.qk_nope_head_dim,
445 self.qk_rope_head_dim,
446 self.v_head_dim,
447 self.q_lora_rank,
448 self.kv_lora_rank,
449 mla_modules,
450 cache_config,
451 quant_config,
452 prefix,
453 )
454
455 def forward(
456 self,
457 positions: torch.Tensor,
458 hidden_states: torch.Tensor,
459 ) -> torch.Tensor:
460 return self.mla_attn(positions, hidden_states)
461
462
463 class KimiDecoderLayer(nn.Module):
464 def __init__(
465 self,
466 config: KimiLinearConfig,
467 vllm_config: VllmConfig,
468 prefix: str = "",
469 ) -> None:
470 super().__init__()
471 self.hidden_size = config.hidden_size
472 self.layer_idx = int(prefix.rsplit(".", 1)[1])
473
474 self.is_moe = config.is_moe
475 layer_idx = self.layer_idx
476 model_config = vllm_config.model_config
477 cache_config = vllm_config.cache_config
478 quant_config = vllm_config.quant_config
479
480 if config.is_kda_layer(layer_idx):
481 # Kimi-K3 sets use_full_rank_gate and uses the ROCm-specific K3 KDA
482 # layer; Kimi-Linear keeps the shared low-rank-gate implementation.
483 kda_config = config.linear_attn_config
484 assert kda_config is not None
485 if kda_config.get("use_full_rank_gate", False):
486 self.self_attn = KimiK3DeltaAttention(
487 config,
488 vllm_config,
489 prefix=f"{prefix}.self_attn",
490 )
491 else:
492 self.self_attn = KimiLinearGatedDeltaNetAttention(
493 config,
494 vllm_config,
495 prefix=f"{prefix}.self_attn",
496 )
497 else:
498 qk_nope_head_dim = config.qk_nope_head_dim
499 qk_rope_head_dim = config.qk_rope_head_dim
500 v_head_dim = config.v_head_dim
501 kv_lora_rank = config.kv_lora_rank
502 mla_use_nope = config.mla_use_nope
503 assert qk_nope_head_dim is not None
504 assert qk_rope_head_dim is not None
505 assert v_head_dim is not None
506 assert kv_lora_rank is not None
507 assert mla_use_nope is not None
508 self.self_attn = KimiMLAAttention(
509 layer_idx=layer_idx,
510 hidden_size=self.hidden_size,
511 num_heads=config.num_attention_heads,
512 quant_config=quant_config,
513 cache_config=cache_config,
514 model_config=model_config,
515 prefix=f"{prefix}.self_attn",
516 config=config,
517 qk_nope_head_dim=qk_nope_head_dim,
518 qk_rope_head_dim=qk_rope_head_dim,
519 v_head_dim=v_head_dim,
520 q_lora_rank=config.q_lora_rank,
521 kv_lora_rank=kv_lora_rank,
522 use_nope=mla_use_nope,
523 )
524
525 if (
526 self.is_moe
527 and config.num_experts is not None
528 and layer_idx >= config.first_k_dense_replace
529 and layer_idx % config.moe_layer_freq == 0
530 ):
531 self.block_sparse_moe = KimiMoE(
532 config=config,
533 quant_config=quant_config,
534 prefix=f"{prefix}.block_sparse_moe",
535 layer_idx=layer_idx,
536 )
537 self.mlp = self.block_sparse_moe
538 else:
539 self.mlp = KimiMLP(
540 hidden_size=self.hidden_size,
541 intermediate_size=config.intermediate_size,
542 hidden_act=config.hidden_act,
543 quant_config=quant_config,
544 prefix=f"{prefix}.mlp",
545 activation_situ_beta=config.activation_situ_beta,
546 activation_situ_linear_beta=config.activation_situ_linear_beta,
547 )
548 self.input_layernorm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
549 self.post_attention_layernorm = RMSNorm(
550 config.hidden_size, eps=config.rms_norm_eps
551 )
552
553 attn_res_block_size = config.attn_res_block_size
554 self.use_attn_residuals = attn_res_block_size is not None
555 if attn_res_block_size is not None:
556 self.attn_res_block_size = attn_res_block_size
557 self.is_block_write_layer = layer_idx % self.attn_res_block_size == 0
558 self.block_write_idx = layer_idx // self.attn_res_block_size
559 self.prev_valid_blocks = cdiv(layer_idx, self.attn_res_block_size)
560 self.self_attention_res_norm = RMSNorm(
561 config.hidden_size, eps=config.rms_norm_eps
562 )
563 self.mlp_res_norm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
564 self.self_attention_res_proj = ReplicatedLinear(
565 config.hidden_size,
566 1,
567 bias=False,
568 quant_config=None,
569 prefix=f"{prefix}.self_attention_res_proj",
570 )
571 self.mlp_res_proj = ReplicatedLinear(
572 config.hidden_size,
573 1,
574 bias=False,
575 quant_config=None,
576 prefix=f"{prefix}.mlp_res_proj",
577 )
578
579 def _run_self_attn(
580 self,
581 positions: torch.Tensor,
582 hidden_states: torch.Tensor,
583 ) -> torch.Tensor:
584 return self.self_attn(
585 hidden_states=hidden_states,
586 positions=positions,
587 )
588
589 def forward(
590 self,
591 positions: torch.Tensor,
592 hidden_states: torch.Tensor,
593 residual: torch.Tensor | None,
594 prefix_delta: torch.Tensor | None = None,
595 **kwargs,
596 ) -> (
597 tuple[torch.Tensor, torch.Tensor]
598 | tuple[torch.Tensor, torch.Tensor, torch.Tensor]
599 ):
600 if self.use_attn_residuals:
601 assert residual is not None
602 return self.forward_attn_residual(
603 positions, hidden_states, residual, prefix_delta
604 )
605
606 assert prefix_delta is None
607 # Self Attention
608 if residual is None:
609 residual = hidden_states
610 hidden_states = self.input_layernorm(hidden_states)
611 else:
612 hidden_states, residual = self.input_layernorm(hidden_states, residual)
613
614 hidden_states = self._run_self_attn(positions, hidden_states)
615
616 # Fully Connected
617 hidden_states, residual = self.post_attention_layernorm(hidden_states, residual)
618 hidden_states = self.mlp(hidden_states)
619 return hidden_states, residual
620
621 def forward_attn_residual(
622 self,
623 positions: torch.Tensor,
624 hidden_states: torch.Tensor,
625 block_residual: torch.Tensor,
626 prefix_delta: torch.Tensor | None,
627 ) -> tuple[torch.Tensor, torch.Tensor, torch.Tensor]:
628 prefix_sum = hidden_states
629 hidden_states = _apply_attn_res(
630 prefix_sum,
631 block_residual,
632 self.self_attention_res_proj,
633 self.self_attention_res_norm,
634 self.prev_valid_blocks,
635 delta=prefix_delta,
636 output_norm=self.input_layernorm,
637 block_write_idx=(self.block_write_idx if self.is_block_write_layer else -1),
638 )
639
640 if self.is_block_write_layer:
641 prefix_sum = None
642
643 hidden_states = self._run_self_attn(positions, hidden_states)
644
645 if prefix_sum is None:
646 prefix_sum = hidden_states
647 prefix_delta = None
648 else:
649 prefix_delta = hidden_states
650
651 mlp_valid_blocks = self.prev_valid_blocks + (
652 1 if self.is_block_write_layer else 0
653 )
654 hidden_states = _apply_attn_res(
655 prefix_sum,
656 block_residual,
657 self.mlp_res_proj,
658 self.mlp_res_norm,
659 mlp_valid_blocks,
660 delta=prefix_delta,
661 output_norm=self.post_attention_layernorm,
662 )
663
664 hidden_states = self.mlp(hidden_states)
665 return prefix_sum, block_residual, hidden_states
666
667
668 class KimiLinearModel(nn.Module, EagleModelMixin):
669 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
670 super().__init__()
671
672 config = vllm_config.model_config.hf_text_config
673 self.config = config
674
675 self.vocab_size = config.vocab_size
676
677 if get_pp_group().is_first_rank:
678 self.embed_tokens = VocabParallelEmbedding(
679 config.vocab_size,
680 config.hidden_size,
681 prefix=f"{prefix}.embed_tokens",
682 )
683 else:
684 self.embed_tokens = PPMissingLayer()
685
686 def get_layer(prefix: str):
687 return KimiDecoderLayer(
688 config,
689 vllm_config,
690 prefix,
691 )
692
693 self.start_layer, self.end_layer, self.layers = make_layers(
694 config.num_hidden_layers,
695 get_layer,
696 prefix=f"{prefix}.layers",
697 )
698
699 if get_pp_group().is_last_rank:
700 self.norm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
701 if config.attn_res_block_size is not None:
702 self.output_attn_res_norm = RMSNorm(
703 config.hidden_size, eps=config.rms_norm_eps
704 )
705 self.output_attn_res_proj = ReplicatedLinear(
706 config.hidden_size,
707 1,
708 bias=False,
709 quant_config=None,
710 prefix=f"{prefix}.output_attn_res_proj",
711 )
712 else:
713 self.norm = PPMissingLayer()
714 if config.attn_res_block_size is not None:
715 self.output_attn_res_norm = PPMissingLayer()
716 self.output_attn_res_proj = PPMissingLayer()
717
718 world_size = get_tensor_model_parallel_world_size()
719 assert config.num_attention_heads % world_size == 0, (
720 "num_attention_heads must be divisible by world_size"
721 )
722
723 def make_empty_intermediate_tensors(
724 self,
725 batch_size: int,
726 dtype: torch.dtype,
727 device: torch.device,
728 ) -> IntermediateTensors:
729 residual_shape: tuple[int, ...] = (batch_size, self.config.hidden_size)
730 if self.config.attn_res_block_size is not None:
731 residual_shape = (
732 batch_size,
733 cdiv(self.start_layer, self.config.attn_res_block_size),
734 self.config.hidden_size,
735 )
736 return IntermediateTensors(
737 {
738 "hidden_states": torch.zeros(
739 (batch_size, self.config.hidden_size), dtype=dtype, device=device
740 ),
741 "residual": torch.zeros(residual_shape, dtype=dtype, device=device),
742 }
743 )
744
745 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
746 return self.embed_tokens(input_ids)
747
748 def _maybe_add_hidden_state(
749 self,
750 aux_hidden_states: list[torch.Tensor],
751 layer_idx: int,
752 hidden_states: torch.Tensor,
753 residual: torch.Tensor | None,
754 ) -> list[torch.Tensor]:
755 if self.config.attn_res_block_size is not None:
756 # attn-res `residual` is a block-state bank, not an additive
757 # residual; None makes the mixin capture the prefix sum directly.
758 residual = None
759 return super()._maybe_add_hidden_state(
760 aux_hidden_states, layer_idx, hidden_states, residual
761 )
762
763 def forward(
764 self,
765 input_ids: torch.Tensor | None,
766 positions: torch.Tensor,
767 intermediate_tensors: IntermediateTensors | None,
768 inputs_embeds: torch.Tensor | None = None,
769 **kwargs,
770 ) -> torch.Tensor | IntermediateTensors | tuple[torch.Tensor, list[torch.Tensor]]:
771 if get_pp_group().is_first_rank:
772 if inputs_embeds is not None:
773 hidden_states = inputs_embeds
774 else:
775 hidden_states = self.embed_input_ids(input_ids)
776 residual = None
777 else:
778 assert intermediate_tensors is not None
779 hidden_states = intermediate_tensors["hidden_states"]
780 residual = intermediate_tensors["residual"]
781
782 aux_hidden_states = self._maybe_add_hidden_state(
783 [], self.start_layer, hidden_states, residual
784 )
785
786 if self.config.attn_res_block_size is None:
787 for layer_idx, layer in enumerate(
788 self.layers[self.start_layer : self.end_layer],
789 start=self.start_layer,
790 ):
791 hidden_states, residual = layer(
792 positions=positions,
793 hidden_states=hidden_states,
794 residual=residual,
795 )
796 self._maybe_add_hidden_state(
797 aux_hidden_states, layer_idx + 1, hidden_states, residual
798 )
799
800 if not get_pp_group().is_last_rank:
801 return IntermediateTensors(
802 {"hidden_states": hidden_states, "residual": residual}
803 )
804
805 # NOTE: the final norm is applied in compute_logits instead of here,
806 # so the MTP draft model receives the pre-norm hidden states.
807 if residual is not None:
808 hidden_states = hidden_states + residual
809 if aux_hidden_states:
810 return hidden_states, aux_hidden_states
811 return hidden_states
812
813 attn_res_block_num = cdiv(self.end_layer, self.config.attn_res_block_size)
814 block_residual = hidden_states.new_empty(
815 hidden_states.size(0), attn_res_block_num, hidden_states.size(1)
816 )
817 if residual is not None:
818 block_residual[:, : residual.size(1), :].copy_(residual)
819 residual = block_residual
820 prefix_delta = None
821
822 for layer_idx, layer in enumerate(
823 self.layers[self.start_layer : self.end_layer],
824 start=self.start_layer,
825 ):
826 hidden_states, residual, prefix_delta = layer(
827 positions=positions,
828 hidden_states=hidden_states,
829 residual=residual,
830 prefix_delta=prefix_delta,
831 )
832 if (layer_idx + 1) in self.aux_hidden_state_layers:
833 self._maybe_add_hidden_state(
834 aux_hidden_states,
835 layer_idx + 1,
836 hidden_states + prefix_delta,
837 residual,
838 )
839
840 if not get_pp_group().is_last_rank:
841 hidden_states = hidden_states + prefix_delta
842 return IntermediateTensors(
843 {"hidden_states": hidden_states, "residual": residual}
844 )
845
846 hidden_states = _apply_attn_res(
847 hidden_states,
848 residual,
849 self.output_attn_res_proj,
850 self.output_attn_res_norm,
851 attn_res_block_num,
852 delta=prefix_delta,
853 )
854 # NOTE: the final norm is applied in compute_logits instead of here, so
855 # the MTP draft model receives the pre-norm hidden states.
856 if aux_hidden_states:
857 return hidden_states, aux_hidden_states
858 return hidden_states
859
860 def load_weights(
861 self,
862 weights: Iterable[
863 tuple[str, torch.Tensor] | tuple[str, torch.Tensor, dict[str, Any]]
864 ],
865 ) -> set[str]:
866 kda_config = self.config.linear_attn_config
867 use_full_rank_gate = bool(
868 kda_config and kda_config.get("use_full_rank_gate", False)
869 )
870 beta_shard_id = 5 if use_full_rank_gate else 3
871 stacked_params_mapping = [
872 # (param_name, shard_name, shard_id)
873 (".in_proj_qkvgfab", ".q_proj", 0),
874 (".in_proj_qkvgfab", ".k_proj", 1),
875 (".in_proj_qkvgfab", ".v_proj", 2),
876 (".in_proj_qkvgfab", ".b_proj", beta_shard_id),
877 (".in_proj_qkvgfab", ".f_a_proj", 4),
878 (".conv1d", ".q_conv1d", 0),
879 (".conv1d", ".k_conv1d", 1),
880 (".conv1d", ".v_conv1d", 2),
881 (".gate_up_proj", ".gate_proj", 0),
882 (".gate_up_proj", ".up_proj", 1),
883 ]
884 if use_full_rank_gate:
885 stacked_params_mapping.append((".in_proj_qkvgfab", ".g_proj", 3))
886 if getattr(self.config, "q_lora_rank", None) is not None:
887 stacked_params_mapping += [
888 (".fused_qkv_a_proj", ".q_a_proj", 0),
889 (".fused_qkv_a_proj", ".kv_a_proj_with_mqa", 1),
890 ]
891 if self.config.is_moe:
892 # Params for weights, fp8 weight scales, fp8 activation scales
893 # (param_name, weight_name, expert_id, shard_id)
894 expert_params_mapping = fused_moe_make_expert_params_mapping(
895 self,
896 ckpt_gate_proj_name="w1",
897 ckpt_down_proj_name="w2",
898 ckpt_up_proj_name="w3",
899 num_experts=self.config.num_experts,
900 )
901 else:
902 expert_params_mapping = []
903 params_dict = dict(self.named_parameters())
904 # Under the MXFP4 quant interface the routed experts register unpacked
905 # params (``w13_weight``), while the compressed-tensors checkpoint names
906 # them ``.weight_packed``. Rebind so the expert mapping resolves; scales
907 # already share the ``.weight_scale`` suffix.
908 experts_unpacked = not any(n.endswith("w13_weight_packed") for n in params_dict)
909 loaded_params: set[str] = set()
910 for args in weights:
911 name, loaded_weight = args[0], args[1]
912 kwargs: dict[str, Any] = args[2] if len(args) > 2 else {}
913 if "rotary_emb.inv_freq" in name:
914 continue
915 if experts_unpacked and name.endswith(".weight_packed"):
916 name = name.replace(".weight_packed", ".weight")
917
918 spec_layer = get_spec_layer_idx_from_weight_name(self.config, name)
919 if spec_layer is not None:
920 continue # skip spec decode layers for main model
921 if "rotary_emb.cos_cached" in name or "rotary_emb.sin_cached" in name:
922 # Models trained using ColossalAI may include these tensors in
923 # the checkpoint. Skip them.
924 continue
925 for param_name, weight_name, shard_id in stacked_params_mapping:
926 if weight_name not in name:
927 continue
928 # We have mlp.experts[0].gate_proj in the checkpoint.
929 # Since we handle the experts below in expert_params_mapping,
930 # we need to skip here BEFORE we update the name, otherwise
931 # name will be updated to mlp.experts[0].gate_up_proj, which
932 # will then be updated below in expert_params_mapping
933 # for mlp.experts[0].gate_gate_up_proj, which breaks load.
934 if ("mlp.experts." in name) and name not in params_dict:
935 continue
936 name_mapped = name.replace(weight_name, param_name)
937 # Packed projections are only present on compatible layers.
938 if name_mapped not in params_dict:
939 continue
940 name = name_mapped
941 # Skip loading extra bias for GPTQ models.
942 if name.endswith(".bias") and name not in params_dict:
943 continue
944 if is_pp_missing_parameter(name, self):
945 continue
946 param = params_dict[name]
947 weight_loader = param.weight_loader
948 weight_loader(param, loaded_weight, shard_id)
949 break
950 else:
951 for (
952 expert_param_name,
953 expert_weight_name,
954 expert_id,
955 expert_shard_id,
956 ) in expert_params_mapping:
957 if expert_weight_name not in name:
958 continue
959 name = name.replace(expert_weight_name, expert_param_name)
960 if is_pp_missing_parameter(name, self):
961 continue
962 param = params_dict[name]
963 weight_loader = param.weight_loader
964 weight_loader(
965 param,
966 loaded_weight,
967 name,
968 expert_id=expert_id,
969 shard_id=expert_shard_id,
970 )
971 break
972 else:
973 # Skip loading extra bias for GPTQ models.
974 if (
975 name.endswith(".bias")
976 and name not in params_dict
977 and not self.config.is_linear_attn
978 ): # noqa: E501
979 continue
980 # Remapping the name of FP8 kv-scale.
981 remapped_name = maybe_remap_kv_scale_name(name, params_dict)
982 if remapped_name is None:
983 continue
984 name = remapped_name
985 if is_pp_missing_parameter(name, self):
986 continue
987
988 param = params_dict[name]
989 weight_loader = getattr(
990 param, "weight_loader", default_weight_loader
991 )
992 weight_loader(param, loaded_weight, **kwargs)
993 loaded_params.add(name)
994 return loaded_params
995
996
997 class KimiLinearForCausalLM(
998 nn.Module, HasInnerState, SupportsPP, MixtureOfExperts, IsHybrid
999 ):
1000 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
1001 super().__init__()
1002 self.model_config = vllm_config.model_config
1003 self.vllm_config = vllm_config
1004 self.config = self.model_config.hf_config
1005 quant_config = vllm_config.quant_config
1006 self.quant_config = quant_config
1007 self.model = KimiLinearModel(
1008 vllm_config=vllm_config, prefix=maybe_prefix(prefix, "model")
1009 )
1010 if get_pp_group().is_last_rank:
1011 self.lm_head = ParallelLMHead(
1012 self.config.vocab_size,
1013 self.config.hidden_size,
1014 quant_config=quant_config,
1015 prefix=maybe_prefix(prefix, "lm_head"),
1016 )
1017 else:
1018 self.lm_head = PPMissingLayer()
1019 logit_scale = getattr(self.config, "logit_scale", 1.0)
1020 self.logits_processor = LogitsProcessor(
1021 self.config.vocab_size, scale=logit_scale
1022 )
1023
1024 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
1025 return self.model.embed_input_ids(input_ids)
1026
1027 def make_empty_intermediate_tensors(
1028 self,
1029 batch_size: int,
1030 dtype: torch.dtype,
1031 device: torch.device,
1032 ) -> IntermediateTensors:
1033 return self.model.make_empty_intermediate_tensors(batch_size, dtype, device)
1034
1035 def forward(
1036 self,
1037 input_ids: torch.Tensor | None,
1038 positions: torch.Tensor,
1039 intermediate_tensors: IntermediateTensors | None = None,
1040 inputs_embeds: torch.Tensor | None = None,
1041 **kwargs,
1042 ) -> torch.Tensor | IntermediateTensors:
1043 hidden_states = self.model(
1044 input_ids, positions, intermediate_tensors, inputs_embeds, **kwargs
1045 )
1046 return hidden_states
1047
1048 @classmethod
1049 def get_mamba_state_dtype_from_config(
1050 cls,
1051 vllm_config: "VllmConfig",
1052 ) -> tuple[torch.dtype, torch.dtype]:
1053 return MambaStateDtypeCalculator.kda_state_dtype(
1054 vllm_config.model_config.dtype, vllm_config.cache_config.mamba_cache_dtype
1055 )
1056
1057 @classmethod
1058 def get_mamba_state_shape_from_config(
1059 cls, vllm_config: "VllmConfig"
1060 ) -> tuple[tuple[int, int], tuple[int, int, int]]:
1061 parallel_config = vllm_config.parallel_config
1062 hf_config = vllm_config.model_config.hf_config
1063 tp_size = parallel_config.tensor_parallel_size
1064 num_spec = (
1065 vllm_config.speculative_config.num_speculative_tokens
1066 if vllm_config.speculative_config
1067 else 0
1068 )
1069 return MambaStateShapeCalculator.kda_state_shape(
1070 tp_size,
1071 hf_config.linear_attn_config["num_heads"],
1072 hf_config.linear_attn_config["head_dim"],
1073 conv_kernel_size=hf_config.linear_attn_config["short_conv_kernel_size"],
1074 num_spec=num_spec,
1075 )
1076
1077 @classmethod
1078 def get_mamba_state_copy_func(
1079 cls,
1080 ) -> tuple[MambaStateCopyFunc, MambaStateCopyFunc]:
1081 return MambaStateCopyFuncCalculator.kda_state_copy_func()
1082
1083 def compute_logits(
1084 self,
1085 hidden_states: torch.Tensor,
1086 ) -> torch.Tensor | None:
1087 # The model's final norm is applied here (not at the end of forward) so
1088 # that the pre-norm hidden states can be fed to the MTP draft model.
1089 hidden_states = self.model.norm(hidden_states, None)
1090 return self.logits_processor(self.lm_head, hidden_states)
1091
1092 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
1093 loader = AutoWeightsLoader(self)
1094 return loader.load_weights(weights)