main
py 713 lines 25.1 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3 #
4 # Copyright 2026 BharatGen AI team. All rights reserved.
5 #
6 # This code has been modified to accommodate Param2MoE's GQA-based MoE architecture.
7 #
8 # Licensed under the Apache License, Version 2.0 (the "License");
9 # you may not use this file except in compliance with the License.
10 # You may obtain a copy of the License at
11 #
12 # http://www.apache.org/licenses/LICENSE-2.0
13 #
14 # Unless required by applicable law or agreed to in writing, software
15 # distributed under the License is distributed on an "AS IS" BASIS,
16 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 # See the License for the specific language governing permissions and
18 # limitations under the License.
19 from __future__ import annotations
20
21 from collections.abc import Iterable, Iterator
22 from itertools import islice
23
24 import torch
25 from torch import nn
26
27 from vllm.config import CacheConfig, VllmConfig
28 from vllm.distributed import (
29 get_pp_group,
30 get_tensor_model_parallel_world_size,
31 )
32 from vllm.model_executor.layers.activation import SiluAndMul
33 from vllm.model_executor.layers.attention import Attention
34 from vllm.model_executor.layers.fused_moe import (
35 FusedMoEFactory,
36 GateLinear,
37 MoERunner,
38 )
39 from vllm.model_executor.layers.layernorm import RMSNorm
40 from vllm.model_executor.layers.linear import (
41 MergedColumnParallelLinear,
42 QKVParallelLinear,
43 RowParallelLinear,
44 )
45 from vllm.model_executor.layers.logits_processor import LogitsProcessor
46 from vllm.model_executor.layers.quantization import QuantizationConfig
47 from vllm.model_executor.layers.rotary_embedding import get_rope
48 from vllm.model_executor.layers.vocab_parallel_embedding import (
49 ParallelLMHead,
50 VocabParallelEmbedding,
51 )
52 from vllm.sequence import IntermediateTensors
53
54 from .interfaces import MixtureOfExperts, SupportsLoRA, SupportsPP
55 from .utils import (
56 AutoWeightsLoader,
57 PPMissingLayer,
58 WeightsMapper,
59 make_empty_intermediate_tensors_factory,
60 make_layers,
61 maybe_prefix,
62 )
63
64
65 def _is_expert_bias_name(name: str) -> bool:
66 """True when the weight is the MoE router's per-expert score bias."""
67 return name.endswith(".mlp.gate.expert_bias")
68
69
70 def _zero_mean_tensor(t: torch.Tensor) -> torch.Tensor:
71 if t.numel() == 0:
72 return t
73 return t - t.mean()
74
75
76 def _normalize_expert_bias(
77 weights: Iterable[tuple[str, torch.Tensor]],
78 ) -> Iterator[tuple[str, torch.Tensor]]:
79 """Zero-mean the MoE router's per-expert score bias for load balance.
80
81 The rename to ``e_score_correction_bias`` is done by the mapper; only the
82 tensor adjustment lives here, since a WeightsMapper cannot transform data.
83 """
84 for name, w in weights:
85 if _is_expert_bias_name(name):
86 w = _zero_mean_tensor(w)
87 yield name, w
88
89
90 class Param2MoEAttention(nn.Module):
91 """Grouped-Query Attention (GQA) for Param2MoE.
92
93 Notable differences from a vanilla GQA layer:
94 * The checkpoint fuses Q, K, V into a single ``query_key_value`` weight.
95 vLLM receives it already renamed to ``qkv_proj`` by the weight-name
96 translator and loads it directly; ``QKVParallelLinear`` splits the
97 fused ``[Q|K|V]`` tensor internally.
98 * Optional per-head RMS norms on Q and K (``use_qk_norm=True``).
99 """
100
101 def __init__(
102 self,
103 config,
104 cache_config: CacheConfig | None = None,
105 quant_config: QuantizationConfig | None = None,
106 prefix: str = "",
107 ) -> None:
108 super().__init__()
109
110 self.hidden_size = config.hidden_size
111 self.num_heads = config.num_attention_heads
112 self.num_kv_heads = config.num_key_value_heads
113 self.head_dim = config.head_dim or (self.hidden_size // self.num_heads)
114 self.use_qk_norm: bool = getattr(config, "use_qk_norm", False)
115
116 tp_size = get_tensor_model_parallel_world_size()
117 assert self.num_heads % tp_size == 0, (
118 f"num_attention_heads ({self.num_heads}) must be divisible "
119 f"by tensor-parallel world size ({tp_size})."
120 )
121 assert self.num_kv_heads % tp_size == 0, (
122 f"num_key_value_heads ({self.num_kv_heads}) must be divisible "
123 f"by tensor-parallel world size ({tp_size})."
124 )
125 self.num_local_heads = self.num_heads // tp_size
126 self.num_local_kv_heads = self.num_kv_heads // tp_size
127
128 # Sizes after TP split (used in forward to split qkv output)
129 self.q_size_local = self.num_local_heads * self.head_dim
130 self.kv_size_local = self.num_local_kv_heads * self.head_dim
131
132 self.scaling = self.head_dim**-0.5
133
134 self.qkv_proj = QKVParallelLinear(
135 hidden_size=self.hidden_size,
136 head_size=self.head_dim,
137 total_num_heads=self.num_heads,
138 total_num_kv_heads=self.num_kv_heads,
139 bias=getattr(config, "use_qkv_bias", False),
140 quant_config=quant_config,
141 prefix=f"{prefix}.qkv_proj",
142 )
143
144 self.o_proj = RowParallelLinear(
145 input_size=self.num_heads * self.head_dim,
146 output_size=self.hidden_size,
147 bias=getattr(config, "use_bias", False),
148 quant_config=quant_config,
149 prefix=f"{prefix}.o_proj",
150 )
151
152 if self.use_qk_norm:
153 self.q_layernorm = RMSNorm(self.head_dim, eps=config.rms_norm_eps)
154 self.k_layernorm = RMSNorm(self.head_dim, eps=config.rms_norm_eps)
155
156 # `partial_rotary_factor` defaults to 1.0 (full RoPE) if not in config
157 partial_rotary_factor: float = getattr(config, "partial_rotary_factor", 1.0)
158 rope_dim = int(self.head_dim * partial_rotary_factor)
159
160 rope_parameters: dict = {
161 "rope_type": "default",
162 "base": config.rope_theta,
163 }
164 if config.rope_scaling is not None:
165 rope_parameters.update(config.rope_scaling)
166 # Normalise key: some checkpoints use "type", vLLM wants "rope_type"
167 if "type" in rope_parameters and "rope_type" not in rope_parameters:
168 rope_parameters["rope_type"] = rope_parameters.pop("type")
169
170 self.rotary_emb = get_rope(
171 rope_dim,
172 max_position=config.max_position_embeddings,
173 rope_parameters=rope_parameters,
174 is_neox_style=True,
175 )
176
177 self.attn = Attention(
178 num_heads=self.num_local_heads,
179 head_size=self.head_dim,
180 scale=self.scaling,
181 num_kv_heads=self.num_local_kv_heads,
182 cache_config=cache_config,
183 quant_config=quant_config,
184 prefix=f"{prefix}.attn",
185 )
186
187 def forward(
188 self,
189 positions: torch.Tensor,
190 hidden_states: torch.Tensor,
191 ) -> torch.Tensor:
192 qkv, _ = self.qkv_proj(hidden_states)
193 q, k, v = qkv.split(
194 [self.q_size_local, self.kv_size_local, self.kv_size_local],
195 dim=-1,
196 )
197 q = q.contiguous()
198 k = k.contiguous()
199 v = v.contiguous()
200
201 if self.use_qk_norm:
202 T = q.shape[0]
203 q = self.q_layernorm(q.view(T, self.num_local_heads, self.head_dim)).view(
204 T, self.q_size_local
205 )
206 k = self.k_layernorm(
207 k.view(T, self.num_local_kv_heads, self.head_dim)
208 ).view(T, self.kv_size_local)
209
210 q, k = self.rotary_emb(positions, q, k)
211 attn_output = self.attn(q, k, v)
212 output, _ = self.o_proj(attn_output)
213 return output
214
215
216 class Param2MoEMLP(nn.Module):
217 """SwiGLU feed-forward block used for dense layers."""
218
219 def __init__(
220 self,
221 intermediate_size: int,
222 config,
223 quant_config: QuantizationConfig | None = None,
224 reduce_results: bool = True,
225 prefix: str = "",
226 ) -> None:
227 super().__init__()
228
229 self.gate_up_proj = MergedColumnParallelLinear(
230 input_size=config.hidden_size,
231 output_sizes=[intermediate_size, intermediate_size],
232 bias=False,
233 quant_config=quant_config,
234 prefix=f"{prefix}.gate_up_proj",
235 )
236 self.down_proj = RowParallelLinear(
237 input_size=intermediate_size,
238 output_size=config.hidden_size,
239 bias=False,
240 quant_config=quant_config,
241 reduce_results=reduce_results,
242 prefix=f"{prefix}.down_proj",
243 )
244 self.act_fn = SiluAndMul()
245
246 def forward(self, x: torch.Tensor) -> torch.Tensor:
247 gate_up, _ = self.gate_up_proj(x)
248 x = self.act_fn(gate_up)
249 x, _ = self.down_proj(x)
250 return x
251
252
253 class Param2MoEMoEBlock(nn.Module):
254 """Mixture-of-Experts block for Param2MoE.
255
256 Routing:
257 * Sigmoid scoring (config.score_function = "sigmoid")
258 * Grouped top-k (n_group, topk_group)
259 * Per-expert bias (gate.expert_bias → e_score_correction_bias)
260 * routed_scaling_factor normalisation
261
262 One set of shared (always-active) experts is added on top.
263 """
264
265 def __init__(
266 self,
267 config,
268 quant_config: QuantizationConfig | None = None,
269 prefix: str = "",
270 ) -> None:
271 super().__init__()
272
273 self.config = config
274 self.tp_size = get_tensor_model_parallel_world_size()
275 self.hidden_size = config.hidden_size
276
277 self.num_experts: int = config.num_experts
278 self.top_k: int = config.num_experts_per_tok
279 self.routed_scaling_factor: float = getattr(
280 config, "routed_scaling_factor", 1.0
281 )
282
283 self.n_group: int | None = getattr(config, "n_group", None)
284 self.topk_group: int | None = getattr(config, "topk_group", None)
285 self.use_grouped_topk: bool = (
286 self.n_group is not None and self.topk_group is not None
287 )
288
289 self.norm_expert_prob: bool = getattr(config, "norm_topk_prob", True)
290 self.score_function: str = getattr(config, "score_function", "sigmoid")
291
292 self.gate = GateLinear(
293 self.hidden_size,
294 self.num_experts,
295 out_dtype=torch.float32,
296 params_dtype=torch.float32,
297 prefix=f"{prefix}.gate",
298 )
299
300 if getattr(config, "moe_router_enable_expert_bias", True):
301 self.gate.e_score_correction_bias = nn.Parameter(
302 torch.zeros(self.num_experts, dtype=torch.float32)
303 )
304 else:
305 self.gate.e_score_correction_bias = None # type: ignore[assignment]
306
307 self.num_shared_experts: int = getattr(config, "num_shared_experts", 1)
308 if self.num_shared_experts > 0:
309 # If moe_shared_expert_intermediate_size is present in the config
310 # it already encodes the TOTAL intermediate size across all shared
311 # experts (i.e. it equals moe_intermediate_size * num_shared_experts).
312 # Do NOT multiply again. Fall back to computing the product only
313 # when the dedicated field is absent.
314 if (
315 hasattr(config, "moe_shared_expert_intermediate_size")
316 and config.moe_shared_expert_intermediate_size is not None
317 ):
318 shared_int: int = config.moe_shared_expert_intermediate_size
319 else:
320 shared_int = config.moe_intermediate_size * self.num_shared_experts
321 self.shared_experts = Param2MoEMLP(
322 intermediate_size=shared_int,
323 config=config,
324 quant_config=quant_config,
325 reduce_results=False,
326 prefix=f"{prefix}.shared_experts",
327 )
328 else:
329 self.shared_experts = None # type: ignore[assignment]
330
331 self.experts = FusedMoEFactory(
332 shared_experts=self.shared_experts,
333 num_experts=self.num_experts,
334 top_k=self.top_k,
335 hidden_size=self.hidden_size,
336 intermediate_size=config.moe_intermediate_size,
337 renormalize=self.norm_expert_prob,
338 quant_config=quant_config,
339 prefix=f"{prefix}.experts",
340 scoring_func=self.score_function,
341 e_score_correction_bias=self.gate.e_score_correction_bias,
342 num_expert_group=self.n_group,
343 topk_group=self.topk_group,
344 use_grouped_topk=self.use_grouped_topk,
345 routed_scaling_factor=self.routed_scaling_factor,
346 )
347
348 def maybe_get_fused_moe(self) -> MoERunner:
349 return self.experts
350
351 def forward(self, hidden_states: torch.Tensor) -> torch.Tensor:
352 num_tokens, hidden_dim = hidden_states.shape
353 hidden_states = hidden_states.view(-1, hidden_dim)
354
355 # Router logits are accumulated in float32 for numerical stability
356 # (mirrors the original Param2MoEGate behaviour) and handed to the
357 # experts in the activation dtype.
358 router_logits, _ = self.gate(hidden_states)
359 router_logits = router_logits.to(hidden_states.dtype)
360
361 expert_output = self.experts(
362 hidden_states=hidden_states,
363 router_logits=router_logits,
364 )
365
366 return expert_output.view(num_tokens, hidden_dim)
367
368
369 class Param2MoEDecoderLayer(nn.Module):
370 """Single transformer decoder block.
371
372 Dense for the first ``first_k_dense_replace`` layers; MoE thereafter.
373 """
374
375 def __init__(
376 self,
377 vllm_config: VllmConfig,
378 prefix: str = "",
379 ) -> None:
380 super().__init__()
381
382 config = vllm_config.model_config.hf_config
383 cache_config = vllm_config.cache_config
384 quant_config = vllm_config.quant_config
385
386 hidden_size = config.hidden_size
387 # Derive the layer index from the prefix (e.g. "model.layers.3")
388 layer_idx = int(prefix.split(".")[-1])
389
390 self.input_layernorm = RMSNorm(hidden_size, eps=config.rms_norm_eps)
391 self.self_attn = Param2MoEAttention(
392 config=config,
393 cache_config=cache_config,
394 quant_config=quant_config,
395 prefix=f"{prefix}.self_attn",
396 )
397 self.post_attention_layernorm = RMSNorm(hidden_size, eps=config.rms_norm_eps)
398
399 first_k_dense: int = getattr(config, "first_k_dense_replace", 1)
400 is_moe_layer = config.num_experts is not None and layer_idx >= first_k_dense
401
402 if is_moe_layer:
403 self.mlp = Param2MoEMoEBlock(
404 config=config,
405 quant_config=quant_config,
406 prefix=f"{prefix}.mlp",
407 )
408 else:
409 self.mlp = Param2MoEMLP( # type: ignore[assignment]
410 intermediate_size=config.intermediate_size,
411 config=config,
412 quant_config=quant_config,
413 reduce_results=True,
414 prefix=f"{prefix}.mlp",
415 )
416
417 def forward(
418 self,
419 hidden_states: torch.Tensor,
420 positions: torch.Tensor,
421 residual: torch.Tensor | None,
422 ) -> tuple[torch.Tensor, torch.Tensor]:
423 # Pre-norm + attention
424 if residual is None:
425 residual = hidden_states
426 hidden_states = self.input_layernorm(hidden_states)
427 else:
428 hidden_states, residual = self.input_layernorm(hidden_states, residual)
429
430 hidden_states = self.self_attn(
431 positions=positions,
432 hidden_states=hidden_states,
433 )
434
435 # Pre-norm + MLP
436 hidden_states, residual = self.post_attention_layernorm(hidden_states, residual)
437 hidden_states = self.mlp(hidden_states)
438 return hidden_states, residual
439
440
441 class Param2MoEModel(nn.Module):
442 hf_to_vllm_mapper = WeightsMapper(
443 orig_to_new_substr={
444 "word_embeddings.": "embed_tokens.",
445 # Fused query_key_value is renamed to qkv_proj and loaded directly;
446 # QKVParallelLinear splits the fused [Q|K|V] tensor internally.
447 ".attention.query_key_value.": ".self_attn.qkv_proj.",
448 ".attention.dense.": ".self_attn.o_proj.",
449 ".attention.query_layernorm.": ".self_attn.q_layernorm.",
450 ".attention.key_layernorm.": ".self_attn.k_layernorm.",
451 # Catch-all for any remaining .attention. names (must come last).
452 ".attention.": ".self_attn.",
453 ".mlp.gate.expert_bias": ".mlp.gate.e_score_correction_bias",
454 },
455 orig_to_new_stacked={
456 # .experts.gate_up_proj must be handled by MoERunner.load_weights for EP
457 ".mlp.gate_proj": (".mlp.gate_up_proj", 0),
458 ".mlp.up_proj": (".mlp.gate_up_proj", 1),
459 ".shared_experts.gate_proj": (".shared_experts.gate_up_proj", 0),
460 ".shared_experts.up_proj": (".shared_experts.gate_up_proj", 1),
461 },
462 )
463
464 def __init__(
465 self,
466 *,
467 vllm_config: VllmConfig,
468 prefix: str = "",
469 ) -> None:
470 super().__init__()
471
472 config = vllm_config.model_config.hf_config
473 quant_config = vllm_config.quant_config
474
475 self.config = config
476 self.vocab_size = config.vocab_size
477 self.embed_dim = config.hidden_size
478 self.tie_word_embeddings: bool = getattr(config, "tie_word_embeddings", False)
479
480 # Embedding (HF name: word_embeddings → vLLM name: embed_tokens)
481 if get_pp_group().is_first_rank or (
482 self.tie_word_embeddings and get_pp_group().is_last_rank
483 ):
484 self.embed_tokens = VocabParallelEmbedding(
485 self.vocab_size,
486 self.embed_dim,
487 quant_config=quant_config,
488 prefix=f"{prefix}.embed_tokens",
489 )
490 else:
491 self.embed_tokens = PPMissingLayer()
492
493 self.start_layer, self.end_layer, self.layers = make_layers(
494 config.num_hidden_layers,
495 lambda prefix: Param2MoEDecoderLayer(
496 vllm_config=vllm_config,
497 prefix=prefix,
498 ),
499 prefix=f"{prefix}.layers",
500 )
501
502 self.make_empty_intermediate_tensors = make_empty_intermediate_tensors_factory(
503 ["hidden_states", "residual"], config.hidden_size
504 )
505
506 if get_pp_group().is_last_rank:
507 self.norm = RMSNorm(self.embed_dim, eps=config.rms_norm_eps)
508 else:
509 self.norm = PPMissingLayer()
510
511 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
512 return self.embed_tokens(input_ids)
513
514 def forward(
515 self,
516 input_ids: torch.Tensor,
517 positions: torch.Tensor,
518 intermediate_tensors: IntermediateTensors | None,
519 inputs_embeds: torch.Tensor | None = None,
520 ) -> torch.Tensor | IntermediateTensors:
521 if get_pp_group().is_first_rank:
522 if inputs_embeds is not None:
523 hidden_states = inputs_embeds
524 else:
525 hidden_states = self.embed_input_ids(input_ids)
526 residual = None
527 else:
528 assert intermediate_tensors is not None
529 hidden_states = intermediate_tensors["hidden_states"]
530 residual = intermediate_tensors["residual"]
531
532 for layer in islice(self.layers, self.start_layer, self.end_layer):
533 hidden_states, residual = layer(hidden_states, positions, residual)
534
535 if not get_pp_group().is_last_rank:
536 return IntermediateTensors(
537 {"hidden_states": hidden_states, "residual": residual}
538 )
539
540 if residual is None:
541 hidden_states = self.norm(hidden_states)
542 else:
543 hidden_states, _ = self.norm(hidden_states, residual)
544 return hidden_states
545
546 def load_weights(
547 self,
548 weights: Iterable[tuple[str, torch.Tensor]],
549 ) -> set[str]:
550 loader = AutoWeightsLoader(self)
551 return loader.load_weights(
552 _normalize_expert_bias(weights), mapper=self.hf_to_vllm_mapper
553 )
554
555
556 class Param2MoEMixtureOfExperts(MixtureOfExperts):
557 """Implements the vLLM MixtureOfExperts protocol for Param2MoE."""
558
559 moe_mlp_layers: list[Param2MoEMoEBlock]
560
561 def extract_moe_parameters(self, example_moe: Param2MoEMoEBlock | None) -> None:
562 if example_moe is None:
563 raise RuntimeError(
564 "No Param2MoEMoEBlock found in model.layers. "
565 "Check first_k_dense_replace and num_experts in config."
566 )
567 self.num_logical_experts = example_moe.num_experts
568 self.num_routed_experts = example_moe.num_experts
569 self.num_shared_experts = example_moe.num_shared_experts
570
571 self.num_physical_experts = self.num_logical_experts
572 self.num_local_physical_experts = self.num_logical_experts
573 self.num_redundant_experts = 0
574
575 def update_physical_experts_metadata(
576 self,
577 num_physical_experts: int,
578 num_local_physical_experts: int,
579 ) -> None:
580 self.num_physical_experts = num_physical_experts
581 self.num_local_physical_experts = num_local_physical_experts
582 self.num_redundant_experts = num_physical_experts - self.num_logical_experts
583
584 for moe in self.moe_mlp_layers:
585 moe.n_physical_experts = num_physical_experts
586 moe.n_local_physical_experts = num_local_physical_experts
587 moe.n_redundant_experts = self.num_redundant_experts
588
589 fused = moe.experts
590 if hasattr(fused, "n_local_physical_experts"):
591 fused.n_local_physical_experts = num_local_physical_experts
592 if hasattr(fused, "n_physical_experts"):
593 fused.n_physical_experts = num_physical_experts
594 if hasattr(fused, "n_redundant_experts"):
595 fused.n_redundant_experts = self.num_redundant_experts
596 if hasattr(fused, "update_expert_map"):
597 fused.update_expert_map()
598
599
600 class Param2MoEForCausalLM(
601 nn.Module, SupportsPP, SupportsLoRA, Param2MoEMixtureOfExperts
602 ):
603 """vLLM-native Param2MoE CausalLM.
604
605 Uses Grouped-Query Attention (GQA) with a Sigmoid-scored,
606 grouped-topk Mixture-of-Experts MLP.
607 """
608
609 # LoRA packed-module mapping. The fused gate_up_proj handles
610 # gate_proj and up_proj from the checkpoint.
611 packed_modules_mapping = {
612 "qkv_proj": ["query_key_value"],
613 "gate_up_proj": ["gate_proj", "up_proj"],
614 }
615
616 # Modules eligible for LoRA adaptation.
617 supported_lora_modules = [
618 "qkv_proj",
619 "o_proj",
620 "gate_up_proj",
621 "down_proj",
622 ]
623
624 # Embedding layers and their weight-tying counterparts.
625 embedding_modules = {
626 "embed_tokens": "input_embeddings",
627 "lm_head": "output_embeddings",
628 }
629
630 # Modules that need vocab-size padding for LoRA.
631 embedding_padding_modules = ["lm_head"]
632
633 def __init__(self, *, vllm_config: VllmConfig, prefix: str = "") -> None:
634 super().__init__()
635
636 config = vllm_config.model_config.hf_config
637 quant_config = vllm_config.quant_config
638
639 self.config = config
640 self.quant_config = quant_config
641
642 self.model = Param2MoEModel(
643 vllm_config=vllm_config,
644 prefix=maybe_prefix(prefix, "model"),
645 )
646
647 self.tie_word_embeddings: bool = getattr(config, "tie_word_embeddings", False)
648 if get_pp_group().is_last_rank:
649 self.lm_head = ParallelLMHead(
650 config.vocab_size,
651 config.hidden_size,
652 quant_config=quant_config,
653 prefix=maybe_prefix(prefix, "lm_head"),
654 )
655 if self.tie_word_embeddings:
656 self.lm_head = self.lm_head.tie_weights(self.model.embed_tokens)
657 self.logits_processor = LogitsProcessor(config.vocab_size)
658 else:
659 self.lm_head = PPMissingLayer()
660 self.logits_processor = None # type: ignore[assignment]
661
662 self.make_empty_intermediate_tensors = (
663 self.model.make_empty_intermediate_tensors
664 )
665
666 self.num_moe_layers: int = 0
667 self.moe_layers: list = []
668 self.moe_mlp_layers: list = []
669
670 example_moe: Param2MoEMoEBlock | None = None
671 for layer in self.model.layers:
672 if isinstance(layer, PPMissingLayer):
673 continue
674 if isinstance(layer.mlp, Param2MoEMoEBlock):
675 example_moe = layer.mlp
676 self.moe_mlp_layers.append(layer.mlp)
677 self.moe_layers.append(layer.mlp.experts)
678 self.num_moe_layers += 1
679
680 if self.config.num_experts is not None:
681 self.extract_moe_parameters(example_moe)
682
683 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
684 return self.model.embed_input_ids(input_ids)
685
686 def forward(
687 self,
688 input_ids: torch.Tensor,
689 positions: torch.Tensor,
690 intermediate_tensors: IntermediateTensors | None = None,
691 inputs_embeds: torch.Tensor | None = None,
692 ) -> torch.Tensor | IntermediateTensors:
693 return self.model(
694 input_ids=input_ids,
695 positions=positions,
696 intermediate_tensors=intermediate_tensors,
697 inputs_embeds=inputs_embeds,
698 )
699
700 def compute_logits(
701 self,
702 hidden_states: torch.Tensor,
703 ) -> torch.Tensor | None:
704 if not get_pp_group().is_last_rank:
705 return None
706 return self.logits_processor(self.lm_head, hidden_states)
707
708 def load_weights(
709 self,
710 weights: Iterable[tuple[str, torch.Tensor]],
711 ) -> set[str]:
712 loader = AutoWeightsLoader(self)
713 return loader.load_weights(weights)