Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- lib/python3.12/site-packages/peft/tuners/__pycache__/__init__.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/peft/tuners/__pycache__/_buffer_dict.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/peft/tuners/__pycache__/lycoris_utils.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/peft/tuners/__pycache__/tuners_utils.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/peft/tuners/adalora/__init__.py +43 -0
- lib/python3.12/site-packages/peft/tuners/adalora/__pycache__/__init__.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/peft/tuners/adalora/__pycache__/bnb.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/peft/tuners/adalora/__pycache__/config.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/peft/tuners/adalora/__pycache__/gptq.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/peft/tuners/adalora/__pycache__/layer.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/peft/tuners/adalora/__pycache__/model.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/peft/tuners/adalora/bnb.py +143 -0
- lib/python3.12/site-packages/peft/tuners/adalora/config.py +108 -0
- lib/python3.12/site-packages/peft/tuners/adalora/gptq.py +71 -0
- lib/python3.12/site-packages/peft/tuners/adalora/layer.py +358 -0
- lib/python3.12/site-packages/peft/tuners/adalora/model.py +364 -0
- lib/python3.12/site-packages/peft/tuners/adaption_prompt/__init__.py +23 -0
- lib/python3.12/site-packages/peft/tuners/adaption_prompt/__pycache__/__init__.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/peft/tuners/adaption_prompt/__pycache__/config.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/peft/tuners/adaption_prompt/__pycache__/layer.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/peft/tuners/adaption_prompt/__pycache__/model.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/peft/tuners/adaption_prompt/__pycache__/utils.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/peft/tuners/adaption_prompt/config.py +81 -0
- lib/python3.12/site-packages/peft/tuners/adaption_prompt/layer.py +138 -0
- lib/python3.12/site-packages/peft/tuners/adaption_prompt/model.py +162 -0
- lib/python3.12/site-packages/peft/tuners/adaption_prompt/utils.py +132 -0
- lib/python3.12/site-packages/peft/tuners/boft/__init__.py +24 -0
- lib/python3.12/site-packages/peft/tuners/boft/__pycache__/__init__.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/peft/tuners/boft/__pycache__/config.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/peft/tuners/boft/__pycache__/layer.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/peft/tuners/boft/__pycache__/model.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/peft/tuners/boft/config.py +160 -0
- lib/python3.12/site-packages/peft/tuners/boft/fbd/__init__.py +0 -0
- lib/python3.12/site-packages/peft/tuners/boft/fbd/__pycache__/__init__.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/peft/tuners/boft/fbd/fbd_cuda.cpp +28 -0
- lib/python3.12/site-packages/peft/tuners/boft/fbd/fbd_cuda_kernel.cu +109 -0
- lib/python3.12/site-packages/peft/tuners/boft/layer.py +984 -0
- lib/python3.12/site-packages/peft/tuners/boft/model.py +353 -0
- lib/python3.12/site-packages/peft/tuners/cpt/__init__.py +24 -0
- lib/python3.12/site-packages/peft/tuners/cpt/__pycache__/__init__.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/peft/tuners/cpt/__pycache__/config.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/peft/tuners/cpt/__pycache__/model.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/peft/tuners/cpt/config.py +98 -0
- lib/python3.12/site-packages/peft/tuners/cpt/model.py +200 -0
- lib/python3.12/site-packages/peft/tuners/fourierft/__init__.py +24 -0
- lib/python3.12/site-packages/peft/tuners/fourierft/__pycache__/__init__.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/peft/tuners/fourierft/__pycache__/config.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/peft/tuners/fourierft/__pycache__/layer.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/peft/tuners/fourierft/__pycache__/model.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/peft/tuners/fourierft/config.py +205 -0
lib/python3.12/site-packages/peft/tuners/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (2.33 kB). View file
|
|
|
lib/python3.12/site-packages/peft/tuners/__pycache__/_buffer_dict.cpython-312.pyc
ADDED
|
Binary file (8 kB). View file
|
|
|
lib/python3.12/site-packages/peft/tuners/__pycache__/lycoris_utils.cpython-312.pyc
ADDED
|
Binary file (20.9 kB). View file
|
|
|
lib/python3.12/site-packages/peft/tuners/__pycache__/tuners_utils.cpython-312.pyc
ADDED
|
Binary file (53.9 kB). View file
|
|
|
lib/python3.12/site-packages/peft/tuners/adalora/__init__.py
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2023-present the HuggingFace Inc. team.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
from peft.import_utils import is_bnb_4bit_available, is_bnb_available
|
| 16 |
+
from peft.utils import register_peft_method
|
| 17 |
+
|
| 18 |
+
from .config import AdaLoraConfig
|
| 19 |
+
from .gptq import SVDQuantLinear
|
| 20 |
+
from .layer import AdaLoraLayer, RankAllocator, SVDLinear
|
| 21 |
+
from .model import AdaLoraModel
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
__all__ = ["AdaLoraConfig", "AdaLoraLayer", "AdaLoraModel", "RankAllocator", "SVDLinear", "SVDQuantLinear"]
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
register_peft_method(
|
| 28 |
+
name="adalora", config_cls=AdaLoraConfig, model_cls=AdaLoraModel, prefix="lora_", is_mixed_compatible=True
|
| 29 |
+
)
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def __getattr__(name):
|
| 33 |
+
if (name == "SVDLinear8bitLt") and is_bnb_available():
|
| 34 |
+
from .bnb import SVDLinear8bitLt
|
| 35 |
+
|
| 36 |
+
return SVDLinear8bitLt
|
| 37 |
+
|
| 38 |
+
if (name == "SVDLinear4bit") and is_bnb_4bit_available():
|
| 39 |
+
from .bnb import SVDLinear4bit
|
| 40 |
+
|
| 41 |
+
return SVDLinear4bit
|
| 42 |
+
|
| 43 |
+
raise AttributeError(f"module {__name__} has no attribute {name}")
|
lib/python3.12/site-packages/peft/tuners/adalora/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (1.21 kB). View file
|
|
|
lib/python3.12/site-packages/peft/tuners/adalora/__pycache__/bnb.cpython-312.pyc
ADDED
|
Binary file (5.6 kB). View file
|
|
|
lib/python3.12/site-packages/peft/tuners/adalora/__pycache__/config.cpython-312.pyc
ADDED
|
Binary file (6.02 kB). View file
|
|
|
lib/python3.12/site-packages/peft/tuners/adalora/__pycache__/gptq.cpython-312.pyc
ADDED
|
Binary file (2.65 kB). View file
|
|
|
lib/python3.12/site-packages/peft/tuners/adalora/__pycache__/layer.cpython-312.pyc
ADDED
|
Binary file (20.2 kB). View file
|
|
|
lib/python3.12/site-packages/peft/tuners/adalora/__pycache__/model.cpython-312.pyc
ADDED
|
Binary file (17 kB). View file
|
|
|
lib/python3.12/site-packages/peft/tuners/adalora/bnb.py
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2023-present the HuggingFace Inc. team.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
from typing import Any
|
| 16 |
+
|
| 17 |
+
import torch
|
| 18 |
+
|
| 19 |
+
from peft.import_utils import is_bnb_4bit_available, is_bnb_available
|
| 20 |
+
|
| 21 |
+
from .layer import AdaLoraLayer
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
if is_bnb_available():
|
| 25 |
+
|
| 26 |
+
class SVDLinear8bitLt(torch.nn.Module, AdaLoraLayer):
|
| 27 |
+
# Low-rank matrix for SVD-based adaptation
|
| 28 |
+
def __init__(
|
| 29 |
+
self,
|
| 30 |
+
base_layer: torch.nn.Module,
|
| 31 |
+
adapter_name: str,
|
| 32 |
+
r: int = 0,
|
| 33 |
+
lora_alpha: int = 1,
|
| 34 |
+
lora_dropout: float = 0.0,
|
| 35 |
+
init_lora_weights: bool = True,
|
| 36 |
+
**kwargs,
|
| 37 |
+
) -> None:
|
| 38 |
+
super().__init__()
|
| 39 |
+
AdaLoraLayer.__init__(self, base_layer)
|
| 40 |
+
# Freezing the pre-trained weight matrix
|
| 41 |
+
self.get_base_layer().weight.requires_grad = False
|
| 42 |
+
|
| 43 |
+
self._active_adapter = adapter_name
|
| 44 |
+
self.update_layer(adapter_name, r, lora_alpha, lora_dropout, init_lora_weights)
|
| 45 |
+
|
| 46 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 47 |
+
# note: no check for self.merged because merging is not supported (yet)
|
| 48 |
+
result = self.base_layer(x)
|
| 49 |
+
|
| 50 |
+
if self.disable_adapters:
|
| 51 |
+
return result
|
| 52 |
+
|
| 53 |
+
for active_adapter in self.active_adapters:
|
| 54 |
+
if active_adapter not in self.lora_A.keys():
|
| 55 |
+
continue
|
| 56 |
+
requires_conversion = not torch.is_autocast_enabled()
|
| 57 |
+
if requires_conversion:
|
| 58 |
+
expected_dtype = result.dtype
|
| 59 |
+
if x.dtype != torch.float32:
|
| 60 |
+
x = x.float()
|
| 61 |
+
|
| 62 |
+
lora_A = self.lora_A[active_adapter]
|
| 63 |
+
lora_B = self.lora_B[active_adapter]
|
| 64 |
+
lora_E = self.lora_E[active_adapter]
|
| 65 |
+
dropout = self.lora_dropout[active_adapter]
|
| 66 |
+
scaling = self.scaling[active_adapter]
|
| 67 |
+
ranknum = self.ranknum[active_adapter] + 1e-5
|
| 68 |
+
|
| 69 |
+
output = dropout(x) @ (lora_A * lora_E).T @ lora_B.T
|
| 70 |
+
if requires_conversion:
|
| 71 |
+
output = output.to(expected_dtype)
|
| 72 |
+
output = output * scaling / ranknum
|
| 73 |
+
# inplace operation on view is forbidden for MatMul8bitLtBackward, so avoid it
|
| 74 |
+
result = result + output
|
| 75 |
+
return result
|
| 76 |
+
|
| 77 |
+
def __repr__(self) -> str:
|
| 78 |
+
rep = super().__repr__()
|
| 79 |
+
return "adalora." + rep
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
if is_bnb_4bit_available():
|
| 83 |
+
|
| 84 |
+
class SVDLinear4bit(torch.nn.Module, AdaLoraLayer):
|
| 85 |
+
# Low-rank matrix for SVD-based adaptation
|
| 86 |
+
def __init__(
|
| 87 |
+
self,
|
| 88 |
+
base_layer: torch.nn.Module,
|
| 89 |
+
adapter_name: str,
|
| 90 |
+
r: int = 0,
|
| 91 |
+
lora_alpha: int = 1,
|
| 92 |
+
lora_dropout: float = 0.0,
|
| 93 |
+
init_lora_weights: bool = True,
|
| 94 |
+
**kwargs,
|
| 95 |
+
) -> None:
|
| 96 |
+
super().__init__()
|
| 97 |
+
AdaLoraLayer.__init__(self, base_layer)
|
| 98 |
+
# Freezing the pre-trained weight matrix
|
| 99 |
+
self.get_base_layer().weight.requires_grad = False
|
| 100 |
+
|
| 101 |
+
self._active_adapter = adapter_name
|
| 102 |
+
self.update_layer(adapter_name, r, lora_alpha, lora_dropout, init_lora_weights)
|
| 103 |
+
|
| 104 |
+
def forward(self, x: torch.Tensor, *args: Any, **kwargs: Any) -> torch.Tensor:
|
| 105 |
+
# note: no check for self.merged because merging is not supported (yet)
|
| 106 |
+
result = self.base_layer(x, *args, **kwargs)
|
| 107 |
+
|
| 108 |
+
if self.disable_adapters:
|
| 109 |
+
return result
|
| 110 |
+
|
| 111 |
+
# As per Tim Dettmers, for 4bit, we need to defensively clone here.
|
| 112 |
+
# The reason is that in some cases, an error can occur that backprop
|
| 113 |
+
# does not work on a manipulated view. This issue may be solved with
|
| 114 |
+
# newer PyTorch versions but this would need extensive testing to be
|
| 115 |
+
# sure.
|
| 116 |
+
result = result.clone()
|
| 117 |
+
|
| 118 |
+
for active_adapter in self.active_adapters:
|
| 119 |
+
if active_adapter not in self.lora_A.keys():
|
| 120 |
+
continue
|
| 121 |
+
|
| 122 |
+
lora_A = self.lora_A[active_adapter]
|
| 123 |
+
lora_B = self.lora_B[active_adapter]
|
| 124 |
+
lora_E = self.lora_E[active_adapter]
|
| 125 |
+
dropout = self.lora_dropout[active_adapter]
|
| 126 |
+
scaling = self.scaling[active_adapter]
|
| 127 |
+
ranknum = self.ranknum[active_adapter] + 1e-5
|
| 128 |
+
|
| 129 |
+
requires_conversion = not torch.is_autocast_enabled()
|
| 130 |
+
if requires_conversion:
|
| 131 |
+
expected_dtype = result.dtype
|
| 132 |
+
x = self._cast_input_dtype(x, lora_A.dtype)
|
| 133 |
+
|
| 134 |
+
output = dropout(x) @ (lora_A * lora_E).T @ lora_B.T
|
| 135 |
+
if requires_conversion:
|
| 136 |
+
output = output.to(expected_dtype)
|
| 137 |
+
output = output * scaling / ranknum
|
| 138 |
+
result += output
|
| 139 |
+
return result
|
| 140 |
+
|
| 141 |
+
def __repr__(self) -> str:
|
| 142 |
+
rep = super().__repr__()
|
| 143 |
+
return "adalora." + rep
|
lib/python3.12/site-packages/peft/tuners/adalora/config.py
ADDED
|
@@ -0,0 +1,108 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2023-present the HuggingFace Inc. team.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
import warnings
|
| 16 |
+
from dataclasses import dataclass, field
|
| 17 |
+
from typing import Optional
|
| 18 |
+
|
| 19 |
+
from peft.tuners.lora import LoraConfig
|
| 20 |
+
from peft.utils import PeftType
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
@dataclass
|
| 24 |
+
class AdaLoraConfig(LoraConfig):
|
| 25 |
+
"""
|
| 26 |
+
This is the configuration class to store the configuration of a [`~peft.AdaLora`].
|
| 27 |
+
|
| 28 |
+
AdaLoRA has three phases defined by `tinit`, `tfinal` and `total_step`.
|
| 29 |
+
|
| 30 |
+
The initial phase can be understood as a step for pre-training the adapters so that when reducing their rank, there
|
| 31 |
+
is already some information encoded that can be reduced instead of random matrices. This phase is defined by
|
| 32 |
+
supplying `tinit`.
|
| 33 |
+
|
| 34 |
+
After the initial phase is over (`tinit` steps have passed) and the final phase has not begun, AdaLoRA reduces the
|
| 35 |
+
budget of how much rank each layer is allowed to have with each step. This is where the reduction of rank is
|
| 36 |
+
happening. This goes on until `total_step - tfinal` steps are reached.
|
| 37 |
+
|
| 38 |
+
The last phase, beginning once `total_step - tfinal` steps are reached, does not change the layer ranks anymore but
|
| 39 |
+
fine-tunes the reduced-rank layers that resulted from the previous phase.
|
| 40 |
+
|
| 41 |
+
A practical example: `tinit` is 10, `tfinal` is 20, `total_step` is 100. We spend 10 steps doing pre-training
|
| 42 |
+
without rank reduction because our budget is constant (init phase), then we spend 80 (100-20) steps in the
|
| 43 |
+
reduction phase where our budget decreases step-wise and, finally, 20 steps in the final fine-tuning stage without
|
| 44 |
+
reduction.
|
| 45 |
+
|
| 46 |
+
Args:
|
| 47 |
+
target_r (`int`): The target average rank of incremental matrix.
|
| 48 |
+
init_r (`int`): The initial rank for each incremental matrix.
|
| 49 |
+
tinit (`int`): The steps of initial fine-tuning warmup.
|
| 50 |
+
tfinal (`int`): The number of steps of final fine-tuning.
|
| 51 |
+
deltaT (`int`): The time internval between two budget allocations.
|
| 52 |
+
beta1 (`float`): The hyperparameter of EMA for sensitivity smoothing.
|
| 53 |
+
beta2 (`float`): The hyperparameter of EMA for undertainty quantification.
|
| 54 |
+
orth_reg_weight (`float`): The coefficient of orthogonal regularization.
|
| 55 |
+
total_step (`int`): The total training steps that should be specified before training.
|
| 56 |
+
rank_pattern (`list`): The allocated rank for each weight matrix by RankAllocator.
|
| 57 |
+
"""
|
| 58 |
+
|
| 59 |
+
target_r: int = field(default=8, metadata={"help": "Target Lora matrix dimension."})
|
| 60 |
+
init_r: int = field(default=12, metadata={"help": "Initial Lora matrix dimension."})
|
| 61 |
+
tinit: int = field(default=0, metadata={"help": "The steps of initial warmup."})
|
| 62 |
+
tfinal: int = field(default=0, metadata={"help": "The steps of final warmup."})
|
| 63 |
+
deltaT: int = field(default=1, metadata={"help": "Step interval of rank allocation."})
|
| 64 |
+
beta1: float = field(default=0.85, metadata={"help": "Hyperparameter of EMA."})
|
| 65 |
+
beta2: float = field(default=0.85, metadata={"help": "Hyperparameter of EMA."})
|
| 66 |
+
orth_reg_weight: float = field(default=0.5, metadata={"help": "The orthogonal regularization coefficient."})
|
| 67 |
+
total_step: Optional[int] = field(default=None, metadata={"help": "The total training steps."})
|
| 68 |
+
rank_pattern: Optional[dict] = field(default=None, metadata={"help": "The saved rank pattern."})
|
| 69 |
+
|
| 70 |
+
def __post_init__(self):
|
| 71 |
+
super().__post_init__()
|
| 72 |
+
self.peft_type = PeftType.ADALORA
|
| 73 |
+
|
| 74 |
+
if self.use_dora:
|
| 75 |
+
raise ValueError(f"{self.peft_type} does not support DoRA.")
|
| 76 |
+
|
| 77 |
+
if self.loftq_config:
|
| 78 |
+
raise ValueError(f"{self.peft_type} does not support LOFTQ.")
|
| 79 |
+
|
| 80 |
+
self.target_modules = (
|
| 81 |
+
set(self.target_modules) if isinstance(self.target_modules, list) else self.target_modules
|
| 82 |
+
)
|
| 83 |
+
self.exclude_modules = (
|
| 84 |
+
set(self.exclude_modules) if isinstance(self.exclude_modules, list) else self.exclude_modules
|
| 85 |
+
)
|
| 86 |
+
# if target_modules is a regex expression, then layers_to_transform should be None
|
| 87 |
+
if isinstance(self.target_modules, str) and self.layers_to_transform is not None:
|
| 88 |
+
raise ValueError("`layers_to_transform` cannot be used when `target_modules` is a str.")
|
| 89 |
+
|
| 90 |
+
# check for layers_to_transform and layers_pattern
|
| 91 |
+
if self.layers_pattern and not self.layers_to_transform:
|
| 92 |
+
raise ValueError("When `layers_pattern` is specified, `layers_to_transform` must also be specified. ")
|
| 93 |
+
|
| 94 |
+
# Check if 'r' has been set to a non-default value
|
| 95 |
+
if self.r != 8: # 8 is the default value for 'r' in LoraConfig
|
| 96 |
+
warnings.warn(
|
| 97 |
+
"Note that `r` is not used in AdaLora and will be ignored."
|
| 98 |
+
"If you intended to set the initial rank, use `init_r` instead."
|
| 99 |
+
)
|
| 100 |
+
|
| 101 |
+
if self.total_step is None or self.total_step <= 0:
|
| 102 |
+
raise ValueError("AdaLoRA does not work when `total_step` is None, supply a value > 0.")
|
| 103 |
+
|
| 104 |
+
if self.tinit >= (self.total_step - self.tfinal):
|
| 105 |
+
raise ValueError(
|
| 106 |
+
"The supplied schedule values don't allow for a budgeting phase. Decrease `tfinal`/`tinit` or "
|
| 107 |
+
"increase `total_step`."
|
| 108 |
+
)
|
lib/python3.12/site-packages/peft/tuners/adalora/gptq.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2023-present the HuggingFace Inc. team.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
import torch
|
| 15 |
+
|
| 16 |
+
from .layer import AdaLoraLayer
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
class SVDQuantLinear(torch.nn.Module, AdaLoraLayer):
|
| 20 |
+
def __init__(
|
| 21 |
+
self,
|
| 22 |
+
base_layer,
|
| 23 |
+
adapter_name,
|
| 24 |
+
r: int = 0,
|
| 25 |
+
lora_alpha: int = 1,
|
| 26 |
+
lora_dropout: float = 0.0,
|
| 27 |
+
init_lora_weights: bool = True,
|
| 28 |
+
**kwargs,
|
| 29 |
+
) -> None:
|
| 30 |
+
super().__init__()
|
| 31 |
+
AdaLoraLayer.__init__(self, base_layer)
|
| 32 |
+
|
| 33 |
+
# self.base_layer and self.quant_linear_module are the same; we need the former for consistency and the latter
|
| 34 |
+
# for backwards compatibility
|
| 35 |
+
self.quant_linear_module = base_layer
|
| 36 |
+
self._active_adapter = adapter_name
|
| 37 |
+
self.update_layer(adapter_name, r, lora_alpha, lora_dropout, init_lora_weights)
|
| 38 |
+
|
| 39 |
+
def forward(self, x: torch.Tensor) -> torch.Tensor:
|
| 40 |
+
result = self.quant_linear_module(x)
|
| 41 |
+
|
| 42 |
+
if self.disable_adapters:
|
| 43 |
+
return result
|
| 44 |
+
|
| 45 |
+
for active_adapter in self.active_adapters:
|
| 46 |
+
if active_adapter not in self.lora_A.keys():
|
| 47 |
+
continue
|
| 48 |
+
lora_A = self.lora_A[active_adapter]
|
| 49 |
+
lora_B = self.lora_B[active_adapter]
|
| 50 |
+
lora_E = self.lora_E[active_adapter]
|
| 51 |
+
dropout = self.lora_dropout[active_adapter]
|
| 52 |
+
scaling = self.scaling[active_adapter]
|
| 53 |
+
ranknum = self.ranknum[active_adapter] + 1e-5
|
| 54 |
+
|
| 55 |
+
requires_conversion = not torch.is_autocast_enabled()
|
| 56 |
+
if requires_conversion:
|
| 57 |
+
expected_dtype = result.dtype
|
| 58 |
+
x = self._cast_input_dtype(x, torch.float32)
|
| 59 |
+
|
| 60 |
+
output = (dropout(x) @ (lora_A * lora_E).T @ lora_B.T) * scaling / ranknum
|
| 61 |
+
# TODO: here, the dtype conversion is applied on the *whole expression*,
|
| 62 |
+
# not the intermediate result, unlike for SVDLinear8bitLT and
|
| 63 |
+
# SVDLinear4bit, is that correct?
|
| 64 |
+
if requires_conversion:
|
| 65 |
+
output = output.to(expected_dtype)
|
| 66 |
+
result += output
|
| 67 |
+
return result
|
| 68 |
+
|
| 69 |
+
def __repr__(self) -> str:
|
| 70 |
+
rep = super().__repr__()
|
| 71 |
+
return "adalora." + rep
|
lib/python3.12/site-packages/peft/tuners/adalora/layer.py
ADDED
|
@@ -0,0 +1,358 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2023-present the HuggingFace Inc. team.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
import warnings
|
| 16 |
+
from typing import Any, List, Optional
|
| 17 |
+
|
| 18 |
+
import packaging
|
| 19 |
+
import torch
|
| 20 |
+
import transformers
|
| 21 |
+
from torch import nn
|
| 22 |
+
|
| 23 |
+
from peft.tuners.lora import LoraLayer
|
| 24 |
+
from peft.tuners.tuners_utils import check_adapters_to_merge
|
| 25 |
+
from peft.utils import transpose
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
if packaging.version.parse(transformers.__version__) >= packaging.version.parse("4.33.0"):
|
| 29 |
+
from transformers.integrations import deepspeed_config
|
| 30 |
+
else:
|
| 31 |
+
from transformers.deepspeed import deepspeed_config
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
class AdaLoraLayer(LoraLayer):
|
| 35 |
+
# List all names of layers that may contain adapter weights
|
| 36 |
+
# Note: ranknum doesn't need to be included as it is not an nn.Module
|
| 37 |
+
adapter_layer_names = ("lora_A", "lora_B", "lora_E", "lora_embedding_A", "lora_embedding_B")
|
| 38 |
+
# All names of other parameters that may contain adapter-related parameters
|
| 39 |
+
other_param_names = ("r", "lora_alpha", "scaling", "lora_dropout", "ranknum")
|
| 40 |
+
|
| 41 |
+
def __init__(self, base_layer: nn.Module) -> None:
|
| 42 |
+
super().__init__(base_layer)
|
| 43 |
+
self.lora_E = nn.ParameterDict({})
|
| 44 |
+
self.lora_A = nn.ParameterDict({})
|
| 45 |
+
self.lora_B = nn.ParameterDict({})
|
| 46 |
+
self.ranknum = nn.ParameterDict({})
|
| 47 |
+
|
| 48 |
+
def update_layer(self, adapter_name, r, lora_alpha, lora_dropout, init_lora_weights):
|
| 49 |
+
if r < 0:
|
| 50 |
+
# note: r == 0 is allowed for AdaLora, see #1539
|
| 51 |
+
raise ValueError(f"`r` should be a positive integer or 0, but the value passed is {r}")
|
| 52 |
+
|
| 53 |
+
self.r[adapter_name] = r
|
| 54 |
+
self.lora_alpha[adapter_name] = lora_alpha
|
| 55 |
+
if lora_dropout > 0.0:
|
| 56 |
+
lora_dropout_layer = nn.Dropout(p=lora_dropout)
|
| 57 |
+
else:
|
| 58 |
+
lora_dropout_layer = nn.Identity()
|
| 59 |
+
|
| 60 |
+
self.lora_dropout[adapter_name] = lora_dropout_layer
|
| 61 |
+
# Actual trainable parameters
|
| 62 |
+
# Right singular vectors
|
| 63 |
+
self.lora_A[adapter_name] = nn.Parameter(torch.randn(r, self.in_features))
|
| 64 |
+
# Singular values
|
| 65 |
+
self.lora_E[adapter_name] = nn.Parameter(torch.randn(r, 1))
|
| 66 |
+
# Left singular vectors
|
| 67 |
+
self.lora_B[adapter_name] = nn.Parameter(torch.randn(self.out_features, r))
|
| 68 |
+
# The current rank
|
| 69 |
+
self.ranknum[adapter_name] = nn.Parameter(torch.randn(1), requires_grad=False)
|
| 70 |
+
self.ranknum[adapter_name].data.fill_(float(r))
|
| 71 |
+
self.ranknum[adapter_name].requires_grad = False
|
| 72 |
+
self.scaling[adapter_name] = lora_alpha if lora_alpha > 0 else float(r)
|
| 73 |
+
if init_lora_weights:
|
| 74 |
+
self.reset_lora_parameters(adapter_name)
|
| 75 |
+
|
| 76 |
+
self._move_adapter_to_device_of_base_layer(adapter_name)
|
| 77 |
+
self.set_adapter(self.active_adapters)
|
| 78 |
+
|
| 79 |
+
def reset_lora_parameters(self, adapter_name):
|
| 80 |
+
if adapter_name in self.lora_A.keys():
|
| 81 |
+
nn.init.zeros_(self.lora_E[adapter_name])
|
| 82 |
+
nn.init.normal_(self.lora_A[adapter_name], mean=0.0, std=0.02)
|
| 83 |
+
nn.init.normal_(self.lora_B[adapter_name], mean=0.0, std=0.02)
|
| 84 |
+
|
| 85 |
+
|
| 86 |
+
class SVDLinear(nn.Module, AdaLoraLayer):
|
| 87 |
+
# SVD-based adaptation by a dense layer
|
| 88 |
+
def __init__(
|
| 89 |
+
self,
|
| 90 |
+
base_layer: nn.Module,
|
| 91 |
+
adapter_name: str,
|
| 92 |
+
r: int = 0,
|
| 93 |
+
lora_alpha: int = 1,
|
| 94 |
+
lora_dropout: float = 0.0,
|
| 95 |
+
fan_in_fan_out: bool = False,
|
| 96 |
+
init_lora_weights: bool = True,
|
| 97 |
+
**kwargs,
|
| 98 |
+
) -> None:
|
| 99 |
+
super().__init__()
|
| 100 |
+
AdaLoraLayer.__init__(self, base_layer)
|
| 101 |
+
# Freezing the pre-trained weight matrix
|
| 102 |
+
self.get_base_layer().weight.requires_grad = False
|
| 103 |
+
|
| 104 |
+
self.fan_in_fan_out = fan_in_fan_out
|
| 105 |
+
self._active_adapter = adapter_name
|
| 106 |
+
self.update_layer(adapter_name, r, lora_alpha, lora_dropout, init_lora_weights)
|
| 107 |
+
|
| 108 |
+
def merge(self, safe_merge: bool = False, adapter_names: Optional[List[str]] = None) -> None:
|
| 109 |
+
"""
|
| 110 |
+
Merge the active adapter weights into the base weights
|
| 111 |
+
|
| 112 |
+
Args:
|
| 113 |
+
safe_merge (`bool`, *optional*):
|
| 114 |
+
If True, the merge operation will be performed in a copy of the original weights and check for NaNs
|
| 115 |
+
before merging the weights. This is useful if you want to check if the merge operation will produce
|
| 116 |
+
NaNs. Defaults to `False`.
|
| 117 |
+
adapter_names (`List[str]`, *optional*):
|
| 118 |
+
The list of adapter names that should be merged. If None, all active adapters will be merged. Defaults
|
| 119 |
+
to `None`.
|
| 120 |
+
"""
|
| 121 |
+
adapter_names = check_adapters_to_merge(self, adapter_names)
|
| 122 |
+
if not adapter_names:
|
| 123 |
+
# no adapter to merge
|
| 124 |
+
return
|
| 125 |
+
|
| 126 |
+
for active_adapter in adapter_names:
|
| 127 |
+
base_layer = self.get_base_layer()
|
| 128 |
+
if active_adapter in self.lora_A.keys():
|
| 129 |
+
if safe_merge:
|
| 130 |
+
# Note that safe_merge will be slower than the normal merge
|
| 131 |
+
# because of the copy operation.
|
| 132 |
+
orig_weights = base_layer.weight.data.clone()
|
| 133 |
+
orig_weights += self.get_delta_weight(active_adapter)
|
| 134 |
+
|
| 135 |
+
if not torch.isfinite(orig_weights).all():
|
| 136 |
+
raise ValueError(
|
| 137 |
+
f"NaNs detected in the merged weights. The adapter {active_adapter} seems to be broken"
|
| 138 |
+
)
|
| 139 |
+
|
| 140 |
+
base_layer.weight.data = orig_weights
|
| 141 |
+
else:
|
| 142 |
+
base_layer.weight.data += self.get_delta_weight(active_adapter)
|
| 143 |
+
self.merged_adapters.append(active_adapter)
|
| 144 |
+
|
| 145 |
+
def unmerge(self) -> None:
|
| 146 |
+
"""
|
| 147 |
+
This method unmerges all merged adapter layers from the base weights.
|
| 148 |
+
"""
|
| 149 |
+
if not self.merged:
|
| 150 |
+
warnings.warn("Already unmerged. Nothing to do.")
|
| 151 |
+
return
|
| 152 |
+
while len(self.merged_adapters) > 0:
|
| 153 |
+
active_adapter = self.merged_adapters.pop()
|
| 154 |
+
if active_adapter in self.lora_A.keys():
|
| 155 |
+
self.get_base_layer().weight.data -= self.get_delta_weight(active_adapter)
|
| 156 |
+
|
| 157 |
+
def get_delta_weight(self, adapter) -> torch.Tensor:
|
| 158 |
+
return (
|
| 159 |
+
transpose(self.lora_B[adapter] @ (self.lora_A[adapter] * self.lora_E[adapter]), self.fan_in_fan_out)
|
| 160 |
+
* self.scaling[adapter]
|
| 161 |
+
/ (self.ranknum[adapter] + 1e-5)
|
| 162 |
+
)
|
| 163 |
+
|
| 164 |
+
def forward(self, x: torch.Tensor, *args: Any, **kwargs: Any) -> torch.Tensor:
|
| 165 |
+
if self.disable_adapters:
|
| 166 |
+
if self.merged:
|
| 167 |
+
self.unmerge()
|
| 168 |
+
result = self.base_layer(x, *args, **kwargs)
|
| 169 |
+
elif self.merged:
|
| 170 |
+
result = self.base_layer(x, *args, **kwargs)
|
| 171 |
+
else:
|
| 172 |
+
result = self.base_layer(x, *args, **kwargs)
|
| 173 |
+
for active_adapter in self.active_adapters:
|
| 174 |
+
if active_adapter not in self.lora_A.keys():
|
| 175 |
+
continue
|
| 176 |
+
lora_A = self.lora_A[active_adapter]
|
| 177 |
+
lora_B = self.lora_B[active_adapter]
|
| 178 |
+
lora_E = self.lora_E[active_adapter]
|
| 179 |
+
dropout = self.lora_dropout[active_adapter]
|
| 180 |
+
scaling = self.scaling[active_adapter]
|
| 181 |
+
ranknum = self.ranknum[active_adapter] + 1e-5
|
| 182 |
+
|
| 183 |
+
x = self._cast_input_dtype(x, lora_A.dtype)
|
| 184 |
+
result += (dropout(x) @ (lora_A * lora_E).T @ lora_B.T) * scaling / ranknum
|
| 185 |
+
|
| 186 |
+
return result
|
| 187 |
+
|
| 188 |
+
def __repr__(self) -> str:
|
| 189 |
+
rep = super().__repr__()
|
| 190 |
+
return "adalora." + rep
|
| 191 |
+
|
| 192 |
+
|
| 193 |
+
class RankAllocator:
|
| 194 |
+
"""
|
| 195 |
+
The RankAllocator for AdaLoraModel. Paper: https://openreview.net/pdf?id=lq62uWRJjiY
|
| 196 |
+
|
| 197 |
+
Args:
|
| 198 |
+
config ([`AdaLoraConfig`]): The configuration of the AdaLora model.
|
| 199 |
+
model: the model that we apply AdaLoRA to.
|
| 200 |
+
|
| 201 |
+
"""
|
| 202 |
+
|
| 203 |
+
def __init__(self, model, peft_config, adapter_name):
|
| 204 |
+
self.peft_config = peft_config
|
| 205 |
+
self.adapter_name = adapter_name
|
| 206 |
+
self.beta1 = peft_config.beta1
|
| 207 |
+
self.beta2 = peft_config.beta2
|
| 208 |
+
assert self.beta1 > 0 and self.beta1 < 1
|
| 209 |
+
assert self.beta2 > 0 and self.beta2 < 1
|
| 210 |
+
|
| 211 |
+
self.reset_ipt()
|
| 212 |
+
self._set_budget_scheduler(model)
|
| 213 |
+
|
| 214 |
+
def set_total_step(self, total_step):
|
| 215 |
+
self.peft_config.total_step = total_step
|
| 216 |
+
|
| 217 |
+
def reset_ipt(self):
|
| 218 |
+
self.ipt = {}
|
| 219 |
+
self.exp_avg_ipt = {}
|
| 220 |
+
self.exp_avg_unc = {}
|
| 221 |
+
|
| 222 |
+
def _set_budget_scheduler(self, model):
|
| 223 |
+
self.init_bgt = 0
|
| 224 |
+
self.name_set = set()
|
| 225 |
+
for n, p in model.named_parameters():
|
| 226 |
+
if f"lora_A.{self.adapter_name}" in n:
|
| 227 |
+
self.init_bgt += p.size(0)
|
| 228 |
+
self.name_set.add(n.replace("lora_A", "%s"))
|
| 229 |
+
self.name_set = sorted(self.name_set)
|
| 230 |
+
# The total final rank budget
|
| 231 |
+
self.target_bgt = self.peft_config.target_r * len(self.name_set)
|
| 232 |
+
|
| 233 |
+
def budget_schedule(self, step: int):
|
| 234 |
+
tinit = self.peft_config.tinit
|
| 235 |
+
tfinal = self.peft_config.tfinal
|
| 236 |
+
total_step = self.peft_config.total_step
|
| 237 |
+
# Initial warmup
|
| 238 |
+
if step <= tinit:
|
| 239 |
+
budget = self.init_bgt
|
| 240 |
+
mask_ind = False
|
| 241 |
+
# Final fine-tuning
|
| 242 |
+
elif step > total_step - tfinal:
|
| 243 |
+
budget = self.target_bgt
|
| 244 |
+
mask_ind = True
|
| 245 |
+
else:
|
| 246 |
+
# Budget decreasing with a cubic scheduler
|
| 247 |
+
mul_coeff = 1 - (step - tinit) / (total_step - tfinal - tinit)
|
| 248 |
+
budget = int((self.init_bgt - self.target_bgt) * (mul_coeff**3) + self.target_bgt)
|
| 249 |
+
mask_ind = True if step % self.peft_config.deltaT == 0 else False
|
| 250 |
+
return budget, mask_ind
|
| 251 |
+
|
| 252 |
+
def update_ipt(self, model):
|
| 253 |
+
# Update the sensitivity and uncertainty for every weight
|
| 254 |
+
for n, p in model.named_parameters():
|
| 255 |
+
if "lora_" in n and self.adapter_name in n:
|
| 256 |
+
if n not in self.ipt:
|
| 257 |
+
self.ipt[n] = torch.zeros_like(p)
|
| 258 |
+
self.exp_avg_ipt[n] = torch.zeros_like(p)
|
| 259 |
+
self.exp_avg_unc[n] = torch.zeros_like(p)
|
| 260 |
+
with torch.no_grad():
|
| 261 |
+
if deepspeed_config() is not None:
|
| 262 |
+
import deepspeed
|
| 263 |
+
|
| 264 |
+
grad = deepspeed.utils.safe_get_full_grad(p)
|
| 265 |
+
self.ipt[n] = (p * grad).abs().detach()
|
| 266 |
+
else:
|
| 267 |
+
self.ipt[n] = (p * p.grad).abs().detach()
|
| 268 |
+
# Sensitivity smoothing
|
| 269 |
+
self.exp_avg_ipt[n] = self.beta1 * self.exp_avg_ipt[n] + (1 - self.beta1) * self.ipt[n]
|
| 270 |
+
# Uncertainty quantification
|
| 271 |
+
self.exp_avg_unc[n] = (
|
| 272 |
+
self.beta2 * self.exp_avg_unc[n] + (1 - self.beta2) * (self.ipt[n] - self.exp_avg_ipt[n]).abs()
|
| 273 |
+
)
|
| 274 |
+
|
| 275 |
+
def _element_score(self, n):
|
| 276 |
+
return self.exp_avg_ipt[n] * self.exp_avg_unc[n]
|
| 277 |
+
|
| 278 |
+
def _combine_ipt(self, ipt_E, ipt_AB):
|
| 279 |
+
ipt_AB = ipt_AB.sum(dim=1, keepdim=False)
|
| 280 |
+
sum_ipt = ipt_E.view(-1) + ipt_AB.view(-1)
|
| 281 |
+
return sum_ipt
|
| 282 |
+
|
| 283 |
+
def mask_to_budget(self, model, budget):
|
| 284 |
+
value_ipt = {}
|
| 285 |
+
vector_ipt = {}
|
| 286 |
+
triplet_ipt = {}
|
| 287 |
+
# Get the importance score for A, E, B
|
| 288 |
+
for n, p in model.named_parameters():
|
| 289 |
+
if f"lora_A.{self.adapter_name}" in n:
|
| 290 |
+
entry_ipt = self._element_score(n)
|
| 291 |
+
comb_ipt = torch.mean(entry_ipt, dim=1, keepdim=True)
|
| 292 |
+
name_m = n.replace("lora_A", "%s")
|
| 293 |
+
if name_m not in vector_ipt:
|
| 294 |
+
vector_ipt[name_m] = [comb_ipt]
|
| 295 |
+
else:
|
| 296 |
+
vector_ipt[name_m].append(comb_ipt)
|
| 297 |
+
if f"lora_B.{self.adapter_name}" in n:
|
| 298 |
+
entry_ipt = self._element_score(n)
|
| 299 |
+
comb_ipt = torch.mean(entry_ipt, dim=0, keepdim=False).view(-1, 1)
|
| 300 |
+
name_m = n.replace("lora_B", "%s")
|
| 301 |
+
if name_m not in vector_ipt:
|
| 302 |
+
vector_ipt[name_m] = [comb_ipt]
|
| 303 |
+
else:
|
| 304 |
+
vector_ipt[name_m].append(comb_ipt)
|
| 305 |
+
if f"lora_E.{self.adapter_name}" in n:
|
| 306 |
+
entry_ipt = self._element_score(n)
|
| 307 |
+
name_m = n.replace("lora_E", "%s")
|
| 308 |
+
value_ipt[name_m] = entry_ipt
|
| 309 |
+
|
| 310 |
+
all_score = []
|
| 311 |
+
# Calculate the score for each triplet
|
| 312 |
+
for name_m in vector_ipt:
|
| 313 |
+
ipt_E = value_ipt[name_m]
|
| 314 |
+
ipt_AB = torch.cat(vector_ipt[name_m], dim=1)
|
| 315 |
+
sum_ipt = self._combine_ipt(ipt_E, ipt_AB)
|
| 316 |
+
name_E = name_m % "lora_E"
|
| 317 |
+
triplet_ipt[name_E] = sum_ipt.view(-1, 1)
|
| 318 |
+
all_score.append(sum_ipt.view(-1))
|
| 319 |
+
|
| 320 |
+
# Get the threshold by ranking ipt
|
| 321 |
+
mask_threshold = torch.kthvalue(
|
| 322 |
+
torch.cat(all_score),
|
| 323 |
+
k=self.init_bgt - budget,
|
| 324 |
+
)[0].item()
|
| 325 |
+
|
| 326 |
+
rank_pattern = {}
|
| 327 |
+
# Mask the unimportant triplets
|
| 328 |
+
with torch.no_grad():
|
| 329 |
+
for n, p in model.named_parameters():
|
| 330 |
+
if f"lora_E.{self.adapter_name}" in n:
|
| 331 |
+
p.masked_fill_(triplet_ipt[n] <= mask_threshold, 0.0)
|
| 332 |
+
rank_pattern[n] = (~(triplet_ipt[n] <= mask_threshold)).view(-1).tolist()
|
| 333 |
+
return rank_pattern
|
| 334 |
+
|
| 335 |
+
def update_and_allocate(self, model, global_step, force_mask=False):
|
| 336 |
+
# # Update the importance score and allocate the budget
|
| 337 |
+
if global_step < self.peft_config.total_step - self.peft_config.tfinal:
|
| 338 |
+
self.update_ipt(model)
|
| 339 |
+
budget, mask_ind = self.budget_schedule(global_step)
|
| 340 |
+
# Allocate the budget according to importance scores
|
| 341 |
+
if mask_ind or force_mask:
|
| 342 |
+
rank_pattern = self.mask_to_budget(model, budget)
|
| 343 |
+
else:
|
| 344 |
+
rank_pattern = None
|
| 345 |
+
return budget, rank_pattern
|
| 346 |
+
|
| 347 |
+
def mask_using_rank_pattern(self, model, rank_pattern):
|
| 348 |
+
# Mask the unimportant triplets
|
| 349 |
+
is_adapter_name_truncated = False
|
| 350 |
+
if self.adapter_name not in next(iter(rank_pattern.keys())):
|
| 351 |
+
is_adapter_name_truncated = True
|
| 352 |
+
|
| 353 |
+
with torch.no_grad():
|
| 354 |
+
for n, p in model.named_parameters():
|
| 355 |
+
if f"lora_E.{self.adapter_name}" in n:
|
| 356 |
+
key = n if not is_adapter_name_truncated else n.replace(f".{self.adapter_name}", "")
|
| 357 |
+
mask = torch.Tensor(rank_pattern[key]).unsqueeze(-1).to(p.device)
|
| 358 |
+
p.masked_fill_(~mask.bool(), 0.0)
|
lib/python3.12/site-packages/peft/tuners/adalora/model.py
ADDED
|
@@ -0,0 +1,364 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2023-present the HuggingFace Inc. team.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
import warnings
|
| 16 |
+
|
| 17 |
+
import torch
|
| 18 |
+
from transformers.pytorch_utils import Conv1D
|
| 19 |
+
|
| 20 |
+
from peft.import_utils import is_bnb_4bit_available, is_bnb_available, is_gptqmodel_available
|
| 21 |
+
from peft.tuners.lora import LoraConfig, LoraModel
|
| 22 |
+
from peft.tuners.tuners_utils import BaseTunerLayer
|
| 23 |
+
from peft.utils import (
|
| 24 |
+
TRANSFORMERS_MODELS_TO_ADALORA_TARGET_MODULES_MAPPING,
|
| 25 |
+
_freeze_adapter,
|
| 26 |
+
_get_submodules,
|
| 27 |
+
get_auto_gptq_quant_linear,
|
| 28 |
+
get_gptqmodel_quant_linear,
|
| 29 |
+
get_quantization_config,
|
| 30 |
+
)
|
| 31 |
+
from peft.utils.integrations import gather_params_ctx
|
| 32 |
+
|
| 33 |
+
from .gptq import SVDQuantLinear
|
| 34 |
+
from .layer import AdaLoraLayer, RankAllocator, SVDLinear
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
class AdaLoraModel(LoraModel):
|
| 38 |
+
"""
|
| 39 |
+
Creates AdaLoRA (Adaptive LoRA) model from a pretrained transformers model. Paper:
|
| 40 |
+
https://openreview.net/forum?id=lq62uWRJjiY
|
| 41 |
+
|
| 42 |
+
Args:
|
| 43 |
+
model ([`transformers.PreTrainedModel`]): The model to be adapted.
|
| 44 |
+
config ([`AdaLoraConfig`]): The configuration of the AdaLora model.
|
| 45 |
+
adapter_name (`str`): The name of the adapter, defaults to `"default"`.
|
| 46 |
+
low_cpu_mem_usage (`bool`, `optional`, defaults to `False`):
|
| 47 |
+
Create empty adapter weights on meta device. Useful to speed up the loading process.
|
| 48 |
+
|
| 49 |
+
Returns:
|
| 50 |
+
`torch.nn.Module`: The AdaLora model.
|
| 51 |
+
|
| 52 |
+
Example::
|
| 53 |
+
|
| 54 |
+
>>> from transformers import AutoModelForSeq2SeqLM >>> from peft import LoraConfig, AdaLoraModel, AdaLoraConfig
|
| 55 |
+
>>> config = AdaLoraConfig(
|
| 56 |
+
peft_type="ADALORA", task_type="SEQ_2_SEQ_LM", init_r=12, lora_alpha=32, target_modules=["q", "v"],
|
| 57 |
+
lora_dropout=0.01,
|
| 58 |
+
)
|
| 59 |
+
>>> model = AutoModelForSeq2SeqLM.from_pretrained("t5-base") >>> model = AdaLoraModel(model, config, "default")
|
| 60 |
+
|
| 61 |
+
**Attributes**:
|
| 62 |
+
- **model** ([`transformers.PreTrainedModel`]) -- The model to be adapted.
|
| 63 |
+
- **peft_config** ([`AdaLoraConfig`]): The configuration of the AdaLora model.
|
| 64 |
+
"""
|
| 65 |
+
|
| 66 |
+
# Note: don't redefine prefix here, it should be inherited from LoraModel
|
| 67 |
+
|
| 68 |
+
def __init__(self, model, config, adapter_name):
|
| 69 |
+
super().__init__(model, config, adapter_name)
|
| 70 |
+
|
| 71 |
+
traininable_mode_counter = 0
|
| 72 |
+
for config in self.peft_config.values():
|
| 73 |
+
if not config.inference_mode:
|
| 74 |
+
traininable_mode_counter += 1
|
| 75 |
+
|
| 76 |
+
if traininable_mode_counter > 1:
|
| 77 |
+
raise ValueError(
|
| 78 |
+
"AdaLoraModel supports only 1 trainable adapter. "
|
| 79 |
+
"When using multiple adapters, set inference_mode to True for all adapters except the one you want to train."
|
| 80 |
+
)
|
| 81 |
+
|
| 82 |
+
if self.peft_config[adapter_name].inference_mode:
|
| 83 |
+
_freeze_adapter(self.model, adapter_name)
|
| 84 |
+
else:
|
| 85 |
+
self.trainable_adapter_name = adapter_name
|
| 86 |
+
self.rankallocator = RankAllocator(self.model, self.peft_config[adapter_name], self.trainable_adapter_name)
|
| 87 |
+
|
| 88 |
+
def _check_new_adapter_config(self, config: LoraConfig) -> None:
|
| 89 |
+
"""
|
| 90 |
+
A helper method to check the config when a new adapter is being added.
|
| 91 |
+
|
| 92 |
+
Raise a ValueError if there is something wrong with the config or if it conflicts with existing adapters.
|
| 93 |
+
|
| 94 |
+
"""
|
| 95 |
+
super()._check_new_adapter_config(config)
|
| 96 |
+
|
| 97 |
+
traininable_mode_counter = 0
|
| 98 |
+
for config_ in self.peft_config.values():
|
| 99 |
+
if not config_.inference_mode:
|
| 100 |
+
traininable_mode_counter += 1
|
| 101 |
+
|
| 102 |
+
if traininable_mode_counter > 1:
|
| 103 |
+
raise ValueError(
|
| 104 |
+
f"{self.__class__.__name__} supports only 1 trainable adapter. "
|
| 105 |
+
"When using multiple adapters, set inference_mode to True for all adapters except the one "
|
| 106 |
+
"you want to train."
|
| 107 |
+
)
|
| 108 |
+
|
| 109 |
+
def _create_and_replace(
|
| 110 |
+
self,
|
| 111 |
+
lora_config,
|
| 112 |
+
adapter_name,
|
| 113 |
+
target,
|
| 114 |
+
target_name,
|
| 115 |
+
parent,
|
| 116 |
+
current_key,
|
| 117 |
+
):
|
| 118 |
+
kwargs = {
|
| 119 |
+
"r": lora_config.init_r,
|
| 120 |
+
"lora_alpha": lora_config.lora_alpha,
|
| 121 |
+
"lora_dropout": lora_config.lora_dropout,
|
| 122 |
+
"fan_in_fan_out": lora_config.fan_in_fan_out,
|
| 123 |
+
"init_lora_weights": lora_config.init_lora_weights,
|
| 124 |
+
"loaded_in_8bit": getattr(self.model, "is_loaded_in_8bit", False),
|
| 125 |
+
"loaded_in_4bit": getattr(self.model, "is_loaded_in_4bit", False),
|
| 126 |
+
}
|
| 127 |
+
if (kwargs["loaded_in_8bit"] or kwargs["loaded_in_4bit"]) and not is_bnb_available():
|
| 128 |
+
raise ImportError(
|
| 129 |
+
"To use AdaLora with 8-bit quantization, please install the `bitsandbytes` package. "
|
| 130 |
+
"You can install it with `pip install bitsandbytes`."
|
| 131 |
+
)
|
| 132 |
+
|
| 133 |
+
quantization_config = get_quantization_config(self.model, method="gptq")
|
| 134 |
+
if quantization_config is not None:
|
| 135 |
+
kwargs["gptq_quantization_config"] = quantization_config
|
| 136 |
+
|
| 137 |
+
# If it is not an AdaLoraLayer, create a new module, else update it with new adapters
|
| 138 |
+
if not isinstance(target, AdaLoraLayer):
|
| 139 |
+
device_map = self.model.hf_device_map if hasattr(self.model, "hf_device_map") else None
|
| 140 |
+
new_module = self._create_new_module(lora_config, adapter_name, target, device_map=device_map, **kwargs)
|
| 141 |
+
if adapter_name not in self.active_adapters:
|
| 142 |
+
# adding an additional adapter: it is not automatically trainable
|
| 143 |
+
new_module.requires_grad_(False)
|
| 144 |
+
self._replace_module(parent, target_name, new_module, target)
|
| 145 |
+
else:
|
| 146 |
+
target.update_layer(
|
| 147 |
+
adapter_name,
|
| 148 |
+
lora_config.init_r,
|
| 149 |
+
lora_config.lora_alpha,
|
| 150 |
+
lora_config.lora_dropout,
|
| 151 |
+
lora_config.init_lora_weights,
|
| 152 |
+
)
|
| 153 |
+
|
| 154 |
+
@staticmethod
|
| 155 |
+
def _create_new_module(lora_config, adapter_name, target, device_map=None, **kwargs):
|
| 156 |
+
# avoid eager bnb import
|
| 157 |
+
if is_bnb_available():
|
| 158 |
+
import bitsandbytes as bnb
|
| 159 |
+
|
| 160 |
+
from .bnb import SVDLinear8bitLt
|
| 161 |
+
if is_bnb_4bit_available():
|
| 162 |
+
from .bnb import SVDLinear4bit
|
| 163 |
+
|
| 164 |
+
gptq_quantization_config = kwargs.get("gptq_quantization_config", None)
|
| 165 |
+
|
| 166 |
+
if is_gptqmodel_available():
|
| 167 |
+
QuantLinear = get_gptqmodel_quant_linear(gptq_quantization_config, device_map=device_map)
|
| 168 |
+
else:
|
| 169 |
+
QuantLinear = get_auto_gptq_quant_linear(gptq_quantization_config)
|
| 170 |
+
|
| 171 |
+
loaded_in_8bit = kwargs.pop("loaded_in_8bit", False)
|
| 172 |
+
loaded_in_4bit = kwargs.pop("loaded_in_4bit", False)
|
| 173 |
+
|
| 174 |
+
if isinstance(target, BaseTunerLayer):
|
| 175 |
+
target_base_layer = target.get_base_layer()
|
| 176 |
+
else:
|
| 177 |
+
target_base_layer = target
|
| 178 |
+
|
| 179 |
+
if loaded_in_8bit and isinstance(target_base_layer, bnb.nn.Linear8bitLt):
|
| 180 |
+
kwargs.update(
|
| 181 |
+
{
|
| 182 |
+
"has_fp16_weights": target_base_layer.state.has_fp16_weights,
|
| 183 |
+
"threshold": target_base_layer.state.threshold,
|
| 184 |
+
"index": target_base_layer.index,
|
| 185 |
+
}
|
| 186 |
+
)
|
| 187 |
+
new_module = SVDLinear8bitLt(target, adapter_name, **kwargs)
|
| 188 |
+
elif loaded_in_4bit and is_bnb_4bit_available() and isinstance(target_base_layer, bnb.nn.Linear4bit):
|
| 189 |
+
fourbit_kwargs = kwargs.copy()
|
| 190 |
+
fourbit_kwargs.update(
|
| 191 |
+
{
|
| 192 |
+
"compute_dtype": target_base_layer.compute_dtype,
|
| 193 |
+
"compress_statistics": target_base_layer.weight.compress_statistics,
|
| 194 |
+
"quant_type": target_base_layer.weight.quant_type,
|
| 195 |
+
}
|
| 196 |
+
)
|
| 197 |
+
new_module = SVDLinear4bit(target, adapter_name, **fourbit_kwargs)
|
| 198 |
+
elif QuantLinear is not None and isinstance(target, QuantLinear):
|
| 199 |
+
new_module = SVDQuantLinear(target, adapter_name, **kwargs)
|
| 200 |
+
else:
|
| 201 |
+
if isinstance(target_base_layer, torch.nn.Linear):
|
| 202 |
+
if kwargs["fan_in_fan_out"]:
|
| 203 |
+
warnings.warn(
|
| 204 |
+
"fan_in_fan_out is set to True but the target module is `torch.nn.Linear`. "
|
| 205 |
+
"Setting fan_in_fan_out to False."
|
| 206 |
+
)
|
| 207 |
+
kwargs["fan_in_fan_out"] = lora_config.fan_in_fan_out = False
|
| 208 |
+
elif isinstance(target_base_layer, Conv1D):
|
| 209 |
+
if not kwargs["fan_in_fan_out"]:
|
| 210 |
+
warnings.warn(
|
| 211 |
+
"fan_in_fan_out is set to False but the target module is `Conv1D`. "
|
| 212 |
+
"Setting fan_in_fan_out to True."
|
| 213 |
+
)
|
| 214 |
+
kwargs["fan_in_fan_out"] = lora_config.fan_in_fan_out = True
|
| 215 |
+
else:
|
| 216 |
+
raise ValueError(
|
| 217 |
+
f"Target module {target} is not supported. "
|
| 218 |
+
f"Currently, only `torch.nn.Linear` and `Conv1D` are supported."
|
| 219 |
+
)
|
| 220 |
+
new_module = SVDLinear(target, adapter_name, **kwargs)
|
| 221 |
+
|
| 222 |
+
return new_module
|
| 223 |
+
|
| 224 |
+
@staticmethod
|
| 225 |
+
def _prepare_adapter_config(peft_config, model_config):
|
| 226 |
+
if peft_config.target_modules is None:
|
| 227 |
+
if model_config["model_type"] not in TRANSFORMERS_MODELS_TO_ADALORA_TARGET_MODULES_MAPPING:
|
| 228 |
+
raise ValueError("Please specify `target_modules` in `peft_config`")
|
| 229 |
+
peft_config.target_modules = TRANSFORMERS_MODELS_TO_ADALORA_TARGET_MODULES_MAPPING[
|
| 230 |
+
model_config["model_type"]
|
| 231 |
+
]
|
| 232 |
+
return peft_config
|
| 233 |
+
|
| 234 |
+
def __getattr__(self, name: str):
|
| 235 |
+
"""Forward missing attributes to the wrapped module."""
|
| 236 |
+
try:
|
| 237 |
+
return super().__getattr__(name) # defer to nn.Module's logic
|
| 238 |
+
except AttributeError:
|
| 239 |
+
if name == "model": # see #1892: prevent infinite recursion if class is not initialized
|
| 240 |
+
raise
|
| 241 |
+
return getattr(self.model, name)
|
| 242 |
+
|
| 243 |
+
def forward(self, *args, **kwargs):
|
| 244 |
+
outputs = self.model.forward(*args, **kwargs)
|
| 245 |
+
|
| 246 |
+
if (getattr(outputs, "loss", None) is not None) and isinstance(outputs.loss, torch.Tensor):
|
| 247 |
+
# Calculate the orthogonal regularization
|
| 248 |
+
orth_reg_weight = self.peft_config[self.trainable_adapter_name].orth_reg_weight
|
| 249 |
+
|
| 250 |
+
if orth_reg_weight <= 0:
|
| 251 |
+
raise ValueError("orth_reg_weight should be greater than 0. ")
|
| 252 |
+
|
| 253 |
+
regu_loss = 0
|
| 254 |
+
num_param = 0
|
| 255 |
+
for n, p in self.model.named_parameters():
|
| 256 |
+
if ("lora_A" in n or "lora_B" in n) and self.trainable_adapter_name in n:
|
| 257 |
+
if p.shape == torch.Size([0]):
|
| 258 |
+
with gather_params_ctx(p, fwd_module=self):
|
| 259 |
+
para_cov = p @ p.T if "lora_A" in n else p.T @ p
|
| 260 |
+
else:
|
| 261 |
+
para_cov = p @ p.T if "lora_A" in n else p.T @ p
|
| 262 |
+
I = torch.eye(*para_cov.size(), out=torch.empty_like(para_cov)) # noqa: E741
|
| 263 |
+
I.requires_grad = False
|
| 264 |
+
num_param += 1
|
| 265 |
+
regu_loss += torch.norm(para_cov - I, p="fro")
|
| 266 |
+
if num_param > 0:
|
| 267 |
+
regu_loss = regu_loss / num_param
|
| 268 |
+
else:
|
| 269 |
+
regu_loss = 0
|
| 270 |
+
outputs.loss += orth_reg_weight * regu_loss
|
| 271 |
+
return outputs
|
| 272 |
+
|
| 273 |
+
def resize_modules_by_rank_pattern(self, rank_pattern, adapter_name):
|
| 274 |
+
lora_config = self.peft_config[adapter_name]
|
| 275 |
+
for name, rank_idx in rank_pattern.items():
|
| 276 |
+
if isinstance(rank_idx, list):
|
| 277 |
+
rank = sum(rank_idx)
|
| 278 |
+
elif isinstance(rank_idx, torch.Tensor):
|
| 279 |
+
rank_idx = rank_idx.view(-1)
|
| 280 |
+
rank = rank_idx.sum().item()
|
| 281 |
+
else:
|
| 282 |
+
raise ValueError("Unexpected type of rank_idx")
|
| 283 |
+
key = ".".join(name.split(".")[0:-2]) if adapter_name in name else ".".join(name.split(".")[0:-1])
|
| 284 |
+
_, target, _ = _get_submodules(self.model, key)
|
| 285 |
+
lora_E_weights = target.lora_E[adapter_name][rank_idx]
|
| 286 |
+
lora_A_weights = target.lora_A[adapter_name][rank_idx]
|
| 287 |
+
lora_B_weights = target.lora_B[adapter_name][:, rank_idx]
|
| 288 |
+
ranknum = target.ranknum[adapter_name]
|
| 289 |
+
target.update_layer(
|
| 290 |
+
adapter_name,
|
| 291 |
+
rank,
|
| 292 |
+
lora_config.lora_alpha,
|
| 293 |
+
lora_config.lora_dropout,
|
| 294 |
+
lora_config.init_lora_weights,
|
| 295 |
+
)
|
| 296 |
+
with torch.no_grad():
|
| 297 |
+
if rank > 0:
|
| 298 |
+
target.lora_E[adapter_name].copy_(lora_E_weights)
|
| 299 |
+
target.lora_A[adapter_name].copy_(lora_A_weights)
|
| 300 |
+
target.lora_B[adapter_name].copy_(lora_B_weights)
|
| 301 |
+
# The scaling is exactly as the previous
|
| 302 |
+
target.ranknum[adapter_name].copy_(ranknum)
|
| 303 |
+
|
| 304 |
+
def resize_state_dict_by_rank_pattern(self, rank_pattern, state_dict, adapter_name):
|
| 305 |
+
for name, rank_idx in rank_pattern.items():
|
| 306 |
+
rank = sum(rank_idx)
|
| 307 |
+
prefix = ".".join(name.split(".")[0:-2]) if adapter_name in name else ".".join(name.split(".")[0:-1])
|
| 308 |
+
for layer in ["lora_E", "lora_A", "lora_B"]:
|
| 309 |
+
key = f"base_model.model.{prefix}.{layer}.{adapter_name}"
|
| 310 |
+
if layer != "lora_B":
|
| 311 |
+
state_dict[key] = (
|
| 312 |
+
state_dict[key][rank_idx] if rank != state_dict[key].shape[0] else state_dict[key]
|
| 313 |
+
)
|
| 314 |
+
else:
|
| 315 |
+
state_dict[key] = (
|
| 316 |
+
state_dict[key][:, rank_idx] if rank != state_dict[key].shape[1] else state_dict[key]
|
| 317 |
+
)
|
| 318 |
+
return state_dict
|
| 319 |
+
|
| 320 |
+
def update_and_allocate(self, global_step):
|
| 321 |
+
"""
|
| 322 |
+
This method updates Adalora budget and mask.
|
| 323 |
+
|
| 324 |
+
This should be called in every training step after `loss.backward()` and before `zero_grad()`.
|
| 325 |
+
|
| 326 |
+
`tinit`, `tfinal` and `deltaT` are handled with in the method.
|
| 327 |
+
|
| 328 |
+
Args:
|
| 329 |
+
global_step (`int`): The current training step, it is used to calculate adalora budget.
|
| 330 |
+
|
| 331 |
+
Example:
|
| 332 |
+
|
| 333 |
+
```python
|
| 334 |
+
>>> loss = model(**input).loss
|
| 335 |
+
>>> loss.backward()
|
| 336 |
+
>>> optimizer.step()
|
| 337 |
+
>>> model.base_model.update_and_allocate(i_step)
|
| 338 |
+
>>> optimizer.zero_grad()
|
| 339 |
+
```
|
| 340 |
+
"""
|
| 341 |
+
lora_config = self.peft_config[self.trainable_adapter_name]
|
| 342 |
+
# Update the importance score and allocate the budget
|
| 343 |
+
if global_step < lora_config.total_step - lora_config.tfinal:
|
| 344 |
+
_, rank_pattern = self.rankallocator.update_and_allocate(self.model, global_step)
|
| 345 |
+
if rank_pattern:
|
| 346 |
+
lora_config.rank_pattern = rank_pattern
|
| 347 |
+
# Finalize the budget allocation
|
| 348 |
+
elif global_step == lora_config.total_step - lora_config.tfinal:
|
| 349 |
+
_, rank_pattern = self.rankallocator.update_and_allocate(self.model, global_step, force_mask=True)
|
| 350 |
+
# for some reason, this freezes the trainable parameters and nothing gets updates
|
| 351 |
+
# self.resize_modules_by_rank_pattern(rank_pattern, self.trainable_adapter_name)
|
| 352 |
+
lora_config.rank_pattern = rank_pattern
|
| 353 |
+
self.rankallocator.reset_ipt()
|
| 354 |
+
# Currently using inefficient way to mask the unimportant weights using the rank pattern
|
| 355 |
+
# due to problem mentioned above
|
| 356 |
+
elif global_step > lora_config.total_step - lora_config.tfinal:
|
| 357 |
+
self.rankallocator.mask_using_rank_pattern(self.model, lora_config.rank_pattern)
|
| 358 |
+
# Pass the function and do forward propagation
|
| 359 |
+
else:
|
| 360 |
+
return None
|
| 361 |
+
|
| 362 |
+
def add_weighted_adapter(self, *args, **kwargs):
|
| 363 |
+
"""This method is not supported for AdaLoRA, use LoRA instead."""
|
| 364 |
+
raise TypeError(f"{self.__class__.__name__} does not support add_weighted_adapter method.")
|
lib/python3.12/site-packages/peft/tuners/adaption_prompt/__init__.py
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2023-present the HuggingFace Inc. team.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
from peft.utils import register_peft_method
|
| 15 |
+
|
| 16 |
+
from .config import AdaptionPromptConfig
|
| 17 |
+
from .layer import AdaptedAttention
|
| 18 |
+
from .model import AdaptionPromptModel
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
__all__ = ["AdaptedAttention", "AdaptionPromptConfig", "AdaptionPromptModel"]
|
| 22 |
+
|
| 23 |
+
register_peft_method(name="adaption_prompt", config_cls=AdaptionPromptConfig, model_cls=AdaptionPromptModel)
|
lib/python3.12/site-packages/peft/tuners/adaption_prompt/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (547 Bytes). View file
|
|
|
lib/python3.12/site-packages/peft/tuners/adaption_prompt/__pycache__/config.cpython-312.pyc
ADDED
|
Binary file (2.73 kB). View file
|
|
|
lib/python3.12/site-packages/peft/tuners/adaption_prompt/__pycache__/layer.cpython-312.pyc
ADDED
|
Binary file (6.62 kB). View file
|
|
|
lib/python3.12/site-packages/peft/tuners/adaption_prompt/__pycache__/model.cpython-312.pyc
ADDED
|
Binary file (8.36 kB). View file
|
|
|
lib/python3.12/site-packages/peft/tuners/adaption_prompt/__pycache__/utils.cpython-312.pyc
ADDED
|
Binary file (6.45 kB). View file
|
|
|
lib/python3.12/site-packages/peft/tuners/adaption_prompt/config.py
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2023-present the HuggingFace Inc. team.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
from collections import namedtuple
|
| 16 |
+
from dataclasses import dataclass, field
|
| 17 |
+
|
| 18 |
+
from peft.config import PeftConfig
|
| 19 |
+
from peft.utils import PeftType
|
| 20 |
+
|
| 21 |
+
from .utils import llama_compute_query_states
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
@dataclass
|
| 25 |
+
class AdaptionPromptConfig(PeftConfig):
|
| 26 |
+
"""Stores the configuration of an [`AdaptionPromptModel`]."""
|
| 27 |
+
|
| 28 |
+
target_modules: str = field(
|
| 29 |
+
default=None, metadata={"help": "Name of the attention submodules to insert adaption prompts into."}
|
| 30 |
+
)
|
| 31 |
+
adapter_len: int = field(default=None, metadata={"help": "Number of adapter tokens to insert"})
|
| 32 |
+
adapter_layers: int = field(default=None, metadata={"help": "Number of adapter layers (from the top)"})
|
| 33 |
+
|
| 34 |
+
def __post_init__(self):
|
| 35 |
+
super().__post_init__()
|
| 36 |
+
self.peft_type = PeftType.ADAPTION_PROMPT
|
| 37 |
+
|
| 38 |
+
@property
|
| 39 |
+
def is_adaption_prompt(self) -> bool:
|
| 40 |
+
"""Return True if this is an adaption prompt config."""
|
| 41 |
+
return True
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
# Contains the config that is specific to a transformers model type.
|
| 45 |
+
ModelTypeConfig = namedtuple(
|
| 46 |
+
"ModelTypeConfig", ["compute_query_states", "target_modules", "k_proj_layer", "v_proj_layer", "o_proj_layer"]
|
| 47 |
+
)
|
| 48 |
+
|
| 49 |
+
# Mapping of transformers model types to their specific configuration.
|
| 50 |
+
TRANSFORMERS_MODEL_CONFIG = {
|
| 51 |
+
"llama": ModelTypeConfig(
|
| 52 |
+
compute_query_states=llama_compute_query_states,
|
| 53 |
+
target_modules="self_attn",
|
| 54 |
+
k_proj_layer="k_proj",
|
| 55 |
+
v_proj_layer="v_proj",
|
| 56 |
+
o_proj_layer="o_proj",
|
| 57 |
+
),
|
| 58 |
+
"mistral": ModelTypeConfig( # same as llama,
|
| 59 |
+
compute_query_states=llama_compute_query_states,
|
| 60 |
+
target_modules="self_attn",
|
| 61 |
+
k_proj_layer="k_proj",
|
| 62 |
+
v_proj_layer="v_proj",
|
| 63 |
+
o_proj_layer="o_proj",
|
| 64 |
+
),
|
| 65 |
+
}
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
def prepare_config(
|
| 69 |
+
peft_config: AdaptionPromptConfig,
|
| 70 |
+
model,
|
| 71 |
+
) -> AdaptionPromptConfig:
|
| 72 |
+
"""Prepare the config based on the llama model type."""
|
| 73 |
+
if model.config.model_type not in TRANSFORMERS_MODEL_CONFIG:
|
| 74 |
+
raise ValueError("Unsupported model type for adaption prompt: '{model.config.model_type}'.")
|
| 75 |
+
|
| 76 |
+
model_config = TRANSFORMERS_MODEL_CONFIG[model.config.model_type]
|
| 77 |
+
|
| 78 |
+
if peft_config.target_modules is None:
|
| 79 |
+
peft_config.target_modules = model_config.target_modules
|
| 80 |
+
|
| 81 |
+
return peft_config
|
lib/python3.12/site-packages/peft/tuners/adaption_prompt/layer.py
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2023-present the HuggingFace Inc. team.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
import math
|
| 16 |
+
|
| 17 |
+
import torch
|
| 18 |
+
import torch.nn as nn
|
| 19 |
+
import torch.nn.functional as F
|
| 20 |
+
|
| 21 |
+
from .config import TRANSFORMERS_MODEL_CONFIG
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
class AdaptedAttention(nn.Module):
|
| 25 |
+
"""This module wraps a LLamaAttention module and injects adaption prompts."""
|
| 26 |
+
|
| 27 |
+
def __init__(self, model_type: str, adapter_len: int, model):
|
| 28 |
+
"""
|
| 29 |
+
Initialize object.
|
| 30 |
+
|
| 31 |
+
Args:
|
| 32 |
+
model_type: The transformer model type. This is used to retrieve the right method to
|
| 33 |
+
compute query states.
|
| 34 |
+
adapter_len: The length of the adaption prompt to insert.
|
| 35 |
+
model: The original transformer attention module that is being wrapped.
|
| 36 |
+
"""
|
| 37 |
+
assert not isinstance(model, AdaptedAttention)
|
| 38 |
+
super().__init__()
|
| 39 |
+
self.model_type = model_type
|
| 40 |
+
self.model = model
|
| 41 |
+
self.adapter_len = adapter_len
|
| 42 |
+
# Assume all parameters of the attention model we are wrapping are on the same device.
|
| 43 |
+
device = next(model.parameters()).device
|
| 44 |
+
# Don't think this was specified in the paper, but we follow the official repo which used an Embedding
|
| 45 |
+
# which initializes the tokens with standard normal values.
|
| 46 |
+
# https://github.com/ZrrSkywalker/LLaMA-Adapter/blob/41c3546fe1997ab8a65809dc8d8f9252b19d9faf/llama/model.py#L234
|
| 47 |
+
# (bsz, adapter_len, hidden_size)
|
| 48 |
+
target_dtype = (
|
| 49 |
+
model.q_proj.weight.dtype if model.q_proj.weight.dtype not in [torch.int8, torch.uint8] else torch.float32
|
| 50 |
+
)
|
| 51 |
+
if hasattr(self.model, "hidden_size"):
|
| 52 |
+
# TODO: remove this clause after 2026-01-01
|
| 53 |
+
hidden_size = self.model.hidden_size
|
| 54 |
+
else: # changed in https://github.com/huggingface/transformers/pull/35235
|
| 55 |
+
hidden_size = self.model.config.hidden_size
|
| 56 |
+
self.adaption_prompt = nn.Parameter(
|
| 57 |
+
torch.empty(1, adapter_len, hidden_size, device=device, dtype=target_dtype).normal_()
|
| 58 |
+
)
|
| 59 |
+
# Initialize the gate to 0 as this is "zero-init".
|
| 60 |
+
self.adaption_gate = nn.Parameter(torch.zeros(1, device=device, dtype=target_dtype))
|
| 61 |
+
|
| 62 |
+
def forward(self, **kwargs):
|
| 63 |
+
"""
|
| 64 |
+
Forward pass for the adapter which wraps the original LlamaAttention module.
|
| 65 |
+
|
| 66 |
+
"Official" paper implementation:
|
| 67 |
+
https://github.com/ZrrSkywalker/LLaMA-Adapter/blob/41c3546fe1997ab8a65809dc8d8f9252b19d9faf/llama/model.py#L141
|
| 68 |
+
|
| 69 |
+
Args:
|
| 70 |
+
kwargs: See the original LlamaAttention module.
|
| 71 |
+
"""
|
| 72 |
+
if kwargs.get("output_attention", False):
|
| 73 |
+
raise NotImplementedError("output_attention is not currently supported.")
|
| 74 |
+
|
| 75 |
+
output, *_ = self.model(**kwargs)
|
| 76 |
+
bsz = output.shape[0]
|
| 77 |
+
q_len = output.shape[1]
|
| 78 |
+
embed_dim = output.shape[2]
|
| 79 |
+
k_proj_layer = TRANSFORMERS_MODEL_CONFIG[self.model_type].k_proj_layer
|
| 80 |
+
v_proj_layer = TRANSFORMERS_MODEL_CONFIG[self.model_type].v_proj_layer
|
| 81 |
+
o_proj_layer = TRANSFORMERS_MODEL_CONFIG[self.model_type].o_proj_layer
|
| 82 |
+
factor = (
|
| 83 |
+
self.model.k_proj.in_features // self.model.k_proj.out_features
|
| 84 |
+
) # Mistral has different input and output dimension for k_proj and v_proj layers
|
| 85 |
+
|
| 86 |
+
if k_proj_layer == v_proj_layer:
|
| 87 |
+
_, key, value = getattr(self.model, k_proj_layer)(self.adaption_prompt).split(embed_dim, dim=2)
|
| 88 |
+
else:
|
| 89 |
+
key = getattr(self.model, k_proj_layer)(self.adaption_prompt)
|
| 90 |
+
value = getattr(self.model, v_proj_layer)(self.adaption_prompt)
|
| 91 |
+
|
| 92 |
+
if hasattr(self.model, "num_heads"):
|
| 93 |
+
# TODO: remove this clause after 2026-01-01
|
| 94 |
+
num_heads = self.model.num_heads
|
| 95 |
+
else: # changed in https://github.com/huggingface/transformers/pull/35235
|
| 96 |
+
num_heads = self.model.config.num_attention_heads
|
| 97 |
+
# (bsz, num_key_value_heads, adapter_len, head_dim)
|
| 98 |
+
adapter_k = (
|
| 99 |
+
key.view(1, self.adapter_len, (num_heads // factor), self.model.head_dim)
|
| 100 |
+
.repeat(bsz, 1, 1, 1)
|
| 101 |
+
.transpose(1, 2)
|
| 102 |
+
)
|
| 103 |
+
adapter_v = (
|
| 104 |
+
value.view(1, self.adapter_len, (num_heads // factor), self.model.head_dim)
|
| 105 |
+
.repeat(bsz, 1, 1, 1)
|
| 106 |
+
.transpose(1, 2)
|
| 107 |
+
)
|
| 108 |
+
# Below is taken from https://github.com/huggingface/transformers/blob/e547458c43dfdbbb8f6a7757237e234c44e20a8f/src/transformers/models/mistral/modeling_mistral.py#L181
|
| 109 |
+
# (bsz, num_heads, adapter_len, head_dim)
|
| 110 |
+
adapter_k = torch.repeat_interleave(adapter_k, repeats=factor, dim=1)
|
| 111 |
+
adapter_v = torch.repeat_interleave(adapter_v, repeats=factor, dim=1)
|
| 112 |
+
# Recompute query states.
|
| 113 |
+
compute_query_states = TRANSFORMERS_MODEL_CONFIG[self.model_type].compute_query_states
|
| 114 |
+
# (bsz, num_heads, q_len, head_dim)
|
| 115 |
+
query_states = compute_query_states(model=self.model, **kwargs)
|
| 116 |
+
|
| 117 |
+
previous_dtype = query_states.dtype
|
| 118 |
+
|
| 119 |
+
# (bsz, num_heads, q_len, adapter_len)
|
| 120 |
+
scores = torch.matmul(query_states, adapter_k.transpose(2, 3).to(previous_dtype)) / math.sqrt(
|
| 121 |
+
self.model.head_dim
|
| 122 |
+
)
|
| 123 |
+
# Upcast attention to fp32
|
| 124 |
+
# (bsz, num_heads, q_len, adapter_len)
|
| 125 |
+
scores = self.adaption_gate * F.softmax(scores, dim=-1, dtype=torch.float32).to(previous_dtype)
|
| 126 |
+
# (bsz, q_len, num_heads * head_dim)
|
| 127 |
+
adapter_output = torch.matmul(scores, adapter_v).transpose(1, 2).reshape(bsz, q_len, -1)
|
| 128 |
+
|
| 129 |
+
# (bsz, q_len, hidden_size)
|
| 130 |
+
if o_proj_layer is not None:
|
| 131 |
+
adapter_output = getattr(self.model, o_proj_layer)(adapter_output)
|
| 132 |
+
|
| 133 |
+
# Add adaption prompt output to original output.
|
| 134 |
+
output = output + adapter_output
|
| 135 |
+
|
| 136 |
+
# Restore original dtype.
|
| 137 |
+
output = output.to(previous_dtype)
|
| 138 |
+
return output, *_
|
lib/python3.12/site-packages/peft/tuners/adaption_prompt/model.py
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2023-present the HuggingFace Inc. team.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
from typing import Dict, List
|
| 16 |
+
|
| 17 |
+
import torch.nn as nn
|
| 18 |
+
|
| 19 |
+
from peft.utils import _freeze_adapter, _get_submodules
|
| 20 |
+
|
| 21 |
+
from .config import AdaptionPromptConfig, prepare_config
|
| 22 |
+
from .layer import AdaptedAttention
|
| 23 |
+
from .utils import is_adaption_prompt_trainable
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
class AdaptionPromptModel(nn.Module):
|
| 27 |
+
"""
|
| 28 |
+
Implements adaption prompts as described in https://arxiv.org/pdf/2303.16199.pdf.
|
| 29 |
+
|
| 30 |
+
The top L attention modules are replaced with AdaptedAttention modules that wrap the original ones, but insert
|
| 31 |
+
trainable prompts with gates (for zero init).
|
| 32 |
+
|
| 33 |
+
Notes on the multi-adapter pattern:
|
| 34 |
+
- We store the states of different adapters by keeping a dictionary of AdaptedAttention modules indexed by adapter
|
| 35 |
+
name.
|
| 36 |
+
- Every time we switch adapters, we remove the modules of the currently active adapter from the model, store them
|
| 37 |
+
in the dictionary, and replace them with the modules of the new adapter.
|
| 38 |
+
- To avoid duplicated and potentially inconsistent state, the currently active adapter is always removed from the
|
| 39 |
+
dictionary.
|
| 40 |
+
- Disabling the adapter would also result in the modules being removed from the model.
|
| 41 |
+
"""
|
| 42 |
+
|
| 43 |
+
def __init__(self, model, configs: Dict, adapter_name: str):
|
| 44 |
+
super().__init__()
|
| 45 |
+
self.model = model
|
| 46 |
+
# Store adapter configs by name.
|
| 47 |
+
self.peft_config: Dict[str, AdaptionPromptConfig] = {}
|
| 48 |
+
# Store lists of the parents of the affected attention modules by adapter name.
|
| 49 |
+
# We keep references to the parents so we can swap the adapters in-and-out of the model.
|
| 50 |
+
self._parents: Dict[str, List[nn.Module]] = {}
|
| 51 |
+
# Store lists of cached AdaptedAttention modules by name.
|
| 52 |
+
self._cached_adapters: Dict[str, List] = {}
|
| 53 |
+
# The name of the currently active adapter.
|
| 54 |
+
self._active_adapter = None
|
| 55 |
+
# Whether the adapter is enabled.
|
| 56 |
+
self._enabled = True
|
| 57 |
+
self.forward = self.model.forward
|
| 58 |
+
self.add_adapter(adapter_name, configs[adapter_name])
|
| 59 |
+
self._mark_only_adaption_prompts_as_trainable(self.model)
|
| 60 |
+
|
| 61 |
+
def add_adapter(self, adapter_name: str, config: AdaptionPromptConfig) -> None:
|
| 62 |
+
"""Add an adapter with the given name and config."""
|
| 63 |
+
config = prepare_config(config, self.model)
|
| 64 |
+
if adapter_name in self.peft_config:
|
| 65 |
+
raise ValueError(f"Adapter with name '{adapter_name}' already exists.")
|
| 66 |
+
|
| 67 |
+
parents = []
|
| 68 |
+
for name, _ in self.model.named_modules():
|
| 69 |
+
if name.endswith(config.target_modules):
|
| 70 |
+
par, _, _ = _get_submodules(self.model, name)
|
| 71 |
+
parents.append(par)
|
| 72 |
+
if len(parents) < config.adapter_layers:
|
| 73 |
+
raise ValueError(
|
| 74 |
+
f"Config specifies more adapter layers '{config.adapter_layers}' than the model has '{len(parents)}'."
|
| 75 |
+
)
|
| 76 |
+
# Note that if the target modules are not in Sequential, ModuleList, or
|
| 77 |
+
# some other PyTorch ordered container, the behavior is undefined as we
|
| 78 |
+
# assume here that the order of the modules is the same as the order of
|
| 79 |
+
# the transformer decoder layers.
|
| 80 |
+
parents = parents[-config.adapter_layers :]
|
| 81 |
+
self._parents[adapter_name] = parents
|
| 82 |
+
|
| 83 |
+
# It is only None during initialization.
|
| 84 |
+
# If it is disabled, we don't have to remove the modules.
|
| 85 |
+
if self._active_adapter is not None and self._enabled:
|
| 86 |
+
self._remove_adapted_attentions(self._active_adapter)
|
| 87 |
+
self._active_adapter = adapter_name
|
| 88 |
+
self.peft_config[adapter_name] = config
|
| 89 |
+
self._create_adapted_attentions(config, parents)
|
| 90 |
+
if not self._enabled:
|
| 91 |
+
self._remove_adapted_attentions(self._active_adapter)
|
| 92 |
+
|
| 93 |
+
if config.inference_mode:
|
| 94 |
+
_freeze_adapter(self.model, adapter_name)
|
| 95 |
+
|
| 96 |
+
def set_adapter(self, adapter_name: str) -> None:
|
| 97 |
+
"""Set the model to use the adapter with the given name."""
|
| 98 |
+
if self._active_adapter == adapter_name:
|
| 99 |
+
return
|
| 100 |
+
if adapter_name not in self.peft_config:
|
| 101 |
+
raise ValueError(f"Adapter with name '{adapter_name}' does not exist.")
|
| 102 |
+
|
| 103 |
+
if self._enabled:
|
| 104 |
+
self._remove_adapted_attentions(self._active_adapter)
|
| 105 |
+
self._set_adapted_attentions(adapter_name)
|
| 106 |
+
|
| 107 |
+
self._active_adapter = adapter_name
|
| 108 |
+
|
| 109 |
+
def enable_adapter_layers(self):
|
| 110 |
+
"""Enable adapter layers by swapping in cached AdaptedAttention modules."""
|
| 111 |
+
self._enabled = True
|
| 112 |
+
self._set_adapted_attentions(self._active_adapter)
|
| 113 |
+
|
| 114 |
+
def disable_adapter_layers(self):
|
| 115 |
+
"""Disable adapter layers by swapping out AdaptedAttention modules."""
|
| 116 |
+
self._enabled = False
|
| 117 |
+
self._remove_adapted_attentions(self._active_adapter)
|
| 118 |
+
|
| 119 |
+
def _create_adapted_attentions(self, config: AdaptionPromptConfig, parents: List[nn.Module]) -> None:
|
| 120 |
+
"""Wrap LlamaAttention modules with newly created AdaptedAttention modules."""
|
| 121 |
+
for par in parents:
|
| 122 |
+
attn = AdaptedAttention(
|
| 123 |
+
model_type=self.model.config.model_type,
|
| 124 |
+
adapter_len=config.adapter_len,
|
| 125 |
+
model=getattr(par, config.target_modules),
|
| 126 |
+
)
|
| 127 |
+
setattr(par, config.target_modules, attn)
|
| 128 |
+
|
| 129 |
+
def _set_adapted_attentions(self, adapter_name: str) -> None:
|
| 130 |
+
"""Replace LlamaAttention modules with cached AdaptedAttention modules."""
|
| 131 |
+
cached = self._cached_adapters[adapter_name]
|
| 132 |
+
del self._cached_adapters[adapter_name]
|
| 133 |
+
config = self.peft_config[adapter_name]
|
| 134 |
+
for i, par in enumerate(self._parents[adapter_name]):
|
| 135 |
+
setattr(par, config.target_modules, cached[i])
|
| 136 |
+
|
| 137 |
+
def _remove_adapted_attentions(self, adapter_name: str) -> None:
|
| 138 |
+
"""Remove AdaptedAttention modules from the model and store them in the cache."""
|
| 139 |
+
config = self.peft_config[adapter_name]
|
| 140 |
+
adapted_attentions = []
|
| 141 |
+
for par in self._parents[adapter_name]:
|
| 142 |
+
attn = getattr(par, config.target_modules)
|
| 143 |
+
adapted_attentions.append(attn)
|
| 144 |
+
setattr(par, config.target_modules, attn.model)
|
| 145 |
+
self._cached_adapters[adapter_name] = adapted_attentions
|
| 146 |
+
|
| 147 |
+
def _mark_only_adaption_prompts_as_trainable(self, model: nn.Module) -> None:
|
| 148 |
+
"""Freeze all parameters of the model except the adaption prompts."""
|
| 149 |
+
for n, p in model.named_parameters():
|
| 150 |
+
if not is_adaption_prompt_trainable(n):
|
| 151 |
+
p.requires_grad = False
|
| 152 |
+
|
| 153 |
+
def __getattr__(self, name: str):
|
| 154 |
+
"""Forward missing attributes to the wrapped module."""
|
| 155 |
+
try:
|
| 156 |
+
return super().__getattr__(name) # defer to nn.Module's logic
|
| 157 |
+
except AttributeError:
|
| 158 |
+
# This is necessary as e.g. causal models have various methods that we
|
| 159 |
+
# don't want to re-implement here.
|
| 160 |
+
if name == "model": # see #1892: prevent infinite recursion if class is not initialized
|
| 161 |
+
raise
|
| 162 |
+
return getattr(self.model, name)
|
lib/python3.12/site-packages/peft/tuners/adaption_prompt/utils.py
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2023-present the HuggingFace Inc. team.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
import inspect
|
| 15 |
+
|
| 16 |
+
import torch
|
| 17 |
+
import torch.nn as nn
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
def llama_rotate_half(x: torch.Tensor) -> torch.Tensor:
|
| 21 |
+
"""
|
| 22 |
+
Rotate half the hidden dims of the input.
|
| 23 |
+
|
| 24 |
+
This function was duplicated verbatim from:
|
| 25 |
+
https://github.com/huggingface/transformers/blob/1de8ce9ee1191ba761a593ac15d9ccbf5851bfc5/src/transformers/models/llama/modeling_llama.py#L126
|
| 26 |
+
|
| 27 |
+
This was done to eliminate the Llama transformers implementation as a dependency of this file. Note that some other
|
| 28 |
+
functions were also adapted from the transformers implementation but were modified.
|
| 29 |
+
"""
|
| 30 |
+
x1 = x[..., : x.shape[-1] // 2]
|
| 31 |
+
x2 = x[..., x.shape[-1] // 2 :]
|
| 32 |
+
return torch.cat((-x2, x1), dim=-1)
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def llama_apply_rotary_pos_emb(q, cos, sin, position_ids):
|
| 36 |
+
"""
|
| 37 |
+
Apply rotary position embedding to query states in the Llama model.
|
| 38 |
+
|
| 39 |
+
This function was adapted from:
|
| 40 |
+
https://github.com/huggingface/transformers/blob/1de8ce9ee1191ba761a593ac15d9ccbf5851bfc5/src/transformers/models/llama/modeling_llama.py#L133
|
| 41 |
+
|
| 42 |
+
It was modified to remove unnecessary processing of key states. The method is compatible with transformers <=
|
| 43 |
+
4.34.2 and also with the latest version (>=4.35).
|
| 44 |
+
"""
|
| 45 |
+
# In previous transformers version cos/sin cached had a shape of 4D
|
| 46 |
+
if len(cos.shape) == 4:
|
| 47 |
+
gather_indices = position_ids[:, None, :, None] # [bs, 1, seq_len, 1]
|
| 48 |
+
gather_indices = gather_indices.repeat(1, cos.shape[1], 1, cos.shape[3])
|
| 49 |
+
cos = torch.gather(cos.repeat(gather_indices.shape[0], 1, 1, 1), 2, gather_indices)
|
| 50 |
+
sin = torch.gather(sin.repeat(gather_indices.shape[0], 1, 1, 1), 2, gather_indices)
|
| 51 |
+
# In the new version, it is 2D so we fall back to the new implementation
|
| 52 |
+
# https://github.com/huggingface/transformers/blame/eef7ea98c31a333bacdc7ae7a2372bde772be8e4/src/transformers/models/llama/modeling_llama.py#L222-L226
|
| 53 |
+
else:
|
| 54 |
+
cos = cos[position_ids].unsqueeze(1)
|
| 55 |
+
sin = sin[position_ids].unsqueeze(1)
|
| 56 |
+
q_embed = (q * cos) + (llama_rotate_half(q) * sin)
|
| 57 |
+
return q_embed
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
def llama_compute_query_states(model: nn.Module, **kwargs) -> torch.Tensor:
|
| 61 |
+
"""
|
| 62 |
+
Compute query states for Llama models specifically. They need to be recomputed as the forward() method of the
|
| 63 |
+
original LlamaModel in the transformers library does not return them. See the related discussion in the PR:
|
| 64 |
+
https://github.com/huggingface/peft/pull/268
|
| 65 |
+
"""
|
| 66 |
+
hidden_states = kwargs.get("hidden_states")
|
| 67 |
+
position_ids = kwargs.get("position_ids")
|
| 68 |
+
past_key_value = kwargs.get("past_key_value")
|
| 69 |
+
bsz, q_len, _ = hidden_states.size()
|
| 70 |
+
if hasattr(model, "num_heads"):
|
| 71 |
+
# TODO: remove this clause after 2026-01-01
|
| 72 |
+
num_heads = model.num_heads
|
| 73 |
+
else: # changed in https://github.com/huggingface/transformers/pull/35235
|
| 74 |
+
num_heads = model.config.num_attention_heads
|
| 75 |
+
query_states = model.q_proj(hidden_states).view(bsz, q_len, num_heads, model.head_dim).transpose(1, 2)
|
| 76 |
+
|
| 77 |
+
factor = model.k_proj.in_features // model.k_proj.out_features
|
| 78 |
+
value_states = model.v_proj(hidden_states).view(bsz, q_len, (num_heads // factor), model.head_dim).transpose(1, 2)
|
| 79 |
+
|
| 80 |
+
seq_len = q_len
|
| 81 |
+
|
| 82 |
+
if past_key_value is not None:
|
| 83 |
+
if isinstance(past_key_value, tuple):
|
| 84 |
+
# for transformers <= 4.35
|
| 85 |
+
seq_len += past_key_value[0].shape[-2]
|
| 86 |
+
else:
|
| 87 |
+
# since transformers 4.36, this is a DynamicCache instance
|
| 88 |
+
seq_len += past_key_value.get_seq_length(model.layer_idx)
|
| 89 |
+
|
| 90 |
+
# model.rotary_emb is deprecated and will be removed in transformers > 4.47.0. Instead, the position embeddings are
|
| 91 |
+
# passed via the kwargs
|
| 92 |
+
if "position_embeddings" in kwargs:
|
| 93 |
+
cos, sin = kwargs["position_embeddings"]
|
| 94 |
+
cos = cos.unsqueeze(1)
|
| 95 |
+
sin = sin.unsqueeze(1)
|
| 96 |
+
return (query_states * cos) + (llama_rotate_half(query_states) * sin)
|
| 97 |
+
|
| 98 |
+
# For transformers > 4.37.2 `position_ids` became a required arguments in the rotary embedding's forward pass.
|
| 99 |
+
if "position_ids" not in inspect.signature(model.rotary_emb.forward).parameters:
|
| 100 |
+
# TODO we assume that position_ids is not None here, not sure if that is safe but the old code also did that
|
| 101 |
+
cos, sin = model.rotary_emb(value_states, seq_len=seq_len)
|
| 102 |
+
return llama_apply_rotary_pos_emb(query_states, cos, sin, position_ids)
|
| 103 |
+
|
| 104 |
+
past_seen_tokens = 0
|
| 105 |
+
if position_ids is None:
|
| 106 |
+
# Compute position_ids, since they are required for transformers > 4.37.2
|
| 107 |
+
if past_key_value is None:
|
| 108 |
+
new_cache_positions = torch.arange(q_len, q_len + q_len, device=value_states.device)
|
| 109 |
+
else:
|
| 110 |
+
past_seen_tokens = past_key_value.get_usable_length(q_len, model.layer_idx)
|
| 111 |
+
new_cache_positions = torch.arange(past_seen_tokens, past_seen_tokens + q_len, device=value_states.device)
|
| 112 |
+
position_ids = new_cache_positions.unsqueeze(0)
|
| 113 |
+
|
| 114 |
+
rotary_emb_kwargs = {"position_ids": position_ids}
|
| 115 |
+
# The `seq_len` argument has been officially removed in transformers >= 4.39.0
|
| 116 |
+
if "seq_len" in inspect.signature(model.rotary_emb.forward).parameters:
|
| 117 |
+
rotary_emb_kwargs["seq_len"] = q_len + past_seen_tokens
|
| 118 |
+
|
| 119 |
+
cos, sin = model.rotary_emb(value_states, **rotary_emb_kwargs)
|
| 120 |
+
|
| 121 |
+
# For batched inference unsqueeze it on the correct dim
|
| 122 |
+
# since: https://github.com/huggingface/transformers/pull/29109
|
| 123 |
+
if len(cos.shape) == 3:
|
| 124 |
+
cos = cos.unsqueeze(1)
|
| 125 |
+
sin = sin.unsqueeze(1)
|
| 126 |
+
|
| 127 |
+
return (query_states * cos) + (llama_rotate_half(query_states) * sin)
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
def is_adaption_prompt_trainable(params: str) -> bool:
|
| 131 |
+
"""Return True if module is trainable under adaption prompt fine-tuning."""
|
| 132 |
+
return params.split(".")[-1].startswith("adaption_")
|
lib/python3.12/site-packages/peft/tuners/boft/__init__.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2023-present the HuggingFace Inc. team.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
from peft.utils import register_peft_method
|
| 16 |
+
|
| 17 |
+
from .config import BOFTConfig
|
| 18 |
+
from .layer import BOFTLayer
|
| 19 |
+
from .model import BOFTModel
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
__all__ = ["BOFTConfig", "BOFTLayer", "BOFTModel"]
|
| 23 |
+
|
| 24 |
+
register_peft_method(name="boft", config_cls=BOFTConfig, model_cls=BOFTModel)
|
lib/python3.12/site-packages/peft/tuners/boft/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (494 Bytes). View file
|
|
|
lib/python3.12/site-packages/peft/tuners/boft/__pycache__/config.cpython-312.pyc
ADDED
|
Binary file (7.96 kB). View file
|
|
|
lib/python3.12/site-packages/peft/tuners/boft/__pycache__/layer.cpython-312.pyc
ADDED
|
Binary file (43.4 kB). View file
|
|
|
lib/python3.12/site-packages/peft/tuners/boft/__pycache__/model.cpython-312.pyc
ADDED
|
Binary file (16.6 kB). View file
|
|
|
lib/python3.12/site-packages/peft/tuners/boft/config.py
ADDED
|
@@ -0,0 +1,160 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2023-present the HuggingFace Inc. team.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
# The implementation is based on "Parameter-Efficient Orthogonal Finetuning
|
| 16 |
+
# via Butterfly Factorization" (https://arxiv.org/abs/2311.06243) in ICLR 2024.
|
| 17 |
+
|
| 18 |
+
from __future__ import annotations
|
| 19 |
+
|
| 20 |
+
from dataclasses import dataclass, field
|
| 21 |
+
from typing import Optional, Union
|
| 22 |
+
|
| 23 |
+
from peft.config import PeftConfig
|
| 24 |
+
from peft.utils import PeftType
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
@dataclass
|
| 28 |
+
class BOFTConfig(PeftConfig):
|
| 29 |
+
"""
|
| 30 |
+
This is the configuration class to store the configuration of a [`BOFTModel`].
|
| 31 |
+
|
| 32 |
+
Args:
|
| 33 |
+
boft_block_size (`int`): BOFT block size across different layers.
|
| 34 |
+
boft_block_num (`int`): Number of BOFT blocks per injected layer.
|
| 35 |
+
boft_n_butterfly_factor (`int`): Number of butterfly factors across different layers.
|
| 36 |
+
target_modules (`Union[List[str],str]`): The names of the modules to apply the adapter to.
|
| 37 |
+
exclude_modules (`Optional[Union[List[str], str]]`):
|
| 38 |
+
The names of the modules to not apply the adapter. When passing a string, a regex match will be performed.
|
| 39 |
+
When passing a list of strings, either an exact match will be performed or it is checked if the name of the
|
| 40 |
+
module ends with any of the passed strings.
|
| 41 |
+
boft_dropout (`float`):
|
| 42 |
+
The multiplicative dropout probability, by setting OFT blocks to identity during training, similar to the
|
| 43 |
+
dropout layer in LoRA.
|
| 44 |
+
fan_in_fan_out (`bool`): Set this to True if the layer to replace stores weight like (fan_in, fan_out).
|
| 45 |
+
For example, gpt-2 uses `Conv1D` which stores weights like (fan_in, fan_out) and hence this should be set
|
| 46 |
+
to `True`.
|
| 47 |
+
bias (`str`): Bias type for BOFT. Can be 'none', 'all' or 'boft_only'. If 'all' or 'boft_only', the
|
| 48 |
+
corresponding biases will be updated during training. Be aware that this means that, even when disabling
|
| 49 |
+
the adapters, the model will not produce the same output as the base model would have without adaptation.
|
| 50 |
+
modules_to_save (`List[str]`):List of modules apart from BOFT layers to be set as trainable
|
| 51 |
+
and saved in the final checkpoint.
|
| 52 |
+
layers_to_transform (`Union[List[int],int]`):
|
| 53 |
+
The layer indexes to transform, if this argument is specified, it will apply the BOFT transformations on
|
| 54 |
+
the layer indexes that are specified in this list. If a single integer is passed, it will apply the BOFT
|
| 55 |
+
transformations on the layer at this index.
|
| 56 |
+
layers_pattern (`Optional[Union[List[str], str]]`):
|
| 57 |
+
The layer pattern name, used only if `layers_to_transform` is different from `None` and if the layer
|
| 58 |
+
pattern is not in the common layers pattern. This should target the `nn.ModuleList` of the model, which is
|
| 59 |
+
often called `'layers'` or `'h'`.
|
| 60 |
+
"""
|
| 61 |
+
|
| 62 |
+
boft_block_size: int = field(
|
| 63 |
+
default=4,
|
| 64 |
+
metadata={
|
| 65 |
+
"help": "BOFT block size across different layers.",
|
| 66 |
+
"note": "You can only specify either boft_block_size or boft_block_num, but not both simultaneously, because boft_block_size x boft_block_num = layer dimension.",
|
| 67 |
+
},
|
| 68 |
+
)
|
| 69 |
+
boft_block_num: int = field(
|
| 70 |
+
default=0,
|
| 71 |
+
metadata={
|
| 72 |
+
"help": "Number of BOFT blocks per injected layer.",
|
| 73 |
+
"note": "You can only specify either boft_block_size or boft_block_num, but not both simultaneously, because boft_block_size x boft_block_num = layer dimension.",
|
| 74 |
+
},
|
| 75 |
+
)
|
| 76 |
+
boft_n_butterfly_factor: int = field(
|
| 77 |
+
default=1,
|
| 78 |
+
metadata={
|
| 79 |
+
"help": "Number of butterfly factors.",
|
| 80 |
+
"note": (
|
| 81 |
+
"for example, boft_n_butterfly_factor=2, the effective block size of OFT becomes twice as big and the number of blocks become half.",
|
| 82 |
+
"note: for boft_n_butterfly_factor=1, BOFT is the same as vanilla OFT.",
|
| 83 |
+
),
|
| 84 |
+
},
|
| 85 |
+
)
|
| 86 |
+
target_modules: Optional[Union[list[str], str]] = field(
|
| 87 |
+
default=None,
|
| 88 |
+
metadata={
|
| 89 |
+
"help": "List of module names or regex expression of the module names to replace with BOFT.",
|
| 90 |
+
"example": "For example, ['q', 'v'] or '.*decoder.*(SelfAttention|EncDecAttention).*(q|v)$' ",
|
| 91 |
+
},
|
| 92 |
+
)
|
| 93 |
+
exclude_modules: Optional[Union[list[str], str]] = field(
|
| 94 |
+
default=None,
|
| 95 |
+
metadata={"help": "List of module names or regex expression of the module names to exclude from BOFT."},
|
| 96 |
+
)
|
| 97 |
+
boft_dropout: float = field(
|
| 98 |
+
default=0.0,
|
| 99 |
+
metadata={
|
| 100 |
+
"help": "BOFT multiplicative dropout, randomly setting blocks of OFT to be identity matrix, similar to the dropout layer in LoRA."
|
| 101 |
+
},
|
| 102 |
+
)
|
| 103 |
+
fan_in_fan_out: bool = field(
|
| 104 |
+
default=False,
|
| 105 |
+
metadata={"help": "Set this to True if the layer to replace stores weight like (fan_in, fan_out)"},
|
| 106 |
+
)
|
| 107 |
+
bias: str = field(default="none", metadata={"help": "Bias type for BOFT. Can be 'none', 'all' or 'boft_only'"})
|
| 108 |
+
modules_to_save: Optional[list[str]] = field(
|
| 109 |
+
default=None,
|
| 110 |
+
metadata={
|
| 111 |
+
"help": "List of modules apart from BOFT layers to be set as trainable and saved in the final checkpoint. ",
|
| 112 |
+
"note": (
|
| 113 |
+
"For example, in Sequence Classification or Token Classification tasks, ",
|
| 114 |
+
"the final layer `classifier/score` are randomly initialized and as such need to be trainable and saved.",
|
| 115 |
+
),
|
| 116 |
+
},
|
| 117 |
+
)
|
| 118 |
+
init_weights: bool = field(
|
| 119 |
+
default=True,
|
| 120 |
+
metadata={
|
| 121 |
+
"help": (
|
| 122 |
+
"Whether to initialize the weights of the BOFT layers with their default initialization. Don't change ",
|
| 123 |
+
"this setting, except if you know exactly what you're doing.",
|
| 124 |
+
),
|
| 125 |
+
},
|
| 126 |
+
)
|
| 127 |
+
layers_to_transform: Optional[Union[list[int], int]] = field(
|
| 128 |
+
default=None,
|
| 129 |
+
metadata={
|
| 130 |
+
"help": "The layer indexes to transform, is this argument is specified, PEFT will transform only the layers indexes that are specified inside this list. If a single integer is passed, PEFT will transform only the layer at this index."
|
| 131 |
+
},
|
| 132 |
+
)
|
| 133 |
+
layers_pattern: Optional[Union[list[str], str]] = field(
|
| 134 |
+
default=None,
|
| 135 |
+
metadata={
|
| 136 |
+
"help": "The layer pattern name, used only if `layers_to_transform` is different to None and if the layer pattern is not in the common layers pattern. "
|
| 137 |
+
"This should target the `nn.ModuleList` of the model, which is often called `'layers'` or `'h'`."
|
| 138 |
+
},
|
| 139 |
+
)
|
| 140 |
+
|
| 141 |
+
def __post_init__(self):
|
| 142 |
+
super().__post_init__()
|
| 143 |
+
self.peft_type = PeftType.BOFT
|
| 144 |
+
self.target_modules = (
|
| 145 |
+
set(self.target_modules) if isinstance(self.target_modules, list) else self.target_modules
|
| 146 |
+
)
|
| 147 |
+
self.exclude_modules = (
|
| 148 |
+
set(self.exclude_modules) if isinstance(self.exclude_modules, list) else self.exclude_modules
|
| 149 |
+
)
|
| 150 |
+
# check for layers_to_transform and layers_pattern
|
| 151 |
+
if self.layers_pattern and not self.layers_to_transform:
|
| 152 |
+
raise ValueError("When `layers_pattern` is specified, `layers_to_transform` must also be specified. ")
|
| 153 |
+
if self.boft_block_size == 0 and self.boft_block_num == 0:
|
| 154 |
+
raise ValueError(
|
| 155 |
+
f"Either `boft_block_size` or `boft_block_num` must be non-zero. Currently, boft_block_size = {self.boft_block_size} and boft_block_num = {self.boft_block_num}."
|
| 156 |
+
)
|
| 157 |
+
if not (self.boft_block_size != 0) ^ (self.boft_block_num != 0):
|
| 158 |
+
raise ValueError(
|
| 159 |
+
f"You can only specify either boft_block_size ({self.boft_block_size}) or boft_block_num ({self.boft_block_num}), but not both simultaneously, because boft_block_size x boft_block_num == in_features."
|
| 160 |
+
)
|
lib/python3.12/site-packages/peft/tuners/boft/fbd/__init__.py
ADDED
|
File without changes
|
lib/python3.12/site-packages/peft/tuners/boft/fbd/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (199 Bytes). View file
|
|
|
lib/python3.12/site-packages/peft/tuners/boft/fbd/fbd_cuda.cpp
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#include <torch/torch.h>
|
| 2 |
+
#include <vector>
|
| 3 |
+
#include <iostream>
|
| 4 |
+
#include <torch/extension.h>
|
| 5 |
+
|
| 6 |
+
std::vector<at::Tensor> forward_fast_block_diag_cuda(
|
| 7 |
+
at::Tensor input);
|
| 8 |
+
|
| 9 |
+
std::vector<at::Tensor> forward_fast_block_diag(
|
| 10 |
+
at::Tensor input
|
| 11 |
+
) {
|
| 12 |
+
return forward_fast_block_diag_cuda(input);
|
| 13 |
+
}
|
| 14 |
+
|
| 15 |
+
std::vector<at::Tensor> backward_fast_block_diag_cuda(
|
| 16 |
+
at::Tensor grad_output,
|
| 17 |
+
at::Tensor input);
|
| 18 |
+
std::vector<at::Tensor> backward_fast_block_diag(
|
| 19 |
+
at::Tensor grad_output,
|
| 20 |
+
at::Tensor input
|
| 21 |
+
) {
|
| 22 |
+
return backward_fast_block_diag_cuda(grad_output, input);
|
| 23 |
+
}
|
| 24 |
+
|
| 25 |
+
PYBIND11_MODULE(TORCH_EXTENSION_NAME, m) {
|
| 26 |
+
m.def("forward", &forward_fast_block_diag, "FAST BLOCK DIAG (CUDA)");
|
| 27 |
+
m.def("backward", &backward_fast_block_diag, "FAST BLOCK DIAG backward (CUDA)");
|
| 28 |
+
}
|
lib/python3.12/site-packages/peft/tuners/boft/fbd/fbd_cuda_kernel.cu
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
// Author: Yao Feng
|
| 2 |
+
// Date: 2023/08
|
| 3 |
+
// Description: cuda kernel for fast block diag
|
| 4 |
+
|
| 5 |
+
#include <ATen/ATen.h>
|
| 6 |
+
|
| 7 |
+
#include <cuda.h>
|
| 8 |
+
#include <cuda_runtime.h>
|
| 9 |
+
#include <vector>
|
| 10 |
+
|
| 11 |
+
namespace{
|
| 12 |
+
template <typename scalar_t>
|
| 13 |
+
__global__ void forward_fast_block_diag_cuda_kernel(
|
| 14 |
+
const scalar_t* __restrict__ input, //[z, N, b, b]
|
| 15 |
+
scalar_t* output, //[z, Nxb, Nxb]
|
| 16 |
+
int z, int N, int b
|
| 17 |
+
) {
|
| 18 |
+
|
| 19 |
+
const int i = blockIdx.x * blockDim.x + threadIdx.x;
|
| 20 |
+
if (i >= z*N*b*b) {
|
| 21 |
+
return;
|
| 22 |
+
}
|
| 23 |
+
const int zi = i/(N*b*b);
|
| 24 |
+
const int Ni = (i%(N*b*b))/(b*b);
|
| 25 |
+
const int x = ((i%(N*b*b))%(b*b))/b;
|
| 26 |
+
const int y = ((i%(N*b*b))%(b*b))%b;
|
| 27 |
+
|
| 28 |
+
output[zi*N*b*N*b + (Ni*b+x)*N*b + Ni*b + y] = input[zi*N*b*b + Ni*b*b + x*b + y];
|
| 29 |
+
|
| 30 |
+
}
|
| 31 |
+
|
| 32 |
+
template <typename scalar_t>
|
| 33 |
+
__global__ void backward_fast_block_diag_cuda_kernel(
|
| 34 |
+
const scalar_t* __restrict__ grad_output,
|
| 35 |
+
scalar_t* grad_input,
|
| 36 |
+
int z, int N, int b
|
| 37 |
+
) {
|
| 38 |
+
|
| 39 |
+
const int i = blockIdx.x * blockDim.x + threadIdx.x;
|
| 40 |
+
if (i >= z*N*b*b) {
|
| 41 |
+
return;
|
| 42 |
+
}
|
| 43 |
+
const int zi = i/(N*b*b);
|
| 44 |
+
const int Ni = (i%(N*b*b))/(b*b);
|
| 45 |
+
const int x = ((i%(N*b*b))%(b*b))/b;
|
| 46 |
+
const int y = ((i%(N*b*b))%(b*b))%b;
|
| 47 |
+
|
| 48 |
+
grad_input[zi*N*b*b + Ni*b*b + x*b + y] = grad_output[zi*N*b*N*b + (Ni*b+x)*N*b + Ni*b + y];
|
| 49 |
+
|
| 50 |
+
} // namespace
|
| 51 |
+
}
|
| 52 |
+
|
| 53 |
+
std::vector<at::Tensor> forward_fast_block_diag_cuda(
|
| 54 |
+
at::Tensor input
|
| 55 |
+
){
|
| 56 |
+
const auto z = input.size(0);
|
| 57 |
+
const auto N = input.size(1);
|
| 58 |
+
const auto b = input.size(2);
|
| 59 |
+
|
| 60 |
+
// print(channel_size)
|
| 61 |
+
const int threads = 512;
|
| 62 |
+
const dim3 blocks_1 ((z*N*b*b - 1) / threads +1);
|
| 63 |
+
// initlaize output
|
| 64 |
+
auto output = at::zeros({z, N*b, N*b}, input.options());
|
| 65 |
+
|
| 66 |
+
AT_DISPATCH_FLOATING_TYPES_AND_HALF(input.type(), "forward_fast_block_diag1", ([&] {
|
| 67 |
+
forward_fast_block_diag_cuda_kernel<scalar_t><<<blocks_1, threads>>>(
|
| 68 |
+
input.data_ptr<scalar_t>(),
|
| 69 |
+
output.data_ptr<scalar_t>(),
|
| 70 |
+
z, N, b);
|
| 71 |
+
}));
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
cudaError_t err = cudaGetLastError();
|
| 75 |
+
if (err != cudaSuccess)
|
| 76 |
+
printf("Error in forward_fast_block_diag_cuda_kernel: %s\n", cudaGetErrorString(err));
|
| 77 |
+
|
| 78 |
+
return {output};
|
| 79 |
+
}
|
| 80 |
+
|
| 81 |
+
std::vector<at::Tensor> backward_fast_block_diag_cuda(
|
| 82 |
+
at::Tensor grad_output,
|
| 83 |
+
at::Tensor input
|
| 84 |
+
){
|
| 85 |
+
|
| 86 |
+
const auto z = input.size(0);
|
| 87 |
+
const auto N = input.size(1);
|
| 88 |
+
const auto b = input.size(2);
|
| 89 |
+
|
| 90 |
+
// print(channel_size)
|
| 91 |
+
const int threads = 512;
|
| 92 |
+
const dim3 blocks_1 ((z*N*b*b - 1) / threads +1);
|
| 93 |
+
|
| 94 |
+
// initialize grad input
|
| 95 |
+
auto grad_input = at::zeros_like(input);
|
| 96 |
+
|
| 97 |
+
AT_DISPATCH_FLOATING_TYPES_AND_HALF(grad_output.type(), "backward_fast_block_diag", ([&] {
|
| 98 |
+
backward_fast_block_diag_cuda_kernel<scalar_t><<<blocks_1, threads>>>(
|
| 99 |
+
grad_output.data_ptr<scalar_t>(),
|
| 100 |
+
grad_input.data_ptr<scalar_t>(),
|
| 101 |
+
z, N, b);
|
| 102 |
+
}));
|
| 103 |
+
|
| 104 |
+
cudaError_t err = cudaGetLastError();
|
| 105 |
+
if (err != cudaSuccess)
|
| 106 |
+
printf("Error in backward_fast_block_diag_cuda_kernel: %s\n", cudaGetErrorString(err));
|
| 107 |
+
|
| 108 |
+
return {grad_input};
|
| 109 |
+
}
|
lib/python3.12/site-packages/peft/tuners/boft/layer.py
ADDED
|
@@ -0,0 +1,984 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2023-present the HuggingFace Inc. team.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
# The implementation is based on "Parameter-Efficient Orthogonal Finetuning
|
| 16 |
+
# via Butterfly Factorization" (https://arxiv.org/abs/2311.06243) in ICLR 2024.
|
| 17 |
+
|
| 18 |
+
from __future__ import annotations
|
| 19 |
+
|
| 20 |
+
import math
|
| 21 |
+
import os
|
| 22 |
+
import warnings
|
| 23 |
+
from contextlib import contextmanager
|
| 24 |
+
from typing import Any, Optional, Union
|
| 25 |
+
|
| 26 |
+
import torch
|
| 27 |
+
import torch.nn as nn
|
| 28 |
+
import torch.nn.functional as F
|
| 29 |
+
from torch.autograd import Function
|
| 30 |
+
|
| 31 |
+
from peft.tuners.tuners_utils import BaseTunerLayer, check_adapters_to_merge
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
_FBD_CUDA = None
|
| 35 |
+
|
| 36 |
+
|
| 37 |
+
# this function is a 1:1 copy from accelerate
|
| 38 |
+
@contextmanager
|
| 39 |
+
def patch_environment(**kwargs):
|
| 40 |
+
"""
|
| 41 |
+
A context manager that will add each keyword argument passed to `os.environ` and remove them when exiting.
|
| 42 |
+
|
| 43 |
+
Will convert the values in `kwargs` to strings and upper-case all the keys.
|
| 44 |
+
|
| 45 |
+
Example:
|
| 46 |
+
|
| 47 |
+
```python
|
| 48 |
+
>>> import os
|
| 49 |
+
>>> from accelerate.utils import patch_environment
|
| 50 |
+
|
| 51 |
+
>>> with patch_environment(FOO="bar"):
|
| 52 |
+
... print(os.environ["FOO"]) # prints "bar"
|
| 53 |
+
>>> print(os.environ["FOO"]) # raises KeyError
|
| 54 |
+
```
|
| 55 |
+
"""
|
| 56 |
+
existing_vars = {}
|
| 57 |
+
for key, value in kwargs.items():
|
| 58 |
+
key = key.upper()
|
| 59 |
+
if key in os.environ:
|
| 60 |
+
existing_vars[key] = os.environ[key]
|
| 61 |
+
os.environ[key] = str(value)
|
| 62 |
+
|
| 63 |
+
yield
|
| 64 |
+
|
| 65 |
+
for key in kwargs:
|
| 66 |
+
key = key.upper()
|
| 67 |
+
if key in existing_vars:
|
| 68 |
+
# restore previous value
|
| 69 |
+
os.environ[key] = existing_vars[key]
|
| 70 |
+
else:
|
| 71 |
+
os.environ.pop(key, None)
|
| 72 |
+
|
| 73 |
+
|
| 74 |
+
def get_fbd_cuda():
|
| 75 |
+
global _FBD_CUDA
|
| 76 |
+
|
| 77 |
+
if _FBD_CUDA is not None:
|
| 78 |
+
return _FBD_CUDA
|
| 79 |
+
|
| 80 |
+
# This import initializes cuda context and should thus be local, see issue 1877
|
| 81 |
+
from torch.utils.cpp_extension import load
|
| 82 |
+
|
| 83 |
+
curr_dir = os.path.dirname(__file__)
|
| 84 |
+
# need ninja to build the extension
|
| 85 |
+
try:
|
| 86 |
+
with patch_environment(CC="gcc", CXX="gcc"):
|
| 87 |
+
fbd_cuda = load(
|
| 88 |
+
name="fbd_cuda",
|
| 89 |
+
sources=[f"{curr_dir}/fbd/fbd_cuda.cpp", f"{curr_dir}/fbd/fbd_cuda_kernel.cu"],
|
| 90 |
+
verbose=True,
|
| 91 |
+
# build_directory='/tmp/' # for debugging
|
| 92 |
+
)
|
| 93 |
+
# extra_cuda_cflags = ['-std=c++14', '-ccbin=$$(which gcc-7)']) # cuda10.2 is not compatible with gcc9. Specify gcc 7
|
| 94 |
+
except Exception as e:
|
| 95 |
+
warnings.warn(f"Failed to load the CUDA extension: {e}, check if ninja is available.")
|
| 96 |
+
warnings.warn("Setting boft_n_butterfly_factor to 1 to speed up the finetuning process.")
|
| 97 |
+
fbd_cuda = None
|
| 98 |
+
|
| 99 |
+
_FBD_CUDA = fbd_cuda
|
| 100 |
+
return _FBD_CUDA
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
class FastBlockDiag(Function):
|
| 104 |
+
"""
|
| 105 |
+
Implements a custom autograd Function for a fast block diagonal operation using CUDA.
|
| 106 |
+
|
| 107 |
+
This function is optimized for 4D tensors where the last two dimensions are equal, representing block diagonal
|
| 108 |
+
matrices for efficient computation on CUDA devices.
|
| 109 |
+
"""
|
| 110 |
+
|
| 111 |
+
@staticmethod
|
| 112 |
+
def forward(ctx, input):
|
| 113 |
+
"""
|
| 114 |
+
The forward method for FastBlockDiag.
|
| 115 |
+
|
| 116 |
+
Computes the block diagonal operation on the input tensor using a CUDA-optimized function. This method assumes
|
| 117 |
+
that the input is a 4D tensor where the last two dimensions are equal, which represent the blocks to be
|
| 118 |
+
diagonalized.
|
| 119 |
+
|
| 120 |
+
Parameters:
|
| 121 |
+
ctx: A context object that can be used to stash information for backward computation.
|
| 122 |
+
input (Tensor): The input tensor of shape (N, D, H, H), where `N` is the batch size,
|
| 123 |
+
`D` represents one additional dimension (In BOFT, the number of BOFT blocks), and `H` is the
|
| 124 |
+
size of the square blocks along the last two dimensions (In BOFT, the block size).
|
| 125 |
+
|
| 126 |
+
Returns:
|
| 127 |
+
Tensor: The resulting tensor after applying the block diagonal operation,
|
| 128 |
+
will have the shape (N, DxH, DxH).
|
| 129 |
+
"""
|
| 130 |
+
output = get_fbd_cuda().forward(input)[0]
|
| 131 |
+
ctx.save_for_backward(input)
|
| 132 |
+
return output
|
| 133 |
+
|
| 134 |
+
@staticmethod
|
| 135 |
+
def backward(ctx, grad_output):
|
| 136 |
+
(input,) = ctx.saved_tensors
|
| 137 |
+
grad_input = get_fbd_cuda().backward(grad_output, input)[0]
|
| 138 |
+
return grad_input
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
class MultiplicativeDropoutLayer(nn.Module):
|
| 142 |
+
"""
|
| 143 |
+
Implements the multiplicative dropout layer for BOFT.
|
| 144 |
+
"""
|
| 145 |
+
|
| 146 |
+
def __init__(self, p=0.0):
|
| 147 |
+
"""
|
| 148 |
+
Initializes the multiplicative dropout layer.
|
| 149 |
+
|
| 150 |
+
Parameters:
|
| 151 |
+
p (float): The probability of dropping out a block. Defaults to 0.0.
|
| 152 |
+
"""
|
| 153 |
+
super().__init__()
|
| 154 |
+
self.p = p
|
| 155 |
+
|
| 156 |
+
def forward(self, x):
|
| 157 |
+
"""
|
| 158 |
+
Applies multiplicative dropout to the input tensor.
|
| 159 |
+
|
| 160 |
+
Parameters:
|
| 161 |
+
x (Tensor): The input tensor of shape (N, D, H, H), where `N` is the batch size, `D` represents
|
| 162 |
+
one additional dimension (In BOFT, the number of BOFT blocks), and `H` is the size of the square
|
| 163 |
+
blocks along the last two dimensions (In BOFT, the block size).
|
| 164 |
+
"""
|
| 165 |
+
if self.training:
|
| 166 |
+
# Ensure the last two dimensions are the same
|
| 167 |
+
if x.shape[-1] != x.shape[-2]:
|
| 168 |
+
raise ValueError("The last two dimensions of input should be the same!")
|
| 169 |
+
|
| 170 |
+
N, D, H, _ = x.shape
|
| 171 |
+
|
| 172 |
+
# Randomly select one from N
|
| 173 |
+
n_random = torch.randint(0, N, (1,)).item()
|
| 174 |
+
|
| 175 |
+
# Create a mask with 1s for matrices to be replaced with identity and 0s otherwise
|
| 176 |
+
num_to_replace = int(self.p * D)
|
| 177 |
+
num_zeros = D - num_to_replace
|
| 178 |
+
|
| 179 |
+
# Generate a flat tensor with desired number of 1s and 0s
|
| 180 |
+
mask = torch.cat([torch.ones(num_to_replace, device=x.device), torch.zeros(num_zeros, device=x.device)])
|
| 181 |
+
|
| 182 |
+
# Shuffle and reshape the mask
|
| 183 |
+
mask = mask[torch.randperm(D)].view(1, D, 1, 1)
|
| 184 |
+
|
| 185 |
+
full_mask = torch.zeros(N, D, 1, 1, device=x.device)
|
| 186 |
+
full_mask[n_random] = mask
|
| 187 |
+
|
| 188 |
+
# Use the mask to combine original matrices and identity matrices
|
| 189 |
+
eye_matrix = torch.eye(H, device=x.device).repeat(N, D, 1, 1)
|
| 190 |
+
x = (1 - full_mask) * x + full_mask * eye_matrix
|
| 191 |
+
return x
|
| 192 |
+
|
| 193 |
+
|
| 194 |
+
class BOFTLayer(BaseTunerLayer):
|
| 195 |
+
"""
|
| 196 |
+
Implements the BOFT layer.
|
| 197 |
+
"""
|
| 198 |
+
|
| 199 |
+
# All names of layers that may contain (trainable) adapter weights
|
| 200 |
+
adapter_layer_names = ("boft_R", "boft_s")
|
| 201 |
+
# All names of other parameters that may contain adapter-related parameters
|
| 202 |
+
other_param_names = ("boft_block_size", "boft_block_num", "boft_dropout")
|
| 203 |
+
|
| 204 |
+
def __init__(self, base_layer: nn.Module, **kwargs) -> None:
|
| 205 |
+
"""
|
| 206 |
+
Initializes the BOFT layer.
|
| 207 |
+
|
| 208 |
+
Note, currently only support linear layer and convolutional layer, with further support for other layers to be
|
| 209 |
+
added soon.
|
| 210 |
+
|
| 211 |
+
Parameters:
|
| 212 |
+
base_layer: the pretrained model layer
|
| 213 |
+
"""
|
| 214 |
+
self.base_layer = base_layer
|
| 215 |
+
self.boft_block_size = {}
|
| 216 |
+
self.boft_block_num = {}
|
| 217 |
+
self.boft_dropout = nn.ModuleDict({})
|
| 218 |
+
self.boft_R = nn.ParameterDict({})
|
| 219 |
+
self.boft_s = nn.ParameterDict({})
|
| 220 |
+
# Mark the weight as unmerged
|
| 221 |
+
self._disable_adapters = False
|
| 222 |
+
self.merged_adapters = []
|
| 223 |
+
self.kwargs = kwargs
|
| 224 |
+
|
| 225 |
+
base_layer = self.get_base_layer()
|
| 226 |
+
|
| 227 |
+
if isinstance(base_layer, nn.Linear):
|
| 228 |
+
in_features, out_features = base_layer.in_features, base_layer.out_features
|
| 229 |
+
elif isinstance(base_layer, nn.Conv2d):
|
| 230 |
+
in_features, out_features = base_layer.in_channels, base_layer.out_channels
|
| 231 |
+
else:
|
| 232 |
+
raise ValueError(f"Unsupported layer type {type(base_layer)}")
|
| 233 |
+
|
| 234 |
+
self.in_features = in_features
|
| 235 |
+
self.out_features = out_features
|
| 236 |
+
|
| 237 |
+
def set_scale(self, adapter, scale):
|
| 238 |
+
if adapter not in self.scaling:
|
| 239 |
+
# Ignore the case where the adapter is not in the layer
|
| 240 |
+
return
|
| 241 |
+
|
| 242 |
+
warnings.warn("Scaling operation for BOFT not supported! Automatically set scale to 1.")
|
| 243 |
+
|
| 244 |
+
def scale_layer(self, scale: float) -> None:
|
| 245 |
+
if scale == 1:
|
| 246 |
+
return
|
| 247 |
+
|
| 248 |
+
for active_adapter in self.active_adapters:
|
| 249 |
+
if active_adapter not in self.boft_R.keys():
|
| 250 |
+
continue
|
| 251 |
+
|
| 252 |
+
warnings.warn("Scaling operation for BOFT not supported! Automatically set scale to 1.")
|
| 253 |
+
|
| 254 |
+
def unscale_layer(self, scale=None) -> None:
|
| 255 |
+
for active_adapter in self.active_adapters:
|
| 256 |
+
if active_adapter not in self.boft_R.keys():
|
| 257 |
+
continue
|
| 258 |
+
|
| 259 |
+
warnings.warn("Unscaling operation for BOFT not supported! Keeping scale to 1.")
|
| 260 |
+
|
| 261 |
+
def update_layer(
|
| 262 |
+
self, adapter_name, boft_block_size, boft_block_num, boft_n_butterfly_factor, boft_dropout, init_weights
|
| 263 |
+
):
|
| 264 |
+
"""
|
| 265 |
+
Update the linear layer with trainable BOFT weights. Override for other layer types.
|
| 266 |
+
"""
|
| 267 |
+
# Attempt to load the CUDA extension during model initialization
|
| 268 |
+
if not get_fbd_cuda():
|
| 269 |
+
self.fbd_cuda_available = False
|
| 270 |
+
# If the CUDA extension is not available, set the butterfly factor to 1 to speed up the finetuning process
|
| 271 |
+
boft_n_butterfly_factor = 1
|
| 272 |
+
else:
|
| 273 |
+
self.fbd_cuda_available = True
|
| 274 |
+
|
| 275 |
+
# to be consistent with the paper notation
|
| 276 |
+
boft_n_butterfly_factor = boft_n_butterfly_factor - 1
|
| 277 |
+
if boft_n_butterfly_factor < 0:
|
| 278 |
+
raise ValueError(
|
| 279 |
+
f"You can only specify boft_n_butterfly_factor {boft_n_butterfly_factor + 1} to be a positive integer number."
|
| 280 |
+
)
|
| 281 |
+
|
| 282 |
+
# Initialize the MultiplicativeDropoutLayer for boft_dropout > 0.0.
|
| 283 |
+
if boft_dropout > 0.0:
|
| 284 |
+
boft_dropout_layer = MultiplicativeDropoutLayer(p=boft_dropout)
|
| 285 |
+
else:
|
| 286 |
+
boft_dropout_layer = nn.Identity()
|
| 287 |
+
self.boft_dropout.update(nn.ModuleDict({adapter_name: boft_dropout_layer}))
|
| 288 |
+
|
| 289 |
+
if boft_block_size == 0 and boft_block_num != 0:
|
| 290 |
+
if self.in_features % boft_block_num != 0:
|
| 291 |
+
raise ValueError(
|
| 292 |
+
f"in_features ({self.in_features}) must be divisible by boft_block_num ({boft_block_num})!"
|
| 293 |
+
)
|
| 294 |
+
|
| 295 |
+
if boft_n_butterfly_factor != 0:
|
| 296 |
+
if boft_n_butterfly_factor > int(math.log2(boft_block_num)):
|
| 297 |
+
raise ValueError(
|
| 298 |
+
f"Invalid combination of boft_n_butterfly_factor ({boft_n_butterfly_factor + 1}) and boft_block_num ({boft_block_num})!"
|
| 299 |
+
)
|
| 300 |
+
if boft_block_num % (2**boft_n_butterfly_factor) != 0:
|
| 301 |
+
raise ValueError(
|
| 302 |
+
f"boft_block_num ({boft_block_num}) must be a multiple of 2 raised to the power of boft_n_butterfly_factor ({boft_n_butterfly_factor + 1})!"
|
| 303 |
+
)
|
| 304 |
+
|
| 305 |
+
boft_block_size = int(self.in_features // boft_block_num)
|
| 306 |
+
|
| 307 |
+
elif boft_block_size != 0 and boft_block_num == 0:
|
| 308 |
+
if self.in_features % boft_block_size != 0:
|
| 309 |
+
raise ValueError(
|
| 310 |
+
f"in_features ({self.in_features}) must be divisible by boft_block_size ({boft_block_size})!"
|
| 311 |
+
)
|
| 312 |
+
|
| 313 |
+
if boft_n_butterfly_factor != 0:
|
| 314 |
+
if self.in_features < (boft_block_size * (2**boft_n_butterfly_factor)):
|
| 315 |
+
raise ValueError(
|
| 316 |
+
f"Invalid combination of in_features ({self.in_features}), boft_n_butterfly_factor ({boft_n_butterfly_factor + 1}) and boft_block_size ({boft_block_size})!"
|
| 317 |
+
)
|
| 318 |
+
if self.in_features % (boft_block_size * (2**boft_n_butterfly_factor)) != 0:
|
| 319 |
+
raise ValueError(
|
| 320 |
+
f"Invalid combination of in_features ({self.in_features}), boft_n_butterfly_factor ({boft_n_butterfly_factor + 1}) and boft_block_size ({boft_block_size})!"
|
| 321 |
+
)
|
| 322 |
+
|
| 323 |
+
boft_block_num = int(self.in_features // boft_block_size)
|
| 324 |
+
|
| 325 |
+
else:
|
| 326 |
+
raise ValueError(
|
| 327 |
+
"Something went wrong, please report this error: https://github.com/huggingface/peft/issues"
|
| 328 |
+
)
|
| 329 |
+
|
| 330 |
+
# In OFT you can specify the number of blocks to be 1
|
| 331 |
+
if boft_n_butterfly_factor != 0:
|
| 332 |
+
if boft_block_num % 2 != 0:
|
| 333 |
+
raise ValueError(f"boft_block_num ({boft_block_num}) must be an even number!")
|
| 334 |
+
|
| 335 |
+
if boft_block_size % 2 != 0:
|
| 336 |
+
raise ValueError(f"boft_block_size ({boft_block_size}) must be an even number!")
|
| 337 |
+
|
| 338 |
+
# If there is no butterfly factor, then permutation matrix P will be an identity matrix.
|
| 339 |
+
P = torch.empty((boft_n_butterfly_factor + 1, self.in_features, self.in_features))
|
| 340 |
+
for i in range(boft_n_butterfly_factor + 1):
|
| 341 |
+
perm = self.block_butterfly_perm(
|
| 342 |
+
self.in_features, int(boft_block_num / (2 ** (i))), int(boft_block_size / 2), boft_n_butterfly_factor
|
| 343 |
+
)
|
| 344 |
+
perm_mat = self.perm2mat(perm)
|
| 345 |
+
P[i] = perm_mat
|
| 346 |
+
|
| 347 |
+
self.register_buffer("boft_P", P, persistent=False)
|
| 348 |
+
|
| 349 |
+
self.boft_R[adapter_name] = nn.Parameter(
|
| 350 |
+
torch.zeros(boft_n_butterfly_factor + 1, boft_block_num, boft_block_size, boft_block_size)
|
| 351 |
+
)
|
| 352 |
+
self.boft_s[adapter_name] = nn.Parameter(torch.ones(int(self.out_features), 1))
|
| 353 |
+
|
| 354 |
+
self.reset_boft_parameters(adapter_name, init_weights)
|
| 355 |
+
|
| 356 |
+
# set the boft block size and number
|
| 357 |
+
self.boft_block_size[adapter_name] = boft_block_size
|
| 358 |
+
self.boft_block_num[adapter_name] = boft_block_num
|
| 359 |
+
|
| 360 |
+
self._move_adapter_to_device_of_base_layer(adapter_name)
|
| 361 |
+
self.set_adapter(self.active_adapters)
|
| 362 |
+
|
| 363 |
+
def reset_boft_parameters(self, adapter_name, init_weights):
|
| 364 |
+
"""
|
| 365 |
+
Reset the BOFT parameters.
|
| 366 |
+
"""
|
| 367 |
+
if init_weights is False:
|
| 368 |
+
nn.init.normal_(self.boft_R[adapter_name], mean=0.0, std=0.1)
|
| 369 |
+
nn.init.normal_(self.boft_s[adapter_name], mean=1.0, std=0.1)
|
| 370 |
+
return
|
| 371 |
+
|
| 372 |
+
if adapter_name in self.boft_R.keys():
|
| 373 |
+
if init_weights is True:
|
| 374 |
+
# initialize R to zero
|
| 375 |
+
nn.init.zeros_(self.boft_R[adapter_name])
|
| 376 |
+
nn.init.ones_(self.boft_s[adapter_name])
|
| 377 |
+
else:
|
| 378 |
+
raise ValueError(f"Unknown initialization {init_weights=}")
|
| 379 |
+
|
| 380 |
+
def perm2mat(self, indices):
|
| 381 |
+
"""
|
| 382 |
+
Convert permutation indices to permutation matrix.
|
| 383 |
+
|
| 384 |
+
Args:
|
| 385 |
+
indices: A list of indices representing the permutation.
|
| 386 |
+
"""
|
| 387 |
+
# Number of indices determines the size of the square matrix
|
| 388 |
+
n = len(indices)
|
| 389 |
+
|
| 390 |
+
# Initialize a matrix of zeros
|
| 391 |
+
perm_mat = torch.zeros((n, n))
|
| 392 |
+
|
| 393 |
+
# Set the 1s according to the indices
|
| 394 |
+
for i, idx in enumerate(indices):
|
| 395 |
+
perm_mat[i, idx] = 1
|
| 396 |
+
|
| 397 |
+
return perm_mat
|
| 398 |
+
|
| 399 |
+
def block_butterfly_perm(self, n, b, r=3, n_butterfly_factor=1):
|
| 400 |
+
"""
|
| 401 |
+
Define the permutation matrix for the block butterfly permutation.
|
| 402 |
+
|
| 403 |
+
Args:
|
| 404 |
+
n: size of the permutation matrix
|
| 405 |
+
b: desired number of blocks after multiplying with the permutation matrix
|
| 406 |
+
r: base block size of the block diagonal matrix, e.g. 2x2, 3x3, 5x5 etc.
|
| 407 |
+
"""
|
| 408 |
+
|
| 409 |
+
if n_butterfly_factor == 0:
|
| 410 |
+
return torch.arange(n)
|
| 411 |
+
|
| 412 |
+
if b * r * 2 > n:
|
| 413 |
+
raise ValueError("Invalid number of blocks!")
|
| 414 |
+
|
| 415 |
+
block_size = int(n // b)
|
| 416 |
+
indices = torch.arange(n)
|
| 417 |
+
|
| 418 |
+
def sort_block(b, r):
|
| 419 |
+
step = b / r
|
| 420 |
+
initial_order = torch.arange(b)
|
| 421 |
+
sorted_order = torch.empty(b, dtype=torch.long)
|
| 422 |
+
|
| 423 |
+
evens = torch.arange(0, step, 2)
|
| 424 |
+
odds = torch.arange(1, step, 2)
|
| 425 |
+
sorted_seq = torch.cat((evens, odds), dim=0)
|
| 426 |
+
for i, pos in enumerate(sorted_seq):
|
| 427 |
+
sorted_order[int(i * r) : int(i * r + r)] = initial_order[int(pos * r) : int(pos * r + r)]
|
| 428 |
+
return sorted_order
|
| 429 |
+
|
| 430 |
+
sorted_order = sort_block(block_size, r)
|
| 431 |
+
|
| 432 |
+
for i in range(0, n, block_size):
|
| 433 |
+
block_end = i + block_size
|
| 434 |
+
tmp_indices = indices[i:block_end]
|
| 435 |
+
indices[i:block_end] = tmp_indices[sorted_order]
|
| 436 |
+
return indices
|
| 437 |
+
|
| 438 |
+
def cayley_batch(self, data):
|
| 439 |
+
"""
|
| 440 |
+
Perform the Cayley parametrization on a batch of skew-symmetric matrices.
|
| 441 |
+
|
| 442 |
+
Args:
|
| 443 |
+
data: A batch of skew-symmetric matrices of shape (b, r, c).
|
| 444 |
+
"""
|
| 445 |
+
b, r, c = data.shape
|
| 446 |
+
# Ensure the input matrix is skew-symmetric
|
| 447 |
+
skew_mat = 0.5 * (data - data.transpose(1, 2))
|
| 448 |
+
id_mat = torch.eye(r, device=data.device).unsqueeze(0).expand(b, r, c)
|
| 449 |
+
|
| 450 |
+
# Perform the Cayley parametrization
|
| 451 |
+
Q = torch.linalg.solve(id_mat + skew_mat, id_mat - skew_mat, left=False)
|
| 452 |
+
|
| 453 |
+
return Q
|
| 454 |
+
|
| 455 |
+
|
| 456 |
+
class Linear(nn.Module, BOFTLayer):
|
| 457 |
+
"""
|
| 458 |
+
BOFT implemented in a dense layer.
|
| 459 |
+
"""
|
| 460 |
+
|
| 461 |
+
def __init__(
|
| 462 |
+
self,
|
| 463 |
+
base_layer,
|
| 464 |
+
adapter_name: str,
|
| 465 |
+
boft_block_size: int = 8,
|
| 466 |
+
boft_block_num: int = 0,
|
| 467 |
+
boft_n_butterfly_factor: int = 0,
|
| 468 |
+
boft_dropout: float = 0.1,
|
| 469 |
+
fan_in_fan_out: bool = False, # Set this to True if the layer to replace stores weight like (fan_in, fan_out)
|
| 470 |
+
init_weights: Union[bool, str] = True,
|
| 471 |
+
is_target_conv_1d_layer: bool = False,
|
| 472 |
+
**kwargs,
|
| 473 |
+
) -> None:
|
| 474 |
+
super().__init__()
|
| 475 |
+
BOFTLayer.__init__(self, base_layer, **kwargs)
|
| 476 |
+
self.fan_in_fan_out = fan_in_fan_out
|
| 477 |
+
|
| 478 |
+
self._active_adapter = adapter_name
|
| 479 |
+
|
| 480 |
+
self.update_layer(
|
| 481 |
+
adapter_name, boft_block_size, boft_block_num, boft_n_butterfly_factor, boft_dropout, init_weights
|
| 482 |
+
)
|
| 483 |
+
self.is_target_conv_1d_layer = is_target_conv_1d_layer
|
| 484 |
+
|
| 485 |
+
def merge(self, safe_merge: bool = False, adapter_names: Optional[list[str]] = None) -> None:
|
| 486 |
+
"""
|
| 487 |
+
Merge the active adapter weights into the base weights
|
| 488 |
+
|
| 489 |
+
Args:
|
| 490 |
+
safe_merge (`bool`, *optional*):
|
| 491 |
+
If True, the merge operation will be performed in a copy of the original weights and check for NaNs
|
| 492 |
+
before merging the weights. This is useful if you want to check if the merge operation will produce
|
| 493 |
+
NaNs. Defaults to `False`.
|
| 494 |
+
adapter_names (`List[str]`, *optional*):
|
| 495 |
+
The list of adapter names that should be merged. If None, all active adapters will be merged. Defaults
|
| 496 |
+
to `None`.
|
| 497 |
+
"""
|
| 498 |
+
adapter_names = check_adapters_to_merge(self, adapter_names)
|
| 499 |
+
if not adapter_names:
|
| 500 |
+
# no adapter to merge
|
| 501 |
+
return
|
| 502 |
+
|
| 503 |
+
for active_adapter in adapter_names:
|
| 504 |
+
if active_adapter in self.boft_R.keys():
|
| 505 |
+
base_layer = self.get_base_layer()
|
| 506 |
+
if safe_merge:
|
| 507 |
+
# Note that safe_merge will be slower than the normal merge
|
| 508 |
+
# because of the copy operation.
|
| 509 |
+
orig_weight = base_layer.weight.data.clone()
|
| 510 |
+
butterfly_oft_mat, boft_s = self.get_delta_weight(active_adapter)
|
| 511 |
+
orig_weight = torch.transpose(orig_weight, 0, 1)
|
| 512 |
+
orig_weight = torch.mm(butterfly_oft_mat, orig_weight)
|
| 513 |
+
orig_weight = torch.transpose(orig_weight, 0, 1)
|
| 514 |
+
orig_weight = orig_weight * boft_s
|
| 515 |
+
|
| 516 |
+
if not torch.isfinite(orig_weight).all():
|
| 517 |
+
raise ValueError(
|
| 518 |
+
f"NaNs detected in the merged weights. The adapter {active_adapter} seems to be broken"
|
| 519 |
+
)
|
| 520 |
+
|
| 521 |
+
self.base_layer.weight.data = orig_weight.contiguous()
|
| 522 |
+
else:
|
| 523 |
+
butterfly_oft_mat, boft_s = self.get_delta_weight(active_adapter)
|
| 524 |
+
orig_weight = base_layer.weight.data.clone()
|
| 525 |
+
orig_weight = torch.transpose(orig_weight, 0, 1)
|
| 526 |
+
orig_weight = torch.mm(butterfly_oft_mat, orig_weight)
|
| 527 |
+
orig_weight = torch.transpose(orig_weight, 0, 1)
|
| 528 |
+
orig_weight = orig_weight * boft_s
|
| 529 |
+
|
| 530 |
+
self.base_layer.weight.data = orig_weight.contiguous()
|
| 531 |
+
|
| 532 |
+
self.merged_adapters.append(active_adapter)
|
| 533 |
+
|
| 534 |
+
def unmerge(self) -> None:
|
| 535 |
+
"""
|
| 536 |
+
This method unmerges all merged adapter layers from the base weights.
|
| 537 |
+
"""
|
| 538 |
+
if not self.merged:
|
| 539 |
+
warnings.warn("Already unmerged. Nothing to do.")
|
| 540 |
+
return
|
| 541 |
+
while len(self.merged_adapters) > 0:
|
| 542 |
+
active_adapter = self.merged_adapters.pop()
|
| 543 |
+
if active_adapter in self.boft_R.keys():
|
| 544 |
+
butterfly_oft_mat, boft_s = self.get_delta_weight(active_adapter)
|
| 545 |
+
|
| 546 |
+
orig_weight = self.get_base_layer().weight.data.clone()
|
| 547 |
+
orig_weight = torch.transpose(orig_weight, 0, 1)
|
| 548 |
+
orig_weight = torch.mm(butterfly_oft_mat.t(), orig_weight)
|
| 549 |
+
orig_weight = torch.transpose(orig_weight, 0, 1)
|
| 550 |
+
|
| 551 |
+
self.get_base_layer().weight.data = orig_weight * (1 / boft_s)
|
| 552 |
+
|
| 553 |
+
def get_delta_weight(self, adapter) -> tuple[torch.Tensor, torch.Tensor]:
|
| 554 |
+
"""
|
| 555 |
+
Compute the delta weight for the given adapter.
|
| 556 |
+
|
| 557 |
+
Args:
|
| 558 |
+
adapter (str):
|
| 559 |
+
The name of the adapter for which the delta weight should be computed.
|
| 560 |
+
"""
|
| 561 |
+
boft_R = self.boft_R[adapter]
|
| 562 |
+
boft_s = self.boft_s[adapter]
|
| 563 |
+
|
| 564 |
+
N, D, H, _ = boft_R.shape
|
| 565 |
+
boft_R = boft_R.view(N * D, H, H)
|
| 566 |
+
orth_rotate_butterfly = self.cayley_batch(boft_R)
|
| 567 |
+
orth_rotate_butterfly = orth_rotate_butterfly.view(N, D, H, H)
|
| 568 |
+
if self.fbd_cuda_available:
|
| 569 |
+
block_diagonal_butterfly = FastBlockDiag.apply(orth_rotate_butterfly)
|
| 570 |
+
else:
|
| 571 |
+
orth_rotate_butterfly = orth_rotate_butterfly.squeeze(0)
|
| 572 |
+
block_diagonal_butterfly = torch.block_diag(*torch.unbind(orth_rotate_butterfly))
|
| 573 |
+
block_diagonal_butterfly = block_diagonal_butterfly.unsqueeze(0)
|
| 574 |
+
|
| 575 |
+
boft_P = self.boft_P.to(block_diagonal_butterfly.device)
|
| 576 |
+
butterfly_oft_mat_batch = torch.bmm(block_diagonal_butterfly, boft_P.permute(0, 2, 1))
|
| 577 |
+
butterfly_oft_mat_batch = torch.bmm(boft_P, butterfly_oft_mat_batch)
|
| 578 |
+
butterfly_oft_mat = butterfly_oft_mat_batch[0]
|
| 579 |
+
|
| 580 |
+
for i in range(1, butterfly_oft_mat_batch.shape[0]):
|
| 581 |
+
butterfly_oft_mat = butterfly_oft_mat_batch[i] @ butterfly_oft_mat
|
| 582 |
+
|
| 583 |
+
return butterfly_oft_mat, boft_s
|
| 584 |
+
|
| 585 |
+
def forward(self, x: torch.Tensor, *args: Any, **kwargs: Any) -> torch.Tensor:
|
| 586 |
+
previous_dtype = x.dtype
|
| 587 |
+
|
| 588 |
+
if self.disable_adapters:
|
| 589 |
+
if self.merged:
|
| 590 |
+
self.unmerge()
|
| 591 |
+
result = self.base_layer(x, *args, **kwargs)
|
| 592 |
+
elif self.merged:
|
| 593 |
+
result = self.base_layer(x, *args, **kwargs)
|
| 594 |
+
else:
|
| 595 |
+
boft_rotation = torch.eye(self.in_features, device=x.device, dtype=previous_dtype)
|
| 596 |
+
boft_scale = torch.ones((int(self.out_features), 1), device=x.device, dtype=previous_dtype)
|
| 597 |
+
|
| 598 |
+
for active_adapter in self.active_adapters:
|
| 599 |
+
if active_adapter not in self.boft_R.keys():
|
| 600 |
+
continue
|
| 601 |
+
boft_R = self.boft_R[active_adapter]
|
| 602 |
+
boft_s = self.boft_s[active_adapter]
|
| 603 |
+
dropout = self.boft_dropout[active_adapter]
|
| 604 |
+
|
| 605 |
+
N, D, H, _ = boft_R.shape
|
| 606 |
+
boft_R = boft_R.view(N * D, H, H)
|
| 607 |
+
orth_rotate_butterfly = self.cayley_batch(boft_R)
|
| 608 |
+
orth_rotate_butterfly = orth_rotate_butterfly.view(N, D, H, H)
|
| 609 |
+
orth_rotate_butterfly = dropout(orth_rotate_butterfly)
|
| 610 |
+
if self.fbd_cuda_available:
|
| 611 |
+
block_diagonal_butterfly = FastBlockDiag.apply(orth_rotate_butterfly)
|
| 612 |
+
else:
|
| 613 |
+
orth_rotate_butterfly = orth_rotate_butterfly.squeeze(0)
|
| 614 |
+
block_diagonal_butterfly = torch.block_diag(*torch.unbind(orth_rotate_butterfly))
|
| 615 |
+
block_diagonal_butterfly = block_diagonal_butterfly.unsqueeze(0)
|
| 616 |
+
|
| 617 |
+
# The BOFT author's cayley_batch, dropout and FastBlockDiag ONLY return fp32 outputs.
|
| 618 |
+
boft_P = self.boft_P.to(x)
|
| 619 |
+
block_diagonal_butterfly = block_diagonal_butterfly.to(x)
|
| 620 |
+
butterfly_oft_mat_batch = torch.bmm(block_diagonal_butterfly, boft_P.permute(0, 2, 1))
|
| 621 |
+
butterfly_oft_mat_batch = torch.bmm(boft_P, butterfly_oft_mat_batch)
|
| 622 |
+
butterfly_oft_mat = butterfly_oft_mat_batch[0]
|
| 623 |
+
|
| 624 |
+
for i in range(1, butterfly_oft_mat_batch.shape[0]):
|
| 625 |
+
butterfly_oft_mat = butterfly_oft_mat_batch[i] @ butterfly_oft_mat
|
| 626 |
+
|
| 627 |
+
boft_rotation = butterfly_oft_mat @ boft_rotation
|
| 628 |
+
boft_scale = boft_s * boft_scale
|
| 629 |
+
|
| 630 |
+
x = x.to(self.get_base_layer().weight.data.dtype)
|
| 631 |
+
|
| 632 |
+
orig_weight = self.get_base_layer().weight.data
|
| 633 |
+
orig_weight = torch.transpose(orig_weight, 0, 1)
|
| 634 |
+
boft_rotation = boft_rotation.to(previous_dtype)
|
| 635 |
+
orig_weight = orig_weight.to(previous_dtype)
|
| 636 |
+
rotated_weight = torch.mm(boft_rotation, orig_weight)
|
| 637 |
+
rotated_weight = torch.transpose(rotated_weight, 0, 1)
|
| 638 |
+
|
| 639 |
+
scaled_rotated_weight = rotated_weight * boft_scale
|
| 640 |
+
|
| 641 |
+
scaled_rotated_weight = scaled_rotated_weight.to(previous_dtype)
|
| 642 |
+
if self.base_layer.bias is not None:
|
| 643 |
+
self.base_layer.bias = self.base_layer.bias.to(previous_dtype)
|
| 644 |
+
result = F.linear(input=x, weight=scaled_rotated_weight, bias=self.base_layer.bias)
|
| 645 |
+
|
| 646 |
+
result = result.to(previous_dtype)
|
| 647 |
+
return result
|
| 648 |
+
|
| 649 |
+
def __repr__(self) -> str:
|
| 650 |
+
rep = super().__repr__()
|
| 651 |
+
return "boft." + rep
|
| 652 |
+
|
| 653 |
+
|
| 654 |
+
class Conv2d(nn.Module, BOFTLayer):
|
| 655 |
+
"""
|
| 656 |
+
BOFT implemented in a Conv2d layer.
|
| 657 |
+
"""
|
| 658 |
+
|
| 659 |
+
def __init__(
|
| 660 |
+
self,
|
| 661 |
+
base_layer: nn.Module,
|
| 662 |
+
adapter_name: str,
|
| 663 |
+
boft_block_size: int = 8,
|
| 664 |
+
boft_block_num: int = 0,
|
| 665 |
+
boft_n_butterfly_factor: int = 0,
|
| 666 |
+
boft_dropout: float = 0.1,
|
| 667 |
+
init_weights: Union[bool, str] = True,
|
| 668 |
+
**kwargs,
|
| 669 |
+
) -> None:
|
| 670 |
+
super().__init__()
|
| 671 |
+
BOFTLayer.__init__(self, base_layer)
|
| 672 |
+
|
| 673 |
+
self._active_adapter = adapter_name
|
| 674 |
+
self.update_layer(
|
| 675 |
+
adapter_name, boft_block_size, boft_block_num, boft_n_butterfly_factor, boft_dropout, init_weights
|
| 676 |
+
)
|
| 677 |
+
|
| 678 |
+
def update_layer(
|
| 679 |
+
self, adapter_name, boft_block_size, boft_block_num, boft_n_butterfly_factor, boft_dropout, init_weights
|
| 680 |
+
):
|
| 681 |
+
"""
|
| 682 |
+
Update the conv2d layer with trainable BOFT weights.
|
| 683 |
+
"""
|
| 684 |
+
|
| 685 |
+
# Attempt to load the CUDA extension during model initialization
|
| 686 |
+
if not get_fbd_cuda():
|
| 687 |
+
self.fbd_cuda_available = False
|
| 688 |
+
# If the CUDA extension is not available, set the butterfly factor to 1 to speed up the finetuning process
|
| 689 |
+
boft_n_butterfly_factor = 1
|
| 690 |
+
else:
|
| 691 |
+
self.fbd_cuda_available = True
|
| 692 |
+
|
| 693 |
+
# to be consistent with the paper notation
|
| 694 |
+
boft_n_butterfly_factor = boft_n_butterfly_factor - 1
|
| 695 |
+
if boft_n_butterfly_factor < 0:
|
| 696 |
+
raise ValueError(
|
| 697 |
+
f"You can only specify boft_n_butterfly_factor {boft_n_butterfly_factor + 1} to be a positive integer number."
|
| 698 |
+
)
|
| 699 |
+
|
| 700 |
+
# Initialize the MultiplicativeDropoutLayer for boft_dropout > 0.0.
|
| 701 |
+
if boft_dropout > 0.0:
|
| 702 |
+
boft_dropout_layer = MultiplicativeDropoutLayer(p=boft_dropout)
|
| 703 |
+
else:
|
| 704 |
+
boft_dropout_layer = nn.Identity()
|
| 705 |
+
self.boft_dropout.update(nn.ModuleDict({adapter_name: boft_dropout_layer}))
|
| 706 |
+
|
| 707 |
+
# layer information from the base layer
|
| 708 |
+
base_layer = self.get_base_layer()
|
| 709 |
+
conv_filter_dim = self.in_features * base_layer.kernel_size[0] * base_layer.kernel_size[0]
|
| 710 |
+
|
| 711 |
+
# Initialize the BOFT parameters.
|
| 712 |
+
if boft_block_size == 0 and boft_block_num != 0:
|
| 713 |
+
if conv_filter_dim % boft_block_num != 0:
|
| 714 |
+
raise ValueError(
|
| 715 |
+
f"Convolutional kernel dimension ({conv_filter_dim}) must be divisible by boft_block_num ({boft_block_num})!"
|
| 716 |
+
)
|
| 717 |
+
|
| 718 |
+
if boft_n_butterfly_factor != 0:
|
| 719 |
+
if boft_n_butterfly_factor > int(math.log2(boft_block_num)):
|
| 720 |
+
raise ValueError(
|
| 721 |
+
f"Invalid combination of boft_n_butterfly_factor ({boft_n_butterfly_factor + 1}) and boft_block_num ({boft_block_num})!"
|
| 722 |
+
)
|
| 723 |
+
if boft_block_num % (2**boft_n_butterfly_factor) != 0:
|
| 724 |
+
raise ValueError(
|
| 725 |
+
f"boft_block_num ({boft_block_num}) must be a multiple of 2 raised to the power of boft_n_butterfly_factor ({boft_n_butterfly_factor + 1})!"
|
| 726 |
+
)
|
| 727 |
+
|
| 728 |
+
boft_block_size = int(conv_filter_dim // boft_block_num)
|
| 729 |
+
|
| 730 |
+
elif boft_block_size != 0 and boft_block_num == 0:
|
| 731 |
+
if conv_filter_dim % boft_block_size != 0:
|
| 732 |
+
raise ValueError(
|
| 733 |
+
f"Convolutional kernel dimension ({conv_filter_dim}) must be divisible by boft_block_size ({boft_block_size})!"
|
| 734 |
+
)
|
| 735 |
+
|
| 736 |
+
if boft_n_butterfly_factor != 0:
|
| 737 |
+
if conv_filter_dim < (boft_block_size * (2**boft_n_butterfly_factor)):
|
| 738 |
+
raise ValueError(
|
| 739 |
+
f"Invalid combination of convolutional kernel dimension ({conv_filter_dim}), boft_n_butterfly_factor ({boft_n_butterfly_factor + 1}) and boft_block_size ({boft_block_size})!"
|
| 740 |
+
)
|
| 741 |
+
if conv_filter_dim % (boft_block_size * (2**boft_n_butterfly_factor)) != 0:
|
| 742 |
+
raise ValueError(
|
| 743 |
+
f"Invalid combination of convolutional kernel dimension ({conv_filter_dim}), boft_n_butterfly_factor ({boft_n_butterfly_factor + 1}) and boft_block_size ({boft_block_size})!"
|
| 744 |
+
)
|
| 745 |
+
|
| 746 |
+
boft_block_num = int(conv_filter_dim // boft_block_size)
|
| 747 |
+
|
| 748 |
+
else:
|
| 749 |
+
raise ValueError(
|
| 750 |
+
"Something went wrong, please report this error: https://github.com/huggingface/peft/issues"
|
| 751 |
+
)
|
| 752 |
+
|
| 753 |
+
# In OFT you can specify the number of blocks to be 1
|
| 754 |
+
if boft_n_butterfly_factor != 0:
|
| 755 |
+
if boft_block_num % 2 != 0:
|
| 756 |
+
raise ValueError(f"boft_block_num ({boft_block_num}) must be an even number!")
|
| 757 |
+
|
| 758 |
+
if boft_block_size % 2 != 0:
|
| 759 |
+
raise ValueError(f"boft_block_size ({boft_block_size}) must be an even number!")
|
| 760 |
+
|
| 761 |
+
# If there is no butterfly factor, then permutation matrix P will be an identity matrix.
|
| 762 |
+
P = torch.empty((boft_n_butterfly_factor + 1, conv_filter_dim, conv_filter_dim))
|
| 763 |
+
for i in range(boft_n_butterfly_factor + 1):
|
| 764 |
+
perm = self.block_butterfly_perm(
|
| 765 |
+
conv_filter_dim, int(boft_block_num / (2 ** (i))), int(boft_block_size / 2), boft_n_butterfly_factor
|
| 766 |
+
)
|
| 767 |
+
perm_mat = self.perm2mat(perm)
|
| 768 |
+
P[i] = perm_mat
|
| 769 |
+
|
| 770 |
+
self.register_buffer("boft_P", P, persistent=False)
|
| 771 |
+
|
| 772 |
+
self.boft_R[adapter_name] = nn.Parameter(
|
| 773 |
+
torch.zeros(boft_n_butterfly_factor + 1, boft_block_num, boft_block_size, boft_block_size)
|
| 774 |
+
)
|
| 775 |
+
self.boft_s[adapter_name] = nn.Parameter(torch.ones(1, int(self.out_features)))
|
| 776 |
+
|
| 777 |
+
self.reset_boft_parameters(adapter_name, init_weights)
|
| 778 |
+
|
| 779 |
+
# set the boft block size and number
|
| 780 |
+
self.boft_block_size[adapter_name] = boft_block_size
|
| 781 |
+
self.boft_block_num[adapter_name] = boft_block_num
|
| 782 |
+
|
| 783 |
+
self._move_adapter_to_device_of_base_layer(adapter_name)
|
| 784 |
+
self.set_adapter(self.active_adapters)
|
| 785 |
+
|
| 786 |
+
def merge(self, safe_merge: bool = False, adapter_names: Optional[list[str]] = None) -> None:
|
| 787 |
+
"""
|
| 788 |
+
Merge the active adapter weights into the base weights
|
| 789 |
+
|
| 790 |
+
Args:
|
| 791 |
+
safe_merge (`bool`, *optional*):
|
| 792 |
+
If True, the merge operation will be performed in a copy of the original weights and check for NaNs
|
| 793 |
+
before merging the weights. This is useful if you want to check if the merge operation will produce
|
| 794 |
+
NaNs. Defaults to `False`.
|
| 795 |
+
adapter_names (`List[str]`, *optional*):
|
| 796 |
+
The list of adapter names that should be merged. If None, all active adapters will be merged. Defaults
|
| 797 |
+
to `None`.
|
| 798 |
+
"""
|
| 799 |
+
adapter_names = check_adapters_to_merge(self, adapter_names)
|
| 800 |
+
if not adapter_names:
|
| 801 |
+
# no adapter to merge
|
| 802 |
+
return
|
| 803 |
+
|
| 804 |
+
for active_adapter in adapter_names:
|
| 805 |
+
if active_adapter in self.boft_R.keys():
|
| 806 |
+
base_layer = self.get_base_layer()
|
| 807 |
+
if safe_merge:
|
| 808 |
+
# Note that safe_merge will be slower than the normal merge
|
| 809 |
+
# because of the copy operation.
|
| 810 |
+
orig_weight = base_layer.weight.data.clone()
|
| 811 |
+
butterfly_oft_mat, boft_s = self.get_delta_weight(active_adapter)
|
| 812 |
+
|
| 813 |
+
orig_weight = orig_weight.view(
|
| 814 |
+
self.out_features, self.in_features * base_layer.kernel_size[0] * base_layer.kernel_size[0]
|
| 815 |
+
)
|
| 816 |
+
orig_weight = torch.transpose(orig_weight, 0, 1)
|
| 817 |
+
orig_weight = torch.mm(butterfly_oft_mat, orig_weight)
|
| 818 |
+
orig_weight = torch.transpose(orig_weight, 0, 1)
|
| 819 |
+
orig_weight = orig_weight * boft_s
|
| 820 |
+
orig_weight = orig_weight.view(
|
| 821 |
+
self.out_features, self.in_features, base_layer.kernel_size[0], base_layer.kernel_size[0]
|
| 822 |
+
)
|
| 823 |
+
|
| 824 |
+
self.base_layer.weight.data = orig_weight.contiguous()
|
| 825 |
+
else:
|
| 826 |
+
butterfly_oft_mat, boft_s = self.get_delta_weight(active_adapter)
|
| 827 |
+
|
| 828 |
+
orig_weight = base_layer.weight.data.clone()
|
| 829 |
+
orig_weight = orig_weight.view(
|
| 830 |
+
self.out_features, self.in_features * base_layer.kernel_size[0] * base_layer.kernel_size[0]
|
| 831 |
+
)
|
| 832 |
+
orig_weight = torch.transpose(orig_weight, 0, 1)
|
| 833 |
+
orig_weight = torch.mm(butterfly_oft_mat, orig_weight)
|
| 834 |
+
orig_weight = torch.transpose(orig_weight, 0, 1)
|
| 835 |
+
orig_weight = orig_weight * boft_s
|
| 836 |
+
orig_weight = orig_weight.view(
|
| 837 |
+
self.out_features, self.in_features, base_layer.kernel_size[0], base_layer.kernel_size[0]
|
| 838 |
+
)
|
| 839 |
+
|
| 840 |
+
self.base_layer.weight.data = orig_weight.contiguous()
|
| 841 |
+
|
| 842 |
+
self.merged_adapters.append(active_adapter)
|
| 843 |
+
|
| 844 |
+
def unmerge(self) -> None:
|
| 845 |
+
"""
|
| 846 |
+
This method unmerges all merged adapter layers from the base weights.
|
| 847 |
+
"""
|
| 848 |
+
if not self.merged:
|
| 849 |
+
warnings.warn("Already unmerged. Nothing to do.")
|
| 850 |
+
return
|
| 851 |
+
while len(self.merged_adapters) > 0:
|
| 852 |
+
active_adapter = self.merged_adapters.pop()
|
| 853 |
+
if active_adapter in self.boft_R.keys():
|
| 854 |
+
butterfly_oft_mat, boft_s = self.get_delta_weight(active_adapter)
|
| 855 |
+
|
| 856 |
+
orig_weight = self.get_base_layer().weight.data.clone()
|
| 857 |
+
orig_weight = orig_weight.view(
|
| 858 |
+
self.out_features,
|
| 859 |
+
self.in_features * self.get_base_layer().kernel_size[0] * self.get_base_layer().kernel_size[0],
|
| 860 |
+
)
|
| 861 |
+
orig_weight = torch.transpose(orig_weight, 0, 1)
|
| 862 |
+
orig_weight = torch.mm(butterfly_oft_mat.t(), orig_weight)
|
| 863 |
+
orig_weight = torch.transpose(orig_weight, 0, 1)
|
| 864 |
+
orig_weight = orig_weight * (1 / boft_s)
|
| 865 |
+
orig_weight = orig_weight.view(
|
| 866 |
+
self.out_features,
|
| 867 |
+
self.in_features,
|
| 868 |
+
self.get_base_layer().kernel_size[0],
|
| 869 |
+
self.get_base_layer().kernel_size[0],
|
| 870 |
+
)
|
| 871 |
+
|
| 872 |
+
self.get_base_layer().weight.data = orig_weight
|
| 873 |
+
|
| 874 |
+
def get_delta_weight(self, adapter) -> tuple[torch.Tensor, torch.Tensor]:
|
| 875 |
+
"""
|
| 876 |
+
Compute the delta weight for the given adapter.
|
| 877 |
+
|
| 878 |
+
Args:
|
| 879 |
+
adapter (str):
|
| 880 |
+
The name of the adapter for which the delta weight should be computed.
|
| 881 |
+
"""
|
| 882 |
+
|
| 883 |
+
boft_R = self.boft_R[adapter]
|
| 884 |
+
boft_s = self.boft_s[adapter].transpose(0, 1)
|
| 885 |
+
|
| 886 |
+
N, D, H, _ = boft_R.shape
|
| 887 |
+
boft_R = boft_R.view(N * D, H, H)
|
| 888 |
+
orth_rotate_butterfly = self.cayley_batch(boft_R)
|
| 889 |
+
orth_rotate_butterfly = orth_rotate_butterfly.view(N, D, H, H)
|
| 890 |
+
if self.fbd_cuda_available:
|
| 891 |
+
block_diagonal_butterfly = FastBlockDiag.apply(orth_rotate_butterfly)
|
| 892 |
+
else:
|
| 893 |
+
orth_rotate_butterfly = orth_rotate_butterfly.squeeze(0)
|
| 894 |
+
block_diagonal_butterfly = torch.block_diag(*torch.unbind(orth_rotate_butterfly))
|
| 895 |
+
block_diagonal_butterfly = block_diagonal_butterfly.unsqueeze(0)
|
| 896 |
+
|
| 897 |
+
boft_P = self.boft_P.to(block_diagonal_butterfly.device)
|
| 898 |
+
butterfly_oft_mat_batch = torch.bmm(block_diagonal_butterfly, boft_P.permute(0, 2, 1))
|
| 899 |
+
butterfly_oft_mat_batch = torch.bmm(boft_P, butterfly_oft_mat_batch)
|
| 900 |
+
butterfly_oft_mat = butterfly_oft_mat_batch[0]
|
| 901 |
+
|
| 902 |
+
for i in range(1, butterfly_oft_mat_batch.shape[0]):
|
| 903 |
+
butterfly_oft_mat = butterfly_oft_mat_batch[i] @ butterfly_oft_mat
|
| 904 |
+
|
| 905 |
+
return butterfly_oft_mat, boft_s
|
| 906 |
+
|
| 907 |
+
def forward(self, x: torch.Tensor, *args: Any, **kwargs: Any) -> torch.Tensor:
|
| 908 |
+
previous_dtype = x.dtype
|
| 909 |
+
|
| 910 |
+
if self.disable_adapters:
|
| 911 |
+
if self.merged:
|
| 912 |
+
self.unmerge()
|
| 913 |
+
result = self.base_layer(x, *args, **kwargs)
|
| 914 |
+
elif self.merged:
|
| 915 |
+
result = self.base_layer(x, *args, **kwargs)
|
| 916 |
+
else:
|
| 917 |
+
boft_rotation = torch.eye(
|
| 918 |
+
self.in_features * self.base_layer.kernel_size[0] * self.base_layer.kernel_size[0],
|
| 919 |
+
device=x.device,
|
| 920 |
+
dtype=x.dtype,
|
| 921 |
+
)
|
| 922 |
+
boft_scale = torch.ones((int(self.out_features), 1), device=x.device, dtype=x.dtype)
|
| 923 |
+
|
| 924 |
+
for active_adapter in self.active_adapters:
|
| 925 |
+
if active_adapter not in self.boft_R.keys():
|
| 926 |
+
continue
|
| 927 |
+
boft_R = self.boft_R[active_adapter]
|
| 928 |
+
boft_s = self.boft_s[active_adapter].transpose(0, 1)
|
| 929 |
+
dropout = self.boft_dropout[active_adapter]
|
| 930 |
+
|
| 931 |
+
N, D, H, _ = boft_R.shape
|
| 932 |
+
boft_R = boft_R.view(N * D, H, H)
|
| 933 |
+
orth_rotate_butterfly = self.cayley_batch(boft_R)
|
| 934 |
+
orth_rotate_butterfly = orth_rotate_butterfly.view(N, D, H, H)
|
| 935 |
+
orth_rotate_butterfly = dropout(orth_rotate_butterfly)
|
| 936 |
+
if self.fbd_cuda_available:
|
| 937 |
+
block_diagonal_butterfly = FastBlockDiag.apply(orth_rotate_butterfly)
|
| 938 |
+
else:
|
| 939 |
+
orth_rotate_butterfly = orth_rotate_butterfly.squeeze(0)
|
| 940 |
+
block_diagonal_butterfly = torch.block_diag(*torch.unbind(orth_rotate_butterfly))
|
| 941 |
+
block_diagonal_butterfly = block_diagonal_butterfly.unsqueeze(0)
|
| 942 |
+
|
| 943 |
+
boft_P = self.boft_P.to(x)
|
| 944 |
+
block_diagonal_butterfly = block_diagonal_butterfly.to(x)
|
| 945 |
+
butterfly_oft_mat_batch = torch.bmm(block_diagonal_butterfly, boft_P.permute(0, 2, 1))
|
| 946 |
+
butterfly_oft_mat_batch = torch.bmm(boft_P, butterfly_oft_mat_batch)
|
| 947 |
+
butterfly_oft_mat = butterfly_oft_mat_batch[0]
|
| 948 |
+
|
| 949 |
+
for i in range(1, butterfly_oft_mat_batch.shape[0]):
|
| 950 |
+
butterfly_oft_mat = butterfly_oft_mat_batch[i] @ butterfly_oft_mat
|
| 951 |
+
|
| 952 |
+
boft_rotation = butterfly_oft_mat @ boft_rotation
|
| 953 |
+
boft_scale = boft_s * boft_scale
|
| 954 |
+
|
| 955 |
+
x = x.to(self.base_layer.weight.data.dtype)
|
| 956 |
+
|
| 957 |
+
orig_weight = self.base_layer.weight.data
|
| 958 |
+
orig_weight = orig_weight.view(
|
| 959 |
+
self.out_features,
|
| 960 |
+
self.in_features * self.base_layer.kernel_size[0] * self.base_layer.kernel_size[0],
|
| 961 |
+
)
|
| 962 |
+
orig_weight = torch.transpose(orig_weight, 0, 1)
|
| 963 |
+
rotated_weight = torch.mm(boft_rotation, orig_weight)
|
| 964 |
+
rotated_weight = torch.transpose(rotated_weight, 0, 1)
|
| 965 |
+
|
| 966 |
+
scaled_rotated_weight = rotated_weight * boft_scale
|
| 967 |
+
|
| 968 |
+
scaled_rotated_weight = scaled_rotated_weight.view(
|
| 969 |
+
self.out_features, self.in_features, self.base_layer.kernel_size[0], self.base_layer.kernel_size[0]
|
| 970 |
+
)
|
| 971 |
+
result = F.conv2d(
|
| 972 |
+
input=x,
|
| 973 |
+
weight=scaled_rotated_weight,
|
| 974 |
+
bias=self.base_layer.bias,
|
| 975 |
+
padding=self.base_layer.padding[0],
|
| 976 |
+
stride=self.base_layer.stride[0],
|
| 977 |
+
)
|
| 978 |
+
|
| 979 |
+
result = result.to(previous_dtype)
|
| 980 |
+
return result
|
| 981 |
+
|
| 982 |
+
def __repr__(self) -> str:
|
| 983 |
+
rep = super().__repr__()
|
| 984 |
+
return "boft." + rep
|
lib/python3.12/site-packages/peft/tuners/boft/model.py
ADDED
|
@@ -0,0 +1,353 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2023-present the HuggingFace Inc. team.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
# The implementation is based on "Parameter-Efficient Orthogonal Finetuning
|
| 16 |
+
# via Butterfly Factorization" (https://arxiv.org/abs/2311.06243) in ICLR 2024.
|
| 17 |
+
|
| 18 |
+
import warnings
|
| 19 |
+
from dataclasses import asdict
|
| 20 |
+
from enum import Enum
|
| 21 |
+
from typing import List, Optional
|
| 22 |
+
|
| 23 |
+
import torch
|
| 24 |
+
from torch import nn
|
| 25 |
+
from tqdm import tqdm
|
| 26 |
+
|
| 27 |
+
from peft.tuners.tuners_utils import (
|
| 28 |
+
BaseTuner,
|
| 29 |
+
BaseTunerLayer,
|
| 30 |
+
check_target_module_exists,
|
| 31 |
+
onload_layer,
|
| 32 |
+
)
|
| 33 |
+
from peft.utils import (
|
| 34 |
+
TRANSFORMERS_MODELS_TO_LORA_TARGET_MODULES_MAPPING,
|
| 35 |
+
ModulesToSaveWrapper,
|
| 36 |
+
_get_submodules,
|
| 37 |
+
)
|
| 38 |
+
|
| 39 |
+
from .config import BOFTConfig
|
| 40 |
+
from .layer import BOFTLayer, Conv2d, Linear
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
class BOFTModel(BaseTuner):
|
| 44 |
+
"""
|
| 45 |
+
Creates BOFT and OFT model from a pretrained transformers model. Paper: https://arxiv.org/abs/2311.06243
|
| 46 |
+
https://arxiv.org/abs/2306.07280
|
| 47 |
+
|
| 48 |
+
Args:
|
| 49 |
+
model ([`transformers.PreTrainedModel`]): The model to be adapted.
|
| 50 |
+
config ([`BOFTConfig`]): The configuration of the BOFT model.
|
| 51 |
+
adapter_name (`str`): The name of the adapter, defaults to `"default"`.
|
| 52 |
+
low_cpu_mem_usage (`bool`, `optional`, defaults to `False`):
|
| 53 |
+
Create empty adapter weights on meta device. Useful to speed up the loading process.
|
| 54 |
+
|
| 55 |
+
Returns:
|
| 56 |
+
`torch.nn.Module`: The BOFT model.
|
| 57 |
+
|
| 58 |
+
Example::
|
| 59 |
+
|
| 60 |
+
>>> import transformers >>> from transformers import AutoModelForSeq2SeqLM, BOFTConfig >>> from peft import
|
| 61 |
+
BOFTConfig, get_peft_model
|
| 62 |
+
|
| 63 |
+
>>> config = BOFTConfig( ... boft_block_size=8, ... boft_n_butterfly_factor=1, ... target_modules=["query",
|
| 64 |
+
"value", "key", "output.dense", "mlp.fc1", "mlp.fc2"], ... boft_dropout=0.1, ... bias="boft_only", ...
|
| 65 |
+
modules_to_save=["classifier"], ... )
|
| 66 |
+
|
| 67 |
+
>>> model = transformers.Dinov2ForImageClassification.from_pretrained( ... "facebook/dinov2-large", ...
|
| 68 |
+
num_labels=100, ... ) >>> boft_model = get_peft_model(model, config)
|
| 69 |
+
|
| 70 |
+
**Attributes**:
|
| 71 |
+
- **model** ([`transformers.PreTrainedModel`]) -- The model to be adapted.
|
| 72 |
+
- **peft_config** ([`BOFTConfig`]): The configuration of the BOFT model.
|
| 73 |
+
"""
|
| 74 |
+
|
| 75 |
+
prefix: str = "boft_"
|
| 76 |
+
|
| 77 |
+
def __init__(self, model, config, adapter_name, low_cpu_mem_usage: bool = False) -> None:
|
| 78 |
+
super().__init__(model, config, adapter_name, low_cpu_mem_usage=low_cpu_mem_usage)
|
| 79 |
+
|
| 80 |
+
def _check_new_adapter_config(self, config: BOFTConfig) -> None:
|
| 81 |
+
"""
|
| 82 |
+
A helper method to check the config when a new adapter is being added.
|
| 83 |
+
|
| 84 |
+
Raise a ValueError if there is something wrong with the config or if it conflicts with existing adapters.
|
| 85 |
+
|
| 86 |
+
"""
|
| 87 |
+
# TODO: there should be a check if any of the existing adapters actually has bias != "none", or else the check
|
| 88 |
+
# does not fully correspond to the error message.
|
| 89 |
+
if (len(self.peft_config) > 1) and (config.bias != "none"):
|
| 90 |
+
raise ValueError(
|
| 91 |
+
f"{self.__class__.__name__} supports only 1 adapter with bias. When using multiple adapters, "
|
| 92 |
+
"set bias to 'none' for all adapters."
|
| 93 |
+
)
|
| 94 |
+
|
| 95 |
+
@staticmethod
|
| 96 |
+
def _check_target_module_exists(boft_config, key):
|
| 97 |
+
return check_target_module_exists(boft_config, key)
|
| 98 |
+
|
| 99 |
+
def _create_and_replace(
|
| 100 |
+
self,
|
| 101 |
+
boft_config,
|
| 102 |
+
adapter_name,
|
| 103 |
+
target,
|
| 104 |
+
target_name,
|
| 105 |
+
parent,
|
| 106 |
+
current_key,
|
| 107 |
+
**optional_kwargs,
|
| 108 |
+
):
|
| 109 |
+
if current_key is None:
|
| 110 |
+
raise ValueError("Current Key shouldn't be `None`")
|
| 111 |
+
|
| 112 |
+
bias = hasattr(target, "bias") and target.bias is not None
|
| 113 |
+
kwargs = {
|
| 114 |
+
"boft_block_size": boft_config.boft_block_size,
|
| 115 |
+
"boft_block_num": boft_config.boft_block_num,
|
| 116 |
+
"boft_n_butterfly_factor": boft_config.boft_n_butterfly_factor,
|
| 117 |
+
"boft_dropout": boft_config.boft_dropout,
|
| 118 |
+
"fan_in_fan_out": boft_config.fan_in_fan_out,
|
| 119 |
+
"init_weights": boft_config.init_weights,
|
| 120 |
+
}
|
| 121 |
+
kwargs["bias"] = bias
|
| 122 |
+
|
| 123 |
+
# If it is not a BOFTLayer, create a new module, else update it with new adapters
|
| 124 |
+
if not isinstance(target, BOFTLayer):
|
| 125 |
+
new_module = self._create_new_module(boft_config, adapter_name, target, **kwargs)
|
| 126 |
+
if adapter_name not in self.active_adapters:
|
| 127 |
+
# adding an additional adapter: it is not automatically trainable
|
| 128 |
+
new_module.requires_grad_(False)
|
| 129 |
+
self._replace_module(parent, target_name, new_module, target)
|
| 130 |
+
else:
|
| 131 |
+
target.update_layer(
|
| 132 |
+
adapter_name,
|
| 133 |
+
boft_block_size=boft_config.boft_block_size,
|
| 134 |
+
boft_block_num=boft_config.boft_block_num,
|
| 135 |
+
boft_n_butterfly_factor=boft_config.boft_n_butterfly_factor,
|
| 136 |
+
boft_dropout=boft_config.boft_dropout,
|
| 137 |
+
init_weights=boft_config.init_weights,
|
| 138 |
+
)
|
| 139 |
+
|
| 140 |
+
def _replace_module(self, parent, child_name, new_module, child):
|
| 141 |
+
setattr(parent, child_name, new_module)
|
| 142 |
+
# It's not necessary to set requires_grad here, as that is handled by
|
| 143 |
+
# _mark_only_adapters_as_trainable
|
| 144 |
+
|
| 145 |
+
# child layer wraps the original module, unpack it
|
| 146 |
+
if hasattr(child, "base_layer"):
|
| 147 |
+
child = child.base_layer
|
| 148 |
+
|
| 149 |
+
if not hasattr(new_module, "base_layer"):
|
| 150 |
+
new_module.weight = child.weight
|
| 151 |
+
if hasattr(child, "bias"):
|
| 152 |
+
new_module.bias = child.bias
|
| 153 |
+
|
| 154 |
+
if getattr(child, "state", None) is not None:
|
| 155 |
+
if hasattr(new_module, "base_layer"):
|
| 156 |
+
new_module.base_layer.state = child.state
|
| 157 |
+
else:
|
| 158 |
+
new_module.state = child.state
|
| 159 |
+
new_module.to(child.weight.device)
|
| 160 |
+
|
| 161 |
+
meta = torch.device("meta")
|
| 162 |
+
# dispatch to correct device
|
| 163 |
+
for name, module in new_module.named_modules():
|
| 164 |
+
if self.prefix in name:
|
| 165 |
+
if not any(p.device == meta for p in module.parameters()):
|
| 166 |
+
module.to(child.weight.device)
|
| 167 |
+
|
| 168 |
+
def _mark_only_adapters_as_trainable(self, model: nn.Module) -> None:
|
| 169 |
+
for n, p in model.named_parameters():
|
| 170 |
+
if self.prefix not in n:
|
| 171 |
+
p.requires_grad = False
|
| 172 |
+
|
| 173 |
+
for active_adapter in self.active_adapters:
|
| 174 |
+
bias = self.peft_config[active_adapter].bias
|
| 175 |
+
if bias == "none":
|
| 176 |
+
continue
|
| 177 |
+
|
| 178 |
+
if bias == "all":
|
| 179 |
+
for n, p in model.named_parameters():
|
| 180 |
+
if "bias" in n:
|
| 181 |
+
p.requires_grad = True
|
| 182 |
+
elif bias == "boft_only":
|
| 183 |
+
for name, m in model.named_modules():
|
| 184 |
+
if isinstance(m, BOFTLayer) and hasattr(m, "bias") and m.bias is not None:
|
| 185 |
+
m.bias.requires_grad = True
|
| 186 |
+
else:
|
| 187 |
+
raise NotImplementedError(f"Requested bias: {bias}, is not implemented.")
|
| 188 |
+
|
| 189 |
+
@staticmethod
|
| 190 |
+
def _create_new_module(boft_config, adapter_name, target, **kwargs):
|
| 191 |
+
if isinstance(target, BaseTunerLayer):
|
| 192 |
+
target_base_layer = target.get_base_layer()
|
| 193 |
+
else:
|
| 194 |
+
target_base_layer = target
|
| 195 |
+
|
| 196 |
+
if isinstance(target_base_layer, torch.nn.Linear):
|
| 197 |
+
if kwargs["fan_in_fan_out"]:
|
| 198 |
+
warnings.warn(
|
| 199 |
+
"fan_in_fan_out is set to True but the target module is `torch.nn.Linear`. "
|
| 200 |
+
"Setting fan_in_fan_out to False."
|
| 201 |
+
)
|
| 202 |
+
kwargs["fan_in_fan_out"] = boft_config.fan_in_fan_out = False
|
| 203 |
+
new_module = Linear(target, adapter_name, **kwargs)
|
| 204 |
+
elif isinstance(target_base_layer, torch.nn.Conv2d):
|
| 205 |
+
new_module = Conv2d(target, adapter_name, **kwargs)
|
| 206 |
+
else:
|
| 207 |
+
raise ValueError(
|
| 208 |
+
f"Target module {target} is not supported. "
|
| 209 |
+
"Currently, only `torch.nn.Linear` and `torch.nn.Conv2d` are supported."
|
| 210 |
+
)
|
| 211 |
+
|
| 212 |
+
return new_module
|
| 213 |
+
|
| 214 |
+
def __getattr__(self, name: str):
|
| 215 |
+
"""Forward missing attributes to the wrapped module."""
|
| 216 |
+
try:
|
| 217 |
+
return super().__getattr__(name) # defer to nn.Module's logic
|
| 218 |
+
except AttributeError:
|
| 219 |
+
if name == "model": # see #1892: prevent infinite recursion if class is not initialized
|
| 220 |
+
raise
|
| 221 |
+
return getattr(self.model, name)
|
| 222 |
+
|
| 223 |
+
def get_peft_config_as_dict(self, inference: bool = False):
|
| 224 |
+
config_dict = {}
|
| 225 |
+
for key, value in self.peft_config.items():
|
| 226 |
+
config = {k: v.value if isinstance(v, Enum) else v for k, v in asdict(value).items()}
|
| 227 |
+
if inference:
|
| 228 |
+
config["inference_mode"] = True
|
| 229 |
+
config_dict[key] = config
|
| 230 |
+
return config
|
| 231 |
+
|
| 232 |
+
def _set_adapter_layers(self, enabled=True):
|
| 233 |
+
for module in self.model.modules():
|
| 234 |
+
if isinstance(module, (BaseTunerLayer, ModulesToSaveWrapper)):
|
| 235 |
+
module.enable_adapters(enabled)
|
| 236 |
+
|
| 237 |
+
def enable_adapter_layers(self):
|
| 238 |
+
self._set_adapter_layers(enabled=True)
|
| 239 |
+
|
| 240 |
+
def disable_adapter_layers(self):
|
| 241 |
+
for active_adapter in self.active_adapters:
|
| 242 |
+
val = self.peft_config[active_adapter].bias
|
| 243 |
+
if val != "none":
|
| 244 |
+
msg = (
|
| 245 |
+
f"Careful, disabling adapter layers with bias configured to be '{val}' does not produce the same "
|
| 246 |
+
"output as the the base model would without adaption."
|
| 247 |
+
)
|
| 248 |
+
warnings.warn(msg)
|
| 249 |
+
self._set_adapter_layers(enabled=False)
|
| 250 |
+
|
| 251 |
+
def set_adapter(self, adapter_name):
|
| 252 |
+
for module in self.model.modules():
|
| 253 |
+
if isinstance(module, BOFTLayer):
|
| 254 |
+
if module.merged:
|
| 255 |
+
warnings.warn("Adapter cannot be set when the model is merged. Unmerging the model first.")
|
| 256 |
+
module.unmerge()
|
| 257 |
+
module.set_adapter(adapter_name)
|
| 258 |
+
self.active_adapter = adapter_name
|
| 259 |
+
|
| 260 |
+
@staticmethod
|
| 261 |
+
def _prepare_adapter_config(peft_config, model_config):
|
| 262 |
+
if peft_config.target_modules is None:
|
| 263 |
+
if model_config["model_type"] not in TRANSFORMERS_MODELS_TO_LORA_TARGET_MODULES_MAPPING:
|
| 264 |
+
raise ValueError("Please specify `target_modules` in `peft_config`")
|
| 265 |
+
peft_config.target_modules = set(
|
| 266 |
+
TRANSFORMERS_MODELS_TO_LORA_TARGET_MODULES_MAPPING[model_config["model_type"]]
|
| 267 |
+
)
|
| 268 |
+
return peft_config
|
| 269 |
+
|
| 270 |
+
def _unload_and_optionally_merge(
|
| 271 |
+
self,
|
| 272 |
+
merge=True,
|
| 273 |
+
progressbar: bool = False,
|
| 274 |
+
safe_merge: bool = False,
|
| 275 |
+
adapter_names: Optional[List[str]] = None,
|
| 276 |
+
):
|
| 277 |
+
if merge:
|
| 278 |
+
self._check_merge_allowed()
|
| 279 |
+
|
| 280 |
+
key_list = [key for key, _ in self.model.named_modules() if self.prefix not in key]
|
| 281 |
+
desc = "Unloading " + ("and merging " if merge else "") + "model"
|
| 282 |
+
for key in tqdm(key_list, disable=not progressbar, desc=desc):
|
| 283 |
+
try:
|
| 284 |
+
parent, target, target_name = _get_submodules(self.model, key)
|
| 285 |
+
except AttributeError:
|
| 286 |
+
continue
|
| 287 |
+
with onload_layer(target):
|
| 288 |
+
if hasattr(target, "base_layer"):
|
| 289 |
+
if merge:
|
| 290 |
+
target.merge(safe_merge=safe_merge, adapter_names=adapter_names)
|
| 291 |
+
self._replace_module(parent, target_name, target.get_base_layer(), target)
|
| 292 |
+
elif isinstance(target, ModulesToSaveWrapper):
|
| 293 |
+
# save any additional trainable modules part of `modules_to_save`
|
| 294 |
+
new_module = target.modules_to_save[target.active_adapter]
|
| 295 |
+
if hasattr(new_module, "base_layer"):
|
| 296 |
+
# check if the module is itself a tuner layer
|
| 297 |
+
if merge:
|
| 298 |
+
new_module.merge(safe_merge=safe_merge, adapter_names=adapter_names)
|
| 299 |
+
new_module = new_module.get_base_layer()
|
| 300 |
+
setattr(parent, target_name, new_module)
|
| 301 |
+
|
| 302 |
+
return self.model
|
| 303 |
+
|
| 304 |
+
def delete_adapter(self, adapter_name: str) -> None:
|
| 305 |
+
"""
|
| 306 |
+
Deletes an existing adapter.
|
| 307 |
+
|
| 308 |
+
Args:
|
| 309 |
+
adapter_name (str): Name of the adapter to be deleted.
|
| 310 |
+
"""
|
| 311 |
+
if adapter_name not in list(self.peft_config.keys()):
|
| 312 |
+
raise ValueError(f"Adapter {adapter_name} does not exist")
|
| 313 |
+
del self.peft_config[adapter_name]
|
| 314 |
+
|
| 315 |
+
key_list = [key for key, _ in self.model.named_modules() if self.prefix not in key]
|
| 316 |
+
new_adapter = None
|
| 317 |
+
for key in key_list:
|
| 318 |
+
_, target, _ = _get_submodules(self.model, key)
|
| 319 |
+
if isinstance(target, BOFTLayer):
|
| 320 |
+
target.delete_adapter(adapter_name)
|
| 321 |
+
if new_adapter is None:
|
| 322 |
+
new_adapter = target.active_adapters[:]
|
| 323 |
+
|
| 324 |
+
self.active_adapter = new_adapter or []
|
| 325 |
+
|
| 326 |
+
def merge_and_unload(
|
| 327 |
+
self, progressbar: bool = False, safe_merge: bool = False, adapter_names: Optional[List[str]] = None
|
| 328 |
+
) -> torch.nn.Module:
|
| 329 |
+
r"""
|
| 330 |
+
This method merges the BOFT layers into the base model. This is needed if someone wants to use the base model
|
| 331 |
+
as a standalone model.
|
| 332 |
+
|
| 333 |
+
Args:
|
| 334 |
+
progressbar (`bool`):
|
| 335 |
+
whether to show a progressbar indicating the unload and merge process
|
| 336 |
+
safe_merge (`bool`):
|
| 337 |
+
whether to activate the safe merging check to check if there is any potential Nan in the adapter
|
| 338 |
+
weights
|
| 339 |
+
adapter_names (`List[str]`, *optional*):
|
| 340 |
+
The list of adapter names that should be merged. If None, all active adapters will be merged. Defaults
|
| 341 |
+
to `None`.
|
| 342 |
+
|
| 343 |
+
"""
|
| 344 |
+
return self._unload_and_optionally_merge(
|
| 345 |
+
progressbar=progressbar, safe_merge=safe_merge, adapter_names=adapter_names
|
| 346 |
+
)
|
| 347 |
+
|
| 348 |
+
def unload(self) -> torch.nn.Module:
|
| 349 |
+
"""
|
| 350 |
+
Gets back the base model by removing all the boft modules without merging. This gives back the original base
|
| 351 |
+
model.
|
| 352 |
+
"""
|
| 353 |
+
return self._unload_and_optionally_merge(merge=False)
|
lib/python3.12/site-packages/peft/tuners/cpt/__init__.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2024-present the HuggingFace Inc. team.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
from peft.utils import register_peft_method
|
| 17 |
+
|
| 18 |
+
from .config import CPTConfig
|
| 19 |
+
from .model import CPTEmbedding
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
__all__ = ["CPTConfig", "CPTEmbedding"]
|
| 23 |
+
|
| 24 |
+
register_peft_method(name="cpt", config_cls=CPTConfig, model_cls=CPTEmbedding)
|
lib/python3.12/site-packages/peft/tuners/cpt/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (452 Bytes). View file
|
|
|
lib/python3.12/site-packages/peft/tuners/cpt/__pycache__/config.cpython-312.pyc
ADDED
|
Binary file (3.81 kB). View file
|
|
|
lib/python3.12/site-packages/peft/tuners/cpt/__pycache__/model.cpython-312.pyc
ADDED
|
Binary file (10.9 kB). View file
|
|
|
lib/python3.12/site-packages/peft/tuners/cpt/config.py
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2024-present the HuggingFace Inc. team.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
from dataclasses import dataclass, field
|
| 16 |
+
from typing import Literal, Optional
|
| 17 |
+
|
| 18 |
+
from peft.config import PromptLearningConfig
|
| 19 |
+
from peft.utils import PeftType
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
@dataclass
|
| 23 |
+
class CPTConfig(PromptLearningConfig):
|
| 24 |
+
"""
|
| 25 |
+
CPT Configuration class extending PeftConfig for Context-aware Prompt Tuning (CPT).
|
| 26 |
+
|
| 27 |
+
This class introduces additional parameters required for CPT, such as:
|
| 28 |
+
- Token type masks
|
| 29 |
+
- Prompt tuning initialization
|
| 30 |
+
- Loss weighting
|
| 31 |
+
- Projection settings
|
| 32 |
+
|
| 33 |
+
For more details, see the paper: https://arxiv.org/abs/2410.17222
|
| 34 |
+
"""
|
| 35 |
+
|
| 36 |
+
# Token-related configurations
|
| 37 |
+
cpt_token_ids: Optional[list[int]] = field(
|
| 38 |
+
default=None, metadata={"help": "Tensor of token IDs used for CPT prompts."}
|
| 39 |
+
)
|
| 40 |
+
cpt_mask: Optional[list[int]] = field(default=None, metadata={"help": "Tensor mask applied to CPT tokens."})
|
| 41 |
+
cpt_tokens_type_mask: Optional[list[int]] = field(
|
| 42 |
+
default=None, metadata={"help": "Mask indicating the type of each CPT token."}
|
| 43 |
+
)
|
| 44 |
+
|
| 45 |
+
# Loss-related configurations
|
| 46 |
+
opt_weighted_loss_type: Optional[Literal["none", "decay"]] = field(
|
| 47 |
+
default="none", metadata={"help": "Type of weighted loss: 'none' or 'decay'."}
|
| 48 |
+
)
|
| 49 |
+
opt_loss_decay_factor: Optional[float] = field(
|
| 50 |
+
default=1.0, metadata={"help": "Factor for exponential decay in loss weighting."}
|
| 51 |
+
)
|
| 52 |
+
|
| 53 |
+
# Projection-related configurations
|
| 54 |
+
opt_projection_epsilon: Optional[float] = field(
|
| 55 |
+
default=0.1, metadata={"help": "Epsilon value for input projection."}
|
| 56 |
+
)
|
| 57 |
+
opt_projection_format_epsilon: Optional[float] = field(
|
| 58 |
+
default=0.1, metadata={"help": "Epsilon value for format projection."}
|
| 59 |
+
)
|
| 60 |
+
|
| 61 |
+
# Tokenizer configuration
|
| 62 |
+
tokenizer_name_or_path: Optional[str] = field(
|
| 63 |
+
default=None,
|
| 64 |
+
metadata={
|
| 65 |
+
"help": "The tokenizer to use for prompt tuning initialization. Only used if prompt_tuning_init is `TEXT`"
|
| 66 |
+
},
|
| 67 |
+
)
|
| 68 |
+
# Neet to define CPT-specific static attributes
|
| 69 |
+
is_prompt_learning = True # Indicates that CPT is a prompt-learning method.
|
| 70 |
+
|
| 71 |
+
def __post_init__(self):
|
| 72 |
+
"""
|
| 73 |
+
Post-initialization hook to set additional attributes after the config is initialized.
|
| 74 |
+
"""
|
| 75 |
+
# CPT-specific static attributes
|
| 76 |
+
self.is_prompt_learning = True # Indicates that CPT is a prompt-learning method.
|
| 77 |
+
self.num_layers = None # Number of layers (optional, not always required).
|
| 78 |
+
self.token_dim = None # Dimension of token embeddings.
|
| 79 |
+
self.num_attention_heads = None # Number of attention heads (if applicable).
|
| 80 |
+
self.num_transformer_submodules = 1 # Number of transformer submodules used.
|
| 81 |
+
self.peft_type = PeftType.CPT # Specifies that the PEFT type is CPT.
|
| 82 |
+
self.task_type = "CAUSAL_LM" # Ensures task type is causal language modeling.
|
| 83 |
+
|
| 84 |
+
if self.cpt_token_ids is None:
|
| 85 |
+
self.cpt_token_ids = [0]
|
| 86 |
+
|
| 87 |
+
self.num_virtual_tokens = len(self.cpt_token_ids)
|
| 88 |
+
|
| 89 |
+
if self.cpt_mask is None:
|
| 90 |
+
self.cpt_mask = [1 for _ in self.cpt_token_ids]
|
| 91 |
+
|
| 92 |
+
if self.cpt_tokens_type_mask is None:
|
| 93 |
+
self.cpt_tokens_type_mask = [1 for _ in self.cpt_token_ids]
|
| 94 |
+
|
| 95 |
+
if not (
|
| 96 |
+
len(self.cpt_token_ids) == len(self.cpt_mask) == len(self.cpt_tokens_type_mask) == self.num_virtual_tokens
|
| 97 |
+
):
|
| 98 |
+
raise ValueError("cpt_token_ids, cpt_mask and cpt_tokens_type_mask must have the same length.")
|
lib/python3.12/site-packages/peft/tuners/cpt/model.py
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2024-present the HuggingFace Inc. team.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
import copy
|
| 16 |
+
|
| 17 |
+
import torch
|
| 18 |
+
from torch.nn import CrossEntropyLoss
|
| 19 |
+
|
| 20 |
+
from peft.utils.integrations import gather_params_ctx
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
class CPTEmbedding(torch.nn.Module):
|
| 24 |
+
"""
|
| 25 |
+
CPTEmbedding is a custom embedding layer designed for Context-aware Prompt Tuning (CPT) in PEFT. It initializes
|
| 26 |
+
embeddings, applies prompt-specific projections, and computes loss using label masks.
|
| 27 |
+
"""
|
| 28 |
+
|
| 29 |
+
def __init__(self, config, word_embeddings):
|
| 30 |
+
"""
|
| 31 |
+
Initializes the CPTEmbedding module.
|
| 32 |
+
|
| 33 |
+
Args:
|
| 34 |
+
config (Namespace):
|
| 35 |
+
Configuration object containing model hyperparameters and CPT-specific settings.
|
| 36 |
+
word_embeddings (torch.nn.Embedding):
|
| 37 |
+
The base word embedding layer used to initialize CPT embeddings.
|
| 38 |
+
"""
|
| 39 |
+
super().__init__()
|
| 40 |
+
self.config = copy.deepcopy(config)
|
| 41 |
+
num_virtual_tokens = config.num_virtual_tokens
|
| 42 |
+
|
| 43 |
+
# Initialize embeddings with virtual token dimensions
|
| 44 |
+
self.embedding = torch.nn.Embedding(num_virtual_tokens, config.token_dim)
|
| 45 |
+
|
| 46 |
+
# Initialize embeddings using text-based prompt tuning, if configured
|
| 47 |
+
if not config.inference_mode:
|
| 48 |
+
assert config.num_virtual_tokens == len(config.cpt_token_ids)
|
| 49 |
+
|
| 50 |
+
init_token_ids = torch.LongTensor(config.cpt_token_ids).to(word_embeddings.weight.device)
|
| 51 |
+
with gather_params_ctx(word_embeddings.parameters()):
|
| 52 |
+
word_embedding_weights = word_embeddings(init_token_ids).detach().clone()
|
| 53 |
+
word_embedding_weights = word_embedding_weights.to(torch.float32)
|
| 54 |
+
self.embedding.weight = torch.nn.Parameter(word_embedding_weights)
|
| 55 |
+
|
| 56 |
+
# Initialize delta embedding with zero weights
|
| 57 |
+
self.delta_embedding = torch.nn.Embedding(num_virtual_tokens, config.token_dim)
|
| 58 |
+
self.delta_embedding.weight.data = torch.zeros_like(self.delta_embedding.weight).to(torch.float32)
|
| 59 |
+
|
| 60 |
+
# Apply hook for backward gradient updates
|
| 61 |
+
self.set_updated_tokens()
|
| 62 |
+
|
| 63 |
+
def forward(self, indices):
|
| 64 |
+
"""
|
| 65 |
+
Computes the prompt embeddings and applies delta adjustments.
|
| 66 |
+
|
| 67 |
+
Args:
|
| 68 |
+
indices (torch.Tensor):
|
| 69 |
+
Indices of the tokens to be embedded.
|
| 70 |
+
|
| 71 |
+
Returns:
|
| 72 |
+
torch.Tensor:
|
| 73 |
+
Sum of prompt embeddings and delta embeddings.
|
| 74 |
+
"""
|
| 75 |
+
with torch.no_grad():
|
| 76 |
+
prompt_embeddings = self.embedding(indices)
|
| 77 |
+
|
| 78 |
+
self.delta_embedding.weight.data = self.get_projection() # Apply epsilon-based projection
|
| 79 |
+
|
| 80 |
+
delta_prompt_embeddings = self.delta_embedding(indices)
|
| 81 |
+
|
| 82 |
+
return prompt_embeddings + delta_prompt_embeddings
|
| 83 |
+
|
| 84 |
+
def set_updated_tokens(self):
|
| 85 |
+
"""
|
| 86 |
+
Sets up a backward hook to selectively update token gradients based on the CPT token type mask.
|
| 87 |
+
"""
|
| 88 |
+
tensor_ICL_mask = torch.Tensor(self.config.cpt_tokens_type_mask).long()
|
| 89 |
+
mask_input_template = torch.remainder(tensor_ICL_mask, 4) == 1
|
| 90 |
+
mask_input = torch.remainder(tensor_ICL_mask, 4) == 2
|
| 91 |
+
mask_output_template = torch.remainder(tensor_ICL_mask, 4) == 3
|
| 92 |
+
mask = mask_input_template | mask_input | mask_output_template
|
| 93 |
+
mask = mask.view(-1, 1)
|
| 94 |
+
|
| 95 |
+
def backward_hook(grad):
|
| 96 |
+
grad = grad * mask.to(grad.device) # Apply mask to gradients
|
| 97 |
+
return grad
|
| 98 |
+
|
| 99 |
+
self.delta_embedding.weight.register_hook(backward_hook)
|
| 100 |
+
|
| 101 |
+
def get_epsilon(self):
|
| 102 |
+
cpt_tokens_type_mask = self.config.cpt_tokens_type_mask
|
| 103 |
+
|
| 104 |
+
MIN_VALUE = 1e-10
|
| 105 |
+
|
| 106 |
+
# Calculate normalized epsilon values for input, output, and format tokens
|
| 107 |
+
normalized_format_eps = self.config.opt_projection_format_epsilon * torch.sqrt(
|
| 108 |
+
torch.Tensor([self.config.token_dim / 2048])
|
| 109 |
+
)
|
| 110 |
+
normalized_input_eps = self.config.opt_projection_epsilon * torch.sqrt(
|
| 111 |
+
torch.Tensor([self.config.token_dim / 2048])
|
| 112 |
+
)
|
| 113 |
+
|
| 114 |
+
epsilon = torch.ones_like(torch.Tensor(cpt_tokens_type_mask)).to(torch.float32) * MIN_VALUE
|
| 115 |
+
cpt_tokens_type_mask = torch.Tensor(cpt_tokens_type_mask).long()
|
| 116 |
+
|
| 117 |
+
epsilon[(cpt_tokens_type_mask > 0) & (torch.remainder(cpt_tokens_type_mask, 4) == 1)] = normalized_format_eps
|
| 118 |
+
epsilon[(cpt_tokens_type_mask > 0) & (torch.remainder(cpt_tokens_type_mask, 4) == 3)] = normalized_format_eps
|
| 119 |
+
epsilon[(cpt_tokens_type_mask > 0) & (torch.remainder(cpt_tokens_type_mask, 4) == 2)] = normalized_input_eps
|
| 120 |
+
|
| 121 |
+
return epsilon
|
| 122 |
+
|
| 123 |
+
def get_projection(self):
|
| 124 |
+
"""
|
| 125 |
+
Applies epsilon-based projection to the delta embeddings to control their norm.
|
| 126 |
+
"""
|
| 127 |
+
|
| 128 |
+
# Apply projection to control delta embedding norm
|
| 129 |
+
with torch.no_grad():
|
| 130 |
+
new_embeddings_weights = self.delta_embedding.weight.clone().to(self.delta_embedding.weight.device)
|
| 131 |
+
token_norm = torch.norm(new_embeddings_weights, p=2, dim=1)
|
| 132 |
+
|
| 133 |
+
projection_mask = token_norm > 0
|
| 134 |
+
if torch.any(projection_mask):
|
| 135 |
+
epsilon = self.get_epsilon().to(self.delta_embedding.weight.device)
|
| 136 |
+
new_embeddings_weights[projection_mask] *= (
|
| 137 |
+
epsilon[projection_mask] / (token_norm[projection_mask].clamp(min=epsilon[projection_mask]))
|
| 138 |
+
).view(-1, 1)
|
| 139 |
+
return new_embeddings_weights
|
| 140 |
+
|
| 141 |
+
@staticmethod
|
| 142 |
+
def calculate_loss(base_model_output, labels, cpt_type_mask, config):
|
| 143 |
+
"""
|
| 144 |
+
Computes the loss for CPT models with optional exponential decay.
|
| 145 |
+
|
| 146 |
+
Args:
|
| 147 |
+
base_model_output (ModelOutput):
|
| 148 |
+
Output from the base model containing logits.
|
| 149 |
+
labels (torch.Tensor):
|
| 150 |
+
Ground-truth labels for the input tokens.
|
| 151 |
+
cpt_type_mask (torch.Tensor):
|
| 152 |
+
Token type mask used for filtering valid loss terms.
|
| 153 |
+
config (Namespace):
|
| 154 |
+
Configuration object containing loss-related hyperparameters.
|
| 155 |
+
|
| 156 |
+
Returns:
|
| 157 |
+
ModelOutput:
|
| 158 |
+
The base model output with computed loss.
|
| 159 |
+
"""
|
| 160 |
+
|
| 161 |
+
device = base_model_output.logits.device
|
| 162 |
+
|
| 163 |
+
lm_logits = base_model_output.logits
|
| 164 |
+
labels = labels.to(device)
|
| 165 |
+
|
| 166 |
+
# Shift logits and labels for token prediction
|
| 167 |
+
shift_logits = lm_logits[..., :-1, :].contiguous()
|
| 168 |
+
shift_labels = labels[..., 1:].contiguous()
|
| 169 |
+
shift_cpt_type_mask = cpt_type_mask[..., 1:].contiguous()
|
| 170 |
+
|
| 171 |
+
shift_labels_bool = (shift_labels.clone().detach() != -100).bool()
|
| 172 |
+
batch_size, seq_length, vocab_size = shift_logits.shape
|
| 173 |
+
|
| 174 |
+
# Compute cross-entropy loss
|
| 175 |
+
loss_fct = CrossEntropyLoss(reduction="none", ignore_index=-100)
|
| 176 |
+
loss = loss_fct(
|
| 177 |
+
shift_logits.view(batch_size * seq_length, vocab_size), shift_labels.view(batch_size * seq_length)
|
| 178 |
+
)
|
| 179 |
+
loss = loss.view(batch_size, seq_length)
|
| 180 |
+
# Apply exponential decay weights to the loss
|
| 181 |
+
shift_labels_weights = shift_labels_bool.clone().detach().float()
|
| 182 |
+
|
| 183 |
+
for i in range(batch_size):
|
| 184 |
+
idx_labels = (shift_cpt_type_mask[i] > 0) & (shift_cpt_type_mask[i] % 4 == 0)
|
| 185 |
+
labels_ids = shift_cpt_type_mask[i][idx_labels].unique()
|
| 186 |
+
|
| 187 |
+
exponential_decay = torch.ones_like(shift_cpt_type_mask[i]).to(device=device).float()
|
| 188 |
+
decay_value = 1
|
| 189 |
+
for label_mask_idx in torch.flip(labels_ids, [0]):
|
| 190 |
+
exponential_decay[shift_cpt_type_mask[i] == label_mask_idx] = decay_value
|
| 191 |
+
decay_value *= config.opt_loss_decay_factor
|
| 192 |
+
if config.opt_weighted_loss_type == "decay":
|
| 193 |
+
shift_labels_weights[i] *= exponential_decay
|
| 194 |
+
|
| 195 |
+
# Compute the weighted mean loss
|
| 196 |
+
loss = (loss[shift_labels_bool] * shift_labels_weights[shift_labels_bool]).mean()
|
| 197 |
+
|
| 198 |
+
base_model_output.loss = loss
|
| 199 |
+
|
| 200 |
+
return base_model_output
|
lib/python3.12/site-packages/peft/tuners/fourierft/__init__.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2024-present the HuggingFace Inc. team.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
from peft.utils import register_peft_method
|
| 16 |
+
|
| 17 |
+
from .config import FourierFTConfig
|
| 18 |
+
from .layer import FourierFTLayer, FourierFTLinear
|
| 19 |
+
from .model import FourierFTModel
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
__all__ = ["FourierFTConfig", "FourierFTLayer", "FourierFTLinear", "FourierFTModel"]
|
| 23 |
+
|
| 24 |
+
register_peft_method(name="fourierft", model_cls=FourierFTModel, config_cls=FourierFTConfig)
|
lib/python3.12/site-packages/peft/tuners/fourierft/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (551 Bytes). View file
|
|
|
lib/python3.12/site-packages/peft/tuners/fourierft/__pycache__/config.cpython-312.pyc
ADDED
|
Binary file (11.4 kB). View file
|
|
|
lib/python3.12/site-packages/peft/tuners/fourierft/__pycache__/layer.cpython-312.pyc
ADDED
|
Binary file (10.7 kB). View file
|
|
|
lib/python3.12/site-packages/peft/tuners/fourierft/__pycache__/model.cpython-312.pyc
ADDED
|
Binary file (16.8 kB). View file
|
|
|
lib/python3.12/site-packages/peft/tuners/fourierft/config.py
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Copyright 2024-present the HuggingFace Inc. team.
|
| 2 |
+
#
|
| 3 |
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
| 4 |
+
# you may not use this file except in compliance with the License.
|
| 5 |
+
# You may obtain a copy of the License at
|
| 6 |
+
#
|
| 7 |
+
# http://www.apache.org/licenses/LICENSE-2.0
|
| 8 |
+
#
|
| 9 |
+
# Unless required by applicable law or agreed to in writing, software
|
| 10 |
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
| 11 |
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
| 12 |
+
# See the License for the specific language governing permissions and
|
| 13 |
+
# limitations under the License.
|
| 14 |
+
|
| 15 |
+
from __future__ import annotations
|
| 16 |
+
|
| 17 |
+
from dataclasses import dataclass, field
|
| 18 |
+
from typing import Optional, Union
|
| 19 |
+
|
| 20 |
+
from peft.config import PeftConfig
|
| 21 |
+
from peft.utils import PeftType
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
@dataclass
|
| 25 |
+
class FourierFTConfig(PeftConfig):
|
| 26 |
+
"""
|
| 27 |
+
This is the configuration class to store the configuration of a [`FourierFTModel`].
|
| 28 |
+
|
| 29 |
+
Args:
|
| 30 |
+
n_frequency (`int`):
|
| 31 |
+
Num of learnable frequencies for the Discrete Fourier Transform. 'n_frequency' is an integer that is
|
| 32 |
+
greater than 0 and less than or equal to d^2 (assuming the weight W has dimensions of d by d).
|
| 33 |
+
Additionally, it is the number of trainable parameters required to update each delta W weight.
|
| 34 |
+
'n_frequency' will affect the performance and efficiency for PEFT. Specifically, it has little impact on
|
| 35 |
+
training speed, but higher values of it (typically) result in larger GPU memory costs and better accuracy.
|
| 36 |
+
With the same `target_modules`, the number of parameters of LoRA is (2*d*r/n_frequency) times that of
|
| 37 |
+
FourierFT. The following examples of settings regarding 'n_frequency' can be used as reference for users.
|
| 38 |
+
For NLU tasks with the RoBERTa-large model, adopting 'n_frequency': 1000 can almost achieve similar results
|
| 39 |
+
as 'r': 8 in LoRA. At this time, the number of parameters of LoRA is about 16 times that of FourierFT. For
|
| 40 |
+
image classification tasks with Vit-large models, adopting 'n_frequency': 3000 can almost achieve similar
|
| 41 |
+
results as 'r': 16 in LoRA, where the number of parameters of LoRA is about 11 times that of FourierFT.
|
| 42 |
+
scaling (`float`):
|
| 43 |
+
The scaling value for the delta W matrix. This is an important hyperparameter used for scaling, similar to
|
| 44 |
+
the 'lora_alpha' parameter in the LoRA method. 'scaling' can be determined during the hyperparameter search
|
| 45 |
+
process. However, if users want to skip this process, one can refer to the settings in the following
|
| 46 |
+
scenarios. This parameter can be set to 100.0 or 150.0 for both RoBERTa-base and RoBERTa-large models
|
| 47 |
+
across all NLU (GLUE) tasks. This parameter can be set to 300.0 for both LLaMA family models for all
|
| 48 |
+
instruction tuning. This parameter can be set to 300.0 for both ViT-base and ViT-large models across all
|
| 49 |
+
image classification tasks.
|
| 50 |
+
random_loc_seed (`int`):
|
| 51 |
+
Seed for the random location of the frequencies, i.e., the spectral entry matrix.
|
| 52 |
+
target_modules (`Union[list[str],str]`):
|
| 53 |
+
List of module names or regex expression of the module names to replace with FourierFT. For example, ['q',
|
| 54 |
+
'v'] or '.*decoder.*(SelfAttention|EncDecAttention).*(q|v)$'. Only linear layers are supported.
|
| 55 |
+
exclude_modules (`Optional[Union[List[str], str]]`):
|
| 56 |
+
The names of the modules to not apply the adapter. When passing a string, a regex match will be performed.
|
| 57 |
+
When passing a list of strings, either an exact match will be performed or it is checked if the name of the
|
| 58 |
+
module ends with any of the passed strings.
|
| 59 |
+
fan_in_fan_out (`bool`):
|
| 60 |
+
Set this to True if the layer to replace stores weight like (fan_in, fan_out).
|
| 61 |
+
bias (`str`):
|
| 62 |
+
Bias type for FourierFT. Can be 'none', 'all' or 'fourier_only'.
|
| 63 |
+
modules_to_save (`list[str]`):
|
| 64 |
+
List of modules apart from FourierFT layers to be set as trainable and saved in the final checkpoint. For
|
| 65 |
+
example, in Sequence Classification or Token Classification tasks, the final layer `classifier/score` are
|
| 66 |
+
randomly initialized and as such need to be trainable and saved.
|
| 67 |
+
layers_to_transform (`Union[list[int],int]`):
|
| 68 |
+
The layer indexes to transform, is this argument is specified, PEFT will transform only the layers indexes
|
| 69 |
+
that are specified inside this list. If a single integer is passed, PEFT will transform only the layer at
|
| 70 |
+
this index.
|
| 71 |
+
layers_pattern (`Optional[Union[List[str], str]]`):
|
| 72 |
+
The layer pattern name, used only if `layers_to_transform` is different to None and if the layer pattern is
|
| 73 |
+
not in the common layers pattern. This should target the `nn.ModuleList` of the model, which is often
|
| 74 |
+
called `'layers'` or `'h'`.
|
| 75 |
+
n_frequency_pattern (`dict`):
|
| 76 |
+
The mapping from layer names or regexp expression to n_frequency which are different from the default
|
| 77 |
+
specified. For example, `{model.decoder.layers.0.encoder_attn.k_proj: 1000`}.
|
| 78 |
+
init_weights (`bool`):
|
| 79 |
+
The initialization of the Fourier weights. Set this to False if the spectrum are initialized to a standard
|
| 80 |
+
normal distribution. Set this to True if the spectrum are initialized to zeros.
|
| 81 |
+
"""
|
| 82 |
+
|
| 83 |
+
n_frequency: int = field(
|
| 84 |
+
default=1000,
|
| 85 |
+
metadata={
|
| 86 |
+
"help": (
|
| 87 |
+
"Num of learnable frequencies for the Discrete Fourier Transform. 'n_frequency' is an integer that is"
|
| 88 |
+
"greater than 0 and less than or equal to d^2 (assuming the weight W has dimensions of d by d)."
|
| 89 |
+
"Additionally, it is the number of trainable parameters required to update each delta W weight."
|
| 90 |
+
"'n_frequency' will affect the performance and efficiency for PEFT. Specifically, it has little impact on"
|
| 91 |
+
"training speed, but higher values of it (typically) result in larger GPU memory costs and better accuracy."
|
| 92 |
+
"With the same `target_modules`, the number of parameters of LoRA is (2*d*r/n_frequency) times that of FourierFT."
|
| 93 |
+
"The following examples of settings regarding 'n_frequency' can be used as reference for users. For NLU"
|
| 94 |
+
"tasks with the RoBERTa-large model, adopting 'n_frequency': 1000 can almost achieve similar results as"
|
| 95 |
+
"'r': 8 in LoRA. At this time, the number of parameters of LoRA is about 16 times that of FourierFT."
|
| 96 |
+
"For image classification tasks with Vit-large models, adopting 'n_frequency': 3000 can almost achieve"
|
| 97 |
+
"similar results as 'r': 16 in LoRA, where the number of parameters of LoRA is about 11 times that of FourierFT."
|
| 98 |
+
)
|
| 99 |
+
},
|
| 100 |
+
)
|
| 101 |
+
scaling: float = field(
|
| 102 |
+
default=150.0,
|
| 103 |
+
metadata={
|
| 104 |
+
"help": (
|
| 105 |
+
"The scaling value for the delta W matrix. This is an important hyperparameter used for scaling, similar to the"
|
| 106 |
+
"'lora_alpha' parameter in the LoRA method. 'scaling' can be determined during the hyperparameter search process."
|
| 107 |
+
"However, if users want to skip this process, one can refer to the settings in the following scenarios."
|
| 108 |
+
"This parameter can be set to 100.0 or 150.0 for both RoBERTa-base and RoBERTa-large models across all NLU (GLUE) tasks."
|
| 109 |
+
"This parameter can be set to 300.0 for both LLaMA family models for all instruction tuning."
|
| 110 |
+
"This parameter can be set to 300.0 for both ViT-base and ViT-large models across all image classification tasks."
|
| 111 |
+
)
|
| 112 |
+
},
|
| 113 |
+
)
|
| 114 |
+
random_loc_seed: Optional[int] = field(
|
| 115 |
+
default=777, metadata={"help": "Seed for the random location of the frequencies."}
|
| 116 |
+
)
|
| 117 |
+
fan_in_fan_out: bool = field(
|
| 118 |
+
default=False,
|
| 119 |
+
metadata={"help": "Set this to True if the layer to replace stores weight like (fan_in, fan_out)"},
|
| 120 |
+
)
|
| 121 |
+
target_modules: Optional[Union[list[str], str]] = field(
|
| 122 |
+
default=None,
|
| 123 |
+
metadata={
|
| 124 |
+
"help": (
|
| 125 |
+
"List of module names or regex expression of the module names to replace with FourierFT."
|
| 126 |
+
"For example, ['q', 'v'] or '.*decoder.*(SelfAttention|EncDecAttention).*(q|v)$'. "
|
| 127 |
+
"Only linear layers are supported."
|
| 128 |
+
)
|
| 129 |
+
},
|
| 130 |
+
)
|
| 131 |
+
exclude_modules: Optional[Union[list[str], str]] = field(
|
| 132 |
+
default=None,
|
| 133 |
+
metadata={"help": "List of module names or regex expression of the module names to exclude from fourierft."},
|
| 134 |
+
)
|
| 135 |
+
bias: str = field(
|
| 136 |
+
default="none", metadata={"help": "Bias type for FourierFT. Can be 'none', 'all' or 'fourier_only'."}
|
| 137 |
+
)
|
| 138 |
+
modules_to_save: Optional[list[str]] = field(
|
| 139 |
+
default=None,
|
| 140 |
+
metadata={
|
| 141 |
+
"help": (
|
| 142 |
+
"List of modules apart from FourierFT layers to be set as trainable and saved in the final checkpoint. For"
|
| 143 |
+
" example, in Sequence Classification or Token Classification tasks, the final layer"
|
| 144 |
+
" `classifier/score` are randomly initialized and as such need to be trainable and saved."
|
| 145 |
+
)
|
| 146 |
+
},
|
| 147 |
+
)
|
| 148 |
+
layers_to_transform: Optional[Union[list[int], int]] = field(
|
| 149 |
+
default=None,
|
| 150 |
+
metadata={
|
| 151 |
+
"help": (
|
| 152 |
+
"The layer indexes to transform, is this argument is specified, PEFT will transform only the layers"
|
| 153 |
+
" indexes that are specified inside this list. If a single integer is passed, PEFT will transform only"
|
| 154 |
+
" the layer at this index."
|
| 155 |
+
)
|
| 156 |
+
},
|
| 157 |
+
)
|
| 158 |
+
layers_pattern: Optional[Union[list[str], str]] = field(
|
| 159 |
+
default=None,
|
| 160 |
+
metadata={
|
| 161 |
+
"help": (
|
| 162 |
+
"The layer pattern name, used only if `layers_to_transform` is different to None and if the layer"
|
| 163 |
+
" pattern is not in the common layers pattern. This should target the `nn.ModuleList` of the "
|
| 164 |
+
"model, which is often called `'layers'` or `'h'`."
|
| 165 |
+
)
|
| 166 |
+
},
|
| 167 |
+
)
|
| 168 |
+
n_frequency_pattern: Optional[dict] = field(
|
| 169 |
+
default_factory=dict,
|
| 170 |
+
metadata={
|
| 171 |
+
"help": (
|
| 172 |
+
"The mapping from layer names or regexp expression to n_frequency which are different from the default specified."
|
| 173 |
+
"For example, `{model.decoder.layers.0.encoder_attn.k_proj: 500`}."
|
| 174 |
+
)
|
| 175 |
+
},
|
| 176 |
+
)
|
| 177 |
+
init_weights: bool = field(
|
| 178 |
+
default=False,
|
| 179 |
+
metadata={
|
| 180 |
+
"help": (
|
| 181 |
+
"The initialization of the Fourier weights. Set this to False if the spectrum should be initialized to a standard normal distribution."
|
| 182 |
+
"Set this to True if the spectrum should be initialized to zeros."
|
| 183 |
+
)
|
| 184 |
+
},
|
| 185 |
+
)
|
| 186 |
+
|
| 187 |
+
def __post_init__(self):
|
| 188 |
+
super().__post_init__()
|
| 189 |
+
self.peft_type = PeftType.FOURIERFT
|
| 190 |
+
self.target_modules = (
|
| 191 |
+
set(self.target_modules) if isinstance(self.target_modules, list) else self.target_modules
|
| 192 |
+
)
|
| 193 |
+
self.exclude_modules = (
|
| 194 |
+
set(self.exclude_modules) if isinstance(self.exclude_modules, list) else self.exclude_modules
|
| 195 |
+
)
|
| 196 |
+
# if target_modules is a regex expression, then layers_to_transform should be None
|
| 197 |
+
if isinstance(self.target_modules, str) and self.layers_to_transform is not None:
|
| 198 |
+
raise ValueError("`layers_to_transform` cannot be used when `target_modules` is a str.")
|
| 199 |
+
|
| 200 |
+
# if target_modules is a regex expression, then layers_pattern should be None
|
| 201 |
+
if isinstance(self.target_modules, str) and self.layers_pattern is not None:
|
| 202 |
+
raise ValueError("`layers_pattern` cannot be used when `target_modules` is a str.")
|
| 203 |
+
# check for layers_to_transform and layers_pattern
|
| 204 |
+
if self.layers_pattern and not self.layers_to_transform:
|
| 205 |
+
raise ValueError("When `layers_pattern` is specified, `layers_to_transform` must also be specified. ")
|