[LoRA] Avoid false target matches for unsupported module types (#52313)

Signed-off-by: linitra24 <Joy25810@foxmail.com>
Co-authored-by: Jee Jee Li <pandaleefree@gmail.com>
This commit is contained in:
linitra24
2026-08-19 16:48:11 +00:00
committed by GitHub
co-authored by Jee Jee Li
parent 0c8c3f41cf
commit 9b5f3454f2
3 changed files with 39 additions and 51 deletions
+1 -31
View File
@@ -1,37 +1,7 @@
# SPDX-License-Identifier: Apache-2.0
# SPDX-FileCopyrightText: Copyright contributors to the vLLM project
from vllm.lora.utils import is_in_target_modules, is_supported_lora_module
class TestIsSupportedLoraModule:
"""Tests for is_supported_lora_module (model-definition check)."""
def test_suffix_match(self):
assert is_supported_lora_module(
"model.layers.0.self_attn.o_proj", ["o_proj", "q_proj"]
)
def test_no_match(self):
assert not is_supported_lora_module(
"model.layers.0.self_attn.o_proj", ["q_proj", "k_proj"]
)
def test_exact_match(self):
assert is_supported_lora_module("o_proj", ["o_proj"])
def test_regex_suffix_matching(self):
"""Regex anchors to end — partial suffix should not match."""
assert not is_supported_lora_module("model.layers.0.self_attn.o_proj", ["proj"])
def test_empty_supported_modules(self):
assert not is_supported_lora_module("model.layers.0.self_attn.o_proj", [])
def test_multiple_supported_modules(self):
supported = ["q_proj", "k_proj", "v_proj", "o_proj"]
assert is_supported_lora_module("model.layers.0.self_attn.v_proj", supported)
assert not is_supported_lora_module("model.layers.0.mlp.gate_proj", supported)
from vllm.lora.utils import is_in_target_modules
class TestIsInTargetModules:
+18 -4
View File
@@ -418,7 +418,16 @@ class LoRAModelManager:
if isinstance(module, PPMissingLayer):
continue
if not self._match_target_modules(module_name):
target_modules = self.lora_config.target_modules
is_configured_target = target_modules is not None and is_in_target_modules(
module_name,
target_modules,
self.packed_modules_mapping,
)
if (
not self._match_target_modules(module_name, module)
and not is_configured_target
):
continue
punica_wrapper = self._get_punica_wrapper(module_name)
@@ -565,7 +574,7 @@ class LoRAModelManager:
model = LoRAModel(lora_id, rank, {})
for module_name, module in self.model.named_modules():
if (
not self._match_target_modules(module_name)
not self._match_target_modules(module_name, module)
or not isinstance(module, BaseLayerWithLoRA)
or self._get_punica_wrapper(module_name) is None
):
@@ -701,7 +710,7 @@ class LoRAModelManager:
)
return adjusted_rank
def _match_target_modules(self, module_name: str) -> bool:
def _match_target_modules(self, module_name: str, module: nn.Module) -> bool:
"""Check if a module should have LoRA applied.
This method first checks if the module is in vLLM's supported LoRA
@@ -711,11 +720,16 @@ class LoRAModelManager:
Args:
module_name: Full dot-separated module name (e.g.,
"model.layers.0.self_attn.o_proj")
module: Runtime module associated with ``module_name``.
Returns:
True if LoRA should be applied to this module, False otherwise.
"""
if not is_supported_lora_module(module_name, self.supported_lora_modules):
if not is_supported_lora_module(
module_name,
module,
self.supported_lora_modules,
):
return False
return is_in_target_modules(
module_name,
+20 -16
View File
@@ -4,7 +4,6 @@
import os
from typing import TYPE_CHECKING
import regex as re
from huggingface_hub.utils import HfHubHTTPError, HFValidationError
from torch import nn
from transformers import PretrainedConfig
@@ -33,8 +32,10 @@ from vllm.lora.layers import (
RowParallelLinearWithShardedLoRA,
VocabParallelEmbeddingWithLoRA,
)
from vllm.model_executor.custom_op import maybe_get_oot_by_class
from vllm.model_executor.layers.fused_moe import MoERunner
from vllm.model_executor.layers.linear import LinearBase
from vllm.model_executor.layers.vocab_parallel_embedding import VocabParallelEmbedding
from vllm.model_executor.utils import get_moe_expert_mapping, get_packed_modules_mapping
from vllm.transformers_utils.repo_utils import hf_api
@@ -230,11 +231,7 @@ def get_supported_lora_modules(model: nn.Module) -> list[str]:
for name in embedding_modules:
supported_lora_modules.add(name)
# get all the linear subfixes.
if isinstance(module, (LinearBase,)):
supported_lora_modules.add(name.split(".")[-1])
if isinstance(module, (MoERunner,)):
if isinstance(module, (LinearBase, MoERunner)):
supported_lora_modules.add(name.split(".")[-1])
return list(supported_lora_modules)
@@ -242,29 +239,36 @@ def get_supported_lora_modules(model: nn.Module) -> list[str]:
def is_supported_lora_module(
module_name: str,
module: nn.Module,
supported_lora_modules: list[str],
) -> bool:
"""Check if a module is in the model's supported LoRA modules.
Uses regex suffix matching against the model-defined supported modules
list (e.g., matching "model.layers.0.self_attn.o_proj" against
"o_proj").
The module name must match a model-supported suffix, and the runtime
module must belong to a module family handled by LoRA.
Args:
module_name: Full dot-separated module name.
module: Runtime module associated with ``module_name``.
supported_lora_modules: List of module suffixes supported by the
model.
Returns:
True if the module is supported, False otherwise.
"""
return any(
re.match(
r".*\.{target_module}$".format(target_module=target_module),
module_name,
)
or target_module == module_name
for target_module in supported_lora_modules
module_suffix = module_name.rsplit(".", 1)[-1]
if module_suffix not in supported_lora_modules:
return False
return isinstance(
module,
(
LinearBase,
MoERunner,
VocabParallelEmbedding,
maybe_get_oot_by_class(VocabParallelEmbedding),
BaseLayerWithLoRA,
),
)