|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import platform |
|
|
import tempfile |
|
|
from unittest.mock import Mock, call, patch |
|
|
|
|
|
import pytest |
|
|
import torch |
|
|
from safetensors.torch import load_file as safe_load_file |
|
|
from transformers import ( |
|
|
AutoModelForCausalLM, |
|
|
AutoTokenizer, |
|
|
DataCollatorForLanguageModeling, |
|
|
Trainer, |
|
|
TrainingArguments, |
|
|
) |
|
|
|
|
|
from peft import ( |
|
|
AdaLoraConfig, |
|
|
BOFTConfig, |
|
|
BoneConfig, |
|
|
C3AConfig, |
|
|
CPTConfig, |
|
|
FourierFTConfig, |
|
|
HRAConfig, |
|
|
IA3Config, |
|
|
LoraConfig, |
|
|
MissConfig, |
|
|
OFTConfig, |
|
|
PrefixTuningConfig, |
|
|
PromptEncoderConfig, |
|
|
PromptTuningConfig, |
|
|
PromptTuningInit, |
|
|
RoadConfig, |
|
|
ShiraConfig, |
|
|
VBLoRAConfig, |
|
|
VeraConfig, |
|
|
WaveFTConfig, |
|
|
get_peft_model, |
|
|
) |
|
|
|
|
|
from .testing_common import PeftCommonTester |
|
|
from .testing_utils import device_count, hub_online_once, load_dataset_english_quotes, set_init_weights_false |
|
|
|
|
|
|
|
|
PEFT_DECODER_MODELS_TO_TEST = [ |
|
|
"hf-internal-testing/tiny-random-OPTForCausalLM", |
|
|
"hf-internal-testing/tiny-random-GPT2LMHeadModel", |
|
|
"hf-internal-testing/tiny-random-BloomForCausalLM", |
|
|
"hf-internal-testing/tiny-random-gpt_neo", |
|
|
"hf-internal-testing/tiny-random-GPTJForCausalLM", |
|
|
"hf-internal-testing/tiny-random-GPTBigCodeForCausalLM", |
|
|
"trl-internal-testing/tiny-random-LlamaForCausalLM", |
|
|
"peft-internal-testing/tiny-dummy-qwen2", |
|
|
"hf-internal-testing/tiny-random-Gemma3ForCausalLM", |
|
|
] |
|
|
|
|
|
SMALL_GRID_MODELS = [ |
|
|
"hf-internal-testing/tiny-random-gpt2", |
|
|
"hf-internal-testing/tiny-random-OPTForCausalLM", |
|
|
"hf-internal-testing/tiny-random-MistralForCausalLM", |
|
|
"peft-internal-testing/tiny-dummy-qwen2", |
|
|
"trl-internal-testing/tiny-random-LlamaForCausalLM", |
|
|
] |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ALL_CONFIGS = [ |
|
|
( |
|
|
AdaLoraConfig, |
|
|
{ |
|
|
"task_type": "CAUSAL_LM", |
|
|
"target_modules": None, |
|
|
"total_step": 1, |
|
|
}, |
|
|
), |
|
|
( |
|
|
BOFTConfig, |
|
|
{ |
|
|
"task_type": "CAUSAL_LM", |
|
|
"target_modules": None, |
|
|
}, |
|
|
), |
|
|
( |
|
|
BoneConfig, |
|
|
{ |
|
|
"task_type": "CAUSAL_LM", |
|
|
"target_modules": None, |
|
|
"r": 2, |
|
|
}, |
|
|
), |
|
|
( |
|
|
MissConfig, |
|
|
{ |
|
|
"task_type": "CAUSAL_LM", |
|
|
"target_modules": None, |
|
|
"r": 2, |
|
|
}, |
|
|
), |
|
|
( |
|
|
CPTConfig, |
|
|
{ |
|
|
"task_type": "CAUSAL_LM", |
|
|
"cpt_token_ids": [0, 1, 2, 3, 4, 5, 6, 7], |
|
|
"cpt_mask": [1, 1, 1, 1, 1, 1, 1, 1], |
|
|
"cpt_tokens_type_mask": [1, 2, 2, 2, 3, 3, 4, 4], |
|
|
}, |
|
|
), |
|
|
( |
|
|
FourierFTConfig, |
|
|
{ |
|
|
"task_type": "CAUSAL_LM", |
|
|
"n_frequency": 10, |
|
|
"target_modules": None, |
|
|
}, |
|
|
), |
|
|
( |
|
|
HRAConfig, |
|
|
{ |
|
|
"task_type": "CAUSAL_LM", |
|
|
"target_modules": None, |
|
|
}, |
|
|
), |
|
|
( |
|
|
IA3Config, |
|
|
{ |
|
|
"task_type": "CAUSAL_LM", |
|
|
"target_modules": None, |
|
|
"feedforward_modules": None, |
|
|
}, |
|
|
), |
|
|
( |
|
|
LoraConfig, |
|
|
{ |
|
|
"task_type": "CAUSAL_LM", |
|
|
"r": 8, |
|
|
"lora_alpha": 32, |
|
|
"target_modules": None, |
|
|
"lora_dropout": 0.05, |
|
|
"bias": "none", |
|
|
}, |
|
|
), |
|
|
|
|
|
( |
|
|
LoraConfig, |
|
|
{ |
|
|
"task_type": "CAUSAL_LM", |
|
|
"r": 8, |
|
|
"lora_alpha": 32, |
|
|
"target_modules": None, |
|
|
"lora_dropout": 0.05, |
|
|
"bias": "none", |
|
|
"alora_invocation_tokens": [1], |
|
|
}, |
|
|
), |
|
|
( |
|
|
LoraConfig, |
|
|
{ |
|
|
"task_type": "CAUSAL_LM", |
|
|
"r": 8, |
|
|
"lora_alpha": 32, |
|
|
"target_modules": None, |
|
|
"lora_dropout": 0.05, |
|
|
"bias": "none", |
|
|
|
|
|
"alora_invocation_tokens": [1000], |
|
|
}, |
|
|
), |
|
|
|
|
|
( |
|
|
LoraConfig, |
|
|
{ |
|
|
"task_type": "CAUSAL_LM", |
|
|
"r": 8, |
|
|
"lora_alpha": 32, |
|
|
"target_modules": None, |
|
|
"lora_dropout": 0.05, |
|
|
"bias": "none", |
|
|
"trainable_token_indices": [0, 1, 3], |
|
|
}, |
|
|
), |
|
|
( |
|
|
OFTConfig, |
|
|
{ |
|
|
"task_type": "CAUSAL_LM", |
|
|
"target_modules": None, |
|
|
}, |
|
|
), |
|
|
( |
|
|
PrefixTuningConfig, |
|
|
{ |
|
|
"task_type": "CAUSAL_LM", |
|
|
"num_virtual_tokens": 10, |
|
|
}, |
|
|
), |
|
|
( |
|
|
PromptEncoderConfig, |
|
|
{ |
|
|
"task_type": "CAUSAL_LM", |
|
|
"num_virtual_tokens": 10, |
|
|
"encoder_hidden_size": 32, |
|
|
}, |
|
|
), |
|
|
( |
|
|
PromptTuningConfig, |
|
|
{ |
|
|
"task_type": "CAUSAL_LM", |
|
|
"num_virtual_tokens": 10, |
|
|
}, |
|
|
), |
|
|
( |
|
|
RoadConfig, |
|
|
{ |
|
|
"task_type": "CAUSAL_LM", |
|
|
"variant": "road_1", |
|
|
"group_size": 2, |
|
|
}, |
|
|
), |
|
|
( |
|
|
ShiraConfig, |
|
|
{ |
|
|
"r": 1, |
|
|
"task_type": "CAUSAL_LM", |
|
|
"target_modules": None, |
|
|
"init_weights": False, |
|
|
}, |
|
|
), |
|
|
( |
|
|
VBLoRAConfig, |
|
|
{ |
|
|
"task_type": "CAUSAL_LM", |
|
|
"target_modules": None, |
|
|
"vblora_dropout": 0.05, |
|
|
"vector_length": 1, |
|
|
"num_vectors": 2, |
|
|
}, |
|
|
), |
|
|
( |
|
|
VeraConfig, |
|
|
{ |
|
|
"task_type": "CAUSAL_LM", |
|
|
"r": 8, |
|
|
"target_modules": None, |
|
|
"vera_dropout": 0.05, |
|
|
"projection_prng_key": 0xFF, |
|
|
"d_initial": 0.1, |
|
|
"save_projection": True, |
|
|
"bias": "none", |
|
|
}, |
|
|
), |
|
|
( |
|
|
C3AConfig, |
|
|
{ |
|
|
"task_type": "CAUSAL_LM", |
|
|
"block_size": 1, |
|
|
"target_modules": None, |
|
|
}, |
|
|
), |
|
|
( |
|
|
WaveFTConfig, |
|
|
{ |
|
|
"task_type": "CAUSAL_LM", |
|
|
"n_frequency": 8, |
|
|
"target_modules": None, |
|
|
}, |
|
|
), |
|
|
] |
|
|
|
|
|
|
|
|
def _skip_if_not_conv1d_supported(model_id, config_cls): |
|
|
if "GPT2LMHeadModel" in model_id and config_cls in [ |
|
|
BOFTConfig, |
|
|
BoneConfig, |
|
|
HRAConfig, |
|
|
OFTConfig, |
|
|
RoadConfig, |
|
|
ShiraConfig, |
|
|
C3AConfig, |
|
|
MissConfig, |
|
|
]: |
|
|
pytest.skip("Skipping BOFT/HRA/OFT/Bone/Road/SHiRA/C3A/MiSS for GPT2LMHeadModel") |
|
|
|
|
|
|
|
|
def _skip_adalora_oft_hra_bone_for_gpt2(model_id, config_cls): |
|
|
if "GPT2LMHeadModel" in model_id and config_cls in [ |
|
|
AdaLoraConfig, |
|
|
BOFTConfig, |
|
|
HRAConfig, |
|
|
OFTConfig, |
|
|
BoneConfig, |
|
|
C3AConfig, |
|
|
RoadConfig, |
|
|
MissConfig, |
|
|
]: |
|
|
pytest.skip("Skipping AdaLora/BOFT/HRA/OFT/Bone/MiSS for GPT2LMHeadModel") |
|
|
|
|
|
|
|
|
def _skip_alora_no_activation(config_cls, config_kwargs): |
|
|
if config_cls is LoraConfig and config_kwargs.get("alora_invocation_tokens") == [1000]: |
|
|
pytest.skip("Skipping aLoRA no-activation-case because the test expects changed output which there won't be.") |
|
|
|
|
|
|
|
|
class TestDecoderModels(PeftCommonTester): |
|
|
transformers_class = AutoModelForCausalLM |
|
|
|
|
|
def skipTest(self, reason=""): |
|
|
|
|
|
pytest.skip(reason) |
|
|
|
|
|
def prepare_inputs_for_testing(self): |
|
|
input_ids = torch.tensor([[1, 1, 1], [1, 2, 1]]).to(self.torch_device) |
|
|
attention_mask = torch.tensor([[1, 1, 1], [1, 0, 1]]).to(self.torch_device) |
|
|
return {"input_ids": input_ids, "attention_mask": attention_mask} |
|
|
|
|
|
@pytest.mark.parametrize("model_id", PEFT_DECODER_MODELS_TO_TEST) |
|
|
@pytest.mark.parametrize("config_cls,config_kwargs", ALL_CONFIGS) |
|
|
def test_attributes_parametrized(self, model_id, config_cls, config_kwargs): |
|
|
_skip_if_not_conv1d_supported(model_id, config_cls) |
|
|
self._test_model_attr(model_id, config_cls, config_kwargs.copy()) |
|
|
|
|
|
@pytest.mark.parametrize("model_id", PEFT_DECODER_MODELS_TO_TEST) |
|
|
@pytest.mark.parametrize("config_cls,config_kwargs", ALL_CONFIGS) |
|
|
def test_adapter_name(self, model_id, config_cls, config_kwargs): |
|
|
_skip_if_not_conv1d_supported(model_id, config_cls) |
|
|
self._test_adapter_name(model_id, config_cls, config_kwargs.copy()) |
|
|
|
|
|
@pytest.mark.parametrize("model_id", PEFT_DECODER_MODELS_TO_TEST) |
|
|
@pytest.mark.parametrize("config_cls,config_kwargs", ALL_CONFIGS) |
|
|
def test_prepare_for_training_parametrized(self, model_id, config_cls, config_kwargs): |
|
|
_skip_if_not_conv1d_supported(model_id, config_cls) |
|
|
self._test_prepare_for_training(model_id, config_cls, config_kwargs.copy()) |
|
|
|
|
|
@pytest.mark.parametrize("model_id", PEFT_DECODER_MODELS_TO_TEST) |
|
|
@pytest.mark.parametrize("config_cls,config_kwargs", ALL_CONFIGS) |
|
|
def test_prompt_tuning_text_prepare_for_training(self, model_id, config_cls, config_kwargs): |
|
|
if config_cls != PromptTuningConfig: |
|
|
pytest.skip(f"This test does not apply to {config_cls}") |
|
|
config_kwargs = config_kwargs.copy() |
|
|
config_kwargs["prompt_tuning_init"] = PromptTuningInit.TEXT |
|
|
config_kwargs["prompt_tuning_init_text"] = "This is a test prompt." |
|
|
config_kwargs["tokenizer_name_or_path"] = model_id |
|
|
self._test_prepare_for_training(model_id, config_cls, config_kwargs.copy()) |
|
|
|
|
|
def test_prompt_tuning_text_tokenizer_kwargs(self): |
|
|
|
|
|
|
|
|
mock = Mock() |
|
|
orig_from_pretrained = AutoTokenizer.from_pretrained |
|
|
|
|
|
def mock_autotokenizer_from_pretrained(*args, **kwargs): |
|
|
mock(*args, **kwargs) |
|
|
return orig_from_pretrained(config.tokenizer_name_or_path) |
|
|
|
|
|
model_id = "hf-internal-testing/tiny-random-OPTForCausalLM" |
|
|
config = PromptTuningConfig( |
|
|
base_model_name_or_path=model_id, |
|
|
tokenizer_name_or_path=model_id, |
|
|
num_virtual_tokens=10, |
|
|
prompt_tuning_init=PromptTuningInit.TEXT, |
|
|
task_type="CAUSAL_LM", |
|
|
prompt_tuning_init_text="This is a test prompt.", |
|
|
tokenizer_kwargs={"trust_remote_code": True, "foo": "bar"}, |
|
|
) |
|
|
model = self.transformers_class.from_pretrained(model_id).to(self.torch_device) |
|
|
with patch("transformers.AutoTokenizer.from_pretrained", mock_autotokenizer_from_pretrained): |
|
|
_ = get_peft_model(model, config) |
|
|
expected_call = call(model_id, trust_remote_code=True, foo="bar") |
|
|
assert mock.call_args == expected_call |
|
|
|
|
|
@pytest.mark.parametrize("model_id", PEFT_DECODER_MODELS_TO_TEST) |
|
|
@pytest.mark.parametrize("config_cls,config_kwargs", ALL_CONFIGS) |
|
|
def test_prompt_tuning_sample_vocab_prepare_for_training(self, model_id, config_cls, config_kwargs): |
|
|
if config_cls != PromptTuningConfig: |
|
|
pytest.skip(f"This test does not apply to {config_cls}") |
|
|
|
|
|
config_kwargs = config_kwargs.copy() |
|
|
config_kwargs["prompt_tuning_init"] = PromptTuningInit.SAMPLE_VOCAB |
|
|
config_kwargs["tokenizer_name_or_path"] = model_id |
|
|
|
|
|
self._test_prepare_for_training(model_id, config_cls, config_kwargs.copy()) |
|
|
|
|
|
def test_prompt_tuning_config_invalid_args(self): |
|
|
|
|
|
|
|
|
model_id = "hf-internal-testing/tiny-random-OPTForCausalLM" |
|
|
with pytest.raises(ValueError, match="tokenizer_kwargs only valid when using prompt_tuning_init='TEXT'."): |
|
|
PromptTuningConfig( |
|
|
base_model_name_or_path=model_id, |
|
|
tokenizer_name_or_path=model_id, |
|
|
num_virtual_tokens=10, |
|
|
task_type="CAUSAL_LM", |
|
|
prompt_tuning_init_text="This is a test prompt.", |
|
|
prompt_tuning_init=PromptTuningInit.RANDOM, |
|
|
tokenizer_kwargs={"trust_remote_code": True, "foo": "bar"}, |
|
|
) |
|
|
|
|
|
@pytest.mark.parametrize("model_id", PEFT_DECODER_MODELS_TO_TEST) |
|
|
@pytest.mark.parametrize("config_cls,config_kwargs", ALL_CONFIGS) |
|
|
def test_save_pretrained(self, model_id, config_cls, config_kwargs): |
|
|
_skip_if_not_conv1d_supported(model_id, config_cls) |
|
|
self._test_save_pretrained(model_id, config_cls, config_kwargs.copy()) |
|
|
|
|
|
@pytest.mark.parametrize("model_id", PEFT_DECODER_MODELS_TO_TEST) |
|
|
@pytest.mark.parametrize("config_cls,config_kwargs", ALL_CONFIGS) |
|
|
def test_save_pretrained_pickle(self, model_id, config_cls, config_kwargs): |
|
|
_skip_if_not_conv1d_supported(model_id, config_cls) |
|
|
self._test_save_pretrained(model_id, config_cls, config_kwargs.copy(), safe_serialization=False) |
|
|
|
|
|
@pytest.mark.parametrize("model_id", PEFT_DECODER_MODELS_TO_TEST) |
|
|
@pytest.mark.parametrize("config_cls,config_kwargs", ALL_CONFIGS) |
|
|
def test_save_pretrained_selected_adapters(self, model_id, config_cls, config_kwargs): |
|
|
_skip_if_not_conv1d_supported(model_id, config_cls) |
|
|
self._test_save_pretrained_selected_adapters(model_id, config_cls, config_kwargs.copy()) |
|
|
|
|
|
@pytest.mark.parametrize("model_id", PEFT_DECODER_MODELS_TO_TEST) |
|
|
@pytest.mark.parametrize("config_cls,config_kwargs", ALL_CONFIGS) |
|
|
def test_save_pretrained_selected_adapters_pickle(self, model_id, config_cls, config_kwargs): |
|
|
_skip_if_not_conv1d_supported(model_id, config_cls) |
|
|
self._test_save_pretrained_selected_adapters( |
|
|
model_id, config_cls, config_kwargs.copy(), safe_serialization=False |
|
|
) |
|
|
|
|
|
@pytest.mark.parametrize("model_id", PEFT_DECODER_MODELS_TO_TEST) |
|
|
@pytest.mark.parametrize("config_cls,config_kwargs", ALL_CONFIGS) |
|
|
def test_from_pretrained_config_construction(self, model_id, config_cls, config_kwargs): |
|
|
_skip_if_not_conv1d_supported(model_id, config_cls) |
|
|
self._test_from_pretrained_config_construction(model_id, config_cls, config_kwargs.copy()) |
|
|
|
|
|
@pytest.mark.parametrize("model_id", PEFT_DECODER_MODELS_TO_TEST) |
|
|
@pytest.mark.parametrize("config_cls,config_kwargs", ALL_CONFIGS) |
|
|
def test_merge_layers(self, model_id, config_cls, config_kwargs): |
|
|
config_kwargs = set_init_weights_false(config_cls, config_kwargs) |
|
|
self._test_merge_layers(model_id, config_cls, config_kwargs.copy()) |
|
|
|
|
|
@pytest.mark.parametrize("model_id", PEFT_DECODER_MODELS_TO_TEST) |
|
|
@pytest.mark.parametrize("config_cls,config_kwargs", ALL_CONFIGS) |
|
|
def test_merge_layers_multi(self, model_id, config_cls, config_kwargs): |
|
|
_skip_if_not_conv1d_supported(model_id, config_cls) |
|
|
config_kwargs = set_init_weights_false(config_cls, config_kwargs) |
|
|
self._test_merge_layers_multi(model_id, config_cls, config_kwargs.copy()) |
|
|
|
|
|
@pytest.mark.parametrize("model_id", PEFT_DECODER_MODELS_TO_TEST) |
|
|
@pytest.mark.parametrize("config_cls,config_kwargs", ALL_CONFIGS) |
|
|
def test_merge_layers_nan(self, model_id, config_cls, config_kwargs): |
|
|
config_kwargs = set_init_weights_false(config_cls, config_kwargs) |
|
|
self._test_merge_layers_nan(model_id, config_cls, config_kwargs.copy()) |
|
|
|
|
|
@pytest.mark.parametrize("model_id", PEFT_DECODER_MODELS_TO_TEST) |
|
|
@pytest.mark.parametrize("config_cls,config_kwargs", ALL_CONFIGS) |
|
|
def test_mixed_adapter_batches(self, model_id, config_cls, config_kwargs): |
|
|
if config_cls != LoraConfig: |
|
|
pytest.skip("Mixed adapter batches not supported for this config.") |
|
|
_skip_alora_no_activation(config_cls, config_kwargs) |
|
|
config_kwargs = set_init_weights_false(config_cls, config_kwargs) |
|
|
self._test_mixed_adapter_batches(model_id, config_cls, config_kwargs.copy()) |
|
|
|
|
|
@pytest.mark.parametrize("model_id", PEFT_DECODER_MODELS_TO_TEST) |
|
|
@pytest.mark.parametrize("config_cls,config_kwargs", ALL_CONFIGS) |
|
|
def test_generate_with_mixed_adapter_batches(self, model_id, config_cls, config_kwargs): |
|
|
if config_cls != LoraConfig: |
|
|
pytest.skip("Mixed adapter batches not supported for this config.") |
|
|
config_kwargs = set_init_weights_false(config_cls, config_kwargs) |
|
|
self._test_generate_with_mixed_adapter_batches_and_beam_search(model_id, config_cls, config_kwargs.copy()) |
|
|
|
|
|
@pytest.mark.parametrize("model_id", PEFT_DECODER_MODELS_TO_TEST) |
|
|
@pytest.mark.parametrize("config_cls,config_kwargs", ALL_CONFIGS) |
|
|
def test_generate(self, model_id, config_cls, config_kwargs): |
|
|
_skip_if_not_conv1d_supported(model_id, config_cls) |
|
|
self._test_generate(model_id, config_cls, config_kwargs.copy()) |
|
|
|
|
|
@pytest.mark.parametrize("model_id", PEFT_DECODER_MODELS_TO_TEST) |
|
|
@pytest.mark.parametrize("config_cls,config_kwargs", ALL_CONFIGS) |
|
|
def test_generate_pos_args(self, model_id, config_cls, config_kwargs): |
|
|
_skip_if_not_conv1d_supported(model_id, config_cls) |
|
|
self._test_generate_pos_args(model_id, config_cls, config_kwargs.copy(), raises_err=False) |
|
|
|
|
|
@pytest.mark.parametrize("model_id", PEFT_DECODER_MODELS_TO_TEST) |
|
|
@pytest.mark.parametrize("config_cls,config_kwargs", ALL_CONFIGS) |
|
|
def test_merge_layers_fp16(self, model_id, config_cls, config_kwargs): |
|
|
self._test_merge_layers_fp16(model_id, config_cls, config_kwargs.copy()) |
|
|
|
|
|
@pytest.mark.parametrize("model_id", PEFT_DECODER_MODELS_TO_TEST) |
|
|
@pytest.mark.parametrize("config_cls,config_kwargs", ALL_CONFIGS) |
|
|
def test_generate_half_prec(self, model_id, config_cls, config_kwargs): |
|
|
self._test_generate_half_prec(model_id, config_cls, config_kwargs.copy()) |
|
|
|
|
|
@pytest.mark.parametrize("model_id", PEFT_DECODER_MODELS_TO_TEST) |
|
|
@pytest.mark.parametrize("config_cls,config_kwargs", ALL_CONFIGS) |
|
|
def test_prefix_tuning_half_prec_conversion(self, model_id, config_cls, config_kwargs): |
|
|
self._test_prefix_tuning_half_prec_conversion(model_id, config_cls, config_kwargs.copy()) |
|
|
|
|
|
@pytest.mark.parametrize("model_id", PEFT_DECODER_MODELS_TO_TEST) |
|
|
@pytest.mark.parametrize("config_cls,config_kwargs", ALL_CONFIGS) |
|
|
def test_training_decoders(self, model_id, config_cls, config_kwargs): |
|
|
_skip_if_not_conv1d_supported(model_id, config_cls) |
|
|
self._test_training(model_id, config_cls, config_kwargs.copy()) |
|
|
|
|
|
@pytest.mark.parametrize("model_id", PEFT_DECODER_MODELS_TO_TEST) |
|
|
@pytest.mark.parametrize("config_cls,config_kwargs", ALL_CONFIGS) |
|
|
def test_training_decoders_layer_indexing(self, model_id, config_cls, config_kwargs): |
|
|
self._test_training_layer_indexing(model_id, config_cls, config_kwargs.copy()) |
|
|
|
|
|
@pytest.mark.parametrize("model_id", PEFT_DECODER_MODELS_TO_TEST) |
|
|
@pytest.mark.parametrize("config_cls,config_kwargs", ALL_CONFIGS) |
|
|
def test_training_decoders_gradient_checkpointing(self, model_id, config_cls, config_kwargs): |
|
|
_skip_if_not_conv1d_supported(model_id, config_cls) |
|
|
self._test_training_gradient_checkpointing(model_id, config_cls, config_kwargs.copy()) |
|
|
|
|
|
@pytest.mark.parametrize("model_id", PEFT_DECODER_MODELS_TO_TEST) |
|
|
@pytest.mark.parametrize("config_cls,config_kwargs", ALL_CONFIGS) |
|
|
def test_inference_safetensors(self, model_id, config_cls, config_kwargs): |
|
|
_skip_if_not_conv1d_supported(model_id, config_cls) |
|
|
self._test_inference_safetensors(model_id, config_cls, config_kwargs.copy()) |
|
|
|
|
|
@pytest.mark.parametrize("model_id", PEFT_DECODER_MODELS_TO_TEST) |
|
|
@pytest.mark.parametrize("config_cls,config_kwargs", ALL_CONFIGS) |
|
|
def test_peft_model_device_map(self, model_id, config_cls, config_kwargs): |
|
|
self._test_peft_model_device_map(model_id, config_cls, config_kwargs.copy()) |
|
|
|
|
|
@pytest.mark.parametrize("model_id", PEFT_DECODER_MODELS_TO_TEST) |
|
|
@pytest.mark.parametrize("config_cls,config_kwargs", ALL_CONFIGS) |
|
|
def test_delete_adapter(self, model_id, config_cls, config_kwargs): |
|
|
_skip_if_not_conv1d_supported(model_id, config_cls) |
|
|
self._test_delete_adapter(model_id, config_cls, config_kwargs.copy()) |
|
|
|
|
|
@pytest.mark.parametrize("model_id", PEFT_DECODER_MODELS_TO_TEST) |
|
|
@pytest.mark.parametrize("config_cls,config_kwargs", ALL_CONFIGS) |
|
|
def test_delete_inactive_adapter(self, model_id, config_cls, config_kwargs): |
|
|
_skip_if_not_conv1d_supported(model_id, config_cls) |
|
|
self._test_delete_inactive_adapter(model_id, config_cls, config_kwargs.copy()) |
|
|
|
|
|
@pytest.mark.parametrize("model_id", PEFT_DECODER_MODELS_TO_TEST) |
|
|
@pytest.mark.parametrize("config_cls,config_kwargs", ALL_CONFIGS) |
|
|
def test_adding_multiple_adapters_with_bias_raises(self, model_id, config_cls, config_kwargs): |
|
|
_skip_if_not_conv1d_supported(model_id, config_cls) |
|
|
self._test_adding_multiple_adapters_with_bias_raises(model_id, config_cls, config_kwargs.copy()) |
|
|
|
|
|
@pytest.mark.parametrize("model_id", PEFT_DECODER_MODELS_TO_TEST) |
|
|
@pytest.mark.parametrize("config_cls,config_kwargs", ALL_CONFIGS) |
|
|
def test_unload_adapter(self, model_id, config_cls, config_kwargs): |
|
|
_skip_adalora_oft_hra_bone_for_gpt2(model_id, config_cls) |
|
|
_skip_if_not_conv1d_supported(model_id, config_cls) |
|
|
_skip_alora_no_activation(config_cls, config_kwargs) |
|
|
config_kwargs = set_init_weights_false(config_cls, config_kwargs) |
|
|
self._test_unload_adapter(model_id, config_cls, config_kwargs.copy()) |
|
|
|
|
|
@pytest.mark.parametrize("model_id", PEFT_DECODER_MODELS_TO_TEST) |
|
|
@pytest.mark.parametrize("config_cls,config_kwargs", ALL_CONFIGS) |
|
|
def test_weighted_combination_of_adapters(self, model_id, config_cls, config_kwargs): |
|
|
config_kwargs = set_init_weights_false(config_cls, config_kwargs) |
|
|
self._test_weighted_combination_of_adapters(model_id, config_cls, config_kwargs.copy()) |
|
|
|
|
|
@pytest.mark.parametrize("model_id", PEFT_DECODER_MODELS_TO_TEST) |
|
|
@pytest.mark.parametrize("config_cls,config_kwargs", ALL_CONFIGS) |
|
|
def test_training_prompt_learning_tasks(self, model_id, config_cls, config_kwargs): |
|
|
self._test_training_prompt_learning_tasks(model_id, config_cls, config_kwargs.copy()) |
|
|
|
|
|
@pytest.mark.parametrize("model_id", PEFT_DECODER_MODELS_TO_TEST) |
|
|
@pytest.mark.parametrize("config_cls,config_kwargs", ALL_CONFIGS) |
|
|
def test_disable_adapter(self, model_id, config_cls, config_kwargs): |
|
|
_skip_if_not_conv1d_supported(model_id, config_cls) |
|
|
_skip_alora_no_activation(config_cls, config_kwargs) |
|
|
config_kwargs = set_init_weights_false(config_cls, config_kwargs) |
|
|
self._test_disable_adapter(model_id, config_cls, config_kwargs.copy()) |
|
|
|
|
|
def test_generate_adalora_no_dropout(self): |
|
|
|
|
|
model_id = "hf-internal-testing/tiny-random-OPTForCausalLM" |
|
|
config_kwargs = { |
|
|
"target_modules": None, |
|
|
"task_type": "CAUSAL_LM", |
|
|
"lora_dropout": 0.0, |
|
|
"total_step": 1, |
|
|
} |
|
|
self._test_generate(model_id, AdaLoraConfig, config_kwargs.copy()) |
|
|
|
|
|
@pytest.mark.parametrize("model_id", PEFT_DECODER_MODELS_TO_TEST) |
|
|
@pytest.mark.parametrize("config_cls,config_kwargs", ALL_CONFIGS) |
|
|
def test_passing_input_embeds_works(self, model_id, config_cls, config_kwargs): |
|
|
_skip_if_not_conv1d_supported(model_id, config_cls) |
|
|
if (platform.system() == "Darwin") and (config_cls == PrefixTuningConfig): |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
pytest.skip("Prefix tuning fails on MacOS in this case, not worth fixing") |
|
|
self._test_passing_input_embeds_works("", model_id, config_cls, config_kwargs.copy()) |
|
|
|
|
|
def test_lora_layer_replication(self): |
|
|
model_id = "trl-internal-testing/tiny-random-LlamaForCausalLM" |
|
|
config_kwargs = { |
|
|
"target_modules": ["down_proj", "up_proj"], |
|
|
"task_type": "CAUSAL_LM", |
|
|
"lora_dropout": 0.0, |
|
|
"layer_replication": [[0, 1], [0, 2], [1, 2]], |
|
|
} |
|
|
model = self.transformers_class.from_pretrained(model_id).to(self.torch_device) |
|
|
config = LoraConfig(base_model_name_or_path=model_id, **config_kwargs) |
|
|
|
|
|
assert len(model.model.layers), "Expected 2 layers in original model." == 2 |
|
|
model = get_peft_model(model, config) |
|
|
layers = model.base_model.model.model.layers |
|
|
assert len(layers) == 4, "Expected 4 layers in adapted model." |
|
|
assert ( |
|
|
layers[0].mlp.up_proj.base_layer.weight.data.storage().data_ptr() |
|
|
== layers[1].mlp.up_proj.base_layer.weight.data.storage().data_ptr() |
|
|
and layers[2].mlp.up_proj.base_layer.weight.data.storage().data_ptr() |
|
|
== layers[3].mlp.up_proj.base_layer.weight.data.storage().data_ptr() |
|
|
), "Expected layers 0-1 and 2-3 to share weights" |
|
|
assert ( |
|
|
layers[0].mlp.up_proj.base_layer.weight.data.storage().data_ptr() |
|
|
!= layers[2].mlp.up_proj.base_layer.weight.data.storage().data_ptr() |
|
|
), "Expected layers 0 and 2 to have different weights" |
|
|
assert ( |
|
|
layers[0].mlp.up_proj.lora_A.default.weight.data.storage().data_ptr() |
|
|
!= layers[1].mlp.up_proj.lora_A.default.weight.data.storage().data_ptr() |
|
|
and layers[2].mlp.up_proj.lora_A.default.weight.data.storage().data_ptr() |
|
|
!= layers[3].mlp.up_proj.lora_A.default.weight.data.storage().data_ptr() |
|
|
), "Expected all LoRA adapters to have distinct weights" |
|
|
assert len([n for n, _ in model.named_parameters() if ".lora_A." in n]) == 8, ( |
|
|
"Expected 8 LoRA adapters since we are adding one each for up and down." |
|
|
) |
|
|
self._test_prepare_for_training(model_id, LoraConfig, config_kwargs.copy()) |
|
|
self._test_generate(model_id, LoraConfig, config_kwargs.copy()) |
|
|
|
|
|
def test_prompt_learning_with_grouped_query_attention(self): |
|
|
|
|
|
model_id = "peft-internal-testing/tiny-dummy-qwen2" |
|
|
base_model = AutoModelForCausalLM.from_pretrained(model_id) |
|
|
peft_config = PrefixTuningConfig(num_virtual_tokens=10, task_type="CAUSAL_LM") |
|
|
model = get_peft_model(base_model, peft_config) |
|
|
x = torch.tensor([[1, 2, 3]]) |
|
|
|
|
|
model(x) |
|
|
|
|
|
def test_prefix_tuning_mistral(self): |
|
|
|
|
|
model_id = "hf-internal-testing/tiny-random-MistralForCausalLM" |
|
|
base_model = AutoModelForCausalLM.from_pretrained(model_id) |
|
|
peft_config = PrefixTuningConfig(num_virtual_tokens=10, task_type="CAUSAL_LM") |
|
|
model = get_peft_model(base_model, peft_config) |
|
|
|
|
|
tokenizer = AutoTokenizer.from_pretrained(model_id) |
|
|
tokenizer.pad_token = tokenizer.eos_token |
|
|
|
|
|
def process(samples): |
|
|
tokenized = tokenizer(samples["quote"], truncation=True, max_length=128) |
|
|
return tokenized |
|
|
|
|
|
data = load_dataset_english_quotes() |
|
|
data = data.map(process, batched=True) |
|
|
|
|
|
with tempfile.TemporaryDirectory() as tmp_dirname: |
|
|
trainer = Trainer( |
|
|
model=model, |
|
|
train_dataset=data["train"], |
|
|
args=TrainingArguments( |
|
|
num_train_epochs=1, |
|
|
max_steps=5, |
|
|
per_device_train_batch_size=4, |
|
|
output_dir=tmp_dirname, |
|
|
), |
|
|
data_collator=DataCollatorForLanguageModeling(tokenizer, mlm=False), |
|
|
) |
|
|
trainer.train() |
|
|
|
|
|
@pytest.mark.parametrize("model_id", SMALL_GRID_MODELS) |
|
|
@pytest.mark.parametrize( |
|
|
"config_cls,config_kwargs", |
|
|
[ |
|
|
( |
|
|
PromptTuningConfig, |
|
|
{ |
|
|
"num_virtual_tokens": 10, |
|
|
"task_type": "CAUSAL_LM", |
|
|
}, |
|
|
), |
|
|
( |
|
|
PrefixTuningConfig, |
|
|
{ |
|
|
"num_virtual_tokens": 10, |
|
|
"task_type": "CAUSAL_LM", |
|
|
}, |
|
|
), |
|
|
( |
|
|
PromptEncoderConfig, |
|
|
{ |
|
|
"num_virtual_tokens": 10, |
|
|
"encoder_hidden_size": 32, |
|
|
"task_type": "CAUSAL_LM", |
|
|
}, |
|
|
), |
|
|
( |
|
|
CPTConfig, |
|
|
{ |
|
|
"cpt_token_ids": [0, 1, 2, 3, 4, 5, 6, 7], |
|
|
"cpt_mask": [1, 1, 1, 1, 1, 1, 1, 1], |
|
|
"cpt_tokens_type_mask": [1, 2, 2, 2, 3, 3, 4, 4], |
|
|
}, |
|
|
), |
|
|
], |
|
|
) |
|
|
def test_prompt_learning_with_gradient_checkpointing(self, model_id, config_cls, config_kwargs): |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if device_count > 1: |
|
|
pytest.skip("Skip on multi-GPU setups") |
|
|
peft_config = config_cls(base_model_name_or_path=model_id, **config_kwargs) |
|
|
base_model = self.transformers_class.from_pretrained(model_id) |
|
|
base_model.gradient_checkpointing_enable() |
|
|
|
|
|
try: |
|
|
model = get_peft_model(base_model, peft_config) |
|
|
except ValueError as exc: |
|
|
|
|
|
assert config_cls == PrefixTuningConfig |
|
|
assert "Prefix tuning does not work with gradient checkpointing" in str(exc) |
|
|
return |
|
|
|
|
|
tokenizer = AutoTokenizer.from_pretrained(model_id) |
|
|
tokenizer.pad_token = tokenizer.eos_token |
|
|
|
|
|
def process(samples): |
|
|
tokenized = tokenizer(samples["quote"], truncation=True, max_length=128) |
|
|
return tokenized |
|
|
|
|
|
data = load_dataset_english_quotes() |
|
|
data = data.map(process, batched=True) |
|
|
|
|
|
with tempfile.TemporaryDirectory() as tmp_dirname: |
|
|
trainer = Trainer( |
|
|
model=model, |
|
|
train_dataset=data["train"], |
|
|
args=TrainingArguments( |
|
|
num_train_epochs=1, |
|
|
max_steps=3, |
|
|
per_device_train_batch_size=4, |
|
|
output_dir=tmp_dirname, |
|
|
), |
|
|
data_collator=DataCollatorForLanguageModeling(tokenizer, mlm=False), |
|
|
) |
|
|
trainer.train() |
|
|
|
|
|
@pytest.mark.parametrize("save_embedding_layers", ["auto", True, False]) |
|
|
@pytest.mark.parametrize( |
|
|
"peft_config", |
|
|
[ |
|
|
(LoraConfig(target_modules=["lin0", "embed_tokens"], init_lora_weights=False)), |
|
|
(LoraConfig(target_modules=r".*\.embed_tokens", init_lora_weights=False)), |
|
|
], |
|
|
) |
|
|
def test_save_pretrained_targeting_lora_to_embedding_layer(self, save_embedding_layers, tmp_path, peft_config): |
|
|
model_id = "trl-internal-testing/tiny-random-LlamaForCausalLM" |
|
|
|
|
|
with hub_online_once(model_id): |
|
|
model = AutoModelForCausalLM.from_pretrained(model_id) |
|
|
model = get_peft_model(model, peft_config) |
|
|
|
|
|
if save_embedding_layers == "auto": |
|
|
|
|
|
msg_start = "Setting `save_embedding_layers` to `True` as embedding layers found in `target_modules`." |
|
|
with pytest.warns(UserWarning, match=msg_start): |
|
|
model.save_pretrained(tmp_path, save_embedding_layers=save_embedding_layers) |
|
|
else: |
|
|
model.save_pretrained(tmp_path, save_embedding_layers=save_embedding_layers) |
|
|
|
|
|
state_dict = safe_load_file(tmp_path / "adapter_model.safetensors") |
|
|
contains_embedding = "base_model.model.model.embed_tokens.base_layer.weight" in state_dict |
|
|
|
|
|
if save_embedding_layers in ["auto", True]: |
|
|
assert contains_embedding |
|
|
assert torch.allclose( |
|
|
model.base_model.model.model.embed_tokens.base_layer.weight, |
|
|
state_dict["base_model.model.model.embed_tokens.base_layer.weight"], |
|
|
) |
|
|
else: |
|
|
assert not contains_embedding |
|
|
|
|
|
@pytest.mark.parametrize("config_cls,config_kwargs", ALL_CONFIGS) |
|
|
def test_set_requires_grad_prompt_learning_raises(self, config_cls, config_kwargs): |
|
|
|
|
|
|
|
|
|
|
|
model_id = PEFT_DECODER_MODELS_TO_TEST[0] |
|
|
config = config_cls( |
|
|
base_model_name_or_path=model_id, |
|
|
**config_kwargs, |
|
|
) |
|
|
if not config.is_prompt_learning: |
|
|
pytest.skip("This test is only for prompt learning methods.") |
|
|
|
|
|
with hub_online_once(model_id + config_kwargs.get("tokenizer_name_or_path", "")): |
|
|
model = self.transformers_class.from_pretrained(model_id).to(self.torch_device) |
|
|
model = get_peft_model(model, config) |
|
|
msg = "Setting `requires_grad` is not supported for prompt learning methods like" |
|
|
with pytest.raises(TypeError, match=msg): |
|
|
model.set_requires_grad(adapter_names="adpater0") |
|
|
|