Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
4cb6f6e
v2 integration
younesbelkada Jul 25, 2023
d41717c
sanity check
younesbelkada Jul 25, 2023
375a238
Merge remote-tracking branch 'upstream/main' into integration-v2
younesbelkada Jul 26, 2023
a08e858
major refactor
younesbelkada Jul 26, 2023
420d39c
fix doc
younesbelkada Jul 26, 2023
9a9138e
some refactor
younesbelkada Jul 26, 2023
088a1c1
add abstract class
younesbelkada Jul 26, 2023
57fa268
refactor a bit
younesbelkada Jul 27, 2023
d04a91b
properly freeze the base model
younesbelkada Jul 27, 2023
4a08ecb
more refactor
younesbelkada Jul 27, 2023
1e99c8e
v1 of a better abstraction
younesbelkada Jul 27, 2023
6acfeff
better refactoring
younesbelkada Jul 28, 2023
10a9ab2
use classmethod instead
younesbelkada Jul 28, 2023
8fc3a63
Merge branch 'main' into integration-v2
younesbelkada Jul 28, 2023
2817d2f
style
younesbelkada Jul 28, 2023
66a5e3d
fix test
younesbelkada Jul 28, 2023
221ab34
addressed general comments
younesbelkada Jul 28, 2023
3da5b6d
fix failing CIs
younesbelkada Jul 31, 2023
8827879
not checking the type of the args
younesbelkada Jul 31, 2023
2cac1d6
remove `pre_init`
younesbelkada Jul 31, 2023
2d54ea6
Update src/peft/tuners/tuners_utils.py
younesbelkada Jul 31, 2023
b8bd2e2
Update src/peft/tuners/tuners_utils.py
younesbelkada Jul 31, 2023
8efb5f4
tiny comments to remove later
younesbelkada Jul 31, 2023
a92e5ea
adapt from suggestions
younesbelkada Jul 31, 2023
45e8877
added docstrings.
younesbelkada Jul 31, 2023
dbf764f
remove check
younesbelkada Jul 31, 2023
88c91ae
remove manual freezing
younesbelkada Jul 31, 2023
4ce8541
added comments to explain hack.
younesbelkada Jul 31, 2023
ead8545
fix docstring
younesbelkada Jul 31, 2023
c3e6b61
fix docstring
younesbelkada Jul 31, 2023
7648c27
remove `supports_merging`
younesbelkada Jul 31, 2023
8f09a8e
remove unneeded comments
younesbelkada Jul 31, 2023
4d1dffb
extensive docstring on `BaseTuner`
younesbelkada Jul 31, 2023
a6be082
improve type hints
younesbelkada Jul 31, 2023
a032f62
add more comments.
younesbelkada Jul 31, 2023
fccae04
add final suggestion
younesbelkada Jul 31, 2023
2e632f1
Merge branch 'main' into integration-v2
younesbelkada Aug 1, 2023
f8fcba7
minor tweaks
younesbelkada Aug 1, 2023
84a56e0
a bit of refactoring and addressing comments.
younesbelkada Aug 2, 2023
8b88401
use `child` instead of `target`
younesbelkada Aug 2, 2023
8053c88
few other nits
younesbelkada Aug 2, 2023
b4adb6d
Merge branch 'main' into integration-v2
younesbelkada Aug 3, 2023
7171188
nits
younesbelkada Aug 3, 2023
758ecb8
Update src/peft/mapping.py
younesbelkada Aug 3, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/source/package_reference/config.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ The configuration classes stores the configuration of a [`PeftModel`], PEFT adap

## PeftConfigMixin

[[autodoc]] utils.config.PeftConfigMixin
[[autodoc]] config.PeftConfigMixin
- all

## PeftConfig
Expand Down
12 changes: 9 additions & 3 deletions src/peft/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,13 @@
AutoPeftModelForQuestionAnswering,
AutoPeftModelForFeatureExtraction,
)
from .mapping import MODEL_TYPE_TO_PEFT_MODEL_MAPPING, PEFT_TYPE_TO_CONFIG_MAPPING, get_peft_config, get_peft_model
from .mapping import (
MODEL_TYPE_TO_PEFT_MODEL_MAPPING,
PEFT_TYPE_TO_CONFIG_MAPPING,
get_peft_config,
get_peft_model,
inject_adapter_in_model,
)
from .peft_model import (
PeftModel,
PeftModelForCausalLM,
Expand Down Expand Up @@ -58,14 +64,14 @@
)
from .utils import (
TRANSFORMERS_MODELS_TO_PREFIX_TUNING_POSTPROCESS_MAPPING,
PeftConfig,
PeftType,
PromptLearningConfig,
TaskType,
bloom_model_postprocess_past_key_value,
get_peft_model_state_dict,
prepare_model_for_int8_training,
prepare_model_for_kbit_training,
set_peft_model_state_dict,
shift_tokens_right,
load_peft_weights,
)
from .config import PeftConfig, PromptLearningConfig
2 changes: 1 addition & 1 deletion src/peft/auto.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
AutoModelForTokenClassification,
)

