[XPU][LoRA] Support LoRA for DeepSeek V4 on XPU (#53689)
Signed-off-by: Chaojun Zhang <chaojun.zhang@intel.com>
Chaojun Zhang committed
Sep 7, 2026 at 17:44 UTC
86484465153f08f4523d8be964fd6309146eab09
1 file changed
+31
-1
vllm/models/deepseek_v4/xpu/model.py
+31
-1
@@ -46,6 +46,7 @@ from vllm.model_executor.model_loader.weight_utils import default_weight_loader
46
from vllm.model_executor.models.interfaces import (
47
EagleModelMixin,
48
SupportsEagle3,
49
+ SupportsLoRA,
50
SupportsPP,
51
)
52
from vllm.model_executor.models.utils import (
@@ -1170,6 +1171,11 @@ class DeepseekV4Model(nn.Module, EagleModelMixin):
1171
1172
if is_pp_missing_parameter(name, self):
1173
break
1174
+ if name not in params_dict:
1175
+ head, _, leaf = name.rpartition(".")
1176
+ suffixed = f"{head}.base_layer.{leaf}"
1177
+ if suffixed in params_dict:
1178
+ name = suffixed
1179
param = params_dict[name]
1180
weight_loader = param.weight_loader
1181
weight_loader(param, loaded_weight, shard_id)
@@ -1224,6 +1230,13 @@ class DeepseekV4Model(nn.Module, EagleModelMixin):
1230
else:
1231
if is_pp_missing_parameter(name, self):
1232
continue
1233
+ # Non-LoRA params on a LoRA-wrapped module live at
1234
+ # ``<head>.base_layer.<leaf>``; the checkpoint is plain.
1235
+ if name not in params_dict:
1236
+ head, _, leaf = name.rpartition(".")
1237
+ suffixed = f"{head}.base_layer.{leaf}"
1238
+ if suffixed in params_dict:
1239
+ name = suffixed
1240
param = params_dict[name]
1241
weight_loader = getattr(
1242
param, "weight_loader", default_weight_loader
@@ -1260,6 +1273,10 @@ def _make_deepseek_v4_weights_mapper(expert_dtype: str) -> WeightsMapper:
1273
# shared experts use Fp8LinearMethod's block scales, which
1274
# register as ``weight_scale_inv``.
1275
scale_regex = {
1276
+ # ``.base_layer.``-namespace variant (LoRA-wrapped experts).
1277
+ re.compile(
1278
+ r"(\.experts\.\d+\.w[123]\.base_layer)\.scale$"
1279
+ ): r"\1.weight_scale",
1280
re.compile(r"(\.experts\.\d+\.w[123])\.scale$"): r"\1.weight_scale",
1281
re.compile(r"\.scale$"): ".weight_scale_inv",
1282
}
@@ -1268,6 +1285,10 @@ def _make_deepseek_v4_weights_mapper(expert_dtype: str) -> WeightsMapper:
1285
# scales as ``w{13,2}_weight_scale_inv``. Map all ``.scale`` keys
1286
# there.
1287
scale_regex = {
1288
+ # ``.base_layer.``-namespace variant of the above.
1289
+ re.compile(
1290
+ r"(\.experts\.\d+\.w[123]\.base_layer)\.scale$"
1291
+ ): r"\1.weight_scale_inv",
1292
re.compile(r"\.scale$"): ".weight_scale_inv",
1293
}
1294
return WeightsMapper(
@@ -1291,13 +1312,22 @@ def _make_deepseek_v4_weights_mapper(expert_dtype: str) -> WeightsMapper:
1312
)
1313
1314
1294
-class DeepseekV4ForCausalLM(nn.Module, SupportsPP, SupportsEagle3):
1315
+class DeepseekV4ForCausalLM(nn.Module, SupportsPP, SupportsEagle3, SupportsLoRA):
1316
model_cls = DeepseekV4Model
1317
1318
# Default mapper assumes the original FP4-expert checkpoint layout.
1319
# Overridden per-instance in __init__ when expert_dtype != "fp4".
1320
hf_to_vllm_mapper = _make_deepseek_v4_weights_mapper("fp4")
1321
1322
+ packed_modules_mapping = {
1323
+ "gate_up_proj": ["w1", "w3"],
1324
+ "fused_wqa_wkv": ["wq_a", "wkv"],
1325
+ "fused_wkv_wgate": ["wkv", "wgate"],
1326
+ }
1327
+
1328
+ # The MTP draft head is not LoRA-adapted.
1329
+ lora_skip_prefixes = ["mtp."]
1330
+
1331
def __init__(self, *, vllm_config: VllmConfig, prefix: str = ""):
1332
super().__init__()
1333