Skip to content

Commit 2fa1fdd

Browse files
authored
🚨 Remove imports from other models (#49473)
* Remove imports from other models * remove imports in more cases
1 parent 792abab commit 2fa1fdd

9 files changed

Lines changed: 141 additions & 36 deletions

File tree

‎src/transformers/models/embedding_gemma2/modular_embedding_gemma2.py‎

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
TransformersKwargs,
3737
auto_docstring,
3838
can_return_tuple,
39-
is_vision_available,
4039
logging,
4140
torch_compilable_check,
4241
)
@@ -55,14 +54,11 @@
5554
eager_attention_forward,
5655
)
5756
from ..gemma4.processing_gemma4 import Gemma4Processor, Gemma4ProcessorKwargs
58-
from ..gemma4.video_processing_gemma4 import Gemma4VideoProcessor, Gemma4VideoProcessorKwargs
59-
60-
61-
if is_vision_available():
62-
from ..gemma4.image_processing_gemma4 import (
63-
Gemma4ImageProcessorKwargs,
64-
get_aspect_ratio_preserving_size, # noqa: F401 # trf-ignore: TRF039
65-
)
57+
from ..gemma4.video_processing_gemma4 import (
58+
Gemma4VideoProcessor,
59+
Gemma4VideoProcessorKwargs,
60+
get_aspect_ratio_preserving_size, # noqa: F401
61+
)
6662

6763

