main
py 138 lines 4.32 KB
Raw
1 # SPDX-License-Identifier: Apache-2.0
2 # SPDX-FileCopyrightText: Copyright contributors to the vLLM project
3
4 from dataclasses import dataclass
5 from enum import Enum
6
7 import torch
8 import torch.nn as nn
9
10 from vllm import envs
11 from vllm.model_executor.layers.fused_moe.fused_moe import try_get_optimal_moe_config
12 from vllm.platforms import current_platform
13 from vllm.utils.math_utils import next_power_of_2
14
15 _lora_aux_cuda_stream: torch.cuda.Stream | None = None
16
17
18 def _get_lora_aux_cuda_stream() -> torch.cuda.Stream | None:
19 if not envs.VLLM_LORA_ENABLE_DUAL_STREAM:
20 return None
21 global _lora_aux_cuda_stream
22 if _lora_aux_cuda_stream is None and current_platform.is_cuda_alike():
23 _lora_aux_cuda_stream = torch.cuda.Stream()
24 return _lora_aux_cuda_stream
25
26
27 class LoRAMappingType(Enum):
28 LANGUAGE = 1
29 TOWER = 2
30 CONNECTOR = 3
31
32
33 @dataclass
34 class LoRAMapping:
35 index_mapping: tuple[int, ...]
36 prompt_mapping: tuple[int, ...]
37 is_prefill: bool = False
38 type: LoRAMappingType = LoRAMappingType.LANGUAGE
39
40 def __post_init__(self):
41 self.index_mapping = tuple(self.index_mapping)
42 self.prompt_mapping = tuple(self.prompt_mapping)
43
44
45 def _get_lora_device(base_layer: nn.Module) -> torch.device:
46 # code borrowed from https://github.com/fmmoret/vllm/blob/fm-support-lora-on-quantized-models/vllm/lora/layers.py#L34
47 """Returns the device for where to place the LoRA tensors."""
48 if hasattr(base_layer, "routed_experts"):
49 base_layer = base_layer.routed_experts
50
51 # unquantizedLinear
52 if hasattr(base_layer, "weight"):
53 return base_layer.weight.device
54 # Compressed Tensor
55 elif hasattr(base_layer, "weight_packed"):
56 return base_layer.weight_packed.device
57 # GPTQ/AWQ
58 elif hasattr(base_layer, "qweight"):
59 return base_layer.qweight.device
60 # INC WNA16 (AutoRound)
61 elif hasattr(base_layer, "ark_linear"):
62 return base_layer.ark_linear.qweight.device
63 # MoE layer
64 elif hasattr(base_layer, "w2_weight"):
65 return base_layer.w2_weight.device
66 # MoE Compressed Tensor
67 elif hasattr(base_layer, "w2_weight_packed"):
68 return base_layer.w2_weight_packed.device
69 # MoE GPTQ/AWQ/GGUF
70 elif hasattr(base_layer, "w2_qweight"):
71 return base_layer.w2_qweight.device
72 else:
73 raise ValueError(f"Unsupported base layer: {base_layer}")
74
75
76 def _not_fully_sharded_can_replace(can_replace):
77 """Decorator which adds the condition of not using fully sharded loras
78 intended to wrap can_replace_layer()
79 """
80
81 def dec(*args, **kwargs):
82 decorate = kwargs.pop("decorate") if "decorate" in kwargs else True
83 condition = not kwargs["lora_config"].fully_sharded_loras if decorate else True
84 return can_replace(*args, **kwargs) and condition
85
86 return dec
87
88
89 def _fully_sharded_can_replace(can_replace):
90 """Decorator which adds the condition of fully sharded loras
91 intended to wrap can_replace_layer()
92 """
93
94 def dec(*args, **kwargs):
95 return (
96 can_replace(*args, **kwargs) and kwargs["lora_config"].fully_sharded_loras
97 )
98
99 return dec
100
101
102 def try_get_optimal_moe_lora_config(
103 op_type: str,
104 w1_shape: tuple[int, ...],
105 w2_shape: tuple[int, ...],
106 rank: int,
107 top_k: int,
108 dtype: str | None,
109 M: int,
110 ) -> dict[str, int | None]:
111 # LoRA shrink/expand operates on bf16/fp16 adapters regardless of the
112 # base MoE weight's block-wise quantization, so block_shape is omitted
113 # from the config lookup — the non-quantized branch in get_default_config
114 # ignores it anyway.
115 raw_config = try_get_optimal_moe_config(w1_shape, w2_shape, top_k, dtype, M)
116 config: dict[str, int | None] = dict(raw_config)
117 if op_type in [
118 "fused_moe_lora_w13_shrink",
119 "fused_moe_lora_w2_shrink",
120 ]:
121 block_size_n = config.get("BLOCK_SIZE_N")
122 config["BLOCK_SIZE_N"] = min(
123 block_size_n if block_size_n is not None else 64,
124 next_power_of_2(rank),
125 )
126 elif op_type in [
127 "fused_moe_lora_w13_expand",
128 "fused_moe_lora_w2_expand",
129 ]:
130 block_size_k = config.get("BLOCK_SIZE_K")
131 config["BLOCK_SIZE_K"] = max(
132 16,
133 min(
134 block_size_k if block_size_k is not None else 32,
135 next_power_of_2(rank),
136 ),
137 )
138 return config