Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
56 changes: 46 additions & 10 deletions src/transformers/models/auto/auto_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@
from huggingface_hub import repo_exists

from ...configuration_utils import PreTrainedConfig
from ...dynamic_module_utils import get_class_from_dynamic_module, resolve_trust_remote_code
from ...dynamic_module_utils import (
get_class_from_dynamic_module,
resolve_trust_remote_code,
)
from ...utils import (
CONFIG_NAME,
cached_file,
Expand All @@ -36,7 +39,11 @@
logging,
requires_backends,
)
from .configuration_auto import AutoConfig, model_type_to_module_name, replace_list_option_in_docstrings
from .configuration_auto import (
AutoConfig,
model_type_to_module_name,
replace_list_option_in_docstrings,
)


if is_torch_available():
Expand Down Expand Up @@ -206,6 +213,7 @@ def __init__(self, *args, **kwargs) -> None:
@classmethod
def from_config(cls, config, **kwargs):
trust_remote_code = kwargs.pop("trust_remote_code", None)
_ = kwargs.pop("prefer_auto_map", None)
has_remote_code = hasattr(config, "auto_map") and cls.__name__ in config.auto_map
has_local_code = type(config) in cls._model_mapping
explicit_local_code = has_local_code and not _get_model_class(
Expand All @@ -218,7 +226,11 @@ def from_config(cls, config, **kwargs):
else:
upstream_repo = None
trust_remote_code = resolve_trust_remote_code(
trust_remote_code, config._name_or_path, has_local_code, has_remote_code, upstream_repo=upstream_repo
trust_remote_code,
config._name_or_path,
has_local_code,
has_remote_code,
upstream_repo=upstream_repo,
)

if has_remote_code and trust_remote_code and not explicit_local_code:
Expand Down Expand Up @@ -261,8 +273,14 @@ def _prepare_config_for_auto_class(cls, config: PreTrainedConfig) -> PreTrainedC
return config

@classmethod
def from_pretrained(cls, pretrained_model_name_or_path: str | os.PathLike[str], *model_args, **kwargs):
def from_pretrained(
cls,
pretrained_model_name_or_path: str | os.PathLike[str],
*model_args,
**kwargs,
):
config = kwargs.pop("config", None)
prefer_auto_map = kwargs.pop("prefer_auto_map", False)
trust_remote_code = kwargs.get("trust_remote_code")
kwargs["_from_auto"] = True
hub_kwargs_names = [
Expand Down Expand Up @@ -307,7 +325,9 @@ def from_pretrained(cls, pretrained_model_name_or_path: str | os.PathLike[str],
adapter_kwargs["token"] = token

maybe_adapter_path = find_adapter_config_file(
pretrained_model_name_or_path, _commit_hash=commit_hash, **adapter_kwargs
pretrained_model_name_or_path,
_commit_hash=commit_hash,
**adapter_kwargs,
)

if maybe_adapter_path is not None:
Expand Down Expand Up @@ -341,6 +361,7 @@ def from_pretrained(cls, pretrained_model_name_or_path: str | os.PathLike[str],
return_unused_kwargs=True,
code_revision=code_revision,
_commit_hash=commit_hash,
prefer_auto_map=prefer_auto_map,
**hub_kwargs,
**kwargs,
)
Expand Down Expand Up @@ -377,7 +398,11 @@ def from_pretrained(cls, pretrained_model_name_or_path: str | os.PathLike[str],

if has_remote_code and trust_remote_code and not explicit_local_code:
model_class = get_class_from_dynamic_module(
class_ref, pretrained_model_name_or_path, code_revision=code_revision, **hub_kwargs, **kwargs
class_ref,
pretrained_model_name_or_path,
code_revision=code_revision,
**hub_kwargs,
**kwargs,
)
_ = hub_kwargs.pop("code_revision", None)
# This block handles the case where the user is loading a model with `trust_remote_code=True`
Expand All @@ -388,7 +413,11 @@ def from_pretrained(cls, pretrained_model_name_or_path: str | os.PathLike[str],
model_class.register_for_auto_class(auto_class=cls)
model_class = add_generation_mixin_to_remote_model(model_class)
return model_class.from_pretrained(
pretrained_model_name_or_path, *model_args, config=config, **hub_kwargs, **kwargs
pretrained_model_name_or_path,
*model_args,
config=config,
**hub_kwargs,
**kwargs,
)
elif has_local_code:
model_class = _get_model_class(config, cls._model_mapping)
Expand All @@ -403,7 +432,11 @@ def from_pretrained(cls, pretrained_model_name_or_path: str | os.PathLike[str],
if parent_quant is not None:
config.quantization_config = parent_quant
return model_class.from_pretrained(
pretrained_model_name_or_path, *model_args, config=config, **hub_kwargs, **kwargs
pretrained_model_name_or_path,
*model_args,
config=config,
**hub_kwargs,
**kwargs,
)
raise ValueError(
f"Unrecognized configuration class {config.__class__} for this kind of AutoModel: {cls.__name__}.\n"
Expand Down Expand Up @@ -476,7 +509,8 @@ def insert_head_doc(docstring, head_doc: str = ""):
f"one of the model classes of the library (with a {head_doc} head) ",
)
return docstring.replace(
"one of the model classes of the library ", "one of the base model classes of the library "
"one of the model classes of the library ",
"one of the base model classes of the library ",
)


Expand Down Expand Up @@ -569,7 +603,9 @@ def add_generation_mixin_to_remote_model(model_class):
)
if has_custom_generate_in_class or has_custom_prepare_inputs:
model_class_with_generation_mixin = type(
model_class.__name__, (model_class, GenerationMixin), {**model_class.__dict__}
model_class.__name__,
(model_class, GenerationMixin),
{**model_class.__dict__},
)
return model_class_with_generation_mixin
return model_class
Expand Down
29 changes: 25 additions & 4 deletions src/transformers/models/auto/configuration_auto.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,10 @@
from typing import Any, TypeVar

from ...configuration_utils import PreTrainedConfig
from ...dynamic_module_utils import get_class_from_dynamic_module, resolve_trust_remote_code
from ...dynamic_module_utils import (
get_class_from_dynamic_module,
resolve_trust_remote_code,
)
from ...utils import CONFIG_NAME, logging
from .auto_mappings import CONFIG_MAPPING_NAMES, SPECIAL_MODEL_TYPE_TO_MODULE_NAME

Expand Down Expand Up @@ -329,6 +332,10 @@ def from_pretrained(cls, pretrained_model_name_or_path: str | os.PathLike[str],
Whether or not to allow for custom models defined on the Hub in their own modeling files. This option
should only be set to `True` for repositories you trust and in which you have read the code, as it will
execute code present on the Hub on your local machine.
prefer_auto_map (`bool`, *optional*, defaults to `False`):
When `True` and `trust_remote_code=True`, prefer the checkpoint's `auto_map`
code over an explicitly registered local class for the same `model_type`.
By default, explicitly registered local classes keep precedence.
kwargs(additional keyword arguments, *optional*):
The values in kwargs of any keys which are configuration attributes will be used to override the loaded
values. Behavior concerning key/value pairs whose keys are *not* configuration attributes is controlled
Expand Down Expand Up @@ -370,26 +377,40 @@ def from_pretrained(cls, pretrained_model_name_or_path: str | os.PathLike[str],
kwargs["name_or_path"] = pretrained_model_name_or_path
trust_remote_code = kwargs.pop("trust_remote_code", None)
code_revision = kwargs.pop("code_revision", None)
prefer_auto_map = kwargs.pop("prefer_auto_map", False)

config_dict, unused_kwargs = PreTrainedConfig.get_config_dict(pretrained_model_name_or_path, **kwargs)
has_remote_code = "auto_map" in config_dict and "AutoConfig" in config_dict["auto_map"]
has_local_code = "model_type" in config_dict and config_dict["model_type"] in CONFIG_MAPPING
explicit_local_code = has_local_code and not CONFIG_MAPPING[config_dict["model_type"]].__module__.startswith(
"transformers."
)

path = os.fspath(pretrained_model_name_or_path)
if os.path.isfile(path):
dynamic_module_source = os.path.dirname(path) or os.curdir
else:
dynamic_module_source = pretrained_model_name_or_path

if has_remote_code:
class_ref = config_dict["auto_map"]["AutoConfig"]
if "--" in class_ref:
upstream_repo = class_ref.split("--")[0]
else:
upstream_repo = None
trust_remote_code = resolve_trust_remote_code(
trust_remote_code, pretrained_model_name_or_path, has_local_code, has_remote_code, upstream_repo
trust_remote_code,
dynamic_module_source,
has_local_code,
has_remote_code,
upstream_repo=upstream_repo,
)

if has_remote_code and trust_remote_code and not explicit_local_code:
should_prefer_auto_map = prefer_auto_map and explicit_local_code

if has_remote_code and trust_remote_code and (should_prefer_auto_map or not explicit_local_code):
config_class = get_class_from_dynamic_module(
class_ref, pretrained_model_name_or_path, code_revision=code_revision, **kwargs
class_ref, dynamic_module_source, code_revision=code_revision, **kwargs
)
config_class.register_for_auto_class()
return config_class.from_pretrained(pretrained_model_name_or_path, **kwargs)
Expand Down
97 changes: 94 additions & 3 deletions tests/models/auto/test_configuration_auto.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@

from test_module.custom_configuration import CustomConfig # noqa E402


SAMPLE_ROBERTA_CONFIG = get_tests_dir("fixtures/dummy-config.json")


Expand Down Expand Up @@ -83,13 +82,15 @@ def test_new_config_registration(self):

def test_repo_not_found(self):
with self.assertRaisesRegex(
EnvironmentError, "bert-base is not a local folder and is not a valid model identifier"
EnvironmentError,
"bert-base is not a local folder and is not a valid model identifier",
):
_ = AutoConfig.from_pretrained("bert-base")

def test_revision_not_found(self):
with self.assertRaisesRegex(
EnvironmentError, r"aaaaaa is not a valid git identifier \(branch name, tag name or commit id\)"
EnvironmentError,
r"aaaaaa is not a valid git identifier \(branch name, tag name or commit id\)",
):
_ = AutoConfig.from_pretrained(DUMMY_UNKNOWN_IDENTIFIER, revision="aaaaaa")

Expand Down Expand Up @@ -117,6 +118,74 @@ def test_from_pretrained_dynamic_config(self):
self.assertEqual(reloaded_config.auto_map["AutoConfig"], "configuration.NewModelConfig")
self.assertEqual(reloaded_config.__class__.__name__, "NewModelConfig")

def test_from_pretrained_saved_dynamic_config_conflict_prefers_local_by_default(
self,
):
class NewModelConfigLocal(BertConfig):
model_type = "new-model"
local_only_marker = True

def __init__(self, **kwargs):
super().__init__(**kwargs)

try:
config = AutoConfig.from_pretrained(
"hf-internal-testing/test_dynamic_model",
trust_remote_code=True,
)
with tempfile.TemporaryDirectory() as tmp_dir:
config.save_pretrained(tmp_dir)
AutoConfig.register("new-model", NewModelConfigLocal)
reloaded_config = AutoConfig.from_pretrained(
tmp_dir,
trust_remote_code=True,
)
self.assertIs(reloaded_config.__class__, NewModelConfigLocal)
self.assertTrue(getattr(reloaded_config.__class__, "local_only_marker", False))
finally:
if "new-model" in CONFIG_MAPPING._extra_content:
del CONFIG_MAPPING._extra_content["new-model"]

def test_from_pretrained_saved_dynamic_config_conflict_prefer_auto_map_from_custom_config_file(
self,
):
class NewModelConfigLocal(BertConfig):
model_type = "new-model"
local_only_marker = True

def __init__(self, **kwargs):
super().__init__(**kwargs)

try:
config = AutoConfig.from_pretrained(
"hf-internal-testing/test_dynamic_model",
trust_remote_code=True,
)
with tempfile.TemporaryDirectory() as tmp_dir:
config.save_pretrained(tmp_dir)
custom_config_path = os.path.join(tmp_dir, "custom_config.json")
os.replace(os.path.join(tmp_dir, "config.json"), custom_config_path)

self.assertFalse(os.path.exists(os.path.join(tmp_dir, "config.json")))
self.assertTrue(os.path.exists(custom_config_path))

AutoConfig.register("new-model", NewModelConfigLocal)
reloaded_config = AutoConfig.from_pretrained(
custom_config_path,
trust_remote_code=True,
prefer_auto_map=True,
)
self.assertIsNot(reloaded_config.__class__, NewModelConfigLocal)
self.assertFalse(getattr(reloaded_config.__class__, "local_only_marker", False))
self.assertEqual(
reloaded_config.auto_map["AutoConfig"],
"configuration.NewModelConfig",
)
self.assertEqual(reloaded_config.__class__.__name__, "NewModelConfig")
finally:
if "new-model" in CONFIG_MAPPING._extra_content:
del CONFIG_MAPPING._extra_content["new-model"]

def test_from_pretrained_dynamic_config_conflict(self):
class NewModelConfigLocal(BertConfig):
model_type = "new-model"
Expand Down Expand Up @@ -147,6 +216,28 @@ def __init__(self, **kwargs):
if "new-model" in CONFIG_MAPPING._extra_content:
del CONFIG_MAPPING._extra_content["new-model"]

def test_from_pretrained_dynamic_config_conflict_prefer_auto_map(self):
class NewModelConfigLocal(BertConfig):
model_type = "new-model"
local_only_marker = True

def __init__(self, **kwargs):
super().__init__(**kwargs)

try:
AutoConfig.register("new-model", NewModelConfigLocal)
reloaded_config = AutoConfig.from_pretrained(
"hf-internal-testing/test_dynamic_model",
trust_remote_code=True,
prefer_auto_map=True,
)
self.assertIsNot(reloaded_config.__class__, NewModelConfigLocal)
self.assertFalse(getattr(reloaded_config.__class__, "local_only_marker", False))
self.assertEqual(reloaded_config.__class__.__name__, "NewModelConfig")
finally:
if "new-model" in CONFIG_MAPPING._extra_content:
del CONFIG_MAPPING._extra_content["new-model"]

def test_config_missing_model_type(self):
with tempfile.TemporaryDirectory() as tmp_dir:
config_dict = {
Expand Down
Loading
Loading