ZhengyangZhang commited on
Commit
53d1860
·
verified ·
1 Parent(s): 70cfec3

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. lib/python3.12/site-packages/peft/tuners/__pycache__/__init__.cpython-312.pyc +0 -0
  2. lib/python3.12/site-packages/peft/tuners/__pycache__/_buffer_dict.cpython-312.pyc +0 -0
  3. lib/python3.12/site-packages/peft/tuners/__pycache__/lycoris_utils.cpython-312.pyc +0 -0
  4. lib/python3.12/site-packages/peft/tuners/__pycache__/tuners_utils.cpython-312.pyc +0 -0
  5. lib/python3.12/site-packages/peft/tuners/adalora/__init__.py +43 -0
  6. lib/python3.12/site-packages/peft/tuners/adalora/__pycache__/__init__.cpython-312.pyc +0 -0
  7. lib/python3.12/site-packages/peft/tuners/adalora/__pycache__/bnb.cpython-312.pyc +0 -0
  8. lib/python3.12/site-packages/peft/tuners/adalora/__pycache__/config.cpython-312.pyc +0 -0
  9. lib/python3.12/site-packages/peft/tuners/adalora/__pycache__/gptq.cpython-312.pyc +0 -0
  10. lib/python3.12/site-packages/peft/tuners/adalora/__pycache__/layer.cpython-312.pyc +0 -0
  11. lib/python3.12/site-packages/peft/tuners/adalora/__pycache__/model.cpython-312.pyc +0 -0
  12. lib/python3.12/site-packages/peft/tuners/adalora/bnb.py +143 -0
  13. lib/python3.12/site-packages/peft/tuners/adalora/config.py +108 -0
  14. lib/python3.12/site-packages/peft/tuners/adalora/gptq.py +71 -0
  15. lib/python3.12/site-packages/peft/tuners/adalora/layer.py +358 -0
  16. lib/python3.12/site-packages/peft/tuners/adalora/model.py +364 -0
  17. lib/python3.12/site-packages/peft/tuners/adaption_prompt/__init__.py +23 -0
  18. lib/python3.12/site-packages/peft/tuners/adaption_prompt/__pycache__/__init__.cpython-312.pyc +0 -0
  19. lib/python3.12/site-packages/peft/tuners/adaption_prompt/__pycache__/config.cpython-312.pyc +0 -0
  20. lib/python3.12/site-packages/peft/tuners/adaption_prompt/__pycache__/layer.cpython-312.pyc +0 -0
  21. lib/python3.12/site-packages/peft/tuners/adaption_prompt/__pycache__/model.cpython-312.pyc +0 -0
  22. lib/python3.12/site-packages/peft/tuners/adaption_prompt/__pycache__/utils.cpython-312.pyc +0 -0
  23. lib/python3.12/site-packages/peft/tuners/adaption_prompt/config.py +81 -0
  24. lib/python3.12/site-packages/peft/tuners/adaption_prompt/layer.py +138 -0
  25. lib/python3.12/site-packages/peft/tuners/adaption_prompt/model.py +162 -0
  26. lib/python3.12/site-packages/peft/tuners/adaption_prompt/utils.py +132 -0
  27. lib/python3.12/site-packages/peft/tuners/boft/__init__.py +24 -0
  28. lib/python3.12/site-packages/peft/tuners/boft/__pycache__/__init__.cpython-312.pyc +0 -0
  29. lib/python3.12/site-packages/peft/tuners/boft/__pycache__/config.cpython-312.pyc +0 -0
  30. lib/python3.12/site-packages/peft/tuners/boft/__pycache__/layer.cpython-312.pyc +0 -0
  31. lib/python3.12/site-packages/peft/tuners/boft/__pycache__/model.cpython-312.pyc +0 -0
  32. lib/python3.12/site-packages/peft/tuners/boft/config.py +160 -0
  33. lib/python3.12/site-packages/peft/tuners/boft/fbd/__init__.py +0 -0
  34. lib/python3.12/site-packages/peft/tuners/boft/fbd/__pycache__/__init__.cpython-312.pyc +0 -0
  35. lib/python3.12/site-packages/peft/tuners/boft/fbd/fbd_cuda.cpp +28 -0
  36. lib/python3.12/site-packages/peft/tuners/boft/fbd/fbd_cuda_kernel.cu +109 -0
  37. lib/python3.12/site-packages/peft/tuners/boft/layer.py +984 -0
  38. lib/python3.12/site-packages/peft/tuners/boft/model.py +353 -0
  39. lib/python3.12/site-packages/peft/tuners/cpt/__init__.py +24 -0
  40. lib/python3.12/site-packages/peft/tuners/cpt/__pycache__/__init__.cpython-312.pyc +0 -0
  41. lib/python3.12/site-packages/peft/tuners/cpt/__pycache__/config.cpython-312.pyc +0 -0
  42. lib/python3.12/site-packages/peft/tuners/cpt/__pycache__/model.cpython-312.pyc +0 -0
  43. lib/python3.12/site-packages/peft/tuners/cpt/config.py +98 -0
  44. lib/python3.12/site-packages/peft/tuners/cpt/model.py +200 -0
  45. lib/python3.12/site-packages/peft/tuners/fourierft/__init__.py +24 -0
  46. lib/python3.12/site-packages/peft/tuners/fourierft/__pycache__/__init__.cpython-312.pyc +0 -0
  47. lib/python3.12/site-packages/peft/tuners/fourierft/__pycache__/config.cpython-312.pyc +0 -0
  48. lib/python3.12/site-packages/peft/tuners/fourierft/__pycache__/layer.cpython-312.pyc +0 -0
  49. lib/python3.12/site-packages/peft/tuners/fourierft/__pycache__/model.cpython-312.pyc +0 -0
  50. 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. ")