6864
logger = logging.get_logger(__name__)
@@ -845,7 +841,8 @@ def sample_frames(
845841

846842

847843
class EmbeddingGemma2ProcessorKwargs(Gemma4ProcessorKwargs):
848-
images_kwargs: Gemma4ImageProcessorKwargs
844+
images_kwargs = AttributeError()
845+
849846
_defaults = {
850847
"text_kwargs": {
851848
"padding": True,

‎src/transformers/models/embedding_gemma2/processing_embedding_gemma2.py‎

Lines changed: 56 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
# See the License for the specific language governing permissions and
1919
# limitations under the License.
2020

21+
import math
22+
2123
import numpy as np
2224

2325
from ...audio_utils import AudioInput, is_valid_audio
@@ -26,18 +28,12 @@
2628
from ...tokenization_utils_base import PreTokenizedInput, TextInput
2729
from ...utils import (
2830
auto_docstring,
29-
is_vision_available,
3031
)
3132
from ...utils.import_utils import requires
3233
from ...video_utils import VideoInput, is_valid_video, make_batched_videos
3334

3435

35-
if is_vision_available():
36-
from ..gemma4.image_processing_gemma4 import Gemma4ImageProcessorKwargs, get_aspect_ratio_preserving_size
37-
38-
3936
class EmbeddingGemma2ProcessorKwargs(ProcessingKwargs, total=False):
40-
images_kwargs: Gemma4ImageProcessorKwargs
4137
_defaults = {
4238
"text_kwargs": {
4339
"padding": True,
@@ -50,6 +46,60 @@ class EmbeddingGemma2ProcessorKwargs(ProcessingKwargs, total=False):
5046
}
5147

5248

49+
def get_aspect_ratio_preserving_size(
50+
height: int,
51+
width: int,
52+
patch_size: int,
53+
max_patches: int,
54+
pooling_kernel_size: int,
55+
) -> tuple[int, int]:
56+
"""
57+
Image is resized to preserve aspect ratio so it fits within the patch budget.
58+
Target dimensions are the largest that:
59+
1) Produce at most `max_patches` patches when patchified with `patch_size`
60+
2) Have height and width divisible by `pooling_kernel_size * patch_size`
61+
"""
62+
total_px = height * width
63+
target_px = max_patches * (patch_size**2)
64+
factor = math.sqrt(target_px / total_px)
65+
ideal_height = factor * height
66+
ideal_width = factor * width
67+
side_mult = pooling_kernel_size * patch_size
68+
69+
# Round down to nearest multiple of side_mult
70+
target_height = int(math.floor(ideal_height / side_mult)) * side_mult
71+
target_width = int(math.floor(ideal_width / side_mult)) * side_mult
72+
73+
# Handle edge cases where one or both dimensions round to 0
74+
if target_height == 0 and target_width == 0:
75+
raise ValueError(
76+
"Attempting to resize to a 0 x 0 image. Resized height should be divisible by "
77+
f"`pooling_kernel_size * patch_size`={pooling_kernel_size * patch_size}."
78+
)
79+
80+
max_side_length = (max_patches // pooling_kernel_size**2) * side_mult
81+
if target_height == 0:
82+
target_height = side_mult
83+
target_width = min(
84+
int(math.floor(width / height)) * side_mult,
85+
max_side_length,
86+
)
87+
elif target_width == 0:
88+
target_width = side_mult
89+
target_height = min(
90+
int(math.floor(height / width)) * side_mult,
91+
max_side_length,
92+
)
93+
94+
if target_height * target_width > target_px:
95+
raise ValueError(
96+
f"Resizing [{height}x{width}] to [{target_height}x{target_width}] "
97+
f"but this exceeds {max_patches} patches with patch_size {patch_size}"
98+
)
99+
100+
return target_height, target_width
101+
102+
53103
@auto_docstring
54104
@requires(backends=("vision",))
55105
class EmbeddingGemma2Processor(ProcessorMixin):

‎src/transformers/models/ijepa/modular_ijepa.py‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
import torch
22
import torch.nn as nn
33

4-
from transformers.models.ijepa.configuration_ijepa import IJepaConfig
5-
64
from ... import initialization as init
75
from ...modeling_outputs import BaseModelOutputWithPooling, ImageClassifierOutput
86
from ...modeling_utils import PreTrainedModel
97
from ...processing_utils import Unpack
108
from ...utils import TransformersKwargs, auto_docstring, torch_int
119
from ..vit.modeling_vit import ViTEmbeddings, ViTForImageClassification, ViTModel, ViTPreTrainedModel
10+
from .configuration_ijepa import IJepaConfig
1211

1312

1413
class IJepaEmbeddings(ViTEmbeddings):

‎src/transformers/models/lightglue/configuration_lightglue.py‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@
2323
from ...configuration_utils import PreTrainedConfig, SubConfigSpec
2424
from ...utils import auto_docstring
2525
from ..auto import AutoConfig
26-
from ..superpoint import SuperPointConfig
2726

2827

2928
@auto_docstring(checkpoint="ETH-CVG/lightglue_superpoint")
@@ -61,7 +60,7 @@ class LightGlueConfig(PreTrainedConfig):
6160
"keypoint_detector_config": SubConfigSpec(config_class=AutoConfig, model_type="superpoint"),
6261
}
6362

64-
keypoint_detector_config: dict | SuperPointConfig | None = None
63+
keypoint_detector_config: dict | PreTrainedConfig | None = None
6564
descriptor_dim: int = 256
6665
num_hidden_layers: int = 9
6766
num_attention_heads: int = 4

‎src/transformers/models/lightglue/modular_lightglue.py‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@
3535
from ..llama.modeling_llama import LlamaAttention, eager_attention_forward
3636
from ..superglue.image_processing_pil_superglue import SuperGlueImageProcessorPil
3737
from ..superglue.image_processing_superglue import SuperGlueImageProcessor, SuperGlueImageProcessorKwargs
38-
from ..superpoint import SuperPointConfig
3938

4039

4140
logger = logging.get_logger(__name__)
@@ -76,7 +75,7 @@ class LightGlueConfig(PreTrainedConfig):
7675
"keypoint_detector_config": SubConfigSpec(config_class=AutoConfig, model_type="superpoint"),
7776
}
7877

79-
keypoint_detector_config: dict | SuperPointConfig | None = None
78+
keypoint_detector_config: dict | PreTrainedConfig | None = None
8079
descriptor_dim: int = 256
8180
num_hidden_layers: int = 9
8281
num_attention_heads: int = 4

‎src/transformers/models/nemotron_h/modeling_nemotron_h.py‎

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,6 @@
4242
from ...modeling_layers import GradientCheckpointingLayer
4343
from ...modeling_outputs import MoeCausalLMOutputWithPast, MoeModelOutputWithPast
4444
from ...modeling_utils import ALL_ATTENTION_FUNCTIONS, PreTrainedModel
45-
from ...models.zamba2.modeling_zamba2 import Zamba2RMSNormGated
4645
from ...processing_utils import Unpack
4746
from ...utils import TransformersKwargs, auto_docstring, can_return_tuple, logging
4847
from ...utils.generic import merge_with_config_defaults
@@ -53,6 +52,27 @@
5352
logger = logging.get_logger(__name__)
5453

5554

55+
class NemotronHRMSNormGated(torch.nn.Module):
56+
def __init__(self, hidden_size, group_size, eps=1e-6):
57+
super().__init__()
58+
self.weight = nn.Parameter(torch.ones(hidden_size))
59+
self.variance_epsilon = eps
60+
self.group_size = group_size
61+
62+
def forward(self, hidden_states, gate=None):
63+
input_dtype = hidden_states.dtype
64+
hidden_states = hidden_states.to(torch.float32)
65+
if gate is not None:
66+
hidden_states = hidden_states * nn.functional.silu(gate.to(torch.float32))
67+
*prefix_dims, last_dim = hidden_states.shape
68+
group_count = last_dim // self.group_size
69+
hidden_states_group = hidden_states.view(*prefix_dims, group_count, self.group_size)
70+
variance = hidden_states_group.pow(2).mean(-1, keepdim=True)
71+
hidden_states_group = hidden_states_group * torch.rsqrt(variance + self.variance_epsilon)
72+
hidden_states = hidden_states_group.view(*prefix_dims, group_count * self.group_size)
73+
return self.weight * hidden_states.to(input_dtype)
74+
75+
5676
# Helper methods for segment sum computation
5777

5878

@@ -418,12 +438,12 @@ def __init__(self, config: NemotronHConfig, layer_idx: int | None = None, initia
418438
# S4D real initialization. These are not discretized!
419439
# The core is to load them, compute the discrete states, then write the updated state. Keeps the memory bounded
420440
self.A_log = nn.Parameter(torch.empty(self.num_heads))
421-
self.norm = Zamba2RMSNormGated(
441+
self.norm = NemotronHRMSNormGated(
422442
self.intermediate_size, group_size=self.intermediate_size // self.n_groups, eps=config.layer_norm_epsilon
423443
)
424444
self.D = nn.Parameter(torch.empty(self.num_heads))
425445
if initialize_mixer_weights and self.dt_bias.device.type != "meta":
426-
self.init_nemotron_h_mamba2_weights()
446+
self.init_nemotron_h_weights()
427447
self.out_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=config.use_bias)
428448

429449
self.layer_type = config.layer_types[layer_idx]
@@ -436,7 +456,7 @@ def __init__(self, config: NemotronHConfig, layer_idx: int | None = None, initia
436456
self.use_mem_eff_path = True
437457

438458
@torch.no_grad()
439-
def init_nemotron_h_mamba2_weights(self):
459+
def init_nemotron_h_weights(self):
440460
A = torch.arange(1, self.num_heads + 1, device=self.A_log.device, dtype=torch.float32)
441461
init.copy_(self.A_log, torch.log(A))
442462
init.ones_(self.D)

‎src/transformers/models/nemotron_h/modular_nemotron_h.py‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@
4444
logger = logging.get_logger(__name__)
4545

4646

47+
class NemotronHRMSNormGated(Zamba2RMSNormGated):
48+
pass
49+
50+
4751
class NemotronHMamba2Mixer(Zamba2MambaMixer):
4852
def __init__(self, config: NemotronHConfig, layer_idx: int | None = None, initialize_mixer_weights: bool = True):
4953
super().__init__(config, layer_idx, initialize_mixer_weights)
@@ -74,7 +78,7 @@ def __init__(self, config: NemotronHConfig, layer_idx: int | None = None, initia
7478
projection_size,
7579
bias=config.use_bias,
7680
)
77-
self.norm = Zamba2RMSNormGated(
81+
self.norm = NemotronHRMSNormGated(
7882
self.intermediate_size, group_size=self.intermediate_size // self.n_groups, eps=config.layer_norm_epsilon
7983
)
8084
self.out_proj = nn.Linear(self.intermediate_size, self.hidden_size, bias=config.use_bias)

‎src/transformers/models/sam3_tracker/modular_sam3_tracker.py‎

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@
2020
from ...configuration_utils import SubConfigSpec
2121
from ...modeling_utils import PreTrainedModel
2222
from ...utils import auto_docstring
23-
from ..auto import AutoModel
24-
from ..auto.configuration_auto import AutoConfig
23+
from ..auto import AutoConfig, AutoModel
2524
from ..sam2.configuration_sam2 import (
2625
Sam2Config,
2726
Sam2MaskDecoderConfig,

‎utils/modular_model_converter.py‎

Lines changed: 45 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@
2626
from create_dependency_mapping import find_priority_list
2727
from libcst import ClassDef, CSTVisitor
2828
from libcst import matchers as m
29+
from libcst.helpers import get_absolute_module_from_package_for_import
2930
from libcst.metadata import MetadataWrapper, ScopeProvider
3031
from modular_integrations import (
3132
EXCLUDED_EXTERNAL_FILES,
@@ -1221,7 +1222,30 @@ def find_file_type(class_name: str, model_name: str) -> str:
12211222
)
12221223

12231224
# These specific modeling imports should not be visited as other modeling files
1224-
IMPORTS_TO_SKIP_IN_MODULAR = ("auto.modeling_auto",)
1225+
MODEL_NAMES_TO_SKIP_IN_MODULAR = {"auto"}
1226+
1227+
1228+
# Stands for the model of the file containing the import, e.g. the current model directory name
1229+
# in `from .configuration_llama import LlamaConfig`
1230+
CURRENT_MODEL = "__current_model__"
1231+
1232+
1233+
def get_imported_model_name(node: cst.ImportFrom) -> str | None:
1234+
"""Return the model directory `node` imports from, e.g. `llama` for `from ..llama.modeling_llama import X`,
1235+
`CURRENT_MODEL` for `from .configuration_llama import LlamaConfig`, or None if it does not import from a model."""
1236+
absolute_name = get_absolute_module_from_package_for_import(f"transformers.models.{CURRENT_MODEL}", node)
1237+
match = re.search(r"(?:^|\.)transformers\.models\.(\w+)", absolute_name or "")
1238+
return match.group(1) if match is not None else None
1239+
1240+
1241+
def is_import_from_other_model(node: cst.CSTNode) -> bool:
1242+
"""Whether `node` is an import statement from another model directory than the current model or MODEL_NAMES_TO_SKIP_IN_MODULAR."""
1243+
is_import_from = m.matches(node, m.SimpleStatementLine(body=[m.ImportFrom()]))
1244+
return is_import_from and get_imported_model_name(node.body[0]) not in {
1245+
None,
1246+
CURRENT_MODEL,
1247+
*MODEL_NAMES_TO_SKIP_IN_MODULAR,
1248+
}
12251249

12261250

12271251
def append_new_import_node(
@@ -1480,9 +1504,16 @@ def visit_ImportFrom(self, node: cst.ImportFrom) -> None:
14801504
# `node.module` is None for fully relative imports, e.g. `from ... import initialization as init`
14811505
import_module = self.python_module.code_for_node(node.module) if node.module is not None else ""
14821506
import_statement = "." * len(node.relative) + import_module
1483-
if any(import_to_skip in import_statement for import_to_skip in IMPORTS_TO_SKIP_IN_MODULAR):
1507+
imported_model_name = get_imported_model_name(node)
1508+
if imported_model_name in MODEL_NAMES_TO_SKIP_IN_MODULAR:
14841509
return
1485-
if m.matches(node.module, m.Attribute()):
1510+
elif get_imported_model_name(node) == self.model_name:
1511+
# do not allow imports like `from transformers.models.current_model import ...` or `from ...models.current_model import ...`
1512+
raise ValueError(
1513+
f"`{self.python_module.code_for_node(node)}` imports from `{self.model_name}` itself. Import files from "
1514+
f"`{self.model_name}` with a single-dot relative import instead, e.g. `from .configuration_{self.model_name} import ...`."
1515+
)
1516+
elif m.matches(node.module, m.Attribute()):
14861517
for imported_ in node.names:
14871518
# If we match here, it's an import from 3rd party lib that we need to skip
14881519
if any(external_file["name"] in import_statement for external_file in self.excluded_external_files):
@@ -1517,6 +1548,11 @@ def visit_ImportFrom(self, node: cst.ImportFrom) -> None:
15171548
f"You are importing from {import_module} directly using global imports. Import from the correct local path"
15181549
)
15191550

1551+
def visit_If(self, node):
1552+
# do not add imports from other models to the list of imports
1553+
statements = [stmt for stmt in node.body.body if not is_import_from_other_model(stmt)]
1554+
super().visit_If(node.with_changes(body=node.body.with_changes(body=statements)))
1555+
15201556
def visit_SimpleStatementLine(self, node):
15211557
"""If we visit an import statement not previously visited, record it. If we visit a module-scope assignment,
15221558
simply record it or, if it is `__all__`, split it between files where we should dispatch it.
@@ -1538,11 +1574,8 @@ def visit_SimpleStatementLine(self, node):
15381574
self.python_module.code_for_node(node.body[0].module) if node.body[0].module is not None else ""
15391575
)
15401576
import_statement = "." * len(node.body[0].relative) + import_module
1541-
if any(
1577+
if not is_import_from_other_model(node) or any(
15421578
external_file["name"] in import_statement for external_file in self.excluded_external_files
1543-
) or not (
1544-
re.search(rf"(?:transformers\.models\.)|(?:\.\.)\w+\.({self.match_patterns}).*", import_statement)
1545-
and not any(import_to_skip in import_statement for import_to_skip in IMPORTS_TO_SKIP_IN_MODULAR)
15461579
):
15471580
self.imports.append(node)
15481581
elif m.matches(node, simple_top_level_assign_structure):
@@ -1617,6 +1650,11 @@ def leave_Module(self, node):
16171650

16181651
self.imported_objects_per_file[file_type].update(mapper.objects_imported_from_modeling)
16191652

1653+
# Functions explicitly imported in the modular are inlined, even if the file of an inherited class imports them
1654+
# from a sibling file
1655+
for imported_objects in self.imported_objects_per_file.values():
1656+
imported_objects.difference_update(self.added_objects_file_mapping)
1657+
16201658
def merge_model_specific_imports(self, visited_modules):
16211659
"""Merge the functions and assignments imported from the modeling files to the modular nodes and dependency graph,
16221660
based on the visited files."""

0 commit comments

Comments
 (0)