1
from copy import deepcopy
2
from typing import Any, Dict, OrderedDict
3
from bigmodelvis import Visualization
5
from opendelta.utils.logging import get_logger
7
from opendelta.delta_configs import BaseDeltaConfig
8
from opendelta.basemodel import DeltaBase
9
logger = get_logger(__name__)
12
DELTA_CONFIG_MAPPING = {
14
"low_rank_adapter": "LowRankAdapterConfig",
15
"bitfit": "BitFitConfig",
16
"adapter":"AdapterConfig",
17
"compacter":"CompacterConfig",
18
"prefix": "PrefixConfig",
19
"soft_prompt": "SoftPromptConfig",
20
"parallel_adapter": "ParallelAdapterConfig",
23
DELTA_MODEL_MAPPING = {
25
"low_rank_adapter": "LowRankAdapterModel",
26
"bitfit": "BitFitModel",
27
"adapter":"AdapterModel",
28
"compacter": "CompacterModel",
29
"prefix": "PrefixModel",
30
"soft_prompt": "SoftPromptModel",
31
"parallel_adapter": "ParallelAdapterModel",
34
class _LazyConfigMapping(OrderedDict):
36
A dictionary that lazily load its values when they are requested.
39
def __init__(self, mapping):
40
self._mapping = mapping
41
self._extra_content = {}
44
def __getitem__(self, key):
45
if key in self._extra_content:
46
return self._extra_content[key]
47
if key not in self._mapping:
49
value = self._mapping[key]
52
self._modules[module_name] = importlib.import_module(f".{module_name}", "opendelta.delta_models")
53
return getattr(self._modules[module_name], value)
56
return list(self._mapping.keys()) + list(self._extra_content.keys())
59
return [self[k] for k in self._mapping.keys()] + list(self._extra_content.values())
62
return [(k, self[k]) for k in self._mapping.keys()] + list(self._extra_content.items())
65
return iter(list(self._mapping.keys()) + list(self._extra_content.keys()))
67
def __contains__(self, item):
68
return item in self._mapping or item in self._extra_content
70
def register(self, key, value):
72
Register a new configuration in this mapping.
74
if key in self._mapping.keys():
75
raise ValueError(f"'{key}' is already used by a Transformers config, pick another name.")
76
self._extra_content[key] = value
79
LAZY_CONFIG_MAPPING = _LazyConfigMapping(DELTA_CONFIG_MAPPING)
85
This is a generic configuration class that will be instantiated as one of the configuration classes of the library
86
when created with the :meth:`~AutoDeltaConfig.from_finetuned` or :meth:`~AutoDeltaConfig.from_dict` class method.
87
This class cannot be instantiated directly using ``__init__()`` (throws an error).
90
def __init__(self, *args, **kwargs):
92
f"{self.__class__.__name__} is designed to be instantiated using\n\t(1) `{self.__class__.__name__}.from_finetuned(finetuned_model_name_or_path)`\nor\t(2) `{self.__class__.__name__}.from_dict(config_dict, **kwargs)` "
96
def from_dict(cls, config_dict: Dict[str, Any], **kwargs):
97
r""" Instantiate a DeltaConfig according to the dict. Automatically load the config specified by
101
config_dict (:obj:`dict`): The dict of configs of delta model.
102
kwargs: Other keyword argument pass to initialize the config.
106
.. code-block:: python
108
config = AutoDeltaConfig.from_dict({"delta_type":"lora"}) # This will load the dault lora config.
109
config = AutoDeltaConfig.from_dict({"delta_type":"lora", "lora_r":5}) # Will load the default lora config, with lora_r = 5
112
config_dict = deepcopy(config_dict)
113
delta_type = config_dict.pop("delta_type", None)
114
if delta_type is None:
115
raise RuntimeError("Do not specify a delta type, cannot load the default config")
116
config_class = LAZY_CONFIG_MAPPING[delta_type]
117
return config_class.from_dict(config_dict, **kwargs)
121
def from_finetuned(cls, finetuned_delta_path, **kwargs):
123
Instantiate one of the configuration classes of the library from a finetuned delta model configuration.
124
The configuration class to instantiate is selected based on the ``delta_type`` property of the config object that
129
finetuned_delta_path (:obj:`str` or :obj:`os.PathLike`, *optional*): Can be either:
131
- A string, the model id of a finetuned delta model configuration hosted inside a model repo on huggingface.co. Valid model ids can be located at the root-level, like ``Davin/lora``, or namespaced under a user or organization name, like ``DeltaHub/lora_t5-base_mrpc``.
132
- A path to a *directory* containing a configuration file saved using the :py:meth:`~opendelta.basemodel.DeltaBase.save_finetuned` method, e.g., ``./my_model_directory/``.
133
- A path or url to a saved configuration JSON *file*, e.g.,``./my_model_directory/configuration.json``.
135
cache_dir (:obj:`str` or :obj:`os.PathLike`, *optional*):
136
Path to a directory in which a downloaded pretrained model configuration should be cached if the
137
standard cache should not be used.
141
.. code-block:: python
143
from transformers import AutoConfig
144
delta_config = AutoDeltaConfig.from_finetuned("thunlp/FactQA_T5-large_Adapter")
149
config_dict, kwargs = BaseDeltaConfig.get_config_dict(finetuned_delta_path, **kwargs)
150
if "delta_type" in config_dict:
151
config_class = LAZY_CONFIG_MAPPING[config_dict["delta_type"]]
152
return config_class.from_dict(config_dict, **kwargs)
155
for pattern, config_class in LAZY_CONFIG_MAPPING.items():
156
if pattern in str(finetuned_delta_path):
157
return config_class.from_dict(config_dict, **kwargs)
160
f"Unrecognized model in {finetuned_delta_path}. "
161
f"Should have a `delta_type` key in the loaded config, or contain one of the following strings "
162
f"in its name: {', '.join(LAZY_CONFIG_MAPPING.keys())}"
167
class _LazyAutoMapping(OrderedDict):
169
" A mapping config to object (model or tokenizer for instance) that will load keys and values when it is accessed.
173
- config_mapping: The map model type to config class
174
- model_mapping: The map model type to model (or tokenizer) class
177
def __init__(self, config_mapping, model_mapping):
178
self._config_mapping = config_mapping
179
self._reverse_config_mapping = {v: k for k, v in config_mapping.items()}
180
self._model_mapping = model_mapping
181
self._extra_content = {}
184
def __getitem__(self, key):
185
if key in self._extra_content:
186
return self._extra_content[key]
187
model_type = self._reverse_config_mapping[key.__name__]
188
if model_type not in self._model_mapping:
190
model_name = self._model_mapping[model_type]
191
return self._load_attr_from_module(model_type, model_name)
193
def _load_attr_from_module(self, model_type, attr):
194
if model_type not in self._modules:
195
self._modules[model_type] = importlib.import_module(f".{model_type}", "opendelta.delta_models")
196
return getattribute_from_module(self._modules[model_type], attr)
200
self._load_attr_from_module(key, name)
201
for key, name in self._config_mapping.items()
202
if key in self._model_mapping.keys()
204
return mapping_keys + list(self._extra_content.keys())
206
def get(self, key, default):
208
return self.__getitem__(key)
213
return bool(self.keys())
217
self._load_attr_from_module(key, name)
218
for key, name in self._model_mapping.items()
219
if key in self._config_mapping.keys()
221
return mapping_values + list(self._extra_content.values())
226
self._load_attr_from_module(key, self._config_mapping[key]),
227
self._load_attr_from_module(key, self._model_mapping[key]),
229
for key in self._model_mapping.keys()
230
if key in self._config_mapping.keys()
232
return mapping_items + list(self._extra_content.items())
235
return iter(self.keys())
237
def __contains__(self, item):
238
if item in self._extra_content:
240
if not hasattr(item, "__name__") or item.__name__ not in self._reverse_config_mapping:
242
model_type = self._reverse_config_mapping[item.__name__]
243
return model_type in self._model_mapping
245
def register(self, key, value):
247
Register a new model in this mapping.
249
if hasattr(key, "__name__") and key.__name__ in self._reverse_config_mapping:
250
model_type = self._reverse_config_mapping[key.__name__]
251
if model_type in self._model_mapping.keys():
252
raise ValueError(f"'{key}' is already used by a Transformers model.")
254
self._extra_content[key] = value
258
LAZY_DELTA_MAPPING = _LazyAutoMapping(DELTA_CONFIG_MAPPING, DELTA_MODEL_MAPPING)
262
def get_values(model_mapping):
264
for model in model_mapping.values():
265
if isinstance(model, (list, tuple)):
266
result += list(model)
273
def getattribute_from_module(module, attr):
276
if isinstance(attr, tuple):
277
return tuple(getattribute_from_module(module, a) for a in attr)
278
if hasattr(module, attr):
279
return getattr(module, attr)
282
transformers_module = importlib.import_module("transformers")
283
return getattribute_from_module(transformers_module, attr)
290
_delta_model_mapping = LAZY_DELTA_MAPPING
291
def __init__(self, *args, **kwargs):
298
raise AttributeError(
299
f"{self.__class__.__name__} is designed to be instantiated using\n\t(1) `{self.__class__.__name__}.from_finetuned(finetuned_delta_path, backbone_model, *model_args, **kwargs)`\nor\t(2) `{self.__class__.__name__}.from_config(delta_config, backbone_model, **kwargs)`"
303
def from_config(cls, config, backbone_model, **kwargs) -> DeltaBase:
304
r"""Automatically instantiates a delta model based on the :obj:`config`. The delta model correspond to the delta
305
:obj:`config` will be loaded and initialized using the arguments in :obj:`config`.
308
Only using :meth:`from_config` method will not load the finetuned weight file (e.g., pytorch_model.bin).
309
Please use from_finetuned directly.
312
config (:obj:`BaseDeltaConfig`):
313
backbone_model (:obj:`nn.Module`):
317
.. code-block:: python
319
config = AutoDeltaConfig.from_finetuned("DeltaHub/lora_t5-base_mrpc")
320
delta_model = AutoDeltaModel.from_config(config, backbone_model)
323
if type(config) in cls._delta_model_mapping.keys():
324
model_class = cls._delta_model_mapping[type(config)]
325
return model_class.from_config(config, backbone_model, **kwargs)
328
f"Unrecognized configuration class {config.__class__} for this kind of AutoModel: {cls.__name__}.\n"
329
f"Model type should be one of {', '.join(c.__name__ for c in cls._delta_model_mapping.keys())}."
333
def from_finetuned(cls, finetuned_delta_path, backbone_model, *model_args, **kwargs) -> DeltaBase:
334
r""" Automatically instantiated a delta model and load the finetuned checkpoints based on the
335
:obj:`finetuned_delta_path`, which can either be a string pointing to a local path or a url pointint to
336
the delta hub. It will check the hash after loading the delta model to see whether the correct backbone and
337
delta checkpoint are used.
340
finetuned_delta_path (:obj:`str` or :obj:`os.PathLike`, *optional*): Can be either:
342
- A string, the model name of a finetuned delta model configuration hosted inside a model repo on `Delta Center <https://www.openbmb.org/toolKits/deltacenter>`_, like ``thunlp/FactQA_T5-large_Adapter``.
343
- A path to a directory containing a configuration file saved using the :meth:`~opendelta.utils.saving_loading_utils.SaveLoadMixin.save_finetuned` method, e.g., ``./my_model_directory/``.
344
- A path or url to a saved configuration JSON *file*, e.g., ``./my_model_directory/configuration.json``.The last two option are not tested but inherited from huggingface.
346
backbone_model (:obj:`nn.Module`): The backbone model to be modified.
347
model_args: Other argument for initialize the model. See :`DeltaBase.from_finetuned` for details.
348
kwargs: Other kwargs that will be passed into DeltaBase.from_finetuned. See `DeltaBase.from_finetuned` for details.
352
.. code-block:: python
354
delta_model = AutoDeltaModel.from_finetuned("thunlp/FactQA_T5-large_Adapter", backbone_model=5)
357
delta_config = kwargs.pop("delta_config", None)
359
if not isinstance(delta_config, BaseDeltaConfig):
360
delta_config, kwargs = AutoDeltaConfig.from_finetuned(
361
finetuned_delta_path, return_unused_kwargs=True, **kwargs
363
if type(delta_config) in cls._delta_model_mapping.keys():
364
model_class = cls._delta_model_mapping[type(delta_config)]
365
return model_class.from_finetuned(finetuned_delta_path, backbone_model, *model_args, delta_config=delta_config, **kwargs)
367
f"Unrecognized configuration class {config.__class__} for this kind of AutoModel: {cls.__name__}.\n"
368
f"Model type should be one of {', '.join(c.__name__ for c in cls._model_mapping.keys())}."
375
if __name__ == "__main__":
377
config = AutoDeltaConfig.from_dict({"delta_type":"lora", "lora_r": 7})
380
from transformers import AutoModelForSequenceClassification
381
model = AutoModelForSequenceClassification.from_pretrained("../../plm_cache/roberta-base/", num_labels=2)
383
delta_model = AutoDeltaModel.from_config(config, model)
384
delta_model.freeze_module(exclude = ['deltas','classifier'], set_state_dict = True)
388
delta_model = AutoDeltaModel.from_finetuned("ShengdingHu/autodelta_try", model, use_auth_token=True)