main
py 1,162 lines 44.8 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 #
8 # This code is based on EleutherAI's GPT-NeoX library and the GPT-NeoX
9 # and OPT implementations in this library. It has been modified from its
10 # original forms to accommodate minor architectural differences compared
11 # to GPT-NeoX and OPT used by the Meta AI team that trained the model.
12 #
13 # Licensed under the Apache License, Version 2.0 (the "License");
14 # you may not use this file except in compliance with the License.
15 # You may obtain a copy of the License at
16 #
17 # http://www.apache.org/licenses/LICENSE-2.0
18 #
19 # Unless required by applicable law or agreed to in writing, software
20 # distributed under the License is distributed on an "AS IS" BASIS,
21 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22 # See the License for the specific language governing permissions and
23 # limitations under the License.
24 """Inference-only A.X K1 model."""
25
26 import typing
27 from collections.abc import Callable, Iterable
28 from itertools import islice
29
30 import torch
31 from torch import nn
32
33 from vllm._aiter_ops import rocm_aiter_ops
34 from vllm.compilation.decorators import support_torch_compile
35 from vllm.config import CacheConfig, ParallelConfig, VllmConfig
36 from vllm.distributed import (
37 get_ep_group,
38 get_pp_group,
39 get_tensor_model_parallel_rank,
40 get_tensor_model_parallel_world_size,
41 tensor_model_parallel_all_gather,
42 )
43 from vllm.logger import init_logger
44 from vllm.model_executor.layers.attention import Attention
45 from vllm.model_executor.layers.fused_moe import (
46 FusedMoEFactory,
47 GateLinear,
48 fused_moe_make_expert_params_mapping,
49 )
50 from vllm.model_executor.layers.fused_moe.utils import (
51 is_model_fused_shared_expert_compatible,
52 resolve_layer_fused_shared_expert,
53 )
54 from vllm.model_executor.layers.layernorm import RMSNorm
55 from vllm.model_executor.layers.linear import (
56 ColumnParallelLinear,
57 MergedColumnParallelLinear,
58 ReplicatedLinear,
59 RowParallelLinear,
60 )
61 from vllm.model_executor.layers.logits_processor import LogitsProcessor
62 from vllm.model_executor.layers.mla import MLAModules, MultiHeadLatentAttentionWrapper
63 from vllm.model_executor.layers.quantization import QuantizationConfig
64 from vllm.model_executor.layers.rotary_embedding import get_rope
65 from vllm.model_executor.layers.vocab_parallel_embedding import (
66 ParallelLMHead,
67 VocabParallelEmbedding,
68 )
69 from vllm.model_executor.model_loader.weight_utils import (
70 default_weight_loader,
71 maybe_remap_kv_scale_name,
72 )
73 from vllm.model_executor.models.deepseek_v2 import (
74 DeepseekAttention,
75 DeepseekV2MLP,
76 yarn_get_mscale,
77 )
78 from vllm.model_executor.models.utils import sequence_parallel_chunk
79 from vllm.platforms import current_platform
80 from vllm.sequence import IntermediateTensors
81 from vllm.transformers_utils.configs.AXK1 import AXK1Config
82
83 from .interfaces import MixtureOfExperts, SupportsEagle, SupportsLoRA, SupportsPP
84 from .utils import (
85 AutoWeightsLoader,
86 PPMissingLayer,
87 get_spec_layer_idx_from_weight_name,
88 is_pp_missing_parameter,
89 make_empty_intermediate_tensors_factory,
90 make_layers,
91 maybe_prefix,
92 )
93
94 logger = init_logger(__name__)
95
96
97 class AXK1MLP(DeepseekV2MLP):
98 pass
99
100
101 class AXK1MoE(nn.Module):
102 def __init__(
103 self,
104 config: AXK1Config,
105 parallel_config: ParallelConfig,
106 quant_config: QuantizationConfig | None = None,
107 prefix: str = "",
108 ):
109 super().__init__()
110 self.tp_size = get_tensor_model_parallel_world_size()
111 self.tp_rank = get_tensor_model_parallel_rank()
112
113 self.routed_scaling_factor = getattr(config, "routed_scaling_factor", 1.0)
114
115 self.ep_group = get_ep_group().device_group
116 self.ep_size = self.ep_group.size()
117 assert config.n_routed_experts is not None
118 assert config.num_experts_per_tok is not None
119 assert config.scoring_func is not None
120 assert config.hidden_act is not None
121 self.n_routed_experts: int = config.n_routed_experts
122 self.n_shared_experts: int | None = config.n_shared_experts
123
124 self.is_sequence_parallel = parallel_config.use_sequence_parallel_moe
125
126 if config.hidden_act != "silu":
127 raise ValueError(
128 f"Unsupported activation: {config.hidden_act}. "
129 "Only silu is supported for now."
130 )
131
132 self.gate = GateLinear(
133 config.hidden_size,
134 config.n_routed_experts,
135 prefix=f"{prefix}.gate",
136 )
137 if config.topk_method == "noaux_tc":
138 self.gate.e_score_correction_bias = nn.Parameter(
139 torch.empty(config.n_routed_experts, dtype=torch.float32)
140 )
141 else:
142 self.gate.e_score_correction_bias = None
143
144 # Load balancing settings.
145 eplb_config = parallel_config.eplb_config
146 self.enable_eplb = parallel_config.enable_eplb
147
148 self.n_redundant_experts = eplb_config.num_redundant_experts
149 self.n_logical_experts = self.n_routed_experts
150 self.n_physical_experts = self.n_logical_experts + self.n_redundant_experts
151 self.n_local_physical_experts = self.n_physical_experts // self.ep_size
152
153 self.is_rocm_aiter_moe_enabled = rocm_aiter_ops.is_fused_moe_enabled()
154
155 self.is_fused_shared_expert_enabled = False
156 if config.n_shared_experts is not None:
157 self.is_fused_shared_expert_enabled = resolve_layer_fused_shared_expert(
158 quant_config, prefix
159 )
160
161 if config.n_shared_experts is None or self.is_fused_shared_expert_enabled:
162 self.shared_experts = None
163 else:
164 intermediate_size = config.moe_intermediate_size * config.n_shared_experts
165
166 self.shared_experts = AXK1MLP(
167 hidden_size=config.hidden_size,
168 intermediate_size=intermediate_size,
169 hidden_act=config.hidden_act,
170 quant_config=quant_config,
171 is_sequence_parallel=self.is_sequence_parallel,
172 reduce_results=False,
173 prefix=f"{prefix}.shared_experts",
174 )
175
176 self.experts = FusedMoEFactory(
177 shared_experts=self.shared_experts,
178 gate=self.gate,
179 num_experts=config.n_routed_experts,
180 top_k=config.num_experts_per_tok,
181 hidden_size=config.hidden_size,
182 intermediate_size=config.moe_intermediate_size,
183 renormalize=config.norm_topk_prob,
184 quant_config=quant_config,
185 use_grouped_topk=True,
186 num_expert_group=config.n_group,
187 topk_group=config.topk_group,
188 prefix=f"{prefix}.experts",
189 scoring_func=config.scoring_func,
190 # we do scaling outside, set factor to 1.0 to avoid double mul
191 # aiter applies routed_scaling_factor internally
192 routed_scaling_factor=self.routed_scaling_factor,
193 apply_routed_scale_to_output=not self.is_rocm_aiter_moe_enabled,
194 e_score_correction_bias=self.gate.e_score_correction_bias,
195 enable_eplb=self.enable_eplb,
196 num_redundant_experts=self.n_redundant_experts,
197 is_sequence_parallel=self.is_sequence_parallel,
198 n_shared_experts=config.n_shared_experts
199 if self.is_fused_shared_expert_enabled
200 else None,
201 fuse_shared_experts=self.is_fused_shared_expert_enabled,
202 )
203
204 def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
205 num_tokens, hidden_dim = hidden_states.shape
206 hidden_states = hidden_states.view(-1, hidden_dim)
207
208 # Chunk the hidden states so they aren't replicated across TP ranks.
209 # This avoids duplicate computation in self.experts.
210 # TODO: We can replace the all_reduce at the end of attn with a
211 # reduce_scatter instead of chunking here.
212 if self.is_sequence_parallel:
213 hidden_states = sequence_parallel_chunk(hidden_states)
214
215 final_hidden_states = self.experts(
216 hidden_states=hidden_states, router_logits=hidden_states
217 )
218
219 if self.is_sequence_parallel:
220 final_hidden_states = tensor_model_parallel_all_gather(
221 final_hidden_states, 0
222 )
223 final_hidden_states = final_hidden_states[:num_tokens]
224
225 return final_hidden_states.view(num_tokens, hidden_dim)
226
227
228 def _get_llama_4_scaling(
229 original_max_position_embeddings: int, scaling_beta: float, positions: torch.Tensor
230 ) -> torch.Tensor:
231 scaling = 1 + scaling_beta * torch.log(
232 1 + torch.floor(positions / original_max_position_embeddings)
233 )
234 # Broadcast over num_heads and head_dim
235 return scaling[..., None, None]
236
237
238 class AXK1Attention(nn.Module):
239 def __init__(
240 self,
241 vllm_config: VllmConfig,
242 config: AXK1Config,
243 hidden_size: int,
244 num_heads: int,
245 qk_nope_head_dim: int,
246 qk_rope_head_dim: int,
247 v_head_dim: int,
248 q_lora_rank: int | None,
249 kv_lora_rank: int,
250 max_position_embeddings: int = 8192,
251 cache_config: CacheConfig | None = None,
252 quant_config: QuantizationConfig | None = None,
253 topk_indices_buffer: torch.Tensor | None = None,
254 prefix: str = "",
255 ) -> None:
256 super().__init__()
257 self.hidden_size = hidden_size
258 self.qk_nope_head_dim = qk_nope_head_dim
259 self.qk_rope_head_dim = qk_rope_head_dim
260 self.qk_head_dim = qk_nope_head_dim + qk_rope_head_dim
261 self.v_head_dim = v_head_dim
262 self.q_lora_rank = q_lora_rank
263 self.kv_lora_rank = kv_lora_rank
264 self.num_heads = num_heads
265 tp_size = get_tensor_model_parallel_world_size()
266 assert num_heads % tp_size == 0
267 self.num_local_heads = num_heads // tp_size
268 self.scaling = self.qk_head_dim**-0.5
269 self.max_position_embeddings = max_position_embeddings
270 assert topk_indices_buffer is None, (
271 "topk_indices_buffer is not \
272 supported for AXK1Attention"
273 )
274
275 if self.q_lora_rank is not None:
276 self.q_a_proj = ReplicatedLinear(
277 self.hidden_size,
278 self.q_lora_rank,
279 bias=False,
280 quant_config=quant_config,
281 prefix=f"{prefix}.q_a_proj",
282 )
283 self.q_a_layernorm = RMSNorm(self.q_lora_rank, eps=config.rms_norm_eps)
284 self.q_b_proj = ColumnParallelLinear(
285 self.q_lora_rank,
286 self.num_heads * self.qk_head_dim,
287 bias=False,
288 quant_config=quant_config,
289 prefix=f"{prefix}.q_b_proj",
290 )
291 else:
292 self.q_proj = ColumnParallelLinear(
293 self.hidden_size,
294 self.num_heads * self.qk_head_dim,
295 bias=False,
296 quant_config=quant_config,
297 prefix=f"{prefix}.q_proj",
298 )
299
300 self.kv_a_proj_with_mqa = ReplicatedLinear(
301 self.hidden_size,
302 self.kv_lora_rank + self.qk_rope_head_dim,
303 bias=False,
304 quant_config=quant_config,
305 prefix=f"{prefix}.kv_a_proj_with_mqa",
306 )
307 self.kv_a_layernorm = RMSNorm(self.kv_lora_rank, eps=config.rms_norm_eps)
308 self.kv_b_proj = ColumnParallelLinear(
309 self.kv_lora_rank,
310 self.num_heads * (self.qk_nope_head_dim + self.v_head_dim),
311 bias=False,
312 quant_config=quant_config,
313 prefix=f"{prefix}.kv_b_proj",
314 )
315 # O projection.
316 self.o_proj = RowParallelLinear(
317 self.num_heads * self.v_head_dim,
318 self.hidden_size,
319 bias=False,
320 quant_config=quant_config,
321 prefix=f"{prefix}.o_proj",
322 )
323 assert config.rope_parameters is not None
324 if config.rope_parameters["rope_type"] != "default":
325 config.rope_parameters["rope_type"] = (
326 "deepseek_llama_scaling"
327 if config.rope_parameters.get("attention_factor") == 1.0
328 else "deepseek_yarn"
329 )
330
331 self.rotary_emb = get_rope(
332 qk_rope_head_dim,
333 max_position=max_position_embeddings,
334 rope_parameters=config.rope_parameters,
335 is_neox_style=False,
336 )
337
338 if config.rope_parameters["rope_type"] == "deepseek_yarn":
339 mscale_all_dim = config.rope_parameters.get("mscale_all_dim", False)
340 scaling_factor = config.rope_parameters["factor"]
341 mscale = yarn_get_mscale(scaling_factor, float(mscale_all_dim))
342 self.scaling = self.scaling * mscale * mscale
343
344 self.attn = Attention(
345 self.num_local_heads,
346 self.qk_head_dim,
347 self.scaling,
348 num_kv_heads=self.num_local_heads,
349 cache_config=cache_config,
350 quant_config=quant_config,
351 prefix=f"{prefix}.attn",
352 )
353
354 def forward(
355 self,
356 positions: torch.Tensor,
357 hidden_states: torch.Tensor,
358 llama_4_scaling: torch.Tensor | None,
359 ) -> torch.Tensor:
360 if self.q_lora_rank is not None:
361 q = self.q_a_proj(hidden_states)[0]
362 q = self.q_a_layernorm(q)
363 q = self.q_b_proj(q)[0].view(-1, self.num_local_heads, self.qk_head_dim)
364 else:
365 q = self.q_proj(hidden_states)[0].view(
366 -1, self.num_local_heads, self.qk_head_dim
367 )
368 q_nope, q_pe = q.split([self.qk_nope_head_dim, self.qk_rope_head_dim], dim=-1)
369 latent_cache = self.kv_a_proj_with_mqa(hidden_states)[0]
370 kv_a, _ = latent_cache.split([self.kv_lora_rank, self.qk_rope_head_dim], dim=-1)
371 latent_cache = latent_cache.unsqueeze(1)
372 kv_a = self.kv_a_layernorm(kv_a)
373 kv = self.kv_b_proj(kv_a)[0]
374 kv = kv.view(-1, self.num_local_heads, self.qk_nope_head_dim + self.v_head_dim)
375 k_nope, v = kv.split([self.qk_nope_head_dim, self.v_head_dim], dim=-1)
376 k_pe = latent_cache[:, :, self.kv_lora_rank :]
377
378 q_pe, k_pe = self.rotary_emb(positions, q_pe, k_pe)
379
380 q[..., self.qk_nope_head_dim :] = q_pe
381 k = torch.empty_like(q)
382 k[..., : self.qk_nope_head_dim] = k_nope
383 k[..., self.qk_nope_head_dim :] = k_pe
384
385 # Apply llama 4 scaling if provided
386 if llama_4_scaling is not None:
387 q *= llama_4_scaling
388
389 # padding value to qk_head_dim for alignment
390 v = torch.nn.functional.pad(
391 v, [0, self.qk_head_dim - self.v_head_dim], value=0
392 ).view(-1, self.num_local_heads * self.qk_head_dim)
393 attn_output = self.attn(q, k, v)
394 attn_output = attn_output.view(-1, self.num_local_heads, self.qk_head_dim)[
395 ..., : self.v_head_dim
396 ].reshape(-1, self.num_local_heads * self.v_head_dim)
397 output, _ = self.o_proj(attn_output)
398 return output
399
400
401 class AXK1MLAAttention(nn.Module):
402 """Main reference: DeepseekV2 paper, and FlashInfer Implementation
403 (https://arxiv.org/abs/2405.04434 and https://github.com/flashinfer-ai/flashinfer/pull/551).
404
405 For more info see MLACommonImpl in:
406 vllm/v1/attention/backends/mla/utils.py
407 """
408
409 def __init__(
410 self,
411 vllm_config: VllmConfig,
412 config: AXK1Config,
413 hidden_size: int,
414 num_heads: int,
415 qk_nope_head_dim: int,
416 qk_rope_head_dim: int,
417 v_head_dim: int,
418 q_lora_rank: int | None,
419 kv_lora_rank: int,
420 max_position_embeddings: int = 8192,
421 cache_config: CacheConfig | None = None,
422 quant_config: QuantizationConfig | None = None,
423 prefix: str = "",
424 topk_indices_buffer: torch.Tensor | None = None,
425 ) -> None:
426 super().__init__()
427 self.hidden_size = hidden_size
428 self.qk_nope_head_dim = qk_nope_head_dim
429 self.qk_rope_head_dim = qk_rope_head_dim
430 self.qk_head_dim = qk_nope_head_dim + qk_rope_head_dim
431 self.v_head_dim = v_head_dim
432
433 self.q_lora_rank = q_lora_rank
434 self.kv_lora_rank = kv_lora_rank
435
436 self.num_heads = num_heads
437 tp_size = get_tensor_model_parallel_world_size()
438 assert num_heads % tp_size == 0
439 self.num_local_heads = num_heads // tp_size
440
441 self.scaling = self.qk_head_dim**-0.5
442 self.max_position_embeddings = max_position_embeddings
443
444 if self.q_lora_rank is not None:
445 self.fused_qkv_a_proj = MergedColumnParallelLinear(
446 self.hidden_size,
447 [self.q_lora_rank, self.kv_lora_rank + self.qk_rope_head_dim],
448 bias=False,
449 quant_config=quant_config,
450 prefix=f"{prefix}.fused_qkv_a_proj",
451 disable_tp=True,
452 )
453 else:
454 self.kv_a_proj_with_mqa = ReplicatedLinear(
455 self.hidden_size,
456 self.kv_lora_rank + self.qk_rope_head_dim,
457 bias=False,
458 quant_config=quant_config,
459 prefix=f"{prefix}.kv_a_proj_with_mqa",
460 )
461
462 if self.q_lora_rank is not None:
463 self.q_a_layernorm = RMSNorm(self.q_lora_rank, eps=config.rms_norm_eps)
464 self.q_b_proj = ColumnParallelLinear(
465 self.q_lora_rank,
466 self.num_heads * self.qk_head_dim,
467 bias=False,
468 quant_config=quant_config,
469 prefix=f"{prefix}.q_b_proj",
470 )
471 else:
472 self.q_proj = ColumnParallelLinear(
473 self.hidden_size,
474 self.num_heads * self.qk_head_dim,
475 bias=False,
476 quant_config=quant_config,
477 prefix=f"{prefix}.q_proj",
478 )
479 self.kv_a_layernorm = RMSNorm(self.kv_lora_rank, eps=config.rms_norm_eps)
480 self.kv_b_proj = ColumnParallelLinear(
481 self.kv_lora_rank,
482 self.num_heads * (self.qk_nope_head_dim + self.v_head_dim),
483 bias=False,
484 quant_config=quant_config,
485 prefix=f"{prefix}.kv_b_proj",
486 )
487 self.o_proj = RowParallelLinear(
488 self.num_heads * self.v_head_dim,
489 self.hidden_size,
490 bias=False,
491 quant_config=quant_config,
492 prefix=f"{prefix}.o_proj",
493 )
494
495 assert config.rope_parameters is not None
496 if config.rope_parameters["rope_type"] != "default":
497 config.rope_parameters["rope_type"] = (
498 "deepseek_llama_scaling"
499 if config.rope_parameters.get("attention_factor") == 1.0
500 else "deepseek_yarn"
501 )
502
503 self.rotary_emb = get_rope(
504 qk_rope_head_dim,
505 max_position=max_position_embeddings,
506 rope_parameters=config.rope_parameters,
507 is_neox_style=False,
508 )
509
510 if config.rope_parameters["rope_type"] == "deepseek_yarn":
511 mscale_all_dim = config.rope_parameters.get("mscale_all_dim", False)
512 scaling_factor = config.rope_parameters["factor"]
513 mscale = yarn_get_mscale(scaling_factor, float(mscale_all_dim))
514 self.scaling = self.scaling * mscale * mscale
515
516 mla_modules = MLAModules(
517 kv_a_layernorm=self.kv_a_layernorm,
518 kv_b_proj=self.kv_b_proj,
519 rotary_emb=self.rotary_emb,
520 o_proj=self.o_proj,
521 fused_qkv_a_proj=self.fused_qkv_a_proj
522 if self.q_lora_rank is not None
523 else None,
524 kv_a_proj_with_mqa=self.kv_a_proj_with_mqa
525 if self.q_lora_rank is None
526 else None,
527 q_a_layernorm=self.q_a_layernorm if self.q_lora_rank is not None else None,
528 q_b_proj=self.q_b_proj if self.q_lora_rank is not None else None,
529 q_proj=self.q_proj if self.q_lora_rank is None else None,
530 indexer=None,
531 indexer_rotary_emb=None,
532 is_sparse=False,
533 topk_indices_buffer=topk_indices_buffer,
534 )
535
536 self.mla_attn = MultiHeadLatentAttentionWrapper(
537 self.hidden_size,
538 self.num_local_heads,
539 self.scaling,
540 self.qk_nope_head_dim,
541 self.qk_rope_head_dim,
542 self.v_head_dim,
543 self.q_lora_rank,
544 self.kv_lora_rank,
545 mla_modules,
546 cache_config,
547 quant_config,
548 prefix,
549 )
550
551 def forward(
552 self,
553 positions: torch.Tensor,
554 hidden_states: torch.Tensor,
555 llama_4_scaling: torch.Tensor | None,
556 ) -> torch.Tensor:
557 return self.mla_attn(positions, hidden_states, llama_4_scaling)
558
559
560 class AXK1DecoderLayer(nn.Module):
561 def __init__(
562 self,
563 vllm_config: VllmConfig,
564 prefix: str,
565 config: AXK1Config | None = None,
566 ) -> None:
567 super().__init__()
568
569 if config is None:
570 config = vllm_config.model_config.hf_config
571 model_config = vllm_config.model_config
572 cache_config = vllm_config.cache_config
573 quant_config = vllm_config.quant_config
574 parallel_config = vllm_config.parallel_config
575 self.config = config
576
577 assert config.max_position_embeddings is not None
578 assert config.hidden_act is not None
579 assert config.routed_scaling_factor is not None
580 assert config.qk_nope_head_dim is not None
581 assert config.qk_rope_head_dim is not None
582 assert config.v_head_dim is not None
583 assert config.kv_lora_rank is not None
584 self.hidden_size = config.hidden_size
585 max_position_embeddings = config.max_position_embeddings
586 # DecoderLayers are created with `make_layers` which passes the prefix
587 # with the layer's index.
588 layer_idx = int(prefix.split(sep=".")[-1])
589 self.layer_idx = layer_idx
590
591 # verify MLA attention specific fields
592 qk_nope_head_dim = config.qk_nope_head_dim
593 qk_rope_head_dim = config.qk_rope_head_dim
594 v_head_dim = config.v_head_dim
595 kv_lora_rank = config.kv_lora_rank
596 use_mha = all(dim == 0 for dim in (qk_nope_head_dim, qk_rope_head_dim))
597 self.use_mha = use_mha
598
599 attn_cls: type[nn.Module]
600 if use_mha:
601 attn_cls = DeepseekAttention
602 elif model_config.use_mla:
603 attn_cls = AXK1MLAAttention
604 else:
605 attn_cls = AXK1Attention
606 self.self_attn = attn_cls(
607 vllm_config=vllm_config,
608 config=config,
609 hidden_size=self.hidden_size,
610 num_heads=config.num_attention_heads,
611 qk_nope_head_dim=qk_nope_head_dim,
612 qk_rope_head_dim=qk_rope_head_dim,
613 v_head_dim=v_head_dim,
614 q_lora_rank=config.q_lora_rank,
615 kv_lora_rank=kv_lora_rank,
616 max_position_embeddings=max_position_embeddings,
617 cache_config=cache_config,
618 quant_config=quant_config,
619 prefix=f"{prefix}.self_attn",
620 topk_indices_buffer=None,
621 )
622
623 self.is_layer_sparse = self._is_layer_sparse()
624 if self.is_layer_sparse:
625 self.mlp = AXK1MoE(
626 config=config,
627 parallel_config=parallel_config,
628 quant_config=quant_config,
629 prefix=f"{prefix}.mlp",
630 )
631 else:
632 self.mlp = AXK1MLP(
633 hidden_size=config.hidden_size,
634 intermediate_size=config.intermediate_size,
635 hidden_act=config.hidden_act,
636 quant_config=quant_config,
637 prefix=f"{prefix}.mlp",
638 )
639 self.input_layernorm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
640 self.post_attention_layernorm = RMSNorm(
641 config.hidden_size, eps=config.rms_norm_eps
642 )
643 self.post_mlp_layernorm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
644 self.routed_scaling_factor = config.routed_scaling_factor
645
646 def _is_layer_sparse(self) -> bool:
647 assert self.config.moe_layer_freq is not None
648 return (
649 self.config.n_routed_experts is not None
650 and self.layer_idx >= self.config.first_k_dense_replace
651 and self.layer_idx % self.config.moe_layer_freq == 0
652 )
653
654 def forward(
655 self,
656 positions: torch.Tensor,
657 hidden_states: torch.Tensor,
658 residual: torch.Tensor | None,
659 llama_4_scaling: torch.Tensor | None = None,
660 ) -> tuple[torch.Tensor, torch.Tensor]:
661 # Self Attention
662 if residual is None:
663 residual = hidden_states
664 hidden_states = self.input_layernorm(hidden_states)
665 else:
666 hidden_states, residual = self.input_layernorm(hidden_states, residual)
667
668 attn_kwargs = {
669 "positions": positions,
670 "hidden_states": hidden_states,
671 }
672 if not self.use_mha:
673 attn_kwargs["llama_4_scaling"] = llama_4_scaling
674 hidden_states = self.self_attn(**attn_kwargs)
675
676 if (
677 not isinstance(self.self_attn, DeepseekAttention)
678 and hidden_states.dtype == torch.float16
679 ):
680 # Fix FP16 overflow
681 # We scale both hidden_states and residual before
682 # rmsnorm, and rmsnorm result would not affect by scale.
683 hidden_states *= 1.0 / self.routed_scaling_factor
684 if self.layer_idx == 0:
685 # The residual is shared by all layers, we only scale it on
686 # first layer.
687 residual *= 1.0 / self.routed_scaling_factor
688
689 # Fully Connected
690 hidden_states, residual = self.post_attention_layernorm(hidden_states, residual)
691 hidden_states = self.mlp(hidden_states)
692
693 if self.is_layer_sparse:
694 hidden_states = self.post_mlp_layernorm(hidden_states)
695
696 if isinstance(self.mlp, AXK1MLP) and hidden_states.dtype == torch.float16:
697 # Fix FP16 overflow
698 # Scaling the AXK1MLP output, it is the input of
699 # input_layernorm of next decoder layer.
700 # The scaling of AXK1MOE output would be done in the forward
701 # of AXK1MOE
702 hidden_states *= 1.0 / self.routed_scaling_factor
703
704 return hidden_states, residual
705
706
707 @support_torch_compile
708 class AXK1Model(nn.Module):
709 fall_back_to_pt_during_load = False
710
711 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
712 super().__init__()
713
714 config: AXK1Config = vllm_config.model_config.hf_config
715 quant_config = vllm_config.quant_config
716 self.config = config
717 self.device = current_platform.device_type
718 self.vocab_size = config.vocab_size
719
720 if get_pp_group().is_first_rank:
721 self.embed_tokens = VocabParallelEmbedding(
722 config.vocab_size,
723 config.hidden_size,
724 quant_config=quant_config,
725 prefix=f"{prefix}.embed_tokens",
726 )
727 else:
728 self.embed_tokens = PPMissingLayer()
729 self.start_layer, self.end_layer, self.layers = make_layers(
730 config.num_hidden_layers,
731 lambda prefix: AXK1DecoderLayer(vllm_config, prefix),
732 prefix=f"{prefix}.layers",
733 )
734
735 self.is_fused_shared_expert_enabled = is_model_fused_shared_expert_compatible(
736 self.layers,
737 AXK1MoE,
738 "mlp",
739 )
740
741 if get_pp_group().is_last_rank:
742 self.norm = RMSNorm(config.hidden_size, eps=config.rms_norm_eps)
743 else:
744 self.norm = PPMissingLayer()
745 self.make_empty_intermediate_tensors = make_empty_intermediate_tensors_factory(
746 ["hidden_states", "residual"], config.hidden_size
747 )
748 self.use_mha = all(
749 dim == 0 for dim in (config.qk_nope_head_dim, config.qk_rope_head_dim)
750 )
751 self.fuse_qkv_a_proj = config.q_lora_rank is not None
752 self.num_redundant_experts = (
753 vllm_config.parallel_config.eplb_config.num_redundant_experts
754 )
755
756 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
757 return self.embed_tokens(input_ids)
758
759 def forward(
760 self,
761 input_ids: torch.Tensor | None,
762 positions: torch.Tensor,
763 intermediate_tensors: IntermediateTensors | None,
764 inputs_embeds: torch.Tensor | None = None,
765 ) -> torch.Tensor | IntermediateTensors:
766 if get_pp_group().is_first_rank:
767 if inputs_embeds is not None:
768 hidden_states = inputs_embeds
769 else:
770 hidden_states = self.embed_input_ids(input_ids)
771 residual = None
772 else:
773 assert intermediate_tensors is not None
774 hidden_states = intermediate_tensors["hidden_states"]
775 residual = intermediate_tensors["residual"]
776
777 # Compute llama 4 scaling once per forward pass if enabled
778 llama_4_scaling_config = getattr(self.config, "llama_4_scaling", None)
779 llama_4_scaling: torch.Tensor | None
780 if llama_4_scaling_config is not None:
781 llama_4_scaling = _get_llama_4_scaling(
782 original_max_position_embeddings=llama_4_scaling_config[
783 "original_max_position_embeddings"
784 ],
785 scaling_beta=llama_4_scaling_config["beta"],
786 positions=positions,
787 )
788 else:
789 llama_4_scaling = None
790
791 for layer in islice(self.layers, self.start_layer, self.end_layer):
792 hidden_states, residual = layer(
793 positions, hidden_states, residual, llama_4_scaling
794 )
795
796 if not get_pp_group().is_last_rank:
797 return IntermediateTensors(
798 {"hidden_states": hidden_states, "residual": residual}
799 )
800
801 hidden_states, _ = self.norm(hidden_states, residual)
802 return hidden_states
803
804 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
805 stacked_params_mapping: list[tuple[str, str, int | str]] = [
806 # (param_name, shard_name, shard_id)
807 ("gate_up_proj", "gate_proj", 0),
808 ("gate_up_proj", "up_proj", 1),
809 ]
810 mla_params_mapping = [
811 ("fused_qkv_a_proj", "q_a_proj", 0),
812 ("fused_qkv_a_proj", "kv_a_proj_with_mqa", 1),
813 ]
814 mha_params_mapping = [
815 ("qkv_proj", "q_proj", "q"),
816 ("qkv_proj", "k_proj", "k"),
817 ("qkv_proj", "v_proj", "v"),
818 ]
819 if self.use_mha:
820 stacked_params_mapping.extend(mha_params_mapping)
821 else:
822 stacked_params_mapping.extend(mla_params_mapping)
823
824 # Params for weights, fp8 weight scales, fp8 activation scales
825 # (param_name, weight_name, expert_id, shard_id)
826 assert self.config.n_routed_experts is not None
827 expert_params_mapping = fused_moe_make_expert_params_mapping(
828 self,
829 ckpt_gate_proj_name="gate_proj",
830 ckpt_down_proj_name="down_proj",
831 ckpt_up_proj_name="up_proj",
832 num_experts=self.config.n_routed_experts
833 + (
834 (self.config.n_shared_experts or 0)
835 if self.is_fused_shared_expert_enabled
836 else 0
837 ),
838 num_redundant_experts=self.num_redundant_experts,
839 )
840
841 params_dict = dict(self.named_parameters())
842 loaded_params: set[str] = set()
843 for name, loaded_weight in weights:
844 if "rotary_emb.inv_freq" in name:
845 continue
846
847 spec_layer = get_spec_layer_idx_from_weight_name(self.config, name)
848 if spec_layer is not None:
849 continue # skip spec decode layers for main model
850
851 is_fusion_moe_shared_experts_layer = (
852 self.is_fused_shared_expert_enabled and ("mlp.shared_experts" in name)
853 )
854
855 for param_name, weight_name, shard_id in stacked_params_mapping:
856 # Skip non-stacked layers and experts (experts handled below).
857 if weight_name not in name:
858 continue
859 # We have mlp.experts[0].gate_proj in the checkpoint.
860 # Since we handle the experts below in expert_params_mapping,
861 # we need to skip here BEFORE we update the name, otherwise
862 # name will be updated to mlp.experts[0].gate_up_proj, which
863 # will then be updated below in expert_params_mapping
864 # for mlp.experts[0].gate_gate_up_proj, which breaks load.
865 if ("mlp.experts." in name) and name not in params_dict:
866 continue
867 if is_fusion_moe_shared_experts_layer:
868 continue
869 name_mapped = name.replace(weight_name, param_name)
870
871 # QKV fusion is optional, fall back to normal
872 # weight loading if it's not enabled
873 # if go with fusion option, then update name
874 if (
875 param_name == "fused_qkv_a_proj"
876 ) and name_mapped not in params_dict:
877 continue
878 else:
879 name = name_mapped
880 # Skip loading extra bias for GPTQ models.
881 if name.endswith(".bias") and name not in params_dict:
882 continue
883
884 if is_pp_missing_parameter(name, self):
885 continue
886
887 param = params_dict[name]
888 weight_loader = param.weight_loader
889 weight_loader(param, loaded_weight, shard_id)
890 break
891 else:
892 is_expert_weight = False
893
894 # Special handling: when AITER fusion_shared_experts is enabled,
895 # checkpoints may provide a single widened shared_experts tensor
896 # without explicit expert indices
897 # (e.g. ...mlp.shared_experts.gate_proj.weight).
898 # For models with multiple shared experts, split that tensor
899 # evenly into per-shared-expert slices and load them into
900 # appended expert slots mlp.experts.{n_routed_experts + j}.*
901 # accordingly.
902 num_chunks = 1
903 if is_fusion_moe_shared_experts_layer:
904 num_chunks = getattr(self.config, "n_shared_experts", 1) or 1
905 # Determine split axis based on op type
906 # gate/up: ColumnParallel → split along dim 0
907 # down: RowParallel → split along dim 1
908 split_dim = (
909 1
910 if ("down_proj.weight" in name and loaded_weight.ndim > 1)
911 else 0
912 )
913 total = loaded_weight.shape[split_dim]
914 assert total % num_chunks == 0, (
915 f"Shared expert weight dim {total} "
916 f"not divisible by num_chunks {num_chunks}"
917 )
918 chunk_size = total // num_chunks
919
920 for j in range(num_chunks):
921 chunk_name = name
922 weight_to_load = loaded_weight
923
924 if is_fusion_moe_shared_experts_layer:
925 chunk_slice = slice(j * chunk_size, (j + 1) * chunk_size)
926 if loaded_weight.ndim == 1:
927 weight_to_load = loaded_weight[chunk_slice]
928 elif split_dim == 0:
929 weight_to_load = loaded_weight[chunk_slice, :]
930 else:
931 weight_to_load = loaded_weight[:, chunk_slice]
932 # Synthesize an expert-style name so expert mapping
933 # can route it
934 chunk_name = name.replace(
935 "mlp.shared_experts",
936 f"mlp.experts.{self.config.n_routed_experts + j}",
937 )
938
939 # Use expert_params_mapping to locate the destination
940 # param and delegate to its expert-aware weight_loader
941 # with expert_id.
942 for mapping in expert_params_mapping:
943 param_name, weight_name, expert_id, expert_shard_id = mapping
944 if weight_name not in chunk_name:
945 continue
946
947 # Anyway, this is an expert weight and should not be
948 # attempted to load as other weights later
949 is_expert_weight = True
950
951 # Do not modify `name` since the loop may continue here
952 # Instead, create a new variable
953 name_mapped = chunk_name.replace(weight_name, param_name)
954
955 if is_pp_missing_parameter(name_mapped, self):
956 continue
957
958 param = params_dict[name_mapped]
959 # We should ask the weight loader to return success or
960 # not here since otherwise we may skip experts with
961 # other available replicas.
962 weight_loader = typing.cast(
963 Callable[..., bool], param.weight_loader
964 )
965 success = weight_loader(
966 param,
967 weight_to_load,
968 name_mapped,
969 shard_id=expert_shard_id,
970 expert_id=expert_id,
971 return_success=True,
972 )
973 if success:
974 if not is_fusion_moe_shared_experts_layer:
975 name = name_mapped
976 else:
977 loaded_params.add(name_mapped)
978 break
979 else:
980 if is_expert_weight:
981 # We've checked that this is an expert weight
982 # However it's not mapped locally to this rank
983 # So we simply skip it
984 continue
985
986 # Skip loading extra bias for GPTQ models.
987 if name.endswith(".bias") and name not in params_dict:
988 continue
989
990 # Remapping the name of FP8 kv-scale.
991 remapped_name = maybe_remap_kv_scale_name(name, params_dict)
992 if remapped_name is None:
993 continue
994 name = remapped_name
995
996 if is_pp_missing_parameter(name, self):
997 continue
998
999 param = params_dict[name]
1000 weight_loader = getattr(
1001 param, "weight_loader", default_weight_loader
1002 )
1003 weight_loader(param, loaded_weight)
1004 if not is_fusion_moe_shared_experts_layer:
1005 loaded_params.add(name)
1006
1007 return loaded_params
1008
1009
1010 class AXK1MixtureOfExperts(MixtureOfExperts):
1011 moe_mlp_layers: list[AXK1MoE]
1012 """
1013 List of MoE MLP layers in the model.
1014 """
1015
1016 def extract_moe_parameters(self, example_moe: AXK1MoE | None):
1017 if example_moe is None:
1018 self.num_moe_layers = 0
1019 self.num_expert_groups = 0
1020 self.num_logical_experts = 0
1021 self.num_physical_experts = 0
1022 self.num_local_physical_experts = 0
1023 self.num_routed_experts = 0
1024 self.num_shared_experts = 0
1025 self.num_redundant_experts = 0
1026 logger.warning("AXK1: No AXK1MoE layer found in model.layers.")
1027 else:
1028 self.num_logical_experts = example_moe.n_logical_experts
1029 self.num_physical_experts = example_moe.n_physical_experts
1030 self.num_local_physical_experts = example_moe.n_local_physical_experts
1031 self.num_routed_experts = example_moe.n_routed_experts
1032 self.num_shared_experts = example_moe.n_shared_experts or 0
1033 self.num_redundant_experts = example_moe.n_redundant_experts
1034
1035 def update_physical_experts_metadata(
1036 self,
1037 num_physical_experts: int,
1038 num_local_physical_experts: int,
1039 ) -> None:
1040 assert self.num_local_physical_experts == num_local_physical_experts
1041 self.num_physical_experts = num_physical_experts
1042 self.num_local_physical_experts = num_local_physical_experts
1043 self.num_redundant_experts = num_physical_experts - self.num_logical_experts
1044 for moe in self.moe_mlp_layers:
1045 moe.n_local_physical_experts = num_local_physical_experts
1046 moe.n_physical_experts = num_physical_experts
1047 moe.n_redundant_experts = self.num_redundant_experts
1048 moe.experts.update_expert_map()
1049
1050
1051 class AXK1ForCausalLM(
1052 nn.Module, SupportsPP, AXK1MixtureOfExperts, SupportsLoRA, SupportsEagle
1053 ):
1054 packed_modules_mapping = {
1055 "gate_up_proj": ["gate_proj", "up_proj"],
1056 }
1057 model_cls = AXK1Model
1058
1059 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
1060 super().__init__()
1061 config: AXK1Config = vllm_config.model_config.hf_config
1062 quant_config = vllm_config.quant_config
1063 self.config = config
1064 self.quant_config = quant_config
1065
1066 qk_nope_head_dim = config.qk_nope_head_dim
1067 qk_rope_head_dim = config.qk_rope_head_dim
1068 self.use_mha = all(dim == 0 for dim in (qk_nope_head_dim, qk_rope_head_dim))
1069
1070 if self.use_mha:
1071 self.packed_modules_mapping["qkv_proj"] = ["q_proj", "k_proj", "v_proj"]
1072
1073 # `packed_modules_mapping` needs to be modified before
1074 # initializing AXK1Model, as it is passed inplace to
1075 # quantization config init and may be used to select the
1076 # quant_method for relevant layers during initialization.
1077 self.fuse_qkv_a_proj = config.q_lora_rank is not None
1078 if self.fuse_qkv_a_proj:
1079 self.packed_modules_mapping["fused_qkv_a_proj"] = [
1080 "q_a_proj",
1081 "kv_a_proj_with_mqa",
1082 ]
1083
1084 self.model = self.model_cls(
1085 vllm_config=vllm_config, prefix=maybe_prefix(prefix, "model")
1086 )
1087 if get_pp_group().is_last_rank:
1088 self.lm_head = ParallelLMHead(
1089 config.vocab_size,
1090 config.hidden_size,
1091 quant_config=quant_config,
1092 prefix=maybe_prefix(prefix, "lm_head"),
1093 )
1094 else:
1095 self.lm_head = PPMissingLayer()
1096 self.logits_processor = LogitsProcessor(config.vocab_size)
1097 self.make_empty_intermediate_tensors = (
1098 self.model.make_empty_intermediate_tensors
1099 )
1100 # Set MoE hyperparameters
1101 self.num_moe_layers = (
1102 self.config.num_hidden_layers - self.config.first_k_dense_replace
1103 )
1104 self.set_moe_parameters()
1105
1106 def set_moe_parameters(self):
1107 self.num_expert_groups = getattr(self.config, "n_group", 1)
1108
1109 self.moe_layers = []
1110 self.moe_mlp_layers = []
1111 example_moe = None
1112 for layer in self.model.layers:
1113 if isinstance(layer, PPMissingLayer):
1114 continue
1115
1116 assert isinstance(layer, AXK1DecoderLayer)
1117 if isinstance(layer.mlp, AXK1MoE):
1118 # Pick last one layer since the first ones may be dense layers.
1119 example_moe = layer.mlp
1120 self.moe_mlp_layers.append(layer.mlp)
1121 self.moe_layers.append(layer.mlp.experts)
1122
1123 self.extract_moe_parameters(example_moe)
1124
1125 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
1126 return self.model.embed_input_ids(input_ids)
1127
1128 def forward(
1129 self,
1130 input_ids: torch.Tensor | None,
1131 positions: torch.Tensor,
1132 intermediate_tensors: IntermediateTensors | None = None,
1133 inputs_embeds: torch.Tensor | None = None,
1134 ) -> torch.Tensor | IntermediateTensors:
1135 hidden_states = self.model(
1136 input_ids, positions, intermediate_tensors, inputs_embeds
1137 )
1138 return hidden_states
1139
1140 def compute_logits(
1141 self,
1142 hidden_states: torch.Tensor,
1143 ) -> torch.Tensor | None:
1144 logits = self.logits_processor(self.lm_head, hidden_states)
1145 return logits
1146
1147 def get_expert_mapping(self) -> list[tuple[str, str, int, str]]:
1148 # Params for weights, fp8 weight scales, fp8 activation scales
1149 # (param_name, weight_name, expert_id, shard_id)
1150 assert self.config.n_routed_experts is not None
1151 return fused_moe_make_expert_params_mapping(
1152 self,
1153 ckpt_gate_proj_name="gate_proj",
1154 ckpt_down_proj_name="down_proj",
1155 ckpt_up_proj_name="up_proj",
1156 num_experts=self.config.n_routed_experts,
1157 num_redundant_experts=0,
1158 )
1159
1160 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
1161 loader = AutoWeightsLoader(self)
1162 return loader.load_weights(weights)