Spaces:
Paused
Paused
| # coding=utf-8 | |
| # Copyright 2023 The HuggingFace Inc. team. All rights reserved. | |
| # | |
| # Licensed under the Apache License, Version 2.0 (the "License"); | |
| # you may not use this file except in compliance with the License. | |
| # You may obtain a copy of the License at | |
| # | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, software | |
| # distributed under the License is distributed on an "AS IS" BASIS, | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| # See the License for the specific language governing permissions and | |
| # limitations under the License. | |
| import os | |
| import tempfile | |
| import unittest | |
| from transformers import AutoModelForCausalLM, OPTForCausalLM | |
| from transformers.testing_utils import require_peft, require_torch, require_torch_gpu, slow, torch_device | |
| from transformers.utils import is_torch_available | |
| if is_torch_available(): | |
| import torch | |
| class PeftTesterMixin: | |
| peft_test_model_ids = ("peft-internal-testing/tiny-OPTForCausalLM-lora",) | |
| transformers_test_model_ids = ("hf-internal-testing/tiny-random-OPTForCausalLM",) | |
| transformers_test_model_classes = (AutoModelForCausalLM, OPTForCausalLM) | |
| # TODO: run it with CI after PEFT release. | |
| class PeftIntegrationTester(unittest.TestCase, PeftTesterMixin): | |
| """ | |
| A testing suite that makes sure that the PeftModel class is correctly integrated into the transformers library. | |
| """ | |
| def _check_lora_correctly_converted(self, model): | |
| """ | |
| Utility method to check if the model has correctly adapters injected on it. | |
| """ | |
| from peft.tuners.tuners_utils import BaseTunerLayer | |
| is_peft_loaded = False | |
| for _, m in model.named_modules(): | |
| if isinstance(m, BaseTunerLayer): | |
| is_peft_loaded = True | |
| break | |
| return is_peft_loaded | |
| def test_peft_from_pretrained(self): | |
| """ | |
| Simple test that tests the basic usage of PEFT model through `from_pretrained`. | |
| This checks if we pass a remote folder that contains an adapter config and adapter weights, it | |
| should correctly load a model that has adapters injected on it. | |
| """ | |
| for model_id in self.peft_test_model_ids: | |
| for transformers_class in self.transformers_test_model_classes: | |
| peft_model = transformers_class.from_pretrained(model_id).to(torch_device) | |
| self.assertTrue(self._check_lora_correctly_converted(peft_model)) | |
| self.assertTrue(peft_model._hf_peft_config_loaded) | |
| # dummy generation | |
| _ = peft_model.generate(input_ids=torch.LongTensor([[0, 1, 2, 3, 4, 5, 6, 7]]).to(torch_device)) | |
| def test_peft_state_dict(self): | |
| """ | |
| Simple test that checks if the returned state dict of `get_adapter_state_dict()` method contains | |
| the expected keys. | |
| """ | |
| for model_id in self.peft_test_model_ids: | |
| for transformers_class in self.transformers_test_model_classes: | |
| peft_model = transformers_class.from_pretrained(model_id).to(torch_device) | |
| state_dict = peft_model.get_adapter_state_dict() | |
| for key in state_dict.keys(): | |
| self.assertTrue("lora" in key) | |
| def test_peft_save_pretrained(self): | |
| """ | |
| Test that checks various combinations of `save_pretrained` with a model that has adapters loaded | |
| on it. This checks if the saved model contains the expected files (adapter weights and adapter config). | |
| """ | |
| for model_id in self.peft_test_model_ids: | |
| for transformers_class in self.transformers_test_model_classes: | |
| peft_model = transformers_class.from_pretrained(model_id).to(torch_device) | |
| with tempfile.TemporaryDirectory() as tmpdirname: | |
| peft_model.save_pretrained(tmpdirname) | |
| self.assertTrue("adapter_model.bin" in os.listdir(tmpdirname)) | |
| self.assertTrue("adapter_config.json" in os.listdir(tmpdirname)) | |
| self.assertTrue("config.json" not in os.listdir(tmpdirname)) | |
| self.assertTrue("pytorch_model.bin" not in os.listdir(tmpdirname)) | |
| peft_model = transformers_class.from_pretrained(tmpdirname).to(torch_device) | |
| self.assertTrue(self._check_lora_correctly_converted(peft_model)) | |
| peft_model.save_pretrained(tmpdirname, safe_serialization=True) | |
| self.assertTrue("adapter_model.safetensors" in os.listdir(tmpdirname)) | |
| self.assertTrue("adapter_config.json" in os.listdir(tmpdirname)) | |
| peft_model = transformers_class.from_pretrained(tmpdirname).to(torch_device) | |
| self.assertTrue(self._check_lora_correctly_converted(peft_model)) | |
| def test_peft_enable_disable_adapters(self): | |
| """ | |
| A test that checks if `enable_adapters` and `disable_adapters` methods work as expected. | |
| """ | |
| from peft import LoraConfig | |
| dummy_input = torch.LongTensor([[0, 1, 2, 3, 4, 5, 6, 7]]).to(torch_device) | |
| for model_id in self.transformers_test_model_ids: | |
| for transformers_class in self.transformers_test_model_classes: | |
| peft_model = transformers_class.from_pretrained(model_id).to(torch_device) | |
| peft_config = LoraConfig(init_lora_weights=False) | |
| peft_model.add_adapter(peft_config) | |
| peft_logits = peft_model(dummy_input).logits | |
| peft_model.disable_adapters() | |
| peft_logits_disabled = peft_model(dummy_input).logits | |
| peft_model.enable_adapters() | |
| peft_logits_enabled = peft_model(dummy_input).logits | |
| self.assertTrue(torch.allclose(peft_logits, peft_logits_enabled, atol=1e-12, rtol=1e-12)) | |
| self.assertFalse(torch.allclose(peft_logits_enabled, peft_logits_disabled, atol=1e-12, rtol=1e-12)) | |
| def test_peft_add_adapter(self): | |
| """ | |
| Simple test that tests if `add_adapter` works as expected | |
| """ | |
| from peft import LoraConfig | |
| for model_id in self.transformers_test_model_ids: | |
| for transformers_class in self.transformers_test_model_classes: | |
| model = transformers_class.from_pretrained(model_id).to(torch_device) | |
| peft_config = LoraConfig(init_lora_weights=False) | |
| model.add_adapter(peft_config) | |
| self.assertTrue(self._check_lora_correctly_converted(model)) | |
| # dummy generation | |
| _ = model.generate(input_ids=torch.LongTensor([[0, 1, 2, 3, 4, 5, 6, 7]]).to(torch_device)) | |
| def test_peft_add_adapter_from_pretrained(self): | |
| """ | |
| Simple test that tests if `add_adapter` works as expected | |
| """ | |
| from peft import LoraConfig | |
| for model_id in self.transformers_test_model_ids: | |
| for transformers_class in self.transformers_test_model_classes: | |
| model = transformers_class.from_pretrained(model_id).to(torch_device) | |
| peft_config = LoraConfig(init_lora_weights=False) | |
| model.add_adapter(peft_config) | |
| self.assertTrue(self._check_lora_correctly_converted(model)) | |
| with tempfile.TemporaryDirectory() as tmpdirname: | |
| model.save_pretrained(tmpdirname) | |
| model_from_pretrained = transformers_class.from_pretrained(tmpdirname).to(torch_device) | |
| self.assertTrue(self._check_lora_correctly_converted(model_from_pretrained)) | |
| def test_peft_add_multi_adapter(self): | |
| """ | |
| Simple test that tests the basic usage of PEFT model through `from_pretrained`. This test tests if | |
| add_adapter works as expected in multi-adapter setting. | |
| """ | |
| from peft import LoraConfig | |
| from peft.tuners.tuners_utils import BaseTunerLayer | |
| dummy_input = torch.LongTensor([[0, 1, 2, 3, 4, 5, 6, 7]]).to(torch_device) | |
| for model_id in self.transformers_test_model_ids: | |
| for transformers_class in self.transformers_test_model_classes: | |
| is_peft_loaded = False | |
| model = transformers_class.from_pretrained(model_id).to(torch_device) | |
| logits_original_model = model(dummy_input).logits | |
| peft_config = LoraConfig(init_lora_weights=False) | |
| model.add_adapter(peft_config) | |
| logits_adapter_1 = model(dummy_input) | |
| model.add_adapter(peft_config, adapter_name="adapter-2") | |
| logits_adapter_2 = model(dummy_input) | |
| for _, m in model.named_modules(): | |
| if isinstance(m, BaseTunerLayer): | |
| is_peft_loaded = True | |
| break | |
| self.assertTrue(is_peft_loaded) | |
| # dummy generation | |
| _ = model.generate(input_ids=dummy_input) | |
| model.set_adapter("default") | |
| self.assertTrue(model.active_adapter() == "default") | |
| model.set_adapter("adapter-2") | |
| self.assertTrue(model.active_adapter() == "adapter-2") | |
| # Logits comparison | |
| self.assertFalse( | |
| torch.allclose(logits_adapter_1.logits, logits_adapter_2.logits, atol=1e-6, rtol=1e-6) | |
| ) | |
| self.assertFalse(torch.allclose(logits_original_model, logits_adapter_2.logits, atol=1e-6, rtol=1e-6)) | |
| def test_peft_from_pretrained_kwargs(self): | |
| """ | |
| Simple test that tests the basic usage of PEFT model through `from_pretrained` + additional kwargs | |
| and see if the integraiton behaves as expected. | |
| """ | |
| for model_id in self.peft_test_model_ids: | |
| for transformers_class in self.transformers_test_model_classes: | |
| peft_model = transformers_class.from_pretrained(model_id, load_in_8bit=True, device_map="auto") | |
| module = peft_model.model.decoder.layers[0].self_attn.v_proj | |
| self.assertTrue(module.__class__.__name__ == "Linear8bitLt") | |
| self.assertTrue(peft_model.hf_device_map is not None) | |
| # dummy generation | |
| _ = peft_model.generate(input_ids=torch.LongTensor([[0, 1, 2, 3, 4, 5, 6, 7]]).to(torch_device)) | |
| def test_peft_pipeline(self): | |
| """ | |
| Simple test that tests the basic usage of PEFT model + pipeline | |
| """ | |
| from transformers import pipeline | |
| for model_id in self.peft_test_model_ids: | |
| pipe = pipeline("text-generation", model_id) | |
| _ = pipe("Hello") | |