main
py 716 lines 29.8 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 # Adapted from
5 # Copyright 2023 The vLLM team.
6 # Copyright 2022 EleutherAI and the HuggingFace Inc. team. All rights reserved.
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 HY V4 model compatible with HuggingFace weights (NVIDIA)."""
25
26 import typing
27 from collections.abc import Callable, Iterable, MutableSequence, Sequence
28 from itertools import islice
29
30 import regex as re
31 import torch
32 from torch import nn
33 from transformers import PreTrainedConfig
34
35 from vllm.config import CacheConfig, VllmConfig
36 from vllm.distributed import (
37 get_pp_group,
38 get_tensor_model_parallel_rank,
39 get_tensor_model_parallel_world_size,
40 )
41 from vllm.logger import init_logger
42 from vllm.model_executor.layers.fused_moe import fused_moe_make_expert_params_mapping
43 from vllm.model_executor.layers.layernorm import RMSNorm
44 from vllm.model_executor.layers.logits_processor import LogitsProcessor
45 from vllm.model_executor.layers.quantization import QuantizationConfig
46 from vllm.model_executor.layers.vocab_parallel_embedding import (
47 ParallelLMHead,
48 VocabParallelEmbedding,
49 )
50 from vllm.model_executor.model_loader.weight_utils import default_weight_loader
51 from vllm.model_executor.models.deepseek_v2 import _try_load_fp8_indexer_wk
52 from vllm.model_executor.models.interfaces import SupportsLoRA, SupportsPP
53 from vllm.model_executor.models.utils import (
54 AutoWeightsLoader,
55 PPMissingLayer,
56 WeightsMapper,
57 get_pp_missing_layer_names,
58 is_pp_missing_parameter,
59 make_empty_intermediate_tensors_factory,
60 make_layers,
61 maybe_prefix,
62 )
63 from vllm.platforms import current_platform
64 from vllm.sequence import IntermediateTensors
65
66 from .attention import (
67 HYV4MLAAttention,
68 compute_skip_topk_layers,
69 is_skip_topk_indexer_weight,
70 )
71 from .hc import HYV4HCHeadLayer, HYV4HCLayer
72 from .moe import HYV4FeedForward, HYV4MoEFused
73
74 logger = init_logger(__name__)
75
76
77 def _normalize_hyv4_config(config: PreTrainedConfig) -> PreTrainedConfig:
78 """Populate the aliases consumed by the shared MoE implementation."""
79 config.router_scaling_factor = config.routed_scaling_factor
80 config.num_experts = config.n_routed_experts
81 config.expert_hidden_dim = config.moe_intermediate_size
82 config.num_shared_experts = config.n_shared_experts
83 config.route_norm = config.norm_topk_prob
84 return config
85
86
87 class HYV4DecoderLayer(nn.Module):
88 """One HY V4 decoder layer: MLA attention plus a dense or MoE MLP.
89
90 When``config.enable_ihc`` is set the layer runs on ``hc_mult`` residual
91 channels and each sub-block is wrapped by an `HYV4HCLayer` boundary;
92 otherwise it uses the standard single-stream residual.
93 """
94
95 def __init__(
96 self,
97 config: PreTrainedConfig,
98 vllm_config: VllmConfig,
99 cache_config: CacheConfig | None = None,
100 quant_config: QuantizationConfig | None = None,
101 prefix: str = "",
102 topk_indices_buffer: torch.Tensor | None = None,
103 ) -> None:
104 super().__init__()
105 self.hidden_size = config.hidden_size
106 self.config = config
107 self.enable_ihc = getattr(config, "enable_ihc", False)
108 layer_idx = int(prefix.split(".")[-1])
109 self.layer_idx = layer_idx
110
111 max_position_embeddings = getattr(config, "max_position_embeddings", 8192)
112 self.self_attn = HYV4MLAAttention(
113 vllm_config=vllm_config,
114 config=config,
115 hidden_size=self.hidden_size,
116 num_heads=config.num_attention_heads,
117 qk_nope_head_dim=config.qk_nope_head_dim,
118 qk_rope_head_dim=config.qk_rope_head_dim,
119 v_head_dim=config.v_head_dim,
120 q_lora_rank=config.q_lora_rank,
121 kv_lora_rank=config.kv_lora_rank,
122 max_position_embeddings=max_position_embeddings,
123 cache_config=cache_config,
124 quant_config=quant_config,
125 prefix=f"{prefix}.self_attn",
126 layer_idx=layer_idx,
127 topk_indices_buffer=topk_indices_buffer,
128 )
129 self.input_layernorm = RMSNorm(config.hidden_size, config.rms_norm_eps)
130 if config.mlp_layer_types[layer_idx] == "dense":
131 self.mlp = HYV4FeedForward(
132 hidden_size=config.hidden_size,
133 intermediate_size=config.intermediate_size,
134 hidden_act=config.hidden_act,
135 quant_config=quant_config,
136 prefix=f"{prefix}.mlp",
137 )
138 self.block_type = "feedforward"
139 else:
140 self.mlp = HYV4MoEFused(
141 config=config, quant_config=quant_config, prefix=f"{prefix}.mlp"
142 )
143 self.block_type = "moe"
144 self.post_attention_layernorm = RMSNorm(config.hidden_size, config.rms_norm_eps)
145 self.hc_attn_layer = HYV4HCLayer(
146 config, layer_idx, prefix=f"{prefix}.hc_attn_layer"
147 )
148 self.hc_mlp_layer = HYV4HCLayer(
149 config, layer_idx, prefix=f"{prefix}.hc_mlp_layer"
150 )
151
152 def forward(
153 self,
154 positions: torch.Tensor,
155 hidden_states: torch.Tensor,
156 residual: torch.Tensor | None,
157 ) -> tuple[torch.Tensor, torch.Tensor | None]:
158 if self.enable_ihc:
159 return self._forward_ihc(positions, hidden_states)
160 return self._forward_normal(positions, hidden_states, residual)
161
162 def _forward_normal(
163 self,
164 positions: torch.Tensor,
165 hidden_states: torch.Tensor,
166 residual: torch.Tensor | None,
167 ) -> tuple[torch.Tensor, torch.Tensor]:
168 """Standard single-residual-stream forward (iHC disabled)."""
169 if residual is not None:
170 hidden_states = hidden_states + residual
171 residual = hidden_states
172 hidden_states = self.input_layernorm(hidden_states)
173
174 hidden_states = self.self_attn(
175 positions=positions,
176 hidden_states=hidden_states,
177 )
178
179 hidden_states = hidden_states + residual
180 residual = hidden_states
181 hidden_states = self.post_attention_layernorm(hidden_states)
182
183 hidden_states = self.mlp(hidden_states)
184
185 return hidden_states, residual
186
187 def _forward_ihc(
188 self,
189 positions: torch.Tensor,
190 hidden_states: torch.Tensor,
191 ) -> tuple[torch.Tensor, None]:
192 """IHC forward: each sub-block reduces and re-scatters the channels."""
193 hidden_states = self.hc_attn_layer.prepare_input(hidden_states)
194 hidden_states, post_gates, residual = self.hc_attn_layer.pre(hidden_states)
195 hidden_states = self.input_layernorm(hidden_states)
196 hidden_states = self.self_attn(
197 positions=positions,
198 hidden_states=hidden_states,
199 )
200 hidden_states = self.hc_attn_layer.post(hidden_states, residual, post_gates)
201
202 hidden_states = self.hc_mlp_layer.prepare_input(hidden_states)
203 hidden_states, post_gates, residual = self.hc_mlp_layer.pre(hidden_states)
204 hidden_states = self.post_attention_layernorm(hidden_states)
205 hidden_states = self.mlp(hidden_states)
206 hidden_states = self.hc_mlp_layer.post(hidden_states, residual, post_gates)
207
208 # Under iHC the residual is carried inside hidden_states.
209 return hidden_states, None
210
211
212 class HYV4Model(nn.Module):
213 """HY V4 backbone."""
214
215 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
216 super().__init__()
217
218 config = _normalize_hyv4_config(vllm_config.model_config.hf_config)
219 cache_config = vllm_config.cache_config
220 quant_config = vllm_config.quant_config
221 parallel_config = vllm_config.parallel_config
222 eplb_config = parallel_config.eplb_config
223 self.num_redundant_experts = eplb_config.num_redundant_experts
224 self.device = current_platform.device_type
225 self.vocab_size = config.vocab_size
226 self.is_sparse = hasattr(config, "index_topk")
227 if self.is_sparse:
228 self.topk_indices_buffer = torch.empty(
229 vllm_config.scheduler_config.max_num_batched_tokens,
230 config.index_topk,
231 dtype=torch.int32,
232 device=self.device,
233 )
234 else:
235 self.topk_indices_buffer = None
236 self.config = config
237 self.quant_config = quant_config
238 self.enable_ihc = getattr(config, "enable_ihc", False)
239
240 if get_pp_group().is_first_rank or (
241 self.config.tie_word_embeddings and get_pp_group().is_last_rank
242 ):
243 self.embed_tokens = VocabParallelEmbedding(
244 config.vocab_size,
245 config.hidden_size,
246 )
247 else:
248 self.embed_tokens = PPMissingLayer()
249 self.start_layer, self.end_layer, self.layers = make_layers(
250 config.num_hidden_layers,
251 lambda prefix: HYV4DecoderLayer(
252 config=config,
253 vllm_config=vllm_config,
254 cache_config=cache_config,
255 quant_config=quant_config,
256 prefix=prefix,
257 topk_indices_buffer=self.topk_indices_buffer,
258 ),
259 prefix=f"{prefix}.layers",
260 )
261 if get_pp_group().is_last_rank:
262 self.norm = RMSNorm(config.hidden_size, config.rms_norm_eps)
263 else:
264 self.norm = PPMissingLayer()
265 if self.enable_ihc:
266 # iHC head layer: merge the residual channels into one stream.
267 if get_pp_group().is_last_rank:
268 self.hc_head = HYV4HCHeadLayer(
269 config,
270 hidden_size=config.hidden_size,
271 hc_mult=config.hc_mult,
272 hc_eps=config.hc_eps,
273 prefix=f"{prefix}.hc_head",
274 )
275 else:
276 self.hc_head = PPMissingLayer()
277 self.make_empty_intermediate_tensors = (
278 make_empty_intermediate_tensors_factory(
279 ["hidden_states"], config.hc_mult * config.hidden_size
280 )
281 )
282 else:
283 self.make_empty_intermediate_tensors = (
284 make_empty_intermediate_tensors_factory(
285 ["hidden_states", "residual"], config.hidden_size
286 )
287 )
288
289 # MoE hyperparameters (consumed by EPLB).
290 self.expert_weights: MutableSequence[Sequence[torch.Tensor]] = []
291 self.num_expert_groups = 1
292 self.moe_layers: list[nn.Module] = []
293 example_layer: HYV4MoEFused | None = None
294 for layer in self.layers:
295 if isinstance(layer, PPMissingLayer):
296 continue
297
298 assert isinstance(layer, HYV4DecoderLayer)
299 if layer.block_type == "moe":
300 assert isinstance(layer.mlp, HYV4MoEFused)
301 example_layer = layer.mlp
302 self.moe_layers.append(layer.mlp.experts)
303
304 if example_layer is None:
305 self.num_moe_layers = 0
306 self.num_logical_experts = 0
307 self.num_physical_experts = 0
308 self.num_local_physical_experts = 0
309 self.num_routed_experts = 0
310 self.num_redundant_experts = 0
311 return
312
313 self.num_moe_layers = len(self.moe_layers)
314 self.num_logical_experts = example_layer.n_logical_experts
315 self.num_physical_experts = example_layer.n_physical_experts
316 self.num_local_physical_experts = example_layer.n_local_physical_experts
317 self.num_routed_experts = example_layer.n_routed_experts
318 self.num_redundant_experts = example_layer.n_redundant_experts
319
320 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
321 return self.embed_tokens(input_ids)
322
323 def update_physical_experts_metadata(
324 self,
325 num_physical_experts: int,
326 num_local_physical_experts: int,
327 ) -> None:
328 assert self.num_local_physical_experts == num_local_physical_experts
329 self.num_physical_experts = num_physical_experts
330 self.num_local_physical_experts = num_local_physical_experts
331 self.num_redundant_experts = num_physical_experts - self.num_logical_experts
332 for layer in self.layers:
333 if isinstance(layer, PPMissingLayer):
334 continue
335 if isinstance(layer.mlp, HYV4MoEFused):
336 moe = layer.mlp
337 moe.n_local_physical_experts = num_local_physical_experts
338 moe.n_physical_experts = num_physical_experts
339 moe.n_redundant_experts = self.num_redundant_experts
340 moe.experts.update_expert_map()
341
342 def get_expert_mapping(self) -> list[tuple[str, str, int, str]]:
343 # (param_name, weight_name, expert_id, shard_id) for weights, fp8
344 # weight scales and fp8 activation scales.
345 if not hasattr(self, "_cached_expert_params_mapping"):
346 self._cached_expert_params_mapping = fused_moe_make_expert_params_mapping(
347 self,
348 ckpt_gate_proj_name="gate_proj",
349 ckpt_down_proj_name="down_proj",
350 ckpt_up_proj_name="up_proj",
351 num_experts=self.config.num_experts,
352 num_redundant_experts=self.num_redundant_experts,
353 )
354 return self._cached_expert_params_mapping
355
356 def forward(
357 self,
358 input_ids: torch.Tensor,
359 positions: torch.Tensor,
360 intermediate_tensors: IntermediateTensors | None = None,
361 inputs_embeds: torch.Tensor | None = None,
362 ) -> torch.Tensor | IntermediateTensors:
363 if get_pp_group().is_first_rank:
364 if inputs_embeds is not None:
365 hidden_states = inputs_embeds
366 else:
367 hidden_states = self.embed_input_ids(input_ids)
368 residual = None
369 else:
370 assert intermediate_tensors is not None
371 hidden_states = intermediate_tensors["hidden_states"]
372 # In iHC mode the flattened [num_tokens, hc*h] tensor from the
373 # previous PP stage is reshaped back to 3D by the first layer's
374 # prepare_input, and residual is unused.
375 residual = None if self.enable_ihc else intermediate_tensors["residual"]
376
377 for layer in islice(self.layers, self.start_layer, self.end_layer):
378 hidden_states, residual = layer(positions, hidden_states, residual)
379
380 if not get_pp_group().is_last_rank:
381 if self.enable_ihc:
382 # hidden_states is [num_tokens, hc, h]; flatten the channel dim
383 # for PP transfer (matches the 2D receive buffer).
384 return IntermediateTensors({"hidden_states": hidden_states.flatten(1)})
385 return IntermediateTensors(
386 {"hidden_states": hidden_states, "residual": residual}
387 )
388 if self.enable_ihc:
389 hidden_states = self.hc_head(hidden_states)
390 else:
391 hidden_states = hidden_states + residual
392
393 return self.norm(hidden_states)
394
395 def load_fused_expert_weights(
396 self,
397 name: str,
398 params_dict: dict,
399 loaded_weight: torch.Tensor,
400 shard_id: str,
401 num_experts: int,
402 ) -> bool:
403 param = params_dict[name]
404 weight_loader = typing.cast(Callable[..., bool], param.weight_loader)
405 loaded_local_expert = False
406 for expert_id in range(num_experts):
407 curr_expert_weight = loaded_weight[expert_id]
408 success = weight_loader(
409 param,
410 curr_expert_weight,
411 name,
412 shard_id,
413 expert_id,
414 return_success=True,
415 )
416 if success:
417 loaded_local_expert = True
418
419 return loaded_local_expert
420
421 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
422 # FP8 indexer wk dequant buffer (weight and scale arrive separately).
423 pending_wk_fp8: dict[str, dict[str, torch.Tensor]] = {}
424 pp_missing_layer_names = get_pp_missing_layer_names(self)
425 skip_topk_layers = compute_skip_topk_layers(self.config)
426
427 # Must not be cached on `self`: `process_weights_after_loading` swaps in
428 # kernel-specific expert weights via `replace_parameter`, and a cache
429 # outliving this call would pin the pre-shuffle storage (OOM on large
430 # MoE) and make a later reload target orphaned tensors.
431 params_dict = dict(self.named_parameters())
432 # Split per-expert mapping (V3 style): experts.0.gate_proj.weight
433 split_expert_params_mapping = self.get_expert_mapping()
434 loaded_params: set[str] = set()
435
436 # Sink weights are sharded like the q/k/v linears.
437 sink_tp_size = get_tensor_model_parallel_world_size()
438 sink_tp_rank = get_tensor_model_parallel_rank()
439 n_local_head = self.config.num_attention_heads // sink_tp_size
440 head_rank_start = n_local_head * sink_tp_rank
441 head_rank_end = n_local_head * (sink_tp_rank + 1)
442 base_layer = (
443 "base_layer." if any(".base_layer." in name for name in params_dict) else ""
444 )
445 # Fused expert mapping: experts.gate_up_proj (all experts in one tensor).
446 # The packed weights are owned by the RoutedExperts submodule, so the
447 # targets are experts.routed_experts.[base_layer.]w{13,2}_weight.
448 fused_expert_prefix = f".experts.routed_experts.{base_layer}"
449 fused_expert_params_mapping = [
450 (f"{fused_expert_prefix}w13_weight", ".experts.gate_up_proj", 0, "w1"),
451 (f"{fused_expert_prefix}w2_weight", ".experts.down_proj", 0, "w2"),
452 ]
453 num_experts = getattr(self.config, "num_experts", 0)
454
455 def _should_skip_missing_param(param_name: str) -> bool:
456 # Sparse checkpoints may contain indexer weights for layers that
457 # fall back to dense MLA on unsupported GPUs.
458 return ".indexer." in param_name
459
460 def _is_split_expert_weight(weight_name: str) -> bool:
461 """Whether this weight is in split (per-expert) format."""
462 # Split format: mlp.experts.<id>.gate_proj.weight
463 return bool(re.search(r"\.experts\.\d+\.", weight_name))
464
465 def _is_fused_expert_weight(weight_name: str) -> bool:
466 """Whether this weight is in fused (all-experts-packed) format."""
467 return ".experts.gate_up_proj" in weight_name or (
468 ".experts.down_proj" in weight_name
469 and not _is_split_expert_weight(weight_name)
470 )
471
472 for name, loaded_weight in weights:
473 if is_skip_topk_indexer_weight(name, skip_topk_layers):
474 continue
475 if _try_load_fp8_indexer_wk(
476 name,
477 loaded_weight,
478 pending_wk_fp8,
479 params_dict,
480 loaded_params,
481 pp_missing_layer_names,
482 ):
483 continue
484 if self.config.tie_word_embeddings and "lm_head.weight" in name:
485 continue
486 # Names and stacked shard ids are normalized upstream by
487 # AutoWeightsLoader, through quant_config.get_cache_scale_mapper()
488 # and hf_to_vllm_mapper.
489 stacked_shard_id = getattr(loaded_weight, "shard_id", None)
490 if ".indexer.wk." in name:
491 name = name.replace(".indexer.wk.", ".indexer.wk_weights_proj.")
492 stacked_shard_id = 0
493 # Skip loading extra bias for GPTQ models.
494 if name.endswith(".bias") and name not in params_dict:
495 continue
496
497 # Determine per-weight whether this is fused or split format.
498 is_fused_expert = _is_fused_expert_weight(name)
499 expert_params_mapping = (
500 fused_expert_params_mapping
501 if is_fused_expert
502 else split_expert_params_mapping
503 )
504
505 is_expert_weight = False
506 loaded_expert_param_names: set[str] = set()
507 for mapping in expert_params_mapping:
508 param_name, weight_name, expert_id, shard_id = mapping
509 if weight_name not in name:
510 continue
511
512 # This is an expert weight and must not be attempted as any
513 # other kind of weight later on.
514 is_expert_weight = True
515
516 # Do not modify `name`: the loop may continue past this point.
517 name_mapped = name.replace(weight_name, param_name)
518 if is_pp_missing_parameter(name_mapped, self):
519 continue
520 if is_fused_expert:
521 if "experts.gate_up_proj" in name:
522 chunks = loaded_weight.chunk(2, dim=-2)
523 success_w1 = self.load_fused_expert_weights(
524 name_mapped, params_dict, chunks[0], "w1", num_experts
525 )
526 success_w3 = self.load_fused_expert_weights(
527 name_mapped, params_dict, chunks[1], "w3", num_experts
528 )
529 success = success_w1 and success_w3
530 else:
531 success = self.load_fused_expert_weights(
532 name_mapped,
533 params_dict,
534 loaded_weight,
535 shard_id,
536 num_experts,
537 )
538 if success:
539 name = name_mapped
540 break
541 else:
542 # Split per-expert format (V3 style).
543 if name_mapped not in params_dict:
544 if _should_skip_missing_param(name_mapped):
545 continue
546 logger.warning_once(
547 "Skipping unknown checkpoint weight: %s",
548 name_mapped,
549 )
550 continue
551 param = params_dict[name_mapped]
552 # Ask the weight loader whether it succeeded, otherwise we
553 # may skip experts that have other available replicas.
554 weight_loader: Callable[..., typing.Any] = param.weight_loader
555 success = weight_loader(
556 param,
557 loaded_weight,
558 name_mapped,
559 shard_id=shard_id,
560 expert_id=expert_id,
561 return_success=True,
562 )
563 if success:
564 if not is_fused_expert:
565 loaded_expert_param_names.add(name_mapped)
566 continue
567 name = name_mapped
568 break
569 else:
570 if loaded_expert_param_names:
571 loaded_params.update(loaded_expert_param_names)
572 continue
573 if "learnable_sink_param" in name:
574 if is_pp_missing_parameter(name, self):
575 continue
576 narrow_weight = loaded_weight[head_rank_start:head_rank_end]
577 n = narrow_weight.shape[0]
578 with torch.no_grad():
579 params_dict[name][:n].copy_(narrow_weight)
580 else:
581 if is_expert_weight:
582 # An expert weight that is not mapped to this rank.
583 continue
584 if "gate.e_score_correction_bias" in name:
585 name = name.replace(
586 "gate.e_score_correction_bias", "expert_bias"
587 )
588 if "router.gate." in name:
589 name = name.replace("router.", "")
590 if "hc_fn" in name:
591 name = name.replace("hc_fn", "hc_fn.weight")
592 if "hc_head_fn" in name:
593 name = name.replace("hc_head_fn", "hc_head_fn.weight")
594
595 if is_pp_missing_parameter(name, self):
596 continue
597
598 if name not in params_dict:
599 if _should_skip_missing_param(name):
600 continue
601 logger.warning_once(
602 "Skipping unknown checkpoint weight: %s",
603 name,
604 )
605 continue
606
607 param = params_dict[name]
608 weight_loader = getattr(
609 param, "weight_loader", default_weight_loader
610 )
611 if stacked_shard_id is None:
612 weight_loader(param, loaded_weight)
613 else:
614 weight_loader(param, loaded_weight, stacked_shard_id)
615 loaded_params.add(name)
616
617 return loaded_params
618
619
620 class HYV4ForCausalLM(nn.Module, SupportsPP, SupportsLoRA):
621 hf_to_vllm_mapper = WeightsMapper(
622 orig_to_new_stacked={
623 ".q_a_proj": (".fused_qkv_a_proj", 0),
624 ".kv_a_proj_with_mqa": (".fused_qkv_a_proj", 1),
625 ".mlp.gate_proj": (".mlp.gate_up_proj", 0),
626 ".mlp.up_proj": (".mlp.gate_up_proj", 1),
627 ".shared_experts.gate_proj": (".shared_experts.gate_up_proj", 0),
628 ".shared_experts.up_proj": (".shared_experts.gate_up_proj", 1),
629 ".indexer.weights_proj.": (".indexer.wk_weights_proj.", 1),
630 }
631 )
632 packed_modules_mapping = {
633 "gate_up_proj": ["gate_proj", "up_proj"],
634 # MLA runs both latent down-projections as one GEMM.
635 "fused_qkv_a_proj": ["q_a_proj", "kv_a_proj_with_mqa"],
636 # The indexer fuses wk and weights_proj into one GEMM.
637 "wk_weights_proj": ["wk", "weights_proj"],
638 }
639
640 def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
641 super().__init__()
642 config = vllm_config.model_config.hf_config
643 quant_config = vllm_config.quant_config
644 self.config = config
645 self.quant_config = quant_config
646 if quant_config is not None:
647 quant_config.packed_modules_mapping = self.packed_modules_mapping
648
649 parallel_config = vllm_config.parallel_config
650 eplb_config = parallel_config.eplb_config
651 self.num_redundant_experts = eplb_config.num_redundant_experts
652
653 self.model = HYV4Model(
654 vllm_config=vllm_config, prefix=maybe_prefix(prefix, "model")
655 )
656 if get_pp_group().is_last_rank:
657 # The head stays in the model dtype; ``enable_lm_head_fp32`` is
658 # surfaced as ``head_dtype`` on the config (see HYV4Config), which
659 # makes LogitsProcessor accumulate the projection straight into
660 # fp32 instead of materializing an fp32 copy of the weight.
661 self.lm_head = ParallelLMHead(
662 config.vocab_size,
663 config.hidden_size,
664 quant_config=quant_config,
665 prefix=maybe_prefix(prefix, "lm_head"),
666 )
667 # With tie_word_embeddings, embed_tokens is kept on the last rank
668 # (see HYV4Model.__init__) so the weight can be shared here.
669 if self.config.tie_word_embeddings:
670 self.lm_head.weight = self.model.embed_tokens.weight
671 else:
672 self.lm_head = PPMissingLayer()
673 self.logits_processor = LogitsProcessor(config.vocab_size)
674 self.make_empty_intermediate_tensors = (
675 self.model.make_empty_intermediate_tensors
676 )
677
678 def embed_input_ids(self, input_ids: torch.Tensor) -> torch.Tensor:
679 return self.model.embed_input_ids(input_ids)
680
681 def forward(
682 self,
683 input_ids: torch.Tensor,
684 positions: torch.Tensor,
685 intermediate_tensors: IntermediateTensors | None = None,
686 inputs_embeds: torch.Tensor | None = None,
687 ) -> torch.Tensor | IntermediateTensors:
688 return self.model(input_ids, positions, intermediate_tensors, inputs_embeds)
689
690 def compute_logits(
691 self,
692 hidden_states: torch.Tensor,
693 ) -> torch.Tensor | None:
694 logits = self.logits_processor(self.lm_head, hidden_states)
695
696 if getattr(self.config, "soft_logits_capping", False):
697 soft_cap = self.config.soft_logits_capping_logits
698 logits = soft_cap * torch.nn.functional.tanh(logits / soft_cap)
699
700 return logits
701
702 def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]:
703 # MTP layers are loaded by the MTP head, not by the target model. They
704 # appear as model.mtp_layers.<i>.* and as the layer ids past the backbone.
705 mtp_start = self.config.num_hidden_layers
706 drop_prefixes: dict[str, str | None] = {"model.mtp_layers.": None}
707 for i in range(getattr(self.config, "num_nextn_predict_layers", 0)):
708 drop_prefixes[f"model.layers.{mtp_start + i}."] = None
709 if self.config.tie_word_embeddings:
710 drop_prefixes["lm_head."] = None
711 drop_mapper = WeightsMapper(orig_to_new_prefix=drop_prefixes)
712 loader = AutoWeightsLoader(self)
713 return loader.load_weights(weights, mapper=self.hf_to_vllm_mapper | drop_mapper)
714
715 def get_expert_mapping(self) -> list[tuple[str, str, int, str]]:
716 return self.model.get_expert_mapping()