|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import inspect |
|
from contextlib import contextmanager |
|
from copy import deepcopy |
|
from functools import update_wrapper |
|
from types import MethodType |
|
|
|
from .peft_model import PeftConfig, PeftModel |
|
from .tuners.lora.layer import LoraLayer |
|
|
|
|
|
def update_forward_signature(model: PeftModel) -> None: |
|
""" |
|
Updates the forward signature of the PeftModel to include parents class signature |
|
model (`PeftModel`): Peft model to update the forward signature |
|
|
|
Example: |
|
|
|
```python |
|
>>> from transformers import WhisperForConditionalGeneration |
|
>>> from peft import get_peft_model, LoraConfig, update_forward_signature |
|
|
|
>>> model = WhisperForConditionalGeneration.from_pretrained("openai/whisper-tiny.en") |
|
>>> peft_config = LoraConfig(r=8, lora_alpha=32, lora_dropout=0.1, target_modules=["q_proj", "v_proj"]) |
|
|
|
>>> peft_model = get_peft_model(model, peft_config) |
|
>>> update_forward_signature(peft_model) |
|
``` |
|
""" |
|
|
|
|
|
current_signature = inspect.signature(model.forward) |
|
if ( |
|
len(current_signature.parameters) == 2 |
|
and "args" in current_signature.parameters |
|
and "kwargs" in current_signature.parameters |
|
): |
|
forward = deepcopy(model.forward.__func__) |
|
update_wrapper( |
|
forward, type(model.get_base_model()).forward, assigned=("__doc__", "__name__", "__annotations__") |
|
) |
|
model.forward = MethodType(forward, model) |
|
|
|
|
|
def update_generate_signature(model: PeftModel) -> None: |
|
""" |
|
Updates the generate signature of a PeftModel with overriding generate to include parents class signature |
|
model (`PeftModel`): Peft model to update the generate signature |
|
|
|
Example: |
|
|
|
```python |
|
>>> from transformers import AutoModelForSeq2SeqLM, AutoTokenizer |
|
>>> from peft import get_peft_model, LoraConfig, TaskType, update_generate_signature |
|
|
|
>>> model_name_or_path = "bigscience/mt0-large" |
|
>>> tokenizer = AutoTokenizer.from_pretrained(model_name_or_path) |
|
>>> model = AutoModelForSeq2SeqLM.from_pretrained(model_name_or_path) |
|
|
|
>>> peft_config = LoraConfig( |
|
... task_type=TaskType.SEQ_2_SEQ_LM, inference_mode=False, r=8, lora_alpha=32, lora_dropout=0.1 |
|
... ) |
|
>>> peft_model = get_peft_model(model, peft_config) |
|
>>> update_generate_signature(peft_model) |
|
>>> help(peft_model.generate) |
|
``` |
|
""" |
|
if not hasattr(model, "generate"): |
|
return |
|
current_signature = inspect.signature(model.generate) |
|
if ( |
|
len(current_signature.parameters) == 2 |
|
and "args" in current_signature.parameters |
|
and "kwargs" in current_signature.parameters |
|
) or (len(current_signature.parameters) == 1 and "kwargs" in current_signature.parameters): |
|
generate = deepcopy(model.generate.__func__) |
|
update_wrapper( |
|
generate, |
|
type(model.get_base_model()).generate, |
|
assigned=("__doc__", "__name__", "__annotations__"), |
|
) |
|
model.generate = MethodType(generate, model) |
|
|
|
|
|
def update_signature(model: PeftModel, method: str = "all") -> None: |
|
""" |
|
Updates the signature of a PeftModel include parents class signature for forward or generate method |
|
model (`PeftModel`): Peft model to update generate or forward signature method (`str`): method to update |
|
signature choose one of "forward", "generate", "all" |
|
|
|
Example: |
|
```python |
|
>>> from transformers import AutoModelForSeq2SeqLM, AutoTokenizer |
|
>>> from peft import get_peft_model, LoraConfig, TaskType, update_signature |
|
|
|
>>> model_name_or_path = "bigscience/mt0-large" |
|
>>> tokenizer = AutoTokenizer.from_pretrained(model_name_or_path) |
|
>>> model = AutoModelForSeq2SeqLM.from_pretrained(model_name_or_path) |
|
|
|
>>> peft_config = LoraConfig( |
|
... task_type=TaskType.SEQ_2_SEQ_LM, inference_mode=False, r=8, lora_alpha=32, lora_dropout=0.1 |
|
... ) |
|
>>> peft_model = get_peft_model(model, peft_config) |
|
>>> update_signature(peft_model) |
|
>>> help(peft_model.generate) |
|
``` |
|
""" |
|
if method == "forward": |
|
update_forward_signature(model) |
|
elif method == "generate": |
|
update_generate_signature(model) |
|
elif method == "all": |
|
update_forward_signature(model) |
|
update_generate_signature(model) |
|
else: |
|
raise ValueError(f"method {method} is not supported please choose one of ['forward', 'generate', 'all']") |
|
|
|
|
|
def check_if_peft_model(model_name_or_path: str) -> bool: |
|
""" |
|
Check if the model is a PEFT model. |
|
|
|
Args: |
|
model_name_or_path (`str`): |
|
Model id to check, can be local or on the Hugging Face Hub. |
|
|
|
Returns: |
|
`bool`: True if the model is a PEFT model, False otherwise. |
|
""" |
|
is_peft_model = True |
|
try: |
|
PeftConfig.from_pretrained(model_name_or_path) |
|
except Exception: |
|
|
|
is_peft_model = False |
|
|
|
return is_peft_model |
|
|
|
|
|
@contextmanager |
|
def rescale_adapter_scale(model, multiplier): |
|
""" |
|
Context manager to temporarily rescale the scaling of the LoRA adapter in a model. |
|
|
|
The original scaling values are restored when the context manager exits. This context manager works with the |
|
transformers and diffusers models that have directly loaded LoRA adapters. |
|
|
|
For LoRA, applying this context manager with multiplier in [0, 1] is strictly equivalent to applying |
|
[wise-ft](https://arxiv.org/abs/2109.01903) (see [#1940](https://github.com/huggingface/peft/issues/1940) for |
|
details). It can improve the performances of the model if there is a distribution shiftbetween the training data |
|
used for fine-tuning, and the test data used during inference. |
|
|
|
Warning: It has been reported that when using Apple's MPS backend for PyTorch, it is necessary to add a short sleep |
|
time after exiting the context before the scales are fully restored. |
|
|
|
Args: |
|
model: The model containing `LoraLayer` modules whose scaling is to be adjusted. |
|
multiplier (float or int): |
|
The multiplier that rescales the `scaling` attribute. Must be of type float or int. |
|
|
|
Raises: |
|
ValueError: If the model does not contain any `LoraLayer` |
|
instances, indicating that the model does not support scaling. |
|
|
|
Example: |
|
|
|
```python |
|
>>> model = ModelWithLoraLayer() |
|
>>> multiplier = 0.5 |
|
>>> with rescale_adapter_scale(model, multiplier): |
|
... outputs = model(**inputs) # Perform operations with the scaled model |
|
>>> outputs = model(**inputs) # The original scaling values are restored here |
|
``` |
|
""" |
|
|
|
if not isinstance(multiplier, (float, int)): |
|
raise TypeError(f"Argument multiplier should be of type float, got {type(multiplier)}") |
|
|
|
|
|
|
|
original_scaling = {} |
|
for module in model.modules(): |
|
if isinstance(module, LoraLayer): |
|
original_scaling[module] = module.scaling.copy() |
|
module.scaling = {k: v * multiplier for k, v in module.scaling.items()} |
|
|
|
|
|
|
|
|
|
if not original_scaling: |
|
raise ValueError("scaling is only supported for models with `LoraLayer`s") |
|
try: |
|
yield |
|
|
|
finally: |
|
|
|
for module, scaling in original_scaling.items(): |
|
module.scaling = scaling |
|
|