From b28997d0631f14f27d40b94d0de2a26dfd48d3f5 Mon Sep 17 00:00:00 2001 From: mayumeng Date: Mon, 23 Mar 2026 11:29:35 +0800 Subject: [PATCH 01/10] add deepseek 3.2 C8 rot tensor Signed-off-by: mayumeng --- vllm_ascend/attention/sfa_v1.py | 17 +- vllm_ascend/patch/worker/__init__.py | 1 + vllm_ascend/patch/worker/patch_deepseekv2.py | 246 +++++++++++++++++++ vllm_ascend/quantization/methods/kv_c8.py | 26 ++ vllm_ascend/quantization/modelslim_config.py | 19 +- 5 files changed, 302 insertions(+), 7 deletions(-) create mode 100644 vllm_ascend/patch/worker/patch_deepseekv2.py diff --git a/vllm_ascend/attention/sfa_v1.py b/vllm_ascend/attention/sfa_v1.py index da708060b92f..7d787648385d 100644 --- a/vllm_ascend/attention/sfa_v1.py +++ b/vllm_ascend/attention/sfa_v1.py @@ -356,8 +356,9 @@ class AscendSFAImpl(MLAAttentionImpl): # Supports forward using the all-gather o_proj weight for decode requests when Sharded CP is enabled. o_proj_full_pool: torch.Tensor | None = None - # qk_hadamard tensor shared when dsa c8 enabled - qk_hadamard: torch.Tensor | None = None + # q_hadamard and k_hadamard tensor shared when dsa c8 enabled + q_hadamard: torch.Tensor | None = None + k_hadamard: torch.Tensor | None = None def __init__( self, @@ -525,8 +526,12 @@ def process_weights_after_loading(self, act_dtype: torch.dtype): # if mlapo, W_UK_T can't trans nz self.W_UK_T = maybe_trans_nz(self.W_UK_T) - if self.use_sparse_c8_indexer and AscendSFAImpl.qk_hadamard is None: - AscendSFAImpl.qk_hadamard = torch.tensor(scipy.linalg.hadamard(128), dtype=torch.bfloat16, device="npu") / ( + if self.use_sparse_c8_indexer and AscendSFAImpl.q_hadamard is None: + AscendSFAImpl.q_hadamard = torch.tensor(scipy.linalg.hadamard(128), dtype=torch.bfloat16, device="npu") / ( + 128**0.5 + ) + if self.use_sparse_c8_indexer and AscendSFAImpl.k_hadamard is None: + AscendSFAImpl.k_hadamard = torch.tensor(scipy.linalg.hadamard(128), dtype=torch.bfloat16, device="npu") / ( 128**0.5 ) @@ -890,7 +895,7 @@ def indexer_select_pre_process( k_li = torch.cat([k_li_pe, k_li_nope], dim=-1) # [b*s,128] if self.use_sparse_c8_indexer: - k_li = k_li @ AscendSFAImpl.qk_hadamard + k_li = k_li @ AscendSFAImpl.k_hadamard k_li, k_li_scale = torch_npu.npu_dynamic_quant(k_li.view(-1, self.head_dim), dst_type=self.c8_k_cache_dtype) k_li_scale = k_li_scale.to(self.c8_k_scale_cache_dtype) # [b*s,] k_li_scale = k_li_scale.unsqueeze(-1) # [b*s,1] @@ -930,7 +935,7 @@ def indexer_select_post_process( if self.use_sparse_c8_indexer: q_li_shape_ori = q_li.shape - q_li = q_li @ AscendSFAImpl.qk_hadamard + q_li = q_li @ AscendSFAImpl.q_hadamard q_li, q_li_scale = torch_npu.npu_dynamic_quant(q_li.view(-1, self.head_dim), dst_type=self.c8_k_cache_dtype) q_li_scale = q_li_scale.to(self.c8_k_scale_cache_dtype) diff --git a/vllm_ascend/patch/worker/__init__.py b/vllm_ascend/patch/worker/__init__.py index 48c7b4e69933..1646a398fae5 100644 --- a/vllm_ascend/patch/worker/__init__.py +++ b/vllm_ascend/patch/worker/__init__.py @@ -45,3 +45,4 @@ import vllm_ascend.patch.worker.patch_draft_quarot # noqa import vllm_ascend.patch.worker.patch_cudagraph # noqa import vllm_ascend.patch.worker.patch_deepseek_mtp # noqa +import vllm_ascend.patch.worker.patch_deepseekv2 # noqa diff --git a/vllm_ascend/patch/worker/patch_deepseekv2.py b/vllm_ascend/patch/worker/patch_deepseekv2.py new file mode 100644 index 000000000000..11c5a0ff5652 --- /dev/null +++ b/vllm_ascend/patch/worker/patch_deepseekv2.py @@ -0,0 +1,246 @@ +import typing +from typing import Optional +from collections.abc import Callable, Iterable + +import torch + +from vllm.model_executor.layers.fused_moe.shared_fused_moe import \ + SharedFusedMoE +from vllm.model_executor.model_loader.weight_utils import ( + default_weight_loader, maybe_remap_kv_scale_name) +from vllm.model_executor.models.deepseek_v2 import ( + DeepseekV2ForCausalLM, get_spec_layer_idx_from_weight_name) +from vllm.model_executor.models.utils import (is_pp_missing_parameter) +from vllm._aiter_ops import rocm_aiter_ops + +NUM_HIDDEN_LAYERS = 4 + +def maybe_remap_indexer_rot_name(name: str, params_dict: dict) -> Optional[str]: + replace_scale_names = [ + "indexer.q_rot", "indexer.k_rot" + ] + for scale_name in replace_scale_names: + if name.endswith(scale_name): + remap_name = name.replace(scale_name, f"mla_attn.mla_attn.{scale_name}") + if remap_name in params_dict: + return remap_name + else: + return remap_name.replace("mla_attn", "attn") + return name + +class CustomDeepseekV2ForCausalLM(DeepseekV2ForCausalLM): + + def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]: + rocm_aiter_moe_shared_expert_enabled = ( + rocm_aiter_ops.is_fusion_moe_shared_experts_enabled() + ) + stacked_params_mapping = [ + # (param_name, shard_name, shard_id) + ("gate_up_proj", "gate_proj", 0), + ("gate_up_proj", "up_proj", 1), + ] + mla_params_mapping = [ + ("fused_qkv_a_proj", "q_a_proj", 0), + ("fused_qkv_a_proj", "kv_a_proj_with_mqa", 1), + ] + mha_params_mapping = [ + ("qkv_proj", "q_proj", "q"), + ("qkv_proj", "k_proj", "k"), + ("qkv_proj", "v_proj", "v"), + ] + if self.use_mha: + stacked_params_mapping.extend(mha_params_mapping) + else: + stacked_params_mapping.extend(mla_params_mapping) + + # Params for weights, fp8 weight scales, fp8 activation scales + # (param_name, weight_name, expert_id, shard_id) + expert_params_mapping = SharedFusedMoE.make_expert_params_mapping( + self, + ckpt_gate_proj_name="gate_proj", + ckpt_down_proj_name="down_proj", + ckpt_up_proj_name="up_proj", + num_experts=self.config.n_routed_experts + + ( + self.config.n_shared_experts + if rocm_aiter_moe_shared_expert_enabled + else 0 + ), + num_redundant_experts=self.num_redundant_experts, + ) + + params_dict = dict(self.named_parameters()) + loaded_params: set[str] = set() + for name, loaded_weight in weights: + if "rotary_emb.inv_freq" in name: + continue + import re + layer_idx = re.findall(r"\d+", name) + if len(layer_idx) > 0 and int(layer_idx[0]) >= NUM_HIDDEN_LAYERS: + continue + + spec_layer = get_spec_layer_idx_from_weight_name(self.config, name) + if spec_layer is not None: + continue # skip spec decode layers for main model + + is_fusion_moe_shared_experts_layer = ( + rocm_aiter_moe_shared_expert_enabled and ("mlp.shared_experts" in name) + ) + + for param_name, weight_name, shard_id in stacked_params_mapping: + # Skip non-stacked layers and experts (experts handled below). + if weight_name not in name: + continue + # We have mlp.experts[0].gate_proj in the checkpoint. + # Since we handle the experts below in expert_params_mapping, + # we need to skip here BEFORE we update the name, otherwise + # name will be updated to mlp.experts[0].gate_up_proj, which + # will then be updated below in expert_params_mapping + # for mlp.experts[0].gate_gate_up_proj, which breaks load. + if ("mlp.experts." in name) and name not in params_dict: + continue + if is_fusion_moe_shared_experts_layer: + continue + name_mapped = name.replace(weight_name, param_name) + + # QKV fusion is optional, fall back to normal + # weight loading if it's not enabled + # if go with fusion option, then update name + if ( + param_name == "fused_qkv_a_proj" + ) and name_mapped not in params_dict: + continue + else: + name = name_mapped + # Skip loading extra bias for GPTQ models. + if name.endswith(".bias") and name not in params_dict: + continue + + if is_pp_missing_parameter(name, self): + continue + + param = params_dict[name] + weight_loader = param.weight_loader + weight_loader(param, loaded_weight, shard_id) + break + else: + is_expert_weight = False + + # Special handling: when AITER fusion_shared_experts is enabled, + # checkpoints may provide a single widened shared_experts tensor + # without explicit expert indices + # (e.g. ...mlp.shared_experts.gate_proj.weight). + # For models with multiple shared experts, split that tensor + # evenly into per-shared-expert slices and load them into + # appended expert slots mlp.experts.{n_routed_experts + j}.* + # accordingly. + num_chunks = 1 + if is_fusion_moe_shared_experts_layer: + num_chunks = getattr(self.config, "n_shared_experts", 1) or 1 + # Determine split axis based on op type + # gate/up: ColumnParallel → split along dim 0 + # down: RowParallel → split along dim 1 + split_dim = ( + 1 + if ("down_proj.weight" in name and loaded_weight.ndim > 1) + else 0 + ) + total = loaded_weight.shape[split_dim] + assert total % num_chunks == 0, ( + f"Shared expert weight dim {total} " + f"not divisible by num_chunks {num_chunks}" + ) + chunk_size = total // num_chunks + + for j in range(num_chunks): + chunk_name = name + weight_to_load = loaded_weight + + if is_fusion_moe_shared_experts_layer: + chunk_slice = slice(j * chunk_size, (j + 1) * chunk_size) + if loaded_weight.ndim == 1: + weight_to_load = loaded_weight[chunk_slice] + elif split_dim == 0: + weight_to_load = loaded_weight[chunk_slice, :] + else: + weight_to_load = loaded_weight[:, chunk_slice] + # Synthesize an expert-style name so expert mapping + # can route it + chunk_name = name.replace( + "mlp.shared_experts", + f"mlp.experts.{self.config.n_routed_experts + j}", + ) + + # Use expert_params_mapping to locate the destination + # param and delegate to its expert-aware weight_loader + # with expert_id. + for mapping in expert_params_mapping: + param_name, weight_name, expert_id, shard_id = mapping + if weight_name not in chunk_name: + continue + + # Anyway, this is an expert weight and should not be + # attempted to load as other weights later + is_expert_weight = True + + # Do not modify `name` since the loop may continue here + # Instead, create a new variable + name_mapped = chunk_name.replace(weight_name, param_name) + + if is_pp_missing_parameter(name_mapped, self): + continue + + param = params_dict[name_mapped] + # We should ask the weight loader to return success or + # not here since otherwise we may skip experts with + # other available replicas. + weight_loader = typing.cast( + Callable[..., bool], param.weight_loader + ) + success = weight_loader( + param, + weight_to_load, + name_mapped, + shard_id=shard_id, + expert_id=expert_id, + return_success=True, + ) + if success: + if not is_fusion_moe_shared_experts_layer: + name = name_mapped + else: + loaded_params.add(name_mapped) + break + else: + if is_expert_weight: + # We've checked that this is an expert weight + # However it's not mapped locally to this rank + # So we simply skip it + continue + + # Skip loading extra bias for GPTQ models. + if name.endswith(".bias") and name not in params_dict: + continue + + # Remapping the name of FP8 kv-scale. + name = maybe_remap_kv_scale_name(name, params_dict) + name = maybe_remap_indexer_rot_name(name, params_dict) + if name is None: + continue + + if is_pp_missing_parameter(name, self): + continue + + param = params_dict[name] + weight_loader = getattr( + param, "weight_loader", default_weight_loader + ) + weight_loader(param, loaded_weight) + if name is not None and not is_fusion_moe_shared_experts_layer: + loaded_params.add(name) + + return loaded_params + + +DeepseekV2ForCausalLM.load_weights = CustomDeepseekV2ForCausalLM.load_weights + diff --git a/vllm_ascend/quantization/methods/kv_c8.py b/vllm_ascend/quantization/methods/kv_c8.py index 8a7004844056..6b6b9a2dae8b 100644 --- a/vllm_ascend/quantization/methods/kv_c8.py +++ b/vllm_ascend/quantization/methods/kv_c8.py @@ -63,3 +63,29 @@ def process_weights_after_loading(self, layer: torch.nn.Module) -> None: repeated_quant_kscale = fa_k_scale.repeat(self.kv_lora_rank) layer.quant_kscale = repeated_quant_kscale.view(1, self.kv_lora_rank) layer.quant_kscale = 1.0 / torch.nn.Parameter(layer.quant_kscale.to(torch.float), requires_grad=False) + + +@register_scheme("INT8_DYNAMIC", "attention") +class AscendSFAQuantAttentionMethod: + def __init__(self): + vllm_config = get_current_vllm_config() + config = vllm_config.model_config.hf_config + self.index_head_dim = config.index_head_dim + + def create_weights(self, layer: torch.nn.Module) -> None: + extra_module_names = ["indexer"] + for name in extra_module_names: + setattr(layer, name, torch.nn.Module()) + params_dict = {} + params_dict["indexer.q_rot"] = torch.empty((self.index_head_dim, self.index_head_dim), + dtype=torch.float32) + params_dict["indexer.k_rot"] = torch.empty((self.index_head_dim, self.index_head_dim), + dtype=torch.float32) + for name, weight in params_dict.items(): + module_name, weight_name = name.split('.') + module = getattr(layer, module_name) + weight_param = torch.nn.Parameter(weight, requires_grad=False) + module.register_parameter(weight_name, weight_param) + + def process_weights_after_loading(self, layer: torch.nn.Module) -> None: + pass \ No newline at end of file diff --git a/vllm_ascend/quantization/modelslim_config.py b/vllm_ascend/quantization/modelslim_config.py index 151109c53c16..6174ab58fffc 100644 --- a/vllm_ascend/quantization/modelslim_config.py +++ b/vllm_ascend/quantization/modelslim_config.py @@ -379,6 +379,8 @@ def get_quant_type_for_layer( # Attention if layer_type == "attention" and "fa_quant_type" in quant_description: return quant_description["fa_quant_type"] + if layer_type == "attention" and "indexer_quant_type" in quant_description: + return quant_description["indexer_quant_type"] # Linear / MoE return get_linear_quant_type(quant_description, prefix, packed_modules_mapping) @@ -582,7 +584,7 @@ def get_quant_method(self, layer: torch.nn.Module, prefix: str) -> Optional["Qua return AscendUnquantizedLinearMethod() scheme = create_scheme_for_layer(self.quant_description, prefix, "linear", self.packed_modules_mapping) return AscendLinearMethod(scheme) - elif isinstance(layer, AttentionLayerBase) and self.is_fa_quant_layer(prefix): + elif isinstance(layer, AttentionLayerBase) and (self.is_fa_quant_layer(prefix) or self.is_indexer_quant_layer(prefix)): scheme = create_scheme_for_layer(self.quant_description, prefix, "attention", self.packed_modules_mapping) return AscendKVCacheMethod(scheme) elif isinstance(layer, FusedMoE): @@ -636,6 +638,13 @@ def is_fa_quant_layer(self, prefix): return True return False + def is_indexer_quant_layer(self, prefix): + if self.enable_indexer_quant: + layer_id_str = "".join(re.findall(r"\.(\d+)\.", prefix)) + if layer_id_str.isdigit() and int(layer_id_str) in self.indexer_quant_layers: + return True + return False + def enabling_fa_quant(self, vllm_config, layer_name) -> bool: is_decode_instance = ( vllm_config.kv_transfer_config is not None @@ -778,3 +787,11 @@ def _add_kvcache_quant_metadata(self): if "fa_k.scale" in key: _id = "".join(re.findall(r"\.(\d+)\.", key)) self.kvcache_quant_layers.append(int(_id)) + indexer_quant_type = self.quant_description.get("indexer_quant_type", "") + self.enable_indexer_quant = indexer_quant_type != "" + self.indexer_quant_layers = [] + if self.enable_indexer_quant: + for key in self.quant_description: + if "indexer.quant_type" in key: + _id = "".join(re.findall(r"\.(\d+)\.", key)) + self.indexer_quant_layers.append(int(_id)) From 9dd812e9c24bd5450ffd3e416cb93eb54a153342 Mon Sep 17 00:00:00 2001 From: mayumeng Date: Mon, 23 Mar 2026 11:34:16 +0800 Subject: [PATCH 02/10] clena code Signed-off-by: mayumeng --- vllm_ascend/patch/worker/patch_deepseekv2.py | 7 ------- 1 file changed, 7 deletions(-) diff --git a/vllm_ascend/patch/worker/patch_deepseekv2.py b/vllm_ascend/patch/worker/patch_deepseekv2.py index 11c5a0ff5652..e94d8a75b094 100644 --- a/vllm_ascend/patch/worker/patch_deepseekv2.py +++ b/vllm_ascend/patch/worker/patch_deepseekv2.py @@ -13,8 +13,6 @@ from vllm.model_executor.models.utils import (is_pp_missing_parameter) from vllm._aiter_ops import rocm_aiter_ops -NUM_HIDDEN_LAYERS = 4 - def maybe_remap_indexer_rot_name(name: str, params_dict: dict) -> Optional[str]: replace_scale_names = [ "indexer.q_rot", "indexer.k_rot" @@ -74,11 +72,6 @@ def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]: for name, loaded_weight in weights: if "rotary_emb.inv_freq" in name: continue - import re - layer_idx = re.findall(r"\d+", name) - if len(layer_idx) > 0 and int(layer_idx[0]) >= NUM_HIDDEN_LAYERS: - continue - spec_layer = get_spec_layer_idx_from_weight_name(self.config, name) if spec_layer is not None: continue # skip spec decode layers for main model From 6225f5e2ddaa63dec69c897f61944a2a28e07f0a Mon Sep 17 00:00:00 2001 From: mayumeng Date: Mon, 23 Mar 2026 12:04:35 +0800 Subject: [PATCH 03/10] clean code Signed-off-by: mayumeng --- vllm_ascend/patch/worker/patch_deepseekv2.py | 55 ++++++-------------- vllm_ascend/quantization/methods/kv_c8.py | 8 ++- vllm_ascend/quantization/modelslim_config.py | 4 +- 3 files changed, 21 insertions(+), 46 deletions(-) diff --git a/vllm_ascend/patch/worker/patch_deepseekv2.py b/vllm_ascend/patch/worker/patch_deepseekv2.py index e94d8a75b094..80da33afd017 100644 --- a/vllm_ascend/patch/worker/patch_deepseekv2.py +++ b/vllm_ascend/patch/worker/patch_deepseekv2.py @@ -1,22 +1,15 @@ import typing -from typing import Optional from collections.abc import Callable, Iterable - import torch -from vllm.model_executor.layers.fused_moe.shared_fused_moe import \ - SharedFusedMoE -from vllm.model_executor.model_loader.weight_utils import ( - default_weight_loader, maybe_remap_kv_scale_name) -from vllm.model_executor.models.deepseek_v2 import ( - DeepseekV2ForCausalLM, get_spec_layer_idx_from_weight_name) -from vllm.model_executor.models.utils import (is_pp_missing_parameter) from vllm._aiter_ops import rocm_aiter_ops +from vllm.model_executor.layers.fused_moe.shared_fused_moe import SharedFusedMoE +from vllm.model_executor.model_loader.weight_utils import default_weight_loader, maybe_remap_kv_scale_name +from vllm.model_executor.models.deepseek_v2 import DeepseekV2ForCausalLM, get_spec_layer_idx_from_weight_name +from vllm.model_executor.models.utils import is_pp_missing_parameter -def maybe_remap_indexer_rot_name(name: str, params_dict: dict) -> Optional[str]: - replace_scale_names = [ - "indexer.q_rot", "indexer.k_rot" - ] +def maybe_remap_indexer_rot_name(name: str, params_dict: dict) -> str | None: + replace_scale_names = ["indexer.q_rot", "indexer.k_rot"] for scale_name in replace_scale_names: if name.endswith(scale_name): remap_name = name.replace(scale_name, f"mla_attn.mla_attn.{scale_name}") @@ -25,13 +18,11 @@ def maybe_remap_indexer_rot_name(name: str, params_dict: dict) -> Optional[str]: else: return remap_name.replace("mla_attn", "attn") return name - -class CustomDeepseekV2ForCausalLM(DeepseekV2ForCausalLM): + +class CustomDeepseekV2ForCausalLM(DeepseekV2ForCausalLM): def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]: - rocm_aiter_moe_shared_expert_enabled = ( - rocm_aiter_ops.is_fusion_moe_shared_experts_enabled() - ) + rocm_aiter_moe_shared_expert_enabled = rocm_aiter_ops.is_fusion_moe_shared_experts_enabled() stacked_params_mapping = [ # (param_name, shard_name, shard_id) ("gate_up_proj", "gate_proj", 0), @@ -59,11 +50,7 @@ def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]: ckpt_down_proj_name="down_proj", ckpt_up_proj_name="up_proj", num_experts=self.config.n_routed_experts - + ( - self.config.n_shared_experts - if rocm_aiter_moe_shared_expert_enabled - else 0 - ), + + (self.config.n_shared_experts if rocm_aiter_moe_shared_expert_enabled else 0), num_redundant_experts=self.num_redundant_experts, ) @@ -99,9 +86,7 @@ def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]: # QKV fusion is optional, fall back to normal # weight loading if it's not enabled # if go with fusion option, then update name - if ( - param_name == "fused_qkv_a_proj" - ) and name_mapped not in params_dict: + if (param_name == "fused_qkv_a_proj") and name_mapped not in params_dict: continue else: name = name_mapped @@ -133,15 +118,10 @@ def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]: # Determine split axis based on op type # gate/up: ColumnParallel → split along dim 0 # down: RowParallel → split along dim 1 - split_dim = ( - 1 - if ("down_proj.weight" in name and loaded_weight.ndim > 1) - else 0 - ) + split_dim = 1 if ("down_proj.weight" in name and loaded_weight.ndim > 1) else 0 total = loaded_weight.shape[split_dim] assert total % num_chunks == 0, ( - f"Shared expert weight dim {total} " - f"not divisible by num_chunks {num_chunks}" + f"Shared expert weight dim {total} not divisible by num_chunks {num_chunks}" ) chunk_size = total // num_chunks @@ -187,9 +167,7 @@ def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]: # We should ask the weight loader to return success or # not here since otherwise we may skip experts with # other available replicas. - weight_loader = typing.cast( - Callable[..., bool], param.weight_loader - ) + weight_loader = typing.cast(Callable[..., bool], param.weight_loader) success = weight_loader( param, weight_to_load, @@ -225,15 +203,12 @@ def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]: continue param = params_dict[name] - weight_loader = getattr( - param, "weight_loader", default_weight_loader - ) + weight_loader = getattr(param, "weight_loader", default_weight_loader) weight_loader(param, loaded_weight) if name is not None and not is_fusion_moe_shared_experts_layer: loaded_params.add(name) return loaded_params - DeepseekV2ForCausalLM.load_weights = CustomDeepseekV2ForCausalLM.load_weights diff --git a/vllm_ascend/quantization/methods/kv_c8.py b/vllm_ascend/quantization/methods/kv_c8.py index 6b6b9a2dae8b..8794b7191ded 100644 --- a/vllm_ascend/quantization/methods/kv_c8.py +++ b/vllm_ascend/quantization/methods/kv_c8.py @@ -77,12 +77,10 @@ def create_weights(self, layer: torch.nn.Module) -> None: for name in extra_module_names: setattr(layer, name, torch.nn.Module()) params_dict = {} - params_dict["indexer.q_rot"] = torch.empty((self.index_head_dim, self.index_head_dim), - dtype=torch.float32) - params_dict["indexer.k_rot"] = torch.empty((self.index_head_dim, self.index_head_dim), - dtype=torch.float32) + params_dict["indexer.q_rot"] = torch.empty((self.index_head_dim, self.index_head_dim), dtype=torch.float32) + params_dict["indexer.k_rot"] = torch.empty((self.index_head_dim, self.index_head_dim), dtype=torch.float32) for name, weight in params_dict.items(): - module_name, weight_name = name.split('.') + module_name, weight_name = name.split(".") module = getattr(layer, module_name) weight_param = torch.nn.Parameter(weight, requires_grad=False) module.register_parameter(weight_name, weight_param) diff --git a/vllm_ascend/quantization/modelslim_config.py b/vllm_ascend/quantization/modelslim_config.py index 6174ab58fffc..ee55f06516ae 100644 --- a/vllm_ascend/quantization/modelslim_config.py +++ b/vllm_ascend/quantization/modelslim_config.py @@ -584,7 +584,9 @@ def get_quant_method(self, layer: torch.nn.Module, prefix: str) -> Optional["Qua return AscendUnquantizedLinearMethod() scheme = create_scheme_for_layer(self.quant_description, prefix, "linear", self.packed_modules_mapping) return AscendLinearMethod(scheme) - elif isinstance(layer, AttentionLayerBase) and (self.is_fa_quant_layer(prefix) or self.is_indexer_quant_layer(prefix)): + elif isinstance(layer, AttentionLayerBase) and ( + self.is_fa_quant_layer(prefix) or self.is_indexer_quant_layer(prefix) + ): scheme = create_scheme_for_layer(self.quant_description, prefix, "attention", self.packed_modules_mapping) return AscendKVCacheMethod(scheme) elif isinstance(layer, FusedMoE): From a3a711e48c3d5770f2f481354c028d1816f4e22e Mon Sep 17 00:00:00 2001 From: mayumeng Date: Mon, 23 Mar 2026 12:10:09 +0800 Subject: [PATCH 04/10] clena code Signed-off-by: mayumeng --- vllm_ascend/patch/worker/patch_deepseekv2.py | 7 ++----- vllm_ascend/quantization/methods/kv_c8.py | 2 +- vllm_ascend/quantization/modelslim_config.py | 2 +- 3 files changed, 4 insertions(+), 7 deletions(-) diff --git a/vllm_ascend/patch/worker/patch_deepseekv2.py b/vllm_ascend/patch/worker/patch_deepseekv2.py index 80da33afd017..7075f38d9620 100644 --- a/vllm_ascend/patch/worker/patch_deepseekv2.py +++ b/vllm_ascend/patch/worker/patch_deepseekv2.py @@ -1,7 +1,7 @@ import typing from collections.abc import Callable, Iterable -import torch +import torch from vllm._aiter_ops import rocm_aiter_ops from vllm.model_executor.layers.fused_moe.shared_fused_moe import SharedFusedMoE from vllm.model_executor.model_loader.weight_utils import default_weight_loader, maybe_remap_kv_scale_name @@ -63,9 +63,7 @@ def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]: if spec_layer is not None: continue # skip spec decode layers for main model - is_fusion_moe_shared_experts_layer = ( - rocm_aiter_moe_shared_expert_enabled and ("mlp.shared_experts" in name) - ) + is_fusion_moe_shared_experts_layer = rocm_aiter_moe_shared_expert_enabled and ("mlp.shared_experts" in name) for param_name, weight_name, shard_id in stacked_params_mapping: # Skip non-stacked layers and experts (experts handled below). @@ -211,4 +209,3 @@ def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]: return loaded_params DeepseekV2ForCausalLM.load_weights = CustomDeepseekV2ForCausalLM.load_weights - diff --git a/vllm_ascend/quantization/methods/kv_c8.py b/vllm_ascend/quantization/methods/kv_c8.py index 8794b7191ded..100567410b16 100644 --- a/vllm_ascend/quantization/methods/kv_c8.py +++ b/vllm_ascend/quantization/methods/kv_c8.py @@ -86,4 +86,4 @@ def create_weights(self, layer: torch.nn.Module) -> None: module.register_parameter(weight_name, weight_param) def process_weights_after_loading(self, layer: torch.nn.Module) -> None: - pass \ No newline at end of file + pass diff --git a/vllm_ascend/quantization/modelslim_config.py b/vllm_ascend/quantization/modelslim_config.py index ee55f06516ae..866a137006f5 100644 --- a/vllm_ascend/quantization/modelslim_config.py +++ b/vllm_ascend/quantization/modelslim_config.py @@ -645,7 +645,7 @@ def is_indexer_quant_layer(self, prefix): layer_id_str = "".join(re.findall(r"\.(\d+)\.", prefix)) if layer_id_str.isdigit() and int(layer_id_str) in self.indexer_quant_layers: return True - return False + return False def enabling_fa_quant(self, vllm_config, layer_name) -> bool: is_decode_instance = ( From 4b404163efe2a8d17fbc04b0684ff59aa9ba240f Mon Sep 17 00:00:00 2001 From: mayumeng Date: Mon, 23 Mar 2026 13:21:04 +0800 Subject: [PATCH 05/10] clean code Signed-off-by: mayumeng --- vllm_ascend/patch/worker/patch_deepseekv2.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vllm_ascend/patch/worker/patch_deepseekv2.py b/vllm_ascend/patch/worker/patch_deepseekv2.py index 7075f38d9620..3617d90c66fd 100644 --- a/vllm_ascend/patch/worker/patch_deepseekv2.py +++ b/vllm_ascend/patch/worker/patch_deepseekv2.py @@ -8,6 +8,7 @@ from vllm.model_executor.models.deepseek_v2 import DeepseekV2ForCausalLM, get_spec_layer_idx_from_weight_name from vllm.model_executor.models.utils import is_pp_missing_parameter + def maybe_remap_indexer_rot_name(name: str, params_dict: dict) -> str | None: replace_scale_names = ["indexer.q_rot", "indexer.k_rot"] for scale_name in replace_scale_names: @@ -208,4 +209,5 @@ def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]: return loaded_params + DeepseekV2ForCausalLM.load_weights = CustomDeepseekV2ForCausalLM.load_weights From 9d97025d9bd9e2044b70fc54bd73e0bb7225c464 Mon Sep 17 00:00:00 2001 From: mayumeng Date: Mon, 23 Mar 2026 13:27:47 +0800 Subject: [PATCH 06/10] clean code Signed-off-by: mayumeng --- vllm_ascend/patch/worker/patch_deepseekv2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vllm_ascend/patch/worker/patch_deepseekv2.py b/vllm_ascend/patch/worker/patch_deepseekv2.py index 3617d90c66fd..f8ba0beb6cc9 100644 --- a/vllm_ascend/patch/worker/patch_deepseekv2.py +++ b/vllm_ascend/patch/worker/patch_deepseekv2.py @@ -9,7 +9,7 @@ from vllm.model_executor.models.utils import is_pp_missing_parameter -def maybe_remap_indexer_rot_name(name: str, params_dict: dict) -> str | None: +def maybe_remap_indexer_rot_name(name: str, params_dict: dict) -> str: replace_scale_names = ["indexer.q_rot", "indexer.k_rot"] for scale_name in replace_scale_names: if name.endswith(scale_name): From 5fec99f53c510744585bb22658c2d9382b8e4b52 Mon Sep 17 00:00:00 2001 From: mayumeng Date: Mon, 23 Mar 2026 13:36:34 +0800 Subject: [PATCH 07/10] clean code Signed-off-by: mayumeng --- vllm_ascend/patch/worker/patch_deepseekv2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vllm_ascend/patch/worker/patch_deepseekv2.py b/vllm_ascend/patch/worker/patch_deepseekv2.py index f8ba0beb6cc9..5101d1288395 100644 --- a/vllm_ascend/patch/worker/patch_deepseekv2.py +++ b/vllm_ascend/patch/worker/patch_deepseekv2.py @@ -39,7 +39,7 @@ def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]: ("qkv_proj", "v_proj", "v"), ] if self.use_mha: - stacked_params_mapping.extend(mha_params_mapping) + stacked_params_mapping.extend(mha_params_mapping) # type: ignore[arg-type] else: stacked_params_mapping.extend(mla_params_mapping) From 360e8ae3b56e9c368915045488df81b768ec005e Mon Sep 17 00:00:00 2001 From: mayumeng Date: Mon, 23 Mar 2026 13:39:37 +0800 Subject: [PATCH 08/10] clean code Signed-off-by: mayumeng --- vllm_ascend/patch/worker/patch_deepseekv2.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vllm_ascend/patch/worker/patch_deepseekv2.py b/vllm_ascend/patch/worker/patch_deepseekv2.py index 5101d1288395..2dcec79fe2e6 100644 --- a/vllm_ascend/patch/worker/patch_deepseekv2.py +++ b/vllm_ascend/patch/worker/patch_deepseekv2.py @@ -39,7 +39,7 @@ def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]: ("qkv_proj", "v_proj", "v"), ] if self.use_mha: - stacked_params_mapping.extend(mha_params_mapping) # type: ignore[arg-type] + stacked_params_mapping.extend(mha_params_mapping) # type: ignore[arg-type] else: stacked_params_mapping.extend(mla_params_mapping) From a403cf3b291f015e240bcfba6854deee933c1bfd Mon Sep 17 00:00:00 2001 From: mayumeng Date: Mon, 23 Mar 2026 17:52:30 +0800 Subject: [PATCH 09/10] fix suggest Signed-off-by: mayumeng --- vllm_ascend/patch/worker/__init__.py | 1 - vllm_ascend/patch/worker/patch_deepseekv2.py | 213 ------------------ .../patch/worker/patch_weight_utils.py | 2 +- vllm_ascend/quantization/modelslim_config.py | 11 +- 4 files changed, 5 insertions(+), 222 deletions(-) delete mode 100644 vllm_ascend/patch/worker/patch_deepseekv2.py diff --git a/vllm_ascend/patch/worker/__init__.py b/vllm_ascend/patch/worker/__init__.py index 1646a398fae5..48c7b4e69933 100644 --- a/vllm_ascend/patch/worker/__init__.py +++ b/vllm_ascend/patch/worker/__init__.py @@ -45,4 +45,3 @@ import vllm_ascend.patch.worker.patch_draft_quarot # noqa import vllm_ascend.patch.worker.patch_cudagraph # noqa import vllm_ascend.patch.worker.patch_deepseek_mtp # noqa -import vllm_ascend.patch.worker.patch_deepseekv2 # noqa diff --git a/vllm_ascend/patch/worker/patch_deepseekv2.py b/vllm_ascend/patch/worker/patch_deepseekv2.py deleted file mode 100644 index 2dcec79fe2e6..000000000000 --- a/vllm_ascend/patch/worker/patch_deepseekv2.py +++ /dev/null @@ -1,213 +0,0 @@ -import typing -from collections.abc import Callable, Iterable - -import torch -from vllm._aiter_ops import rocm_aiter_ops -from vllm.model_executor.layers.fused_moe.shared_fused_moe import SharedFusedMoE -from vllm.model_executor.model_loader.weight_utils import default_weight_loader, maybe_remap_kv_scale_name -from vllm.model_executor.models.deepseek_v2 import DeepseekV2ForCausalLM, get_spec_layer_idx_from_weight_name -from vllm.model_executor.models.utils import is_pp_missing_parameter - - -def maybe_remap_indexer_rot_name(name: str, params_dict: dict) -> str: - replace_scale_names = ["indexer.q_rot", "indexer.k_rot"] - for scale_name in replace_scale_names: - if name.endswith(scale_name): - remap_name = name.replace(scale_name, f"mla_attn.mla_attn.{scale_name}") - if remap_name in params_dict: - return remap_name - else: - return remap_name.replace("mla_attn", "attn") - return name - - -class CustomDeepseekV2ForCausalLM(DeepseekV2ForCausalLM): - def load_weights(self, weights: Iterable[tuple[str, torch.Tensor]]) -> set[str]: - rocm_aiter_moe_shared_expert_enabled = rocm_aiter_ops.is_fusion_moe_shared_experts_enabled() - stacked_params_mapping = [ - # (param_name, shard_name, shard_id) - ("gate_up_proj", "gate_proj", 0), - ("gate_up_proj", "up_proj", 1), - ] - mla_params_mapping = [ - ("fused_qkv_a_proj", "q_a_proj", 0), - ("fused_qkv_a_proj", "kv_a_proj_with_mqa", 1), - ] - mha_params_mapping = [ - ("qkv_proj", "q_proj", "q"), - ("qkv_proj", "k_proj", "k"), - ("qkv_proj", "v_proj", "v"), - ] - if self.use_mha: - stacked_params_mapping.extend(mha_params_mapping) # type: ignore[arg-type] - else: - stacked_params_mapping.extend(mla_params_mapping) - - # Params for weights, fp8 weight scales, fp8 activation scales - # (param_name, weight_name, expert_id, shard_id) - expert_params_mapping = SharedFusedMoE.make_expert_params_mapping( - self, - ckpt_gate_proj_name="gate_proj", - ckpt_down_proj_name="down_proj", - ckpt_up_proj_name="up_proj", - num_experts=self.config.n_routed_experts - + (self.config.n_shared_experts if rocm_aiter_moe_shared_expert_enabled else 0), - num_redundant_experts=self.num_redundant_experts, - ) - - params_dict = dict(self.named_parameters()) - loaded_params: set[str] = set() - for name, loaded_weight in weights: - if "rotary_emb.inv_freq" in name: - continue - spec_layer = get_spec_layer_idx_from_weight_name(self.config, name) - if spec_layer is not None: - continue # skip spec decode layers for main model - - is_fusion_moe_shared_experts_layer = rocm_aiter_moe_shared_expert_enabled and ("mlp.shared_experts" in name) - - for param_name, weight_name, shard_id in stacked_params_mapping: - # Skip non-stacked layers and experts (experts handled below). - if weight_name not in name: - continue - # We have mlp.experts[0].gate_proj in the checkpoint. - # Since we handle the experts below in expert_params_mapping, - # we need to skip here BEFORE we update the name, otherwise - # name will be updated to mlp.experts[0].gate_up_proj, which - # will then be updated below in expert_params_mapping - # for mlp.experts[0].gate_gate_up_proj, which breaks load. - if ("mlp.experts." in name) and name not in params_dict: - continue - if is_fusion_moe_shared_experts_layer: - continue - name_mapped = name.replace(weight_name, param_name) - - # QKV fusion is optional, fall back to normal - # weight loading if it's not enabled - # if go with fusion option, then update name - if (param_name == "fused_qkv_a_proj") and name_mapped not in params_dict: - continue - else: - name = name_mapped - # Skip loading extra bias for GPTQ models. - if name.endswith(".bias") and name not in params_dict: - continue - - if is_pp_missing_parameter(name, self): - continue - - param = params_dict[name] - weight_loader = param.weight_loader - weight_loader(param, loaded_weight, shard_id) - break - else: - is_expert_weight = False - - # Special handling: when AITER fusion_shared_experts is enabled, - # checkpoints may provide a single widened shared_experts tensor - # without explicit expert indices - # (e.g. ...mlp.shared_experts.gate_proj.weight). - # For models with multiple shared experts, split that tensor - # evenly into per-shared-expert slices and load them into - # appended expert slots mlp.experts.{n_routed_experts + j}.* - # accordingly. - num_chunks = 1 - if is_fusion_moe_shared_experts_layer: - num_chunks = getattr(self.config, "n_shared_experts", 1) or 1 - # Determine split axis based on op type - # gate/up: ColumnParallel → split along dim 0 - # down: RowParallel → split along dim 1 - split_dim = 1 if ("down_proj.weight" in name and loaded_weight.ndim > 1) else 0 - total = loaded_weight.shape[split_dim] - assert total % num_chunks == 0, ( - f"Shared expert weight dim {total} not divisible by num_chunks {num_chunks}" - ) - chunk_size = total // num_chunks - - for j in range(num_chunks): - chunk_name = name - weight_to_load = loaded_weight - - if is_fusion_moe_shared_experts_layer: - chunk_slice = slice(j * chunk_size, (j + 1) * chunk_size) - if loaded_weight.ndim == 1: - weight_to_load = loaded_weight[chunk_slice] - elif split_dim == 0: - weight_to_load = loaded_weight[chunk_slice, :] - else: - weight_to_load = loaded_weight[:, chunk_slice] - # Synthesize an expert-style name so expert mapping - # can route it - chunk_name = name.replace( - "mlp.shared_experts", - f"mlp.experts.{self.config.n_routed_experts + j}", - ) - - # Use expert_params_mapping to locate the destination - # param and delegate to its expert-aware weight_loader - # with expert_id. - for mapping in expert_params_mapping: - param_name, weight_name, expert_id, shard_id = mapping - if weight_name not in chunk_name: - continue - - # Anyway, this is an expert weight and should not be - # attempted to load as other weights later - is_expert_weight = True - - # Do not modify `name` since the loop may continue here - # Instead, create a new variable - name_mapped = chunk_name.replace(weight_name, param_name) - - if is_pp_missing_parameter(name_mapped, self): - continue - - param = params_dict[name_mapped] - # We should ask the weight loader to return success or - # not here since otherwise we may skip experts with - # other available replicas. - weight_loader = typing.cast(Callable[..., bool], param.weight_loader) - success = weight_loader( - param, - weight_to_load, - name_mapped, - shard_id=shard_id, - expert_id=expert_id, - return_success=True, - ) - if success: - if not is_fusion_moe_shared_experts_layer: - name = name_mapped - else: - loaded_params.add(name_mapped) - break - else: - if is_expert_weight: - # We've checked that this is an expert weight - # However it's not mapped locally to this rank - # So we simply skip it - continue - - # Skip loading extra bias for GPTQ models. - if name.endswith(".bias") and name not in params_dict: - continue - - # Remapping the name of FP8 kv-scale. - name = maybe_remap_kv_scale_name(name, params_dict) - name = maybe_remap_indexer_rot_name(name, params_dict) - if name is None: - continue - - if is_pp_missing_parameter(name, self): - continue - - param = params_dict[name] - weight_loader = getattr(param, "weight_loader", default_weight_loader) - weight_loader(param, loaded_weight) - if name is not None and not is_fusion_moe_shared_experts_layer: - loaded_params.add(name) - - return loaded_params - - -DeepseekV2ForCausalLM.load_weights = CustomDeepseekV2ForCausalLM.load_weights diff --git a/vllm_ascend/patch/worker/patch_weight_utils.py b/vllm_ascend/patch/worker/patch_weight_utils.py index 809b168bc23d..4b97e44fa402 100644 --- a/vllm_ascend/patch/worker/patch_weight_utils.py +++ b/vllm_ascend/patch/worker/patch_weight_utils.py @@ -39,7 +39,7 @@ def patch_deepseek(module): def new_remap(name: str, params_dict: dict): name = ori_maybe_remap_kv_scale_name(name, params_dict) - replace_scale_names = ["fa_q.scale", "fa_k.scale", "fa_v.scale", "fa_q.offset", "fa_k.offset", "fa_v.offset"] + replace_scale_names = ["fa_q.scale", "fa_k.scale", "fa_v.scale", "fa_q.offset", "fa_k.offset", "fa_v.offset", "indexer.q_rot", "indexer.k_rot"] for scale_name in replace_scale_names: if name.endswith(scale_name): diff --git a/vllm_ascend/quantization/modelslim_config.py b/vllm_ascend/quantization/modelslim_config.py index 866a137006f5..40162689c307 100644 --- a/vllm_ascend/quantization/modelslim_config.py +++ b/vllm_ascend/quantization/modelslim_config.py @@ -784,16 +784,13 @@ def _add_kvcache_quant_metadata(self): fa_quant_type = self.quant_description.get("fa_quant_type", "") self.enable_fa_quant = fa_quant_type != "" self.kvcache_quant_layers = [] - if self.enable_fa_quant: - for key in self.quant_description: - if "fa_k.scale" in key: - _id = "".join(re.findall(r"\.(\d+)\.", key)) - self.kvcache_quant_layers.append(int(_id)) indexer_quant_type = self.quant_description.get("indexer_quant_type", "") self.enable_indexer_quant = indexer_quant_type != "" self.indexer_quant_layers = [] - if self.enable_indexer_quant: + if self.enable_fa_quant or self.enable_indexer_quant: for key in self.quant_description: + _id = "".join(re.findall(r"\.(\d+)\.", key)) + if "fa_k.scale" in key: + self.kvcache_quant_layers.append(int(_id)) if "indexer.quant_type" in key: - _id = "".join(re.findall(r"\.(\d+)\.", key)) self.indexer_quant_layers.append(int(_id)) From 56684808d5301ad83743595b1178bbcded193bb0 Mon Sep 17 00:00:00 2001 From: mayumeng Date: Mon, 23 Mar 2026 17:58:30 +0800 Subject: [PATCH 10/10] clean code Signed-off-by: mayumeng --- vllm_ascend/patch/worker/patch_weight_utils.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/vllm_ascend/patch/worker/patch_weight_utils.py b/vllm_ascend/patch/worker/patch_weight_utils.py index 4b97e44fa402..685709055789 100644 --- a/vllm_ascend/patch/worker/patch_weight_utils.py +++ b/vllm_ascend/patch/worker/patch_weight_utils.py @@ -39,7 +39,16 @@ def patch_deepseek(module): def new_remap(name: str, params_dict: dict): name = ori_maybe_remap_kv_scale_name(name, params_dict) - replace_scale_names = ["fa_q.scale", "fa_k.scale", "fa_v.scale", "fa_q.offset", "fa_k.offset", "fa_v.offset", "indexer.q_rot", "indexer.k_rot"] + replace_scale_names = [ + "fa_q.scale", + "fa_k.scale", + "fa_v.scale", + "fa_q.offset", + "fa_k.offset", + "fa_v.offset", + "indexer.q_rot", + "indexer.k_rot", + ] for scale_name in replace_scale_names: if name.endswith(scale_name):