@tduncan / Nvidia-Optimization-Stack-VLLM Mirror · read-only / commits / e17e4488bd
Fork 0

A high-throughput and memory-efficient inference and serving engine for LLMs

Mirrored from https://github.com/tduncan-lum/Nvidia-Optimization-Stack---VLLM.git

Synced Nov 15, 21:00 UTC

[LoRA] Remove linear hack outside transformers backend (#14177)

Signed-off-by: Isotr0py <2037008807@qq.com>

Isotr0py committed Mar 5, 2025 at 23:06 UTC e17e4488bdfb6aa23a4731287dbe564a8ff62a9d
4 files changed +142 -105
vllm/lora/layers.py
+30 -21
@@ -395,17 +395,20 @@ class BaseLinearLayerWithLoRA(BaseLayerWithLoRA):
395 x: torch.Tensor,
396 bias: Optional[torch.Tensor] = None) -> torch.Tensor:
397 output = self.base_layer.quant_method.apply(self.base_layer, x, bias)
398 +
399 + # In transformers backend, x and output have extra batch dimension like
400 + # (1, seq_len, hidden_dim), while punica expects (seq_len, hidden_dim),
401 + # therefore we need to flatten the batch dimensions.
402 + if x.ndim == 3 and output.ndim == 3:
403 + output = output.flatten(0, 1)
404 + x = x.flatten(0, 1)
405 +
406 self.punica_wrapper.add_lora_linear(output, x, self.lora_a_stacked,
407 self.lora_b_stacked,
408 self.lora_bias_stacked, 1.0,
409 self.output_slices)
410 return output
411
404 - @classmethod
405 - def get_source_layer(cls, source_layer: nn.Module) -> type:
406 - # Check parent_cls in case source_layer is a HFCompatibleLinear.
407 - return getattr(source_layer, "parent_cls", type(source_layer))
408 -
412
413 class ReplicatedLinearWithLoRA(BaseLinearLayerWithLoRA):
414
@@ -418,7 +421,7 @@ class ReplicatedLinearWithLoRA(BaseLinearLayerWithLoRA):
421
422 def forward(
423 self, input_: torch.Tensor
421 - ) -> Tuple[Optional[torch.Tensor], Optional[torch.Tensor]]:
424 + ) -> Union[torch.Tensor, tuple[torch.Tensor, Optional[torch.Tensor]]]:
425 """Forward of ReplicatedLinearWithLoRA
426
427 Args:
@@ -436,6 +439,10 @@ class ReplicatedLinearWithLoRA(BaseLinearLayerWithLoRA):
439
440 output_bias = (self.base_layer.bias
441 if self.base_layer.skip_bias_add else None)
442 +
443 + if not self.base_layer.return_bias:
444 + return output
445 +
446 return output, output_bias
447
448 # ReplicatedLinear should always be replaced, regardless of the fully
@@ -448,8 +455,7 @@ class ReplicatedLinearWithLoRA(BaseLinearLayerWithLoRA):
455 packed_modules_list: List,
456 model_config: Optional[PretrainedConfig],
457 ) -> bool:
451 - source_layer = cls.get_source_layer(source_layer)
452 - return source_layer is ReplicatedLinear
458 + return type(source_layer) is ReplicatedLinear
459
460
461 class ColumnParallelLinearWithLoRA(BaseLinearLayerWithLoRA):
@@ -512,7 +518,7 @@ class ColumnParallelLinearWithLoRA(BaseLinearLayerWithLoRA):
518
519 def forward(
520 self, input_: torch.Tensor
515 - ) -> Tuple[Optional[torch.Tensor], Optional[torch.Tensor]]:
521 + ) -> Union[torch.Tensor, tuple[torch.Tensor, Optional[torch.Tensor]]]:
522 """Forward of ColumnParallelLinear
523
524 Args:
@@ -532,6 +538,10 @@ class ColumnParallelLinearWithLoRA(BaseLinearLayerWithLoRA):
538 output = tensor_model_parallel_all_gather(output_parallel)
539 else:
540 output = output_parallel
541 +
542 + if not self.base_layer.return_bias:
543 + return output
544 +
545 output_bias = (self.base_layer.bias
546 if self.base_layer.skip_bias_add else None)
547 return output, output_bias
@@ -545,9 +555,8 @@ class ColumnParallelLinearWithLoRA(BaseLinearLayerWithLoRA):
555 packed_modules_list: List,
556 model_config: Optional[PretrainedConfig],
557 ) -> bool:
548 - source_layer = cls.get_source_layer(source_layer)
549 - return source_layer is ColumnParallelLinear or (
550 - source_layer is MergedColumnParallelLinear
558 + return type(source_layer) is ColumnParallelLinear or (
559 + type(source_layer) is MergedColumnParallelLinear
560 and len(packed_modules_list) == 1)
561
562
@@ -689,8 +698,7 @@ class MergedColumnParallelLinearWithLoRA(ColumnParallelLinearWithLoRA):
698 packed_modules_list: List,
699 model_config: Optional[PretrainedConfig],
700 ) -> bool:
692 - source_layer = cls.get_source_layer(source_layer)
693 - return (source_layer is MergedColumnParallelLinear
701 + return (type(source_layer) is MergedColumnParallelLinear
702 and len(packed_modules_list) == 2)
703
704
@@ -758,8 +766,7 @@ class QKVParallelLinearWithLoRA(ColumnParallelLinearWithLoRA):
766 def can_replace_layer(cls, source_layer: nn.Module,
767 lora_config: LoRAConfig, packed_modules_list: List,
768 model_config: Optional[PretrainedConfig]) -> bool:
761 - source_layer = cls.get_source_layer(source_layer)
762 - return source_layer is QKVParallelLinear and len(
769 + return type(source_layer) is QKVParallelLinear and len(
770 packed_modules_list) == 1
771
772
@@ -820,8 +827,7 @@ class MergedQKVParallelLinearWithLoRA(MergedColumnParallelLinearWithLoRA):
827 packed_modules_list: List,
828 model_config: Optional[PretrainedConfig],
829 ) -> bool:
823 - source_layer = cls.get_source_layer(source_layer)
824 - return (source_layer is QKVParallelLinear
830 + return (type(source_layer) is QKVParallelLinear
831 and len(packed_modules_list) == 3)
832
833
@@ -855,7 +861,7 @@ class RowParallelLinearWithLoRA(BaseLinearLayerWithLoRA):
861
862 def forward(
863 self, input_: torch.Tensor
858 - ) -> Tuple[Optional[torch.Tensor], Optional[torch.Tensor]]:
864 + ) -> Union[torch.Tensor, tuple[torch.Tensor, Optional[torch.Tensor]]]:
865 """Forward of RowParallelLinear
866
867 Args:
@@ -890,6 +896,10 @@ class RowParallelLinearWithLoRA(BaseLinearLayerWithLoRA):
896 else:
897 output = output_
898 output_bias = self.base_layer.bias
899 +
900 + if not self.base_layer.return_bias:
901 + return output
902 +
903 return output, output_bias
904
905 @property
@@ -906,8 +916,7 @@ class RowParallelLinearWithLoRA(BaseLinearLayerWithLoRA):
916 packed_modules_list: List,
917 model_config: Optional[PretrainedConfig],
918 ) -> bool:
909 - source_layer = cls.get_source_layer(source_layer)
910 - return source_layer is RowParallelLinear
919 + return type(source_layer) is RowParallelLinear
920
921
922 class LogitsProcessorWithLoRA(BaseLayerWithLoRA):
vllm/lora/utils.py
-10
@@ -67,16 +67,6 @@ def from_layer(layer: nn.Module,
67 packed_modules_list=packed_modules_list,
68 model_config=model_config):
69 instance_layer = lora_cls(layer)
70 - if layer.__class__.__name__ == "HFCompatibleLinear":
71 - # HACK: Make the forward method compatible with the original
72 - # forward method of the instance_layer.
73 - original_forward = instance_layer.forward
74 -
75 - def new_forward(input):
76 - input = input.squeeze(0)
77 - return original_forward(input)[0] # noqa: B023
78 -
79 - instance_layer.forward = new_forward
70 instance_layer.create_lora_weights(max_loras, lora_config,
71 model_config)
72 return instance_layer
vllm/model_executor/layers/linear.py
+110 -61
@@ -2,7 +2,7 @@
2
3 import itertools
4 from abc import abstractmethod
5 -from typing import Optional
5 +from typing import Optional, Union
6
7 import torch
8 import torch.nn.functional as F
@@ -152,6 +152,7 @@ class LinearBase(torch.nn.Module):
152 skip_bias_add: If true, skip adding bias but instead return it.
153 params_dtype: Data type for the parameters.
154 quant_config: Quantization configure.
155 + return_bias: If true, return bias together with outputs in forward pass.
156 """
157
158 def __init__(
@@ -162,6 +163,8 @@ class LinearBase(torch.nn.Module):
163 params_dtype: Optional[torch.dtype] = None,
164 quant_config: Optional[QuantizationConfig] = None,
165 prefix: str = "",
166 + *,
167 + return_bias: bool = True,
168 ):
169 super().__init__()
170
@@ -178,9 +181,11 @@ class LinearBase(torch.nn.Module):
181 else:
182 self.quant_method = quant_config.get_quant_method(self,
183 prefix=prefix)
184 + self.return_bias = return_bias
185
182 - def forward(self,
183 - x: torch.Tensor) -> tuple[torch.Tensor, Optional[Parameter]]:
186 + def forward(
187 + self, x: torch.Tensor
188 + ) -> Union[torch.Tensor, tuple[torch.Tensor, Optional[Parameter]]]:
189 raise NotImplementedError
190
191
@@ -198,20 +203,25 @@ class ReplicatedLinear(LinearBase):
203 (e.g. model.layers.0.qkv_proj)
204 """
205
201 - def __init__(self,
202 - input_size: int,
203 - output_size: int,
204 - bias: bool = True,
205 - skip_bias_add: bool = False,
206 - params_dtype: Optional[torch.dtype] = None,
207 - quant_config: Optional[QuantizationConfig] = None,
208 - prefix: str = ""):
206 + def __init__(
207 + self,
208 + input_size: int,
209 + output_size: int,
210 + bias: bool = True,
211 + skip_bias_add: bool = False,
212 + params_dtype: Optional[torch.dtype] = None,
213 + quant_config: Optional[QuantizationConfig] = None,
214 + prefix: str = "",
215 + *,
216 + return_bias: bool = True,
217 + ):
218 super().__init__(input_size,
219 output_size,
220 skip_bias_add,
221 params_dtype,
222 quant_config,
214 - prefix=prefix)
223 + prefix=prefix,
224 + return_bias=return_bias)
225
226 # All the linear layer supports quant method.
227 assert self.quant_method is not None
@@ -254,12 +264,15 @@ class ReplicatedLinear(LinearBase):
264 f"to a parameter of size {param.size()}")
265 param.data.copy_(loaded_weight)
266
257 - def forward(self,
258 - x: torch.Tensor) -> tuple[torch.Tensor, Optional[Parameter]]:
267 + def forward(
268 + self, x: torch.Tensor
269 + ) -> Union[torch.Tensor, tuple[torch.Tensor, Optional[Parameter]]]:
270 bias = self.bias if not self.skip_bias_add else None
271 assert self.quant_method is not None
272 output = self.quant_method.apply(self, x, bias)
273 output_bias = self.bias if self.skip_bias_add else None
274 + if not self.return_bias:
275 + return output
276 return output, output_bias
277
278 def extra_repr(self) -> str:
@@ -293,16 +306,20 @@ class ColumnParallelLinear(LinearBase):
306 (e.g. model.layers.0.qkv_proj)
307 """
308
296 - def __init__(self,
297 - input_size: int,
298 - output_size: int,
299 - bias: bool = True,
300 - gather_output: bool = False,
301 - skip_bias_add: bool = False,
302 - params_dtype: Optional[torch.dtype] = None,
303 - quant_config: Optional[QuantizationConfig] = None,
304 - output_sizes: Optional[list[int]] = None,
305 - prefix: str = ""):
309 + def __init__(
310 + self,
311 + input_size: int,
312 + output_size: int,
313 + bias: bool = True,
314 + gather_output: bool = False,
315 + skip_bias_add: bool = False,
316 + params_dtype: Optional[torch.dtype] = None,
317 + quant_config: Optional[QuantizationConfig] = None,
318 + output_sizes: Optional[list[int]] = None,
319 + prefix: str = "",
320 + *,
321 + return_bias: bool = True,
322 + ):
323 # Divide the weight matrix along the last dimension.
324 self.tp_size = get_tensor_model_parallel_world_size()
325 self.input_size_per_partition = input_size
@@ -315,8 +332,13 @@ class ColumnParallelLinear(LinearBase):
332 for output_size in self.output_sizes
333 ]
334
318 - super().__init__(input_size, output_size, skip_bias_add, params_dtype,
319 - quant_config, prefix)
335 + super().__init__(input_size,
336 + output_size,
337 + skip_bias_add,
338 + params_dtype,
339 + quant_config,
340 + prefix,
341 + return_bias=return_bias)
342
343 self.gather_output = gather_output
344
@@ -393,7 +415,9 @@ class ColumnParallelLinear(LinearBase):
415 loaded_weight = loaded_weight.reshape(1)
416 param.load_column_parallel_weight(loaded_weight=loaded_weight)
417
396 - def forward(self, input_) -> tuple[torch.Tensor, Optional[Parameter]]:
418 + def forward(
419 + self, input_
420 + ) -> Union[torch.Tensor, tuple[torch.Tensor, Optional[Parameter]]]:
421 bias = self.bias if not self.skip_bias_add else None
422
423 # Matrix multiply.
@@ -405,6 +429,8 @@ class ColumnParallelLinear(LinearBase):
429 else:
430 output = output_parallel
431 output_bias = self.bias if self.skip_bias_add else None
432 + if not self.return_bias:
433 + return output
434 return output, output_bias
435
436 def extra_repr(self) -> str:
@@ -439,15 +465,19 @@ class MergedColumnParallelLinear(ColumnParallelLinear):
465 (e.g. model.layers.0.qkv_proj)
466 """
467
442 - def __init__(self,
443 - input_size: int,
444 - output_sizes: list[int],
445 - bias: bool = True,
446 - gather_output: bool = False,
447 - skip_bias_add: bool = False,
448 - params_dtype: Optional[torch.dtype] = None,
449 - quant_config: Optional[QuantizationConfig] = None,
450 - prefix: str = ""):
468 + def __init__(
469 + self,
470 + input_size: int,
471 + output_sizes: list[int],
472 + bias: bool = True,
473 + gather_output: bool = False,
474 + skip_bias_add: bool = False,
475 + params_dtype: Optional[torch.dtype] = None,
476 + quant_config: Optional[QuantizationConfig] = None,
477 + prefix: str = "",
478 + *,
479 + return_bias: bool = True,
480 + ):
481 self.output_sizes = output_sizes
482 tp_size = get_tensor_model_parallel_world_size()
483 assert all(output_size % tp_size == 0 for output_size in output_sizes)
@@ -458,7 +488,8 @@ class MergedColumnParallelLinear(ColumnParallelLinear):
488 skip_bias_add=skip_bias_add,
489 params_dtype=params_dtype,
490 quant_config=quant_config,
461 - prefix=prefix)
491 + prefix=prefix,
492 + return_bias=return_bias)
493
494 def weight_loader(self,
495 param: Parameter,
@@ -711,16 +742,20 @@ class QKVParallelLinear(ColumnParallelLinear):
742 (e.g. model.layers.0.qkv_proj)
743 """
744
714 - def __init__(self,
715 - hidden_size: int,
716 - head_size: int,
717 - total_num_heads: int,
718 - total_num_kv_heads: Optional[int] = None,
719 - bias: bool = True,
720 - skip_bias_add: bool = False,
721 - params_dtype: Optional[torch.dtype] = None,
722 - quant_config: Optional[QuantizationConfig] = None,
723 - prefix: str = ""):
745 + def __init__(
746 + self,
747 + hidden_size: int,
748 + head_size: int,
749 + total_num_heads: int,
750 + total_num_kv_heads: Optional[int] = None,
751 + bias: bool = True,
752 + skip_bias_add: bool = False,
753 + params_dtype: Optional[torch.dtype] = None,
754 + quant_config: Optional[QuantizationConfig] = None,
755 + prefix: str = "",
756 + *,
757 + return_bias: bool = True,
758 + ):
759 self.hidden_size = hidden_size
760 self.head_size = head_size
761 self.total_num_heads = total_num_heads
@@ -753,7 +788,8 @@ class QKVParallelLinear(ColumnParallelLinear):
788 skip_bias_add=skip_bias_add,
789 params_dtype=params_dtype,
790 quant_config=quant_config,
756 - prefix=prefix)
791 + prefix=prefix,
792 + return_bias=return_bias)
793
794 def _get_shard_offset_mapping(self, loaded_shard_id: str):
795 shard_offset_mapping = {
@@ -1048,16 +1084,20 @@ class RowParallelLinear(LinearBase):
1084 quant_config: Quantization configure.
1085 """
1086
1051 - def __init__(self,
1052 - input_size: int,
1053 - output_size: int,
1054 - bias: bool = True,
1055 - input_is_parallel: bool = True,
1056 - skip_bias_add: bool = False,
1057 - params_dtype: Optional[torch.dtype] = None,
1058 - reduce_results: bool = True,
1059 - quant_config: Optional[QuantizationConfig] = None,
1060 - prefix: str = ""):
1087 + def __init__(
1088 + self,
1089 + input_size: int,
1090 + output_size: int,
1091 + bias: bool = True,
1092 + input_is_parallel: bool = True,
1093 + skip_bias_add: bool = False,
1094 + params_dtype: Optional[torch.dtype] = None,
1095 + reduce_results: bool = True,
1096 + quant_config: Optional[QuantizationConfig] = None,
1097 + prefix: str = "",
1098 + *,
1099 + return_bias: bool = True,
1100 + ):
1101 # Divide the weight matrix along the first dimension.
1102 self.tp_rank = get_tensor_model_parallel_rank()
1103 self.tp_size = get_tensor_model_parallel_world_size()
@@ -1065,8 +1105,13 @@ class RowParallelLinear(LinearBase):
1105 self.output_size_per_partition = output_size
1106 self.output_partition_sizes = [output_size]
1107
1068 - super().__init__(input_size, output_size, skip_bias_add, params_dtype,
1069 - quant_config, prefix)
1108 + super().__init__(input_size,
1109 + output_size,
1110 + skip_bias_add,
1111 + params_dtype,
1112 + quant_config,
1113 + prefix,
1114 + return_bias=return_bias)
1115
1116 self.input_is_parallel = input_is_parallel
1117 self.reduce_results = reduce_results
@@ -1145,7 +1190,9 @@ class RowParallelLinear(LinearBase):
1190
1191 param.load_row_parallel_weight(loaded_weight=loaded_weight)
1192
1148 - def forward(self, input_) -> tuple[torch.Tensor, Optional[Parameter]]:
1193 + def forward(
1194 + self, input_
1195 + ) -> Union[torch.Tensor, tuple[torch.Tensor, Optional[Parameter]]]:
1196 if self.input_is_parallel:
1197 input_parallel = input_
1198 else:
@@ -1169,6 +1216,8 @@ class RowParallelLinear(LinearBase):
1216
1217 output_bias = self.bias if self.skip_bias_add else None
1218
1219 + if not self.return_bias:
1220 + return output
1221 return output, output_bias
1222
1223 def extra_repr(self) -> str:
vllm/model_executor/models/transformers.py
+2 -13
@@ -96,23 +96,12 @@ def replace_linear_class(
96 "rowwise": RowParallelLinear,
97 }.get(style, ReplicatedLinear)
98
99 - class HFCompatibleLinear(vllm_linear_cls):
100 - """
101 - Wrapper class that removes `output_bias` from returned output.
102 - """
103 - # NOTE: The LoRA layer needs to use `parent_cls`.
104 - @property
105 - def parent_cls(self) -> type:
106 - return vllm_linear_cls
107 -
108 - def forward(self, input: torch.Tensor) -> torch.Tensor:
109 - return super().forward(input)[0]
110 -
111 - return HFCompatibleLinear(
99 + return vllm_linear_cls(
100 input_size=linear.in_features,
101 output_size=linear.out_features,
102 bias=linear.bias is not None,
103 quant_config=quant_config,
104 + return_bias=False,
105 )
106
107