18
from copy import deepcopy
21
from diffusers import StableDiffusionPipeline
22
from parameterized import parameterized
24
from transformers import AutoModel, AutoModelForCausalLM, AutoModelForSeq2SeqLM
26
from peft import IA3Config, LoHaConfig, LoraConfig, get_peft_model
27
from peft.tuners.tuners_utils import (
28
_maybe_include_all_linear_layers,
29
check_target_module_exists,
30
inspect_matched_modules,
32
from peft.utils import INCLUDE_LINEAR_LAYERS_SHORTHAND
34
from .testing_utils import require_bitsandbytes, require_torch_gpu
49
("", [], None, None, False),
50
("", ["foo"], None, None, False),
51
("foo", [], None, None, False),
52
("foo", ["foo"], None, None, True),
53
("foo", ["bar"], None, None, False),
54
("foo", ["foo", "bar"], None, None, True),
56
("foo", "foo", None, None, True),
57
("foo", ".*oo", None, None, True),
58
("foo", "fo.*", None, None, True),
59
("foo", ".*bar.*", None, None, False),
60
("foobar", ".*oba.*", None, None, True),
62
("foo.bar.1.baz", ["baz"], [1], ["bar"], True),
63
("foo.bar.1.baz", ["baz"], [0], ["bar"], False),
64
("foo.bar.1.baz", ["baz"], [2], ["bar"], False),
65
("foo.bar.10.baz", ["baz"], [0], ["bar"], False),
66
("foo.bar.10.baz", ["baz"], [1], ["bar"], False),
67
("foo.bar.1.baz", ["baz"], [0, 1, 2], ["bar"], True),
68
("foo.bar.1.baz", ["baz", "spam"], [1], ["bar"], True),
69
("foo.bar.1.baz", ["baz", "spam"], [0, 1, 2], ["bar"], True),
71
("foo.bar.7.baz", ["baz"], [], ["bar"], True),
72
("foo.bar.7.baz", ["baz"], None, ["bar"], True),
74
("foo.whatever.1.baz", ["baz"], [1], [], True),
75
("foo.whatever.1.baz", ["baz"], [0], [], False),
76
("foo.whatever.1.baz", ["baz"], [1], "", True),
77
("foo.whatever.1.baz", ["baz"], [0], "", False),
78
("foo.whatever.1.baz", ["baz"], [1], None, True),
79
("foo.whatever.1.baz", ["baz"], [0], None, False),
81
("transformer.h.1.attn.attention.q_proj.foo", ["q_proj"], None, [], False),
82
("transformer.h.1.attn.attention.q_proj", [], None, [], False),
83
("transformer.h.1.attn.attention.q_proj", ["q_proj"], None, [], True),
84
("transformer.h.1.attn.attention.q_proj", ["q_proj", "v_proj"], None, [], True),
85
("transformer.h.1.attn.attention.resid_dropout", ["q_proj", "v_proj"], None, [], False),
86
("transformer.h.1.attn.attention.q_proj", ["q_proj"], [1], ["h"], True),
87
("transformer.h.1.attn.attention.q_proj", ["q_proj"], [0], ["h"], False),
88
("transformer.h.1.attn.attention.q_proj", ["q_proj"], [2], ["h"], False),
89
("transformer.h.1.attn.attention.q_proj", ["q_proj"], [0, 1, 2], ["h"], True),
90
("transformer.h.1.attn.attention.q_proj", ["q_proj", "v_proj"], [0, 1, 2], ["h"], True),
91
("foo.bar.q_proj", ["q_proj"], None, [], True),
92
("foo.bar.1.baz", ["baz"], [1], ["foo"], False),
95
("foo.bar.1.baz", ["baz"], [1], ["baz"], False),
97
("bar.1.baz", ["baz"], [1], ["bar"], False),
98
("foo.bar.001.baz", ["baz"], [1], ["bar"], True),
99
("foo.bar.1.spam.2.baz", ["baz"], [1], ["bar"], True),
100
("foo.bar.2.spam.1.baz", ["baz"], [1], ["bar"], False),
103
("blocks.1.weight", ["weight"], [1], ["blocks"], False),
104
("blocks.1.bias", ["weight"], [1], ["blocks"], False),
105
("mlp.blocks.1.weight", ["weight"], [1], ["blocks"], True),
106
("mlp.blocks.1.bias", ["weight"], [1], ["blocks"], False),
109
MAYBE_INCLUDE_ALL_LINEAR_LAYERS_TEST_CASES = [
113
"HuggingFaceH4/tiny-random-LlamaForCausalLM",
115
INCLUDE_LINEAR_LAYERS_SHORTHAND,
116
["k_proj", "v_proj", "q_proj", "o_proj", "down_proj", "up_proj", "gate_proj"],
120
"HuggingFaceH4/tiny-random-LlamaForCausalLM",
122
INCLUDE_LINEAR_LAYERS_SHORTHAND,
123
["k_proj", "v_proj", "q_proj", "o_proj", "down_proj", "up_proj", "gate_proj"],
126
("hf-internal-testing/tiny-random-gpt2", "causal", INCLUDE_LINEAR_LAYERS_SHORTHAND, ["c_attn", "c_proj", "c_fc"]),
129
"hf-internal-testing/tiny-random-t5",
131
INCLUDE_LINEAR_LAYERS_SHORTHAND,
132
["k", "q", "v", "o", "wi", "wo"],
136
"hf-internal-testing/tiny-random-GPTNeoXForCausalLM",
138
INCLUDE_LINEAR_LAYERS_SHORTHAND,
139
["query_key_value", "dense", "dense_h_to_4h", "dense_4h_to_h"],
144
MAYBE_INCLUDE_ALL_LINEAR_LAYERS_TEST_INTERNALS = [
146
(["k_proj"], ["k_proj"]),
150
(".*(q_proj|v_proj)$", ".*(q_proj|v_proj)$"),
153
BNB_QUANTIZATIONS = [("4bit",), ("8bit",)]
154
BNB_TEST_CASES = [(x + y) for x in MAYBE_INCLUDE_ALL_LINEAR_LAYERS_TEST_CASES for y in BNB_QUANTIZATIONS]
157
class PeftCustomKwargsTester(unittest.TestCase):
159
Test if the PeftModel is instantiated with correct behaviour for custom kwargs. This includes:
160
- test if regex matching works correctly
161
- test if adapters handle custom kwargs the right way e.g. IA3 for `feedforward_modules`
165
transformers_class_map = {"causal": AutoModelForCausalLM, "seq2seq": AutoModelForSeq2SeqLM, "base": AutoModel}
167
@parameterized.expand(REGEX_TEST_CASES)
168
def test_regex_matching_valid(self, key, target_modules, layers_to_transform, layers_pattern, expected_result):
171
model_id = "peft-internal-testing/tiny-OPTForCausalLM-lora"
173
base_model_name_or_path=model_id,
174
target_modules=target_modules,
175
layers_pattern=layers_pattern,
176
layers_to_transform=layers_to_transform,
178
actual_result = bool(check_target_module_exists(config, key))
179
assert actual_result == expected_result
181
def test_module_matching_lora(self):
186
model_id = "hf-internal-testing/tiny-random-BloomForCausalLM"
187
model = AutoModel.from_pretrained(model_id)
189
config = LoraConfig()
190
peft_model = get_peft_model(model, config)
192
output = inspect_matched_modules(peft_model)
193
matched = output["matched"]
195
"h.0.self_attention.query_key_value",
196
"h.1.self_attention.query_key_value",
197
"h.2.self_attention.query_key_value",
198
"h.3.self_attention.query_key_value",
199
"h.4.self_attention.query_key_value",
201
assert matched == expected
204
unmatched = output["unmatched"]
206
assert key not in unmatched
208
def test_feedforward_matching_ia3(self):
209
model_id = "hf-internal-testing/tiny-random-T5ForConditionalGeneration"
210
model = AutoModelForSeq2SeqLM.from_pretrained(model_id)
213
"target_modules": ".*encoder.*block.0.*(SelfAttention|EncDecAttention|DenseReluDense).(k|q|v|wo|wi)$",
214
"feedforward_modules": ["wo", "wi"],
216
config = IA3Config(base_model_name_or_path=model_id, **config_kwargs)
217
peft_model = get_peft_model(model, config)
218
output = inspect_matched_modules(peft_model)
219
matched = output["matched"]
221
"encoder.block.0.layer.0.SelfAttention.q",
222
"encoder.block.0.layer.0.SelfAttention.k",
223
"encoder.block.0.layer.0.SelfAttention.v",
224
"encoder.block.0.layer.1.DenseReluDense.wi",
225
"encoder.block.0.layer.1.DenseReluDense.wo",
227
expected_feedforward = [
228
"encoder.block.0.layer.1.DenseReluDense.wi",
229
"encoder.block.0.layer.1.DenseReluDense.wo",
231
assert matched == expected
232
module_dict = dict(model.named_modules())
234
module = module_dict[key]
235
if key in expected_feedforward:
236
assert module.is_feedforward
238
assert not module.is_feedforward
240
@parameterized.expand(MAYBE_INCLUDE_ALL_LINEAR_LAYERS_TEST_CASES)
241
def test_maybe_include_all_linear_layers_lora(
242
self, model_id, model_type, initial_target_modules, expected_target_modules
244
model = self.transformers_class_map[model_type].from_pretrained(model_id)
245
config_cls = LoraConfig
246
self._check_match_with_expected_target_modules(
247
model_id, model, config_cls, initial_target_modules, expected_target_modules
250
@parameterized.expand(BNB_TEST_CASES)
252
@require_bitsandbytes
253
def test_maybe_include_all_linear_layers_lora_bnb(
254
self, model_id, model_type, initial_target_modules, expected_target_modules, quantization
256
if quantization == "4bit":
257
config_kwargs = {"load_in_4bit": True}
258
elif quantization == "8bit":
259
config_kwargs = {"load_in_8bit": True}
260
model = self.transformers_class_map[model_type].from_pretrained(model_id, device_map="auto", **config_kwargs)
261
config_cls = LoraConfig
262
self._check_match_with_expected_target_modules(
263
model_id, model, config_cls, initial_target_modules, expected_target_modules
266
def _check_match_with_expected_target_modules(
267
self, model_id, model, config_cls, initial_target_modules, expected_target_modules
270
Helper function for the test for `_maybe_include_all_linear_layers`
272
actual_config = config_cls(base_model_name_or_path=model_id, target_modules=initial_target_modules)
273
expected_config = config_cls(base_model_name_or_path=model_id, target_modules=expected_target_modules)
274
model_copy = deepcopy(model)
275
actual_model = get_peft_model(model, peft_config=actual_config)
276
expected_model = get_peft_model(model_copy, peft_config=expected_config)
277
expected_model_module_dict = dict(expected_model.named_modules())
279
for name, actual_module in actual_model.named_modules():
280
expected_module = expected_model_module_dict[name]
281
assert type(actual_module) == type(expected_module)
283
def test_maybe_include_all_linear_layers_ia3_loha(self):
284
model_id, initial_target_modules, expected_target_modules = (
285
"HuggingFaceH4/tiny-random-LlamaForCausalLM",
286
INCLUDE_LINEAR_LAYERS_SHORTHAND,
287
["k_proj", "v_proj", "q_proj", "o_proj", "down_proj", "up_proj", "gate_proj"],
289
model_ia3 = AutoModelForCausalLM.from_pretrained(model_id)
290
model_loha = deepcopy(model_ia3)
291
config_classes = [IA3Config, LoHaConfig]
292
models = [model_ia3, model_loha]
293
for config_cls, model in zip(config_classes, models):
294
self._check_match_with_expected_target_modules(
295
model_id, model, config_cls, initial_target_modules, expected_target_modules
298
@parameterized.expand(MAYBE_INCLUDE_ALL_LINEAR_LAYERS_TEST_INTERNALS)
299
def test_maybe_include_all_linear_layers_internals(self, initial_target_modules, expected_target_modules):
300
model_id = "HuggingFaceH4/tiny-random-LlamaForCausalLM"
301
model = AutoModelForCausalLM.from_pretrained(model_id)
302
config = LoraConfig(base_model_name_or_path=model_id, target_modules=initial_target_modules)
303
new_config = _maybe_include_all_linear_layers(config, model)
304
if isinstance(expected_target_modules, list):
306
assert set(new_config.target_modules) == set(expected_target_modules)
308
assert new_config.target_modules == expected_target_modules
310
def test_maybe_include_all_linear_layers_diffusion(self):
311
model_id = "hf-internal-testing/tiny-stable-diffusion-torch"
312
model = StableDiffusionPipeline.from_pretrained(model_id)
313
config = LoraConfig(base_model_name_or_path=model_id, target_modules="all-linear")
316
match="Only instances of PreTrainedModel support `target_modules='all-linear'`",
318
model.unet = get_peft_model(model.unet, config)
322
def __init__(self, bias=True):
324
self.lin0 = nn.Linear(10, 20, bias=bias)
325
self.relu = nn.ReLU()
326
self.drop = nn.Dropout(0.5)
327
self.lin1 = nn.Linear(20, 2, bias=bias)
328
self.sm = nn.LogSoftmax(dim=-1)
331
class TestTargetedModuleNames(unittest.TestCase):
332
"""Check that the attribute targeted_module_names is correctly set.
334
This checks LoRA and IA³, but this should be sufficient, testing all other tuners is not necessary.
337
def test_one_targeted_module_regex(self):
339
model = get_peft_model(model, LoraConfig(target_modules="lin0"))
340
assert model.targeted_module_names == ["lin0"]
342
def test_two_targeted_module_regex(self):
344
model = get_peft_model(model, LoraConfig(target_modules="lin.*"))
345
assert model.targeted_module_names == ["lin0", "lin1"]
347
def test_one_targeted_module_list(self):
349
model = get_peft_model(model, LoraConfig(target_modules=["lin0"]))
350
assert model.targeted_module_names == ["lin0"]
352
def test_two_targeted_module_list(self):
354
model = get_peft_model(model, LoraConfig(target_modules=["lin0", "lin1"]))
355
assert model.targeted_module_names == ["lin0", "lin1"]
357
def test_ia3_targeted_module_regex(self):
359
model = get_peft_model(model, IA3Config(target_modules=".*lin.*", feedforward_modules=".*lin.*"))
360
assert model.targeted_module_names == ["lin0", "lin1"]
362
def test_ia3_targeted_module_list(self):
364
model = get_peft_model(model, IA3Config(target_modules=["lin0", "lin1"], feedforward_modules=["lin0", "lin1"]))
365
assert model.targeted_module_names == ["lin0", "lin1"]
367
def test_realistic_example(self):
368
model = AutoModelForCausalLM.from_pretrained("hf-internal-testing/tiny-random-BloomForCausalLM")
369
config = LoraConfig(task_type="CAUSAL_LM")
370
model = get_peft_model(model, config)
372
f"transformer.h.{i}.self_attention.query_key_value" for i in range(len(model.base_model.transformer.h))
374
assert model.targeted_module_names == expected