from .config import PeftConfig
from .mapping import MODEL_TYPE_TO_PEFT_MODEL_MAPPING
from .peft_model import (
PeftModel,
Expand All @@ -37,7 +38,6 @@
PeftModelForSequenceClassification,
PeftModelForTokenClassification,
)
from .utils import PeftConfig


class _BaseAutoPeftModel:
Expand Down
66 changes: 44 additions & 22 deletions src/peft/utils/config.py → src/peft/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
# 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 enum
import inspect
import json
import os
Expand All @@ -22,26 +21,7 @@
from huggingface_hub import hf_hub_download
from transformers.utils import PushToHubMixin

from .other import CONFIG_NAME


class PeftType(str, enum.Enum):
PROMPT_TUNING = "PROMPT_TUNING"
P_TUNING = "P_TUNING"
PREFIX_TUNING = "PREFIX_TUNING"
LORA = "LORA"
ADALORA = "ADALORA"
ADAPTION_PROMPT = "ADAPTION_PROMPT"
IA3 = "IA3"


class TaskType(str, enum.Enum):
SEQ_CLS = "SEQ_CLS"
SEQ_2_SEQ_LM = "SEQ_2_SEQ_LM"
CAUSAL_LM = "CAUSAL_LM"
TOKEN_CLS = "TOKEN_CLS"
QUESTION_ANS = "QUESTION_ANS"
FEATURE_EXTRACTION = "FEATURE_EXTRACTION"
from .utils import CONFIG_NAME, PeftType, TaskType


