Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions vllm/lora/model_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,7 @@ def activate_adapter(
"Activating LoRA. int id: %d, slot index: %d", lora_model.id, index
)
self.lora_index_to_id[index] = lora_model.id
num_applied = 0
for module_name, module in self.modules.items():
module_lora = self._get_lora_layer_weights(lora_model, module_name)
if not module_lora:
Expand All @@ -260,6 +261,16 @@ def activate_adapter(
module_lora.lora_a,
module_lora.lora_b,
)
num_applied += 1

if num_applied == 0 and self.modules:
logger.warning(
"LoRA adapter %d was activated but none of its weights "
"matched any LoRA-eligible module. The adapter will have "
"no effect. This usually means the model class is missing "
"an hf_to_vllm_mapper attribute.",
lora_model.id,
)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These warnings may be misleading, please delete them


return True

Expand Down
18 changes: 18 additions & 0 deletions vllm/lora/worker_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,24 @@ def _load_adapter(self, lora_request: LoRARequest) -> LoRAModel:
skip_prefixes=lora_skip_prefixes,
)

# Check that at least some loaded LoRA modules will match
# the model's module names. When hf_to_vllm_mapper is
# missing, LoRA weights may load successfully but with
# wrong module paths, causing them to be silently ignored.
model_module_names = {name for name, _ in model.named_modules()}
matched = any(
module_name in model_module_names for module_name in lora.loras
)
Comment on lines +137 to +140
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This check is redundant because a more precise check is performed in LoRAModelManager.activate_adapter (which is called immediately after loading in add_adapter). Furthermore, calling model.named_modules() to build a set on every adapter load is inefficient for large models like Pixtral (12B). If an early check is desired, it should be performed against self._adapter_manager.modules, which contains the actual LoRA-eligible module names.

if lora.loras and not matched:
logger.warning(
"None of the LoRA modules in adapter '%s' matched "
"any module in %s. The adapter weights will have no "
"effect. This is usually caused by a missing "
"hf_to_vllm_mapper on the model class.",
lora_request.lora_name,
model.__class__.__name__,
)

except FileNotFoundError as e:
# FileNotFoundError should be raised if both
# - No adapter found to download from huggingface (or in
Expand Down
10 changes: 10 additions & 0 deletions vllm/model_executor/models/pixtral.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
)
from vllm.model_executor.layers.quantization import QuantizationConfig
from vllm.model_executor.model_loader.weight_utils import default_weight_loader
from vllm.model_executor.models.utils import WeightsMapper
from vllm.multimodal import MULTIMODAL_REGISTRY, MultiModalKwargsItems
from vllm.multimodal.inputs import (
MultiModalDataDict,
Expand Down Expand Up @@ -372,6 +373,15 @@ def _cached_apply_hf_processor(
class PixtralForConditionalGeneration(
nn.Module, SupportsLoRA, SupportsMultiModal, SupportsPP
):
hf_to_vllm_mapper = WeightsMapper(
orig_to_new_prefix={
"model.language_model.": "language_model.model.",
"model.vision_tower.": "vision_tower.",
"model.multi_modal_projector.": "multi_modal_projector.",
"lm_head.": "language_model.lm_head.",
}
)
Comment on lines +376 to +383
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The target prefixes in the WeightsMapper do not match the actual attribute names defined in PixtralForConditionalGeneration. Specifically, vision_tower should be vision_encoder and multi_modal_projector should be vision_language_adapter. Additionally, based on the PR description, mapping model.language_model. to language_model.model. will result in an extra model. prefix (e.g., language_model.model.model.layers...), as the internal vLLM path is language_model.model.layers....

Suggested change
hf_to_vllm_mapper = WeightsMapper(
orig_to_new_prefix={
"model.language_model.": "language_model.model.",
"model.vision_tower.": "vision_tower.",
"model.multi_modal_projector.": "multi_modal_projector.",
"lm_head.": "language_model.lm_head.",
}
)
hf_to_vllm_mapper = WeightsMapper(
orig_to_new_prefix={
"model.language_model.": "language_model.",
"model.vision_tower.": "vision_encoder.",
"model.multi_modal_projector.": "vision_language_adapter.",
"lm_head.": "language_model.lm_head.",
}
)


@classmethod
def get_placeholder_str(cls, modality: str, i: int) -> str | None:
if modality.startswith("image"):
Expand Down