Transformers documentation

Règles de structure du modèle

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v5.22.2).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

Règles de structure du modèle

Transformers applique un ensemble de règles statiques à chaque fichier modeling_*.py, modular_*.py et configuration_*.py. Le package mlinter fournit le moteur de vérification, et le dépôt conserve son ensemble de règles actif dans utils/rules.toml. Ce fichier TOML local nous permet d’activer, de désactiver ou d’ajuster rapidement les règles sans attendre la sortie d’une nouvelle version de transformers-mlinter.

Consultez la documentation de mlinter pour la référence complète des vérificateurs et les détails d’utilisation.

Voici les conventions de modélisation attendues pour l’ajout ou la modification du code de modélisation. Elles garantissent la cohérence de la base de code et assurent la compatibilité avec des fonctionnalités telles que le parallélisme de pipeline, les mappages d’appareils et le partage de poids.

Exécution du vérificateur

make typing exécute mlinter en parallèle du vérificateur de types ty via le wrapper du dépôt, ce qui permet de prendre en compte utils/rules.toml. Exécutez directement le même wrapper avec les commandes suivantes.

python utils/check_modeling_structure.py                 # check all modeling files
python utils/check_modeling_structure.py --changed-only  # check only files changed vs origin/main
python utils/check_modeling_structure.py --list-rules    # list all rules and their enabled status
python utils/check_modeling_structure.py --rule TRF001   # show built-in docs for a specific rule

Le drapeau --changed-only est l’option la plus rapide pendant le développement. Il ne vérifie que les fichiers que vous avez modifiés par rapport à la branche principale. Si vous invoquez mlinter directement plutôt que le wrapper, passez --rules-toml utils/rules.toml afin que les remplacements locaux soient appliqués.

Correction d'une violation

Lorsqu’une violation de règle est détectée, l’erreur ressemble à ceci :

src/transformers/models/acme/modeling_acme.py:18: TRF013: AcmeModel.__init__ does not call self.post_init().

Utilisez l’identifiant de la règle pour trouver la correction dans la référence des règles. TRF013 est déclenché lorsqu’une sous-classe de PreTrainedModel n’appelle pas self.post_init(). Cette méthode effectue des étapes de finalisation essentielles, et son omission provoque des bugs d’exécution.

 class AcmeModel(AcmePreTrainedModel):
     def __init__(self, config):
         super().__init__(config)
         self.layers = nn.ModuleList(
             [AcmeDecoderLayer(config) for _ in range(config.num_hidden_layers)]
         )
+        self.post_init()

Référence des règles

Chaque règle ci-dessous indique ce qu’elle applique et un diff montrant la correction. Exécutez python utils/check_modeling_structure.py --rule TRF001 pour afficher la documentation intégrée de n’importe quelle règle avec l’ensemble de règles actuel du dépôt.

TRF001

Vérifie que le config_class de <Model>PreTrainedModel est nommé <Model>Config. Un écart peut casser le chargement, les classes auto et les attentes des développeurs.

class AcmePreTrainedModel(PreTrainedModel):
-    config_class = WileConfig
+    config_class = AcmeConfig

TRF002

Vérifie que base_model_prefix est une chaîne littérale non vide et sans espaces. Des préfixes invalides peuvent rompre la correspondance des clés lors du chargement des poids et l'accès au modèle de base.

class AcmePreTrainedModel(PreTrainedModel):
-    base_model_prefix = ""
+    base_model_prefix = "model"

TRF003

Signale l'ancien motif if not return_dict: return (x,) dans forward. La gestion manuelle de return_dict est verbeuse et source d'erreurs. Laissez @capture_output ou @can_return_tuple s'en charger.

-def forward(self, x, return_dict=None):
-    if not return_dict:
-        return (x,)
-    return AcmeModelOutput(last_hidden_state=x)
+@can_return_tuple
+def forward(self, x):
+    return AcmeModelOutput(last_hidden_state=x)

TRF004

Vérifie qu'aucune classe de modèle ne définit une méthode tie_weights. L'écrasement de tie_weights casse le chargement, le calcul de device_map et l'enregistrement. Déclarez les poids liés dans l'attribut de classe _tied_weights_keys.

-def tie_weights(self):
-    self.lm_head.weight = self.emb.weight
+class AcmeForCausalLM(AcmePreTrainedModel):
+    _tied_weights_keys = ["lm_head.weight"]

TRF005

Vérifie la forme de _no_split_modules lorsqu'il est présent. Des valeurs mal formées peuvent perturber le partitionnement et le fractionnement par carte de périphérique.

-_no_split_modules = [SomeLayerClass, ""]
+_no_split_modules = ["AcmeDecoderLayer", "AcmeAttention"]

TRF006

Vérifie que les arguments de cache dans la signature d'une fonction forward sont utilisés dans le corps de la fonction. Un argument de cache non utilisé suggère un support du cache incomplet et un comportement d'API incohérent.

def forward(self, x, past_key_values=None, use_cache=False):
+    if use_cache:
+        ...
     return x

TRF007

Vérifie les affectations d'attributs de self après self.post_init() dans init. Modifier la structure du modèle après post_init contourne son travail d'initialisation et de finalisation.

def __init__(self, config):
     ...
-    self.post_init()
-    self.proj = nn.Linear(...)
+    self.proj = nn.Linear(...)
+    self.post_init()

TRF008

Vérifie que add_start_docstrings sur une classe de modèle reçoit un argument non vide. Un argument vide génère une documentation API de faible qualité et peu claire.

-@add_start_docstrings("")
+@add_start_docstrings("The Acme model.")
 class AcmeModel(AcmePreTrainedModel):
     ...

TRF009

Flags imports into another model’s package from a model’s shipped implementation files: modeling*, configuration, processing_, imageprocessing, videoprocessing, featureextraction, tokenization_ and generation*.py. Three forms count: package path (from transformers.models.other.modeling_other import X), relative path (from ..other.configuration_other import X) and public API (from transformers import OtherModel) — for the last the owning directory is inferred from the class name and confirmed against the classes it defines, so an unresolvable name is left alone. Out of scope: `modular.py(building on another model is its purpose, and the converter flattens those imports away),convert_.py,init.pyand files underauto. Allowed targets: the model's own directory,autoandtimm_wrapper`. One model, one definition: model A’s behavior lives in its own files, and a change to model B must not silently change model A — for every file kind, not just modeling. Reach sub-configs through AutoConfig and CONFIG_MAPPING, and use a modular file to build on another model’s code.

-from transformers.models.llama.modeling_llama import LlamaAttention
-from transformers import CLIPTextModelWithProjection
+# Keep implementation local to this model's own files.
+# To build on another model, write modular_acme.py; to reuse a snippet,
+# copy it with a # Copied from comment.

TRF010

Vérifie que les sous-classes directes de PreTrainedConfig/PretrainedConfig dans configuration*.py et modular*.py portent l’attribut @strict(accept_kwargs=True). Sans cet attribut, une nouvelle configuration omet le contrat de validation de type au moment de l’exécution du dépôt et s’écarte de la norme de configuration basée sur les dataclass.

+@strict(accept_kwargs=True)
 class AcmeConfig(PreTrainedConfig):
     ...

TRF011

Dans forward() des sous-classes de PreTrainedModel, les accès aux attributs des sous-modules que torch.nn.Identity ne possède pas : sur les variables de boucle parcourant self.layers, et sur self.<submodule>.<attr> où <attr> n'est pas un attribut standard de nn.Module. Le parallélisme par pipeline peut remplacer n'importe quel sous-module par torch.nn.Identity, de sorte que la lecture d'un attribut personnalisé (par exemple decoder_layer.attention_type) sur celui-ci provoque une AttributeError à l'exécution. Lisez les métadonnées par couche depuis self.config.

def forward(self, ...):
-    for decoder_layer in self.layers:
+    for i, decoder_layer in enumerate(self.layers):
         hidden_states = decoder_layer(
             hidden_states,
-            attention_mask=causal_mask_mapping[decoder_layer.attention_type],
+            attention_mask=causal_mask_mapping[self.config.layer_types[i]],
         )

TRF012

Les opérations in-place (.normal(), .zero(), …) appliquées aux poids d’un module dans _init_weights sont ignorées. Les paramètres portent des drapeaux internes qui indiquent s’ils doivent encore être réinitialisés ; les opérations in-place contournent ces drapeaux. Utilisez les primitives init.

+from transformers import initialization as init
+
 def _init_weights(self, module):
-    module.weight.normal_(mean=0.0, std=0.02)
+    init.normal_(module.weight, mean=0.0, std=0.02)

TRF013

Vérifie que chaque sous-classe de PreTrainedModel définissant init appelle self.post_init(). Dans les fichiers modulaires, super().init() est pris en compte, car il propage le post_init du parent. post_init effectue des finalisations essentielles (initialisation des poids, configuration du gradient checkpointing, …) ; son omission provoque des bugs d'exécution subtils.

class AcmeModel(AcmePreTrainedModel):
     def __init__(self, config):
         super().__init__(config)
         self.layers = nn.ModuleList(...)
+        self.post_init()

TRF014

Les drapeaux trust_remote_code utilisés ou transmis (par exemple en tant qu'argument mot-clé) dans les fichiers d'intégration de modèles natifs. trust_remote_code charge du code arbitraire, y compris des binaires — une fonctionnalité puissante pour les utilisateurs, mais pas quelque chose sur lequel une intégration native peut s'appuyer, car le code distant ne peut pas être examiné ni maintenu dans transformers.

class AcmeModel(AcmePreTrainedModel):
     def __init__(self, config):
         super().__init__(config)
-        self.model = AutoModel.from_pretrained(..., trust_remote_code=True)
+        self.model = AutoModel.from_pretrained(...)

TRF015

Lorsqu'une sous-classe de PreTrainedModel définit _tied_weights_keys non vide, elle vérifie la présence du champ tie_word_embeddings dans le fichier de configuration associé. Sans ce champ, les utilisateurs ne peuvent pas contrôler l'attache des poids : le modèle attache les poids de manière inconditionnelle, ce qui casse les allers-retours de sérialisation et l'ajustement fin avec des têtes non attachées.

# configuration_foo.py
 @strict(accept_kwargs=True)
 class FooConfig(PreTrainedConfig):
     hidden_size: int = 768
+    tie_word_embeddings: bool = True

TRF016

Lorsqu'une classe processing.py ou videoprocessing.py déclare des attributs booléens do_* (do_resize, do_rescale, do_normalize, do_convert_rgb, …) et redéfinit preprocess() ou _preprocess(), il est vérifié que chaque indicateur est toujours consommé dans cette méthode : soit il est référencé directement, soit il est délégué via super().preprocess/_preprocess(…, **kwargs), soit — pour les processeurs d'images uniquement — il est transmis via _preprocess_image_like_inputs/_prepare_image_like_inputs. do_sample_frames est exempté : le preprocess() de base le consomme avant l'exécution de _preprocess(). Un do_X jamais référencé par la redéfinition est inutile : définir do_X=False n'a aucun effet, l'opération s'exécute de toute façon, et les redéfinitions par appel sont silencieusement ignorées.

class AcmeVideoProcessor(BaseVideoProcessor):
     do_resize = True
     do_normalize = True

     def _preprocess(
         self,
         videos,
+        do_resize: bool,
+        do_normalize: bool,
         size,
         image_mean,
         image_std,
         **kwargs,
     ):
         for video in videos:
-            video = self.resize(video, size=size)
-            video = self.normalize(video, image_mean, image_std)
+            if do_resize:
+                video = self.resize(video, size=size)
+            if do_normalize:
+                video = self.normalize(video, image_mean, image_std)

TRF017

Sur les classes portant à la fois @auto_docstring et @dataclass, il est vérifié que @auto_docstring apparaît en premier. Les décorateurs s’appliquent de bas en haut, donc @dataclass placé au-dessus exécute @auto_docstring en premier, sur une classe qui ne possède pas encore de init synthétisé : il modifie alors init.doc de la classe parente au lieu de celui de la sous-classe.

-@dataclass
 @auto_docstring(
     custom_intro="""
     Output type of `AcmeForPreTraining`.
     """
 )
+@dataclass
 class AcmeForPreTrainingOutput(ModelOutput):
     ...

TRF018

Vérifie que chaque sous-classe de _init_weights(self, module, ...) qui redéfinit _init_weights(self, module, ...) appelle la méthode de la classe parente via super()._init_weights(...). Les fichiers modulaires acceptent également les sentinelles PreTrainedModel._init_weights(self, module), PreTrainedModel._init_weights(module) et raise AttributeError(...). Pour une redéfinition complète volontaire, supprimez l'avertissement en ajoutant # trf-ignore: TRF018 au-dessus de la méthode. La méthode de base _init_weights gère les types de modules standard (Linear, Embedding, LayerNorm, RotaryEmbedding, …). Si vous l'ignorez, tous les sous-modules non pris en charge par la redéfinition resteront non initialisés — ce qui passera les tests mais se manifestera bien plus tard sous forme d'un bug subtil d'initialisation des poids (cf. https://github.com/huggingface/transformers/pull/45597).

from ... import initialization as init

 def _init_weights(self, module):
+    super()._init_weights(module)
     if isinstance(module, AcmeCustomLayer):
-        module.gate.data.zero_()
+        init.zeros_(module.gate)

TRF019

Signale un _defaults non vide sur les TypedDict *ProcessorKwargs dans processing_*.py. Les modèles publiés avant la date limite sont exemptés. Les _defaults codés en dur dispersent la configuration du processeur dans le code source Python, sont difficiles à remplacer depuis la configuration et alourdissent le code. Dans processor_config.json sur le hub, ils voyagent avec le point de contrôle et peuvent être mis à jour sans modification du code.

class Gemma4ProcessorKwargs(ProcessingKwargs, total=False):
-    _defaults = {
-        "text_kwargs": {"padding": False},
-        "images_kwargs": {"return_tensors": "pt"},
-    }
     images_kwargs: Gemma4ImageProcessorKwargs

TRF020

Dans les répertoires de modèles dont la configuration déclare kv_lora_rank (Multi-head Latent Attention), vérifie la classe d'attention propriétaire de la projection d'expansion KV LoRA (kv_b_proj, ou tout nn.Linear(config.kv_lora_rank, ...)) : l'expansion doit résider dans une méthode dédiée (par ex. expand_kv) appelée par forward(), et non être écrite en ligne. Dans les fichiers modulaires, une méthode héritée d'une classe de base importée est prise en compte. Les backends externes (vLLM/SGLang) remplacent l'expansion pour consommer directement le cache KV compressé. Si elle est écrite en ligne dans forward(), il n'y a rien à remplacer, et le backend doit donc matérialiser le cache complet — ce qui fait perdre les gains de mémoire pour lesquels MLA existe.

+    def expand_kv(self, k_nope, k_pe):
+        key_shape = (*k_nope.shape[:-1], -1, self.qk_nope_head_dim + self.v_head_dim)
+        k_nope = self.kv_b_proj(k_nope).view(key_shape).transpose(1, 2)
+        k_nope, value_states = torch.split(k_nope, [self.qk_nope_head_dim, self.v_head_dim], dim=-1)
+        k_pe = k_pe.expand(*k_nope.shape[:-1], -1)
+        key_states = torch.cat((k_nope, k_pe), dim=-1)
+        return key_states, value_states
+
     def forward(self, hidden_states, ...):
         ...
-        k_nope = self.kv_b_proj(k_pass).view(key_shape).transpose(1, 2)
-        k_nope, value_states = torch.split(k_nope, [self.qk_nope_head_dim, self.v_head_dim], dim=-1)
-        k_pe = k_rot.expand(*k_nope.shape[:-1], -1)
-        key_states = torch.cat((k_nope, k_pe), dim=-1)
+        key_states, value_states = self.expand_kv(k_pass, k_rot)

TRF021

Dans *.py et modular.py, les drapeaux torch.tensor(<value>, ..., device=<non-cpu>) où <value> se résout de manière prouvable à un scalaire Python — issu de littéraux numériques et d'opérations arithmétiques sur ceux-ci, de champs torch.finfo/iinfo, d'appels de fonctions intégrées et de math. renvoyant un scalaire, de variables locales liées exactement une fois, de self.<attr> assignées dans le corps de la classe, et de champs de configuration annotés int/float/bool dans le fichier de configuration associé (en suivant attribute_map). Tout ce qui peut aussi être une séquence (eos_token_id: int | list[int] | None) ou ce qui ne peut pas être résolu est laissé tel quel. init, _init_weights, post_init et post_init sont exemptés : ils ne s'exécutent jamais à l'intérieur d'une région de capture. torch.tensor(<python scalar>, device=<accelerator>) matérialise la valeur sur l'hôte puis la copie sur l'appareil ; la capture de graphes CUDA interdit cette copie, de sorte que le modèle ne peut pas être capturé. torch.full((), <value>, dtype=…, device=…) remplit le même tenseur 0-d sur l'appareil avec un noyau capturable et sans synchronisation.

def get_placeholder_mask(self, input_ids, inputs_embeds):
     special_image_mask = (
         inputs_embeds
         == self.get_input_embeddings()(
-            torch.tensor(self.config.image_token_id, dtype=torch.long, device=inputs_embeds.device)
+            torch.full((), self.config.image_token_id, dtype=torch.long, device=inputs_embeds.device)
         )
     ).all(-1)

TRF022

Vérifie que chaque chaîne de caractères de la liste _no_split_modules dans modeling*.py ou modular*.py désigne une classe définie dans ce fichier, importée dans celui-ci, ou définie par un module frère du même répertoire de modèle. Complète TRF005, qui ne vérifie que la forme de la valeur. device_map compare ces chaînes à module.__class__.__name__ à l’exécution, de sorte qu’un nom obsolète ou mal orthographié ne correspond à rien et est ignoré silencieusement — le module qu’il est censé maintenir ensemble peut alors être réparti sur plusieurs appareils. Supprimez les entrées qui désignent des classes d’un autre modèle plutôt que de les corriger : post_init collecte déjà _no_split_modules depuis les sous-modèles enfants.

class VideoLlavaPreTrainedModel(PreTrainedModel):
-    _no_split_modules = ["VideoLlavaVisionAttention"]

TRF023

In configuration*.py and modular.py, flags `Configfields named after an upstream paper's abbreviation instead of the canonical name: d_model/n_embd -> hidden_size, d_ff/d_inner/ffn_dim/ffn_hidden_size/expansion_ratio -> intermediate_size, d_head -> head_dim, n_head/n_heads -> num_attention_heads, n_layer/n_layers/num_blocks -> num_hidden_layers. Fields are read from the class body and from __init__/__post_init__ assignments and defaults. Ambiguous but idiomatic names (num_heads, num_layers, embed_dim, mlp_ratio) are not flagged, and models added before cutoff_date keep theirs. Everything that reads a model's shape -- device_map planning, tensor/pipeline parallel plans, quantization, PEFT, attention-backend selection,attribute_mapconsumers -- looks up the canonical names, so a config spelling the same quantityd_model` silently opts out of all of them. Map the checkpoint’s own spelling in the conversion script.

@strict(accept_kwargs=True)
 class AcmeConfig(PreTrainedConfig):
-    d_model: int = 1024
-    d_ff: int = 4096
-    n_heads: int = 16
-    n_layers: int = 24
+    hidden_size: int = 1024
+    intermediate_size: int = 4096
+    num_attention_heads: int = 16
+    num_hidden_layers: int = 24

TRF024

Dans *.py et modular.py, signale un littéral entier supérieur à 8 dans un argument de dimension d'un constructeur de torch.nn (Linear, Embedding, LayerNorm, RMSNorm, GroupNorm, BatchNorm, InstanceNorm, Convd, ConvTranspose*d, Bilinear, MultiheadAttention), qu'il soit positionnel ou passé par mot-clé (in_features, out_features, in_channels, out_channels, num_embeddings, embedding_dim, embed_dim, normalized_shape, num_channels, hidden_size). Les arguments de forme d'opérateur (kernel_size, stride, padding, num_groups) sont ignorés ; les littéraux jusqu'à 8 maintiennent des têtes scalaires, des classifieurs binaires et des comptes de canaux RGB propres. Les modèles ajoutés avant cutoff_date sont exemptés. Une largeur codée en dur fixe le module à une taille de checkpoint : la même architecture à une autre échelle se charge avec un décalage de forme, et from_pretrained ne peut pas indiquer quelle valeur est incorrecte car aucun champ de configuration ne pointe dessus. Cela divise également la source de vérité, de sorte que la modification de la configuration ne modifie plus le modèle qui est construit.

class AcmeAtomEmbedding(nn.Module):
     def __init__(self, config):
         super().__init__()
-        self.proj = nn.Linear(768, 3072, bias=False)
-        self.norm = nn.LayerNorm(3072)
+        self.proj = nn.Linear(config.hidden_size, config.intermediate_size, bias=False)
+        self.norm = nn.LayerNorm(config.intermediate_size)

TRF025

Dans *.py et modular.py, les appels à une fabrique de masques (les points d’entrée de masquage utils : create_causal_mask, create_bidirectional_mask, create_sliding_window_causal_mask, create_chunked_causal_mask, create_masks_for_generate, ainsi que tout helper create_mask) à l’intérieur d’une classe qui n’hérite pas de PreTrainedModel — des blocs nn.Module simples tels que des couches, des modules d’attention et des encodeurs. La construction du masque est une opération en O(longueur de séquence au carré) qui ne varie pas d’une couche à l’autre ; la construire dans la couche fait donc payer ce coût une fois par couche, et chaque couche possède alors son propre masque, ce qui empêche les backends d’attention de recevoir un masque unique déjà préparé. Construisez-le une seule fois dans le modèle et transmettez-le vers le bas.

class AcmeLayer(nn.Module):
     def forward(self, hidden_states, attention_mask=None, **kwargs):
-        attention_mask = create_causal_mask(
-            config=self.config, input_embeds=hidden_states, attention_mask=attention_mask, ...
-        )
         return self.self_attn(hidden_states, attention_mask, **kwargs)

 class AcmeModel(AcmePreTrainedModel):
     def forward(self, input_ids=None, attention_mask=None, **kwargs):
+        causal_mask = create_causal_mask(
+            config=self.config, input_embeds=inputs_embeds, attention_mask=attention_mask, ...
+        )
         for layer in self.layers:
-            hidden_states = layer(hidden_states, attention_mask, **kwargs)
+            hidden_states = layer(hidden_states, causal_mask, **kwargs)

TRF026

Dans *.py et modular*.py, un drapeau est posé sur une classe qui n'est pas une sous-classe de PreTrainedModel, qui ne définit que init et forward, qui affecte exactement un self.<attr> dans init, et dont le corps de forward est exactement return self.<attr>(...) pour ce dernier (une docstring en tête est ignorée). Toute autre méthode, attribut supplémentaire, instruction avant le return, ou appel à super() dans forward signifie que la classe effectue un travail propre. L'enveloppe ajoute un niveau à chaque nom de poids, à _no_split_modules, aux plans de parallélisme et à chaque correspondance de conversion, sans rien calculer — et les lecteurs doivent ouvrir une classe de plus pour le découvrir. Les sous-classes de PreTrainedModel sont exemptées : elles existent pour from_pretrained et les classes auto, même lorsque forward ne fait que déléguer.

-class AcmeAtomTransformer(nn.Module):
-    def __init__(self, config):
-        super().__init__()
-        self.encoder = AcmeEncoder(config)
-
-    def forward(self, hidden_states, **kwargs):
-        return self.encoder(hidden_states, **kwargs)
-
 class AcmeModel(AcmePreTrainedModel):
     def __init__(self, config):
         super().__init__(config)
-        self.atom_transformer = AcmeAtomTransformer(config)
+        self.encoder = AcmeEncoder(config)

TRF027

Signale toute déclaration assert dans modeling*.py, modular.py et configuration_.py. python -O supprime les asserts, de sorte qu'une vérification de forme ou de configuration écrite sous cette forme disparaît silencieusement lors des exécutions optimisées. Un assert ne produit qu'une AssertionError nue, alors qu'une ValueError peut indiquer la valeur en cause et préciser la marche à suivre.

def forward(self, hidden_states):
-    assert hidden_states.dim() == 3
+    if hidden_states.dim() != 3:
+        raise ValueError(f"Expected a 3D tensor, got shape {tuple(hidden_states.shape)}.")

TRF028

Vérifie les 25 premières lignes de modeling*.py, modular.py, configuration_.py, processing*.py, image_processing.py et videoprocessing.py à la recherche d'une ligne Licensed under the <name> License suivie de chaque clause du paragraphe de garantie standard, de You may obtain a copy of the License at jusqu'à limitations under the License.. Les lignes sont d'abord aplaties et converties en minuscules, de sorte que le retour à la ligne et le style des commentaires n'ont pas d'importance. Le nom de la licence et la ligne de copyright ne sont pas vérifiés : ils varient d'un modèle à l'autre. Un fichier livré sans en-tête laisse son origine ambiguë, et l'ajouter ultérieurement implique de modifier un fichier déjà publié. Ne correspondre qu'à Apache License laisserait passer les défauts qui se produisent réellement : un paragraphe tronqué en cours de route, un en-tête qui s'arrête après l'URL, ou un en-tête altéré par une mauvaise opération de recherche et remplacement.

+# Copyright 2026 The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# ...
 """PyTorch Acme model."""

TRF029

Dans *.py et modular*.py, signale un __init__ qui reçoit config en plus d'un argument dont le nom désigne sans ambiguïté un champ de configuration (hidden_size, num_attention_heads, intermediate_size, head_dim, num_hidden_layers, embed_dim, dropout, eps, patch_size, rope_theta, …). Un paramètre optionnel avec une valeur par défaut None est exempté : il s'agit d'une surcharge, et non d'une seconde source de vérité, et c'est ainsi qu'une classe MLP peut servir à la fois la largeur dense et celle des experts d'un modèle MoE. Une valeur par défaut codée en dur, comme hidden_size: int = 1024, ne l'est pas — elle prime sur la configuration lorsque l'appelant ne transmet rien. kosmos2 est sur la liste blanche : sa page de documentation n'est pas dérivable du nom du répertoire, donc le seuil ne peut pas le couvrir. Le même nombre possède désormais deux sources de vérité et l'appelant choisit le vainqueur, si bien que modifier la configuration ne change plus le modèle qui est construit. Cela pousse également les connaissances sur l'architecture vers chaque site d'appel, où elles n'ont pas leur place.

class AcmeAttention(nn.Module):
-    def __init__(self, config, embed_dim, num_heads, dropout):
+    def __init__(self, config, layer_idx=None):
         super().__init__()
-        self.embed_dim = embed_dim
-        self.num_heads = num_heads
+        self.embed_dim = config.hidden_size
+        self.num_heads = config.num_attention_heads

 class AcmeMLP(nn.Module):
     # an optional override is fine: omitting it reads the config
     def __init__(self, config, intermediate_size=None):
         super().__init__()
         self.intermediate_size = intermediate_size or config.intermediate_size

TRF030

Dans *.py et modular*.py, les chaînes d’attributs enracinées dans config ou self.config qui descendent à trois niveaux ou plus sont signalées. config.hidden_size (un saut) et config.text_config.hidden_size (deux sauts, l’accès normal à une sous-configuration) sont acceptables. Une violation par ligne. Un module qui parcourt config.diffusion_config.atom_encoder_config.hidden_size est couplé à l’ensemble de la hiérarchie de configuration plutôt qu’à sa propre portion, ce qui l’empêche d’être réutilisé, testé ou fourni avec une sous-configuration différente. Transmettez la sous-configuration pertinente en aval et la chaîne se réduit à un seul saut.

class AcmeAtomEncoder(nn.Module):
     def __init__(self, config):
         super().__init__()
-        self.norm = AcmeLayerNorm(config.diffusion_config.atom_encoder_config.hidden_size)
+        self.norm = AcmeLayerNorm(config.hidden_size)

TRF031

Dans *.py et modular*.py, un @dataclass de niveau supérieur dont les bases ne se terminent pas par Output est signalé, à moins qu'il ne possède deux champs obligatoires ou plus — il s'agit de paquets d'arguments internes, que ModelOutput rejette à l'exécution. Un dataclass de sortie simple ne peut pas être indexé comme un tuple, ne survit pas à return_dict=False et est invisible pour @auto_docstring, si bien que ses champs n'apparaissent jamais dans la documentation API générée. ModelOutput obtient les trois gratuitement.

@auto_docstring
 @dataclass
-class AcmeStructureOutput:
+class AcmeStructureOutput(ModelOutput):
     positions: torch.Tensor
     confidence: Optional[torch.Tensor] = None

TRF032

Dans modeling*.py et modular*.py, les drapeaux maskedfill, masked_fill, full, full_like et new_full sont appelés avec un littéral numérique négatif d'ordre de grandeur supérieur ou égal à 1e3. Un -1e9 codé en dur déborde en -inf en float16 et est très loin de la valeur minimale en float32, si bien que le même masque se comporte différemment selon le type de données et peut générer des NaN après softmax. torch.finfo(dtype).min est la valeur minimale représentable dans le type de données réellement utilisé.

-attention_scores = attention_scores.masked_fill(~mask, -1e9)
+attention_scores = attention_scores.masked_fill(~mask, torch.finfo(attention_scores.dtype).min)

TRF033

Dans *.py et modular*.py, les méthodes dont le nom commence par set_ sont des drapeaux, à l'exception des méthodes du contrat PreTrainedModel : set_input_embeddings, set_output_embeddings, set_decoder, set_encoder, set_attn_implementation et set_default_language. Un setter rend le comportement dépendant de l'ordre d'appel : la valeur n'est pas dans la configuration, elle n'est donc ni sauvegardée, ni restaurée par from_pretrained, et n'est pas visible par la planification de device-map ou de parallélisme. Les utilisateurs doivent savoir l'appeler, et l'oublier est silencieux.

class AcmeTriangleAttention(nn.Module):
-    def set_chunk_size(self, chunk_size):
-        self.chunk_size = chunk_size
+    def __init__(self, config):
+        super().__init__()
+        self.chunk_size = config.chunk_size

TRF034

In modeling*.py and modular.py, flags a locally-defined `Layer/Blockclass instantiated in annn.ModuleList(…)that does not reachGradientCheckpointingLayerthrough its base chain; modular files follow relative imports into sibling models, and unresolved chains are inconclusive. Out of scope: a model that never setssupports_gradient_checkpointing = True, which raises fromgradient_checkpointing_enable()instead of skipping a layer; a layer holdingnn.BatchNorm/nn.InstanceNorm*, whose statistics would be recomputed twice; and a stack that is not the model's token mixer, shown by an attention, modulation, mixer or SSM module assigned asself.x = Y(…).gradient_checkpointing_enable()` wraps a layer only if it is a GradientCheckpointingLayer. A plain nn.Module on the trunk is skipped silently, so training looks checkpointed while still allocating full activations, and the OOM surfaces far from the cause. Elsewhere — a conv backbone, a decode head — the trade is the author’s call, so the rule stays out.

-class AcmeDecoderLayer(nn.Module):
+class AcmeDecoderLayer(GradientCheckpointingLayer):
     def __init__(self, config, layer_idx):
         super().__init__()

TRF035

Signale les commentaires # noqa, avec ou sans codes, dans les fichiers modeling*.py, modular.py et configuration_.py. Dans un fichier modulaire, F401, F821 et F822 sont acceptés : il s'agit d'une source de génération qui ne définit volontairement pas tous les noms qu'elle utilise, de sorte que la famille de règles « nom non défini » de ruff se déclenche sur du code correct — entrées de __all__ remplies par le convertisseur, classes situées dans le modèle parent, imports conservés pour être réexportés. Un # noqa ne mentionnant que ces codes est ignoré ; un commentaire mentionnant d'autres codes est signalé pour les codes restants. Un # noqa nu est toujours signalé ; dans un fichier modulaire, le message demande d'indiquer le code plutôt que de réécrire. Les fichiers de modèle sont du code ordinaire soumis aux règles de lint du dépôt, donc une suppression signifie que le problème sous-jacent a été laissé en place — et un # noqa nu masque également toute violation future sur cette ligne. Corrigez le code à la place.

-from ...modeling_utils import PreTrainedModel  # noqa: F401
+from ...modeling_utils import PreTrainedModel

TRF036

Dans *.py et modular*.py, signale toute construction nn.Sequential(...). Sequential nomme ses enfants par position, de sorte que les poids se retrouvent à mlp.0.weight et mlp.2.weight : la correspondance de conversion, _tied_weights_keys et chaque plan de parallélisme référencent alors des indices, et l’insertion d’une couche renomme tout ce qui suit. Elle masque également le forward, rendant invisibles les casts de dtype et les résidus entre les étapes là où ils ont lieu.

-        self.mlp = nn.Sequential(
-            nn.Linear(config.hidden_size, config.intermediate_size),
-            nn.GELU(),
-            nn.Linear(config.intermediate_size, config.hidden_size),
-        )
+        self.fc1 = nn.Linear(config.hidden_size, config.intermediate_size)
+        self.act = ACT2FN[config.hidden_act]
+        self.fc2 = nn.Linear(config.intermediate_size, config.hidden_size)

TRF037

Dans *.py et modular*.py, les appels à einsum sont signalés, en affichant l'équation lorsqu'elle est littérale. Désactivé par défaut : einsum est parfois le moyen le plus clair d'exprimer une contraction, il s'agit donc d'une option et non d'une convention stricte. Une équation einsum encode les formes dans une notation que le lecteur doit décoder, et une équation construite dynamiquement masque la contraction réellement exécutée. Développez-la en opérations explicites de matmul/transpose.

-        pair_bias = torch.einsum("bqhc,bkhc->bhqk", query_states, key_states)
+        pair_bias = query_states.permute(0, 2, 1, 3) @ key_states.permute(0, 2, 3, 1)

TRF038

Vérifie que chaque fichier de modélisation *.py, de traitement.py, de traitement d’images processing.py, de traitement de vidéos processing.py, d’extraction de caractéristiques extraction.py et de tokenisation *.py possède un fichier de test correspondant dans tests/models/<model>/test.py (modelingacme.py -> tests/models/acme/test_modeling_acme.py). Exemptions : configuration.py, couvert par ConfigTester dans le testmodeling.py associé, et tokenization_utils.py, un utilitaire couvert via son tokenizer ; tokenization<name>_fast.py correspond au fichier de test de sa version lente. Un fichier modulaire*.py peut définir plusieurs familles à la fois, ses classes sont donc routées par suffixe de nom — XxxModel/XxxPreTrainedModel/XxxFor<Task> pour la modélisation, XxxImageProcessor(Fast) pour le traitement d’images, XxxProcessor pour le traitement, XxxVideoProcessor pour le traitement de vidéos, XxxFeatureExtractor pour l’extraction de caractéristiques, XxxTokenizer(Fast) pour la tokenisation, XxxConfig ignoré — et une violation est signalée pour chaque fichier de test manquant. # trf-ignore: TRF038 n’est pas pris en charge ; utilisez allowlist_models afin que l’exemption soit visible lors de la revue. Un fichier source sans fichier de test n’a aucune couverture de régression : un passage avant cassé, une conversion mal mappée ou un tokenizer qui supprime un jeton spécial peuvent être intégrés et rester cassés indéfiniment. Une configuration factice avec des poids aléatoires, ou un petit vocabulaire écrit à la main, suffit à tester l’un ou l’autre.

src/transformers/models/acme/modeling_acme.py
+tests/models/acme/test_modeling_acme.py
 src/transformers/models/acme/tokenization_acme.py
+tests/models/acme/test_tokenization_acme.py

TRF039

Détecte les blocs if is_*_available(): import ... (y compris les combinaisons telles que is_vision_available() and is_torch_available()) et signale l’import lorsque le nom n’est référencé nulle part ailleurs dans le fichier, y compris dans les annotations de type sous forme de chaînes et all. La vérification d’import inutilisé de ruff ne les supprime pas : l’import est accessible, donc le bloc semble correct en soi. Une fois qu’un refactor ne nécessite plus PIL.Image, torch, etc., l’import conditionnel persiste comme un poids mort et un signal trompeur sur les dépendances réelles du fichier.

if is_vision_available():
-    from PIL import Image

TRF040

Dans *.py et modular*.py, les méthodes décorées à la fois par @capture_outputs et @can_return_tuple sont signalées. Cette règle complète TRF003, qui couvre le branchement manuel sur return_dict dans forward(). Les deux décorateurs retirent return_dict, si bien que seul le plus externe voit la valeur réelle. @capture_outputs gère déjà la conversion en to_tuple, ce qui rend @can_return_tuple redondant.

-@can_return_tuple
 @merge_with_config_defaults
 @capture_outputs
 @auto_docstring
 def forward(self, x):
     return AcmeModelOutput(last_hidden_state=x)

TRF041

In modeling*.py and modular.py, flags every if/elif and conditional expression whose condition reads a `config.orself.config.attribute without a# CODEPATH:comment, accepted on the branch line or in the contiguous comment block directly above it. Any config attribute counts, not just boolean flags. Exempt by structure:X if X is not None else fallback, where the tested field is itself one of the results --getattr(config, x, default)spelled long, which cannot fork the graph; and a guard, anifwith noelsewhose body only raises or only warns/logs, since one side aborts and nothing diverges past it. Merely mentioning None does not qualify:config.visionconfig is not Nonestill owes a note. Exempt by field: framework plumbing that gates no checkpoint divergence --problem_typeselecting a loss,hidden_actlooking up an activation,num_labels,use_cache,is_decoder, the special token ids, thesummaryhead settings; the full list isDEFAULT_EXEMPT_ATTRIBUTESinmlinter/trf041.py, extended per project byignored_attributes = […]on the rule table. A model exempts one of its own fields file-wide with a module-level# trf-ignore: TRF041 config.scale_embedding, config.auxiliary_lossat column 0 (self.config.x,config.xandxare the same field). It must name at least one field -- a bare# trf-ignore: TRF041keeps its per-line meaning -- and a condition is skipped only when every field it reads is exempt. A config-gated branch is a second architecture in the same file, and the code cannot say whether both halves are still reachable -- which is how dead experimental branches survive for releases. The rule does not forbid the branch: like Rust's// SAFETY:`, it asks for the checkpoints taking each side to be written down next to it. A branch nobody can name one for is a branch to delete.

+        # CODEPATH: ESMC-6B ships pre-normalised embeddings, the 300M/600M checkpoints do not.
         if config.use_embedding_norm:
             hidden_states = self.embedding_norm(hidden_states)

-        if config.msa_encoder_enabled:
-            hidden_states = self.msa_encoder(hidden_states)
+        # no released checkpoint sets msa_encoder_enabled -> branch removed

TRF042

Dans tests/models//testtokenization.py, la vérification s’assure que le fichier définit une classe de test collectée qui hérite de TokenizerTesterMixin. Seules les classes collectées par le runner sont prises en compte — soit une base TestCase, soit la convention de nommage *Test lorsque la base est une classe de test d’un autre modèle — de sorte que les fichiers ne contenant que des utilitaires sont ignorés, et qu’un utilitaire qui intègre la suite ne satisfait pas la règle pour une classe de test réelle. L’héritage est suivi à travers les bases dans le même fichier et vers le test de tokenizer d’un autre modèle importé par son nom ; une base non résolue n’est jamais comptée. Le rapport est émis sur la première classe de test qui n’exécute pas la suite. auto est sur la liste blanche : test_tokenization_auto.py teste la résolution d’AutoTokenizer, et non un tokenizer. TokenizerTesterMixin est l’endroit où sont réellement vérifiés l’aller-retour encode/décoder, le bourrage et la troncature, le traitement des jetons spéciaux, la persistance des jetons ajoutés et l’équivalence de sauvegarde/chargement. Un test qui n’affirme que quelques listes d’identifiants de jetons manuscrites passe alors que le tokenizer est cassé sur chacun d’eux, et semble pourtant testé lors de la revue.

-class AcmeTokenizationTest(unittest.TestCase):
+class AcmeTokenizationTest(TokenizerTesterMixin, unittest.TestCase):
+    tokenizer_class = AcmeTokenizer
+    test_slow_tokenizer = True

TRF043

Signale un paramètre position_ids déclaré dans la signature forward des classes dont le nom se termine par Attention. position_ids est consommé en aval par l'entraînement sans padding de flash-attention et doit transiter via kwargs. Le nommer dans la signature l'absorbe avant que l'interface d'attention puisse le lire ; la norme Llama transmet position_embeddings en plus de kwargs.

class AcmeAttention(nn.Module):
     def forward(
         self,
         hidden_states,
         position_embeddings,
         attention_mask=None,
-        position_ids=None,
         **kwargs: Unpack[TransformersKwargs],
     ):

TRF044

Signale un paramètre nommé position dans n'importe quelle fonction de modeling.py et modular_.py. cache_position a été supprimé de tous les modèles dans la v5. Le réintroduire (généralement copié depuis des sources antérieures à la v5) propage un argument inutile à travers chaque couche ; la mise à jour du cache est past_key_values.update(key_states, value_states, self.layer_idx), sans propagation de position.

def forward(
     self,
     hidden_states,
     past_key_values=None,
-    cache_position=None,
     **kwargs,
 ):
-    key_states, value_states = past_key_values.update(key_states, value_states, self.layer_idx, cache_position)
+    key_states, value_states = past_key_values.update(key_states, value_states, self.layer_idx)

TRF045

Signale les paramètres de sortie hérités attentions, output_hidden_states et return_dict dans les signatures forward des fichiers modeling.py et modular_.py. Les modèles contribuant avant la date limite sont exemptés. La pile de décorateurs gère le contrôle des sorties : @captureoutputs résout les drapeaux output* par rapport à la configuration et enregistre les tenseurs via _can_record_outputs, tandis que @can_return_tuple gère return_dict. Les déclarer dans la signature réintroduit un acheminement manuel des drapeaux qui diverge des décorateurs.

+@capture_outputs
 def forward(
     self,
     input_ids,
-    output_attentions=None,
-    output_hidden_states=None,
-    return_dict=None,
     **kwargs: Unpack[TransformersKwargs],
 ):

TRF046

Les affectations de drapeaux aux attributs de self dans les méthodes forward des fichiers modeling*.py et modular*.py. L'état écrit pendant le forward compromet le batching, torch.compile et le raisonnement sur le module. L'état porté est transmis explicitement (objets de cache, boucle generate) ; les valeurs qui ne dépendent que de la config ou des formes statiques appartiennent à init.

def forward(self, hidden_states):
-    self.sequence_length = hidden_states.shape[1]
-    embeddings = self.compute_embeddings(self.sequence_length)
+    embeddings = self.compute_embeddings(hidden_states.shape[1])

TRF047

Affectations de drapeaux aux attributs de self dans preprocess, preprocess, call et post_process* des méthodes de image_processing.py et videoprocessing.py. Un processeur qui conserve un état entre les appels casse le lotissement de type « prétraiter beaucoup puis post-traiter » : le second preprocess écrase l'état dont le premier postprocess a besoin. Retournez la valeur ou transmettez-la à travers la chaîne de méthodes.

def _preprocess(self, images, **kwargs):
-    self.original_sizes = [image.shape[-2:] for image in images]
+    original_sizes = [image.shape[-2:] for image in images]
     ...
+    return BatchFeature(data={"pixel_values": pixel_values, "original_sizes": original_sizes})

TRF048

Détecte les littéraux de liste, de tuple ou d'ensemble dans les déclarations de niveau de classe de _tied_weights_keys. La v5 a modifié _tied_weights_keys pour en faire un dictionnaire qui associe chaque paramètre lié cible à sa source. La forme en liste ne précise plus quel paramètre est la source, ce qui entraîne un comportement silencieux erroné lors de l'association, du calcul de device_map et de la sauvegarde.

class AcmeForCausalLM(AcmePreTrainedModel):
-    _tied_weights_keys = ["lm_head.weight"]
+    _tied_weights_keys = {"lm_head.weight": "model.embed_tokens.weight"}

TRF049

Les appels d'initialisation dans les méthodes init des fichiers modeling*.py et modular.py : primitives nn.init. / init.* et initialiseurs en place sur les propres paramètres (self.weight.data.normal_()). Les modèles sont instanciés sur l'appareil meta, de sorte que les valeurs de tenseurs écrites dans init sont jetées avant le chargement : un paramètre initialisé uniquement à cet endroit contient un contenu aléatoire lors de l'ajustement fin depuis zéro ou après un rechargement sur l'appareil meta. Allouez avec torch.empty dans init, initialisez dans _init_weights.

class AcmeEmbeddings(nn.Module):
     def __init__(self, config):
         super().__init__()
         self.position_embedding = nn.Parameter(torch.empty(config.num_positions, config.hidden_size))
-        nn.init.trunc_normal_(self.position_embedding, std=config.initializer_range)

 class AcmePreTrainedModel(PreTrainedModel):
     def _init_weights(self, module):
         super()._init_weights(module)
+        if isinstance(module, AcmeEmbeddings):
+            init.trunc_normal_(module.position_embedding, std=self.config.initializer_range)

TRF050

Les appels à une classe *RotaryEmbedding dans le init des classes dont le nom se termine par Attention. Le Model possède un unique rotary_emb, construit inv_freq une seule fois et transmet cos/sin en aval sous forme de position_embeddings. Un module rotary par couche d'attention duplique les buffers, recalcule les fréquences à chaque couche et s'écarte du contrat selon lequel l'attention reçoit position_embeddings.

class AcmeAttention(nn.Module):
     def __init__(self, config, layer_idx):
         super().__init__()
-        self.rotary_emb = AcmeRotaryEmbedding(config)

 class AcmeModel(AcmePreTrainedModel):
     def __init__(self, config):
         super().__init__(config)
+        self.rotary_emb = AcmeRotaryEmbedding(config)

TRF051

Les comparaisons de drapeaux sont effectuées par rapport à l'attribut attn_implementation dans modeling.py et modular_.py. La distribution vers les backends relève de ALL_ATTENTION_FUNCTIONS.get_interface, et la manipulation conditionnelle des tenseurs selon le backend (remplissage, redimensionnement) doit se trouver dans les wrappers partagés sous integrations/. Les branches en ligne maintiennent le corps du modèle conscient des noyaux, mais cela se brise lorsque de nouveaux backends s'enregistrent.

-if self.config._attn_implementation == "flash_attention_2":
-    attn_output = flash_path(query_states, key_states, value_states)
-else:
-    attn_output = eager_path(query_states, key_states, value_states)
+attention_interface = ALL_ATTENTION_FUNCTIONS.get_interface(self.config._attn_implementation, eager_attention_forward)
+attn_output, attn_weights = attention_interface(self, query_states, key_states, value_states, ...)

TRF052

Signale les affectations au niveau du module de noms se terminant par ATTENTION_CLASSES dans modeling.py et modular_.py. Les classes d'attention par backend sélectionnées depuis un dictionnaire constituent l'idiome pré-interface : des classes quasi identiques finissent par diverger, et les noyaux d'attention du hub enregistrés dans ALL_ATTENTION_FUNCTIONS ne les atteignent jamais. Une seule classe d'attention qui effectue la distribution via l'interface remplace le dictionnaire ; ne la propagez pas depuis un parent hérité.

-ACME_ATTENTION_CLASSES = {
-    "eager": AcmeAttention,
-    "flash_attention_2": AcmeFlashAttention2,
-    "sdpa": AcmeSdpaAttention,
-}
+class AcmeAttention(nn.Module):
+    def forward(self, ...):
+        attention_interface = ALL_ATTENTION_FUNCTIONS.get_interface(self.config._attn_implementation, eager_attention_forward)

TRF053

Dans modeling*.py et modular*.py, les affectations de drapeaux qui construisent shiftlogits/shift_labels (et les variantes décalées) par tranchage, comme dans labels[…, 1:], sont signalées. Recevoir des étiquettes déjà décalées (shift_labels = kwargs.pop(“shift_labels”, labels)) est l’idiome correct et n’est pas signalé. self.loss_function décale lui-même les étiquettes, de sorte que le pré-décalage dans le code de modélisation entraîne un entraînement sur des cibles doublement décalées ou impose un chemin de perte sur mesure. Les modèles uniquement décodeurs transmettent les étiquettes brutes et laissent la fonction de perte les décaler. Les modèles encodeur-décodeur sont le cas miroir : leurs étiquettes sont déjà décalées par le jeton de début du décodeur préfixé, ils transmettent donc shift_labels=labels pour empêcher la fonction de perte de les décaler à nouveau.

if labels is not None:
-    shift_logits = logits[..., :-1, :].contiguous()
-    shift_labels = labels[..., 1:].contiguous()
-    loss = nn.functional.cross_entropy(shift_logits.view(-1, self.config.vocab_size), shift_labels.view(-1))
+    # decoder-only: labels are unshifted, the loss shifts them
+    loss = self.loss_function(logits=logits, labels=labels, vocab_size=self.config.vocab_size)
+    # encoder-decoder: labels are already shifted, hand them over as shift_labels
+    loss = self.loss_function(logits=logits, labels=labels, shift_labels=labels, vocab_size=self.config.vocab_size)

TRF055

Flags config = SomeConfig en tant qu’attribut de classe sur les sous-classes de PreTrainedModel dans modeling*.py et modular*.py. PreTrainedModel.__init_subclass__ dérive config_class à partir d’une annotation config, via inspect.get_annotations(cls). Une affectation crée un attribut de classe orphelin que cet appel ne signale pas, si bien que la sous-classe conserve silencieusement le config_class de la classe parente. Une annotation nue n’a pas de valeur d’exécution, ne crée aucun attribut et est correctement détectée.

class Gemma4VisionModel(Gemma4PreTrainedModel):
     """The Gemma 4 Vision Encoder."""
-    config = Gemma4VisionConfig
+    config: Gemma4VisionConfig

TRF056

Dans modeling*.py et modular*.py, les appels .item() et .tolist() à l'intérieur de n'importe quel forward sont signalés. Un appel .tolist() qui alimente l'argument de taille de découpage de split(...) est exempté : torch.split a besoin d'entiers Python. Les deux appels lisent un tenseur sur l'hôte, ce que dynamo ne peut pas tracer, ce qui provoque une rupture du graphe.

-        for grid, item in zip(grid_thw.tolist(), split_items):
-            _, height, width = grid
-            merged.append(self.patch_merger(item, size=(height, width)))
+        for grid, item in zip(grid_thw, split_items):
+            merged.append(self.patch_merger(item, size=(grid[1], grid[2])))

TRF057

Vérifie @auto_docstring sur les sous-classes publiques de PreTrainedModel (<Model>PreTrainedModel, <Model>Model, <Model>For<Task>, backbones), les sous-classes de PreTrainedConfig, les sous-classes de ModelOutput, les image processors et les sous-classes de ProcessorMixin, ainsi que sur leurs méthodes publiques forward, get_image_features, get_video_features, get_audio_features, get_text_features, preprocess et __call__. Un fichier modular_*.py est vérifié par rapport aux fichiers générés à partir de lui. Sans lui, une classe est livrée sans introduction ni documentation des paramètres, et une méthode sans documentation des arguments, sans section Returns et sans exemple d'utilisation — ce qui oblige alors à tout rédiger manuellement pour chaque modèle au lieu de le tirer de auto_docstring.py.

+@auto_docstring
 @dataclass
 class AcmeModelOutputWithPast(ModelOutput):
     logits: torch.Tensor | None = None

+@auto_docstring
 class AcmeForConditionalGeneration(AcmePreTrainedModel):
+    @auto_docstring
     def forward(self, input_ids, pixel_values=None, **kwargs):
         ...

TRF058

Dans *.py et modular*.py, les appels register_buffer("<name>", ...) dont le nom du buffer est une chaîne littérale sont signalés, quel que soit le récepteur (self ou un autre module tel que layer.mamba). Un nom calculé — une variable ou une f-string, par exemple un buffer par couche dans une boucle — n'a pas d'équivalent en affectation d'attribut et est donc exclu. Depuis torch>=2.5, nn.Buffer enregistre un buffer par une simple affectation d'attribut, à l'instar de nn.Parameter. Un buffer créé par un appel de méthode n'existe que comme effet de bord de l'exécution de __init__, de sorte qu'un fichier modulaire souhaitant en modifier un doit redéfinir l'intégralité de __init__. Affecté en tant qu'attribut, il peut être hérité et redéfini indépendamment.

-        self.register_buffer("inv_freq", inv_freq, persistent=False)
-        self.register_buffer(
-            "position_ids", torch.arange(config.max_position_embeddings).expand((1, -1)), persistent=False
-        )
+        self.inv_freq = nn.Buffer(inv_freq, persistent=False)
+        self.position_ids = nn.Buffer(torch.arange(config.max_position_embeddings).expand((1, -1)), persistent=False)

TRF059

Pour les répertoires de modèles dont le plan de parallélisme par tenseurs attribue moe_tp_experts, vérifie que les classes routées *Experts reçoivent les états cachés, les indices top-k et les poids de routage top-k en tant que trois premiers arguments positionnels de forward. Les alias tels que selected_experts et routing_weights sont acceptés. MoeExpertsParallel applique une transformation du gradient à l'argument positionnel 3. Une signature différente l'applique silencieusement au mauvais tenseur ou l'ignore.

class AcmeExperts(nn.Module):
-    def forward(self, hidden_states):
+    def forward(self, hidden_states, top_k_index, top_k_weights):
         ...

Suppression des violations

Si vous devez supprimer une violation de règle, utilisez l’une des deux options ci-dessous.

Suppression en ligne

Ajoutez un commentaire # trf-ignore: RULE_ID sur la ligne en violation. Incluez une explication pour que les relecteurs comprennent pourquoi la suppression est justifiée.

# trf-ignore: TRF011 — mask is derived from self.config, not the layer
hidden_states = layer(hidden_states, attention_mask=mask_from_config)

N’utilisez pas trf-ignore pour masquer des violations qui devraient être corrigées dans le code.

allowlist_models

Pour les modèles dont le code hérité ne peut pas être corrigé immédiatement, ajoutez le nom du répertoire du modèle à la liste allowlist_models de la règle concernée dans mlinter rules.toml.

[rules.TRF004]
allowlist_models = ["existing_model", "your_model_name"]
Update on GitHub