Skip to content
Merged
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
6 changes: 5 additions & 1 deletion src/transformers/configuration_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,9 @@ def __post_init__(self, **kwargs):
# Remap layer types if needed
remap_legacy_layer_types(config=self)

# Remote code assigns `rope_scaling` before init finishes, so the setter only standardizes after this
self._post_init_done = True

def __init_subclass__(cls, *args, **kwargs):
super().__init_subclass__(*args, **kwargs)

Expand Down Expand Up @@ -723,7 +726,7 @@ def rope_scaling(self, value):
FutureWarning,
)
self.rope_parameters = value
if value is not None:
if value is not None and getattr(self, "_post_init_done", False):

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I hate this field 😭

yeah, so the goal is to allow re-setting field via rope_scaling name on local/new models in unified format and keep remote code happy by allowing them set whatever they want
Maybe we can standardize only if getattr rope_parameters is not None

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I don't think so. The issue is that we are validating rope too eagerly before custom configs have had the chance to set everything.

i.e. the following snippet raises an error with that suggestion:

class MyConfig(PreTrainedConfig):
    def __init__(...):
        self.rope_scaling = {...}
        self.__post_init__()

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

iiuc these remote models they don't yet have any rope param set in init anyway, no?
Just don't want to add another private field to track if initialisation is done

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

They do. Here's another example https://huggingface.co/microsoft/Phi-3.5-vision-instruct/blob/main/configuration_phi3_v.py#L166-L167L

    def __init__(
        ...
    ):
        self.rope_scaling = rope_scaling
        self._rope_scaling_validation()

That validation method checks the number of keys in rope_scaling, and eagerly calling standardize_rope_params breaks that.

self.standardize_rope_params()

def save_pretrained(self, save_directory: str | os.PathLike, push_to_hub: bool = False, **kwargs):
Expand Down Expand Up @@ -1414,6 +1417,7 @@ def _remove_keys_not_serialized(self, d: dict[str, Any]) -> None:
"_commit_hash",
"_attn_implementation_internal",
"_experts_implementation_internal",
"_post_init_done",
"ignore_keys_at_rope_validation",
"base_model_tp_plan",
"base_model_pp_plan",
Expand Down
1 change: 1 addition & 0 deletions tests/utils/test_configuration_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ def test_config_common_kwargs_is_complete(self):
"_output_attentions",
"_attn_implementation_internal",
"_experts_implementation_internal",
"_post_init_done",
],
)
keys_with_defaults = [key for key, value in config_common_kwargs.items() if value == getattr(base_config, key)]
Expand Down
20 changes: 19 additions & 1 deletion tests/utils/test_modeling_rope_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@

from parameterized import parameterized

from transformers import Gemma3TextConfig, LlamaConfig
from transformers import Gemma3TextConfig, LlamaConfig, PreTrainedConfig
from transformers.testing_utils import CaptureLogger, is_torch_available, require_torch, torch_device
from transformers.utils import logging as transformers_logging

Expand Down Expand Up @@ -53,6 +53,24 @@ def test_rope_scaling_setter_is_deprecated(self):
self.assertEqual(config.rope_parameters["rope_type"], "yarn")
LlamaRotaryEmbedding(config)

def test_rope_scaling_setter_during_init(self):
# Mimics remote code (e.g. Phi-3) that sets and validates `rope_scaling` before the rest of the config
class RemoteConfig(PreTrainedConfig):
model_type = "remote"

def __init__(self, rope_scaling=None, rope_theta=10000.0, **kwargs):
self.rope_scaling = rope_scaling
if set(self.rope_scaling) != {"type", "factor"}:
raise ValueError(f"Unexpected `rope_scaling`: {self.rope_scaling}")
self.rope_theta = rope_theta
super().__init__(**kwargs)

with warnings.catch_warnings():
warnings.simplefilter("ignore", FutureWarning)
config = RemoteConfig(rope_scaling={"type": "dynamic", "factor": 2.0})
self.assertEqual(config.rope_parameters["rope_type"], "dynamic")
self.assertEqual(config.rope_parameters["rope_theta"], 10000.0)

def test_rope_scaling_setter_accepts_none(self):
config = LlamaConfig(rope_theta=500000.0)
with warnings.catch_warnings(record=True) as caught:
Expand Down
Loading