@dataclass
Expand Down Expand Up @@ -102,6 +82,9 @@ def from_pretrained(cls, pretrained_model_name_or_path, subfolder=None, **kwargs
kwargs (additional keyword arguments, *optional*):
Additional keyword arguments passed along to the child class initialization.
"""
# Avoid circular dependency .. TODO: fix this with a larger refactor
from peft.mapping import PEFT_TYPE_TO_CONFIG_MAPPING

path = (
os.path.join(pretrained_model_name_or_path, subfolder)
if subfolder is not None
Expand All @@ -122,7 +105,27 @@ def from_pretrained(cls, pretrained_model_name_or_path, subfolder=None, **kwargs

loaded_attributes = cls.from_json_file(config_file)

config = cls(**class_kwargs)
# TODO: this hack is needed to fix the following issue (on commit 702f937):
# if someone saves a default config and loads it back with `PeftConfig` class it yields to
# not loading the correct config class.

# from peft import AdaLoraConfig, PeftConfig
# peft_config = AdaLoraConfig()
# print(peft_config)
# >>> AdaLoraConfig(peft_type=<PeftType.ADALORA: 'ADALORA'>, auto_mapping=None, base_model_name_or_path=None,
# revision=None, task_type=None, inference_mode=False, r=8, target_modules=None, lora_alpha=8, lora_dropout=0.0, ...
#
# peft_config.save_pretrained("./test_config")
# peft_config = PeftConfig.from_pretrained("./test_config")
# print(peft_config)
# >>> PeftConfig(peft_type='ADALORA', auto_mapping=None, base_model_name_or_path=None, revision=None, task_type=None, inference_mode=False)
if "peft_type" in loaded_attributes:
peft_type = loaded_attributes["peft_type"]
config_cls = PEFT_TYPE_TO_CONFIG_MAPPING[peft_type]
else:
config_cls = cls

config = config_cls(**class_kwargs)

for key, value in loaded_attributes.items():
if hasattr(config, key):
Expand Down Expand Up @@ -185,6 +188,18 @@ def _get_peft_type(
loaded_attributes = cls.from_json_file(config_file)
return loaded_attributes["peft_type"]

@property
def is_prompt_learning(self):
r"""
Utility method to check if the configuration is for prompt learning.
"""
return False

@property
def is_adaption_prompt(self) -> bool:
"""Return True if this is an adaption prompt config."""
return False


@dataclass
class PeftConfig(PeftConfigMixin):
Expand Down Expand Up @@ -227,3 +242,10 @@ class PromptLearningConfig(PeftConfig):
)
num_attention_heads: Optional[int] = field(default=None, metadata={"help": "Number of attention heads"})
num_layers: Optional[int] = field(default=None, metadata={"help": "Number of transformer layers"})

@property
def is_prompt_learning(self):
r"""
Utility method to check if the configuration is for prompt learning.
"""
return True
52 changes: 45 additions & 7 deletions src/peft/mapping.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@

from typing import TYPE_CHECKING, Any, Dict

import torch

from .config import PeftConfig
from .peft_model import (
PeftModel,
PeftModelForCausalLM,
Expand All @@ -28,21 +31,22 @@
)
from .tuners import (
AdaLoraConfig,
AdaLoraModel,
AdaptionPromptConfig,
IA3Config,
IA3Model,
LoraConfig,
LoraModel,
PrefixTuningConfig,
PromptEncoderConfig,
PromptTuningConfig,
)
from .utils import PromptLearningConfig, _prepare_prompt_learning_config
from .utils import _prepare_prompt_learning_config


if TYPE_CHECKING:
from transformers import PreTrainedModel

from .utils.config import PeftConfig


MODEL_TYPE_TO_PEFT_MODEL_MAPPING = {
"SEQ_CLS": PeftModelForSequenceClassification,
Expand All @@ -63,6 +67,12 @@
"IA3": IA3Config,
}

PEFT_TYPE_TO_TUNER_MAPPING = {
"LORA": LoraModel,
"ADALORA": AdaLoraModel,
"IA3": IA3Model,
}


def get_peft_config(config_dict: Dict[str, Any]):
"""
Expand All @@ -89,10 +99,38 @@ def get_peft_model(model: PreTrainedModel, peft_config: PeftConfig, adapter_name

peft_config.base_model_name_or_path = model.__dict__.get("name_or_path", None)

if peft_config.task_type not in MODEL_TYPE_TO_PEFT_MODEL_MAPPING.keys() and not isinstance(
peft_config, PromptLearningConfig
):
if peft_config.task_type not in MODEL_TYPE_TO_PEFT_MODEL_MAPPING.keys() and not peft_config.is_prompt_learning:
return PeftModel(model, peft_config, adapter_name=adapter_name)
if isinstance(peft_config, PromptLearningConfig):
if peft_config.is_prompt_learning:
peft_config = _prepare_prompt_learning_config(peft_config, model_config)
return MODEL_TYPE_TO_PEFT_MODEL_MAPPING[peft_config.task_type](model, peft_config, adapter_name=adapter_name)


def inject_adapter_in_model(peft_config: PeftConfig, model: torch.nn.Module, adapter_name: str):
r"""
A simple API to create and inject adapter in-place into a model. Currently the API does not support prompt learning
methods and adaption prompt. Make sure to have the correct `target_names` set in the `peft_config` object. The API
calls `get_peft_model` under the hood but would be restricted only to non-prompt learning methods.

Args:
peft_config (`PeftConfig`):
Configuration object containing the parameters of the Peft model.
model (`torch.nn.Module`):
The input model where the adapter will be injected.
adapter_name (`str`):
The name of the adapter to be injected.
"""
if peft_config.is_prompt_learning or peft_config.is_adaption_prompt:
raise ValueError("`create_and_replace` does not support prompt learning and adaption prompt yet.")

if peft_config.peft_type not in PEFT_TYPE_TO_TUNER_MAPPING.keys():
raise ValueError(
f"`inject_adapter_in_model` does not support {peft_config.peft_type} yet. Please use `get_peft_model`."
)

tuner_cls = PEFT_TYPE_TO_TUNER_MAPPING[peft_config.peft_type]

# By instantiating a peft model we are injecting randomly initialized LoRA layers into the model's modules.
peft_model = tuner_cls(model, peft_config, adapter_name=adapter_name)
Copy link
Contributor

@patrickvonplaten patrickvonplaten Aug 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I went down the path correctly here it looks like this line will set required_grad=False for all weights of the model. For huggingface/transformers#25077 this would mean that if a load lora weights of a model all weights of the model (except the lora weights) are frozen - I would not expect such a behavior here.

Also it seems like the peft config decides further down the road here:

if self.peft_config[adapter_name].inference_mode:
if we're in inference mode or not - for the transformers implementation I would make try to not make any assumptions here depending on the config but leave it fully up to the user - so just not do anything (don't freeze any grads).

Can we add a flag here that would allow for this ?

Also I'd maybe add a comment to make it a bit easier for the user to understand what this line does.

Suggested change
peft_model = tuner_cls(model, peft_config, adapter_name=adapter_name)
# By instantiating a peft model we are injecting randomly initialized LoRA layers into the model's modules.
peft_model = tuner_cls(model, peft_config, adapter_name=adapter_name)

Copy link
Contributor Author

@younesbelkada younesbelkada Aug 3, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Freezing the base model should be optional when you load a pretrained adapter I think you are right

However when you attach fresh new adapters usually (for 99% of the usecases) it is to train them, so maybe we should make a distinction

1- load a pretrained adapter --> not necessearly freeze the base model
2- add a new random adapter --> I would expect users to train the adapters --> freeze the base model

WDYT? @pacman100 @BenjaminBossan

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That makes sense, I would maybe give the user full flexibility here then and add a new function argument:
freeze_weights=True/False/"non-lora-only" or something similar? I think when calling this from transformers we don't want all the model to be frozen, but for other use cases it could very much be the case I'd say

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1- load a pretrained adapter --> not necessearly freeze the base model
2- add a new random adapter --> I would expect users to train the adapters --> freeze the base model

That makes sense, I would maybe give the user full flexibility here then and add a new function argument

Personally, I would prefer to give the user an explicit argument, rather than (or in addition to) trying to guess based on the circumstances, so I would go with that option.

@patrickvonplaten: You bring up some good points. I will still merge the PR as is because Younes will be OoO for some time and we don't want this big refactor to become stale. We should be able to address your concerns in a follow up PR.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sounds good! Agree that giving the user an explicit argument is the right option here


return peft_model.model
Loading