Transformers documentation

قواعد بنية النموذج

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

and get access to the augmented documentation experience

to get started

قواعد بنية النموذج

يفرض Transformers مجموعة من القواعد الثابتة على كل ملف من ملفات modeling_*.py وmodular_*.py وconfiguration_*.py. وتوفر حزمة mlinter محرك الفحص، بينما يحتفظ المستودع بمجموعة القواعد النشطة لديه في utils/rules.toml. ويتيح لنا ملف TOML المحلي تمكين القواعد أو تعطيلها أو ضبطها بسرعة دون انتظار إصدار جديد من transformers-mlinter.

راجع توثيق mlinter للاطلاع على مرجع الفاحص الكامل وتفاصيل الاستخدام.

هذه هي الأعراف المتوقعة للنماذج عند إضافة أو تغيير كود النمذجة. فهي تحافظ على اتساق قاعدة الكود وتضمن التوافق مع ميزات مثل توازلي خط الأنابيب (pipeline parallelism)، وخرائط الأجهزة (device maps)، وربط الأوزان (weight tying).

تشغيل أداة الفحص

make typing يشغّل mlinter بالتوازي مع مُتحقّق الأنواع ty عبر غلاف المستودع، بحيث يلتقط utils/rules.toml. شغّل نفس الغلاف مباشرةً باستخدام الأوامر التالية.

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

يُعدّ العلم --changed-only الخيار الأسرع أثناء التطوير. فهو يفحص فقط الملفات التي قمت بتعديلها مقارنةً بفرع main. إذا كنت تستدعي mlinter مباشرةً بدلاً من الغلاف، فمرّر --rules-toml utils/rules.toml حتى تُطبَّق التOverrides المحلية.

إصلاح انتهاك

عند اكتشاف انتهاك لقاعدة، يبدو الخطأ على النحو التالي:

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

استخدم معرّف القاعدة للبحث عن الإصلاح في مرجع القواعد. يتم تفعيل TRF013 عندما لا يستدعي فرع من PreTrainedModel الدالة self.post_init(). تُنفّذ هذه الدالة خطوات نهائية أساسية، وإهمالها يؤدي إلى أخطاء وقت التشغيل.

 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()

مرجع القواعد

تُدرج كل قاعدة أدناه ما تفرضه، مع عرض الفرق (diff) الذي يوضح الإصلاح. شغّل python utils/check_modeling_structure.py --rule TRF001 لعرض التوثيق المدمج لأي قاعدة ضمن مجموعة القواعد الحالية للمستودع.

TRF001

يتحقق من أن اسم config_class الخاص بـ <Model>PreTrainedModel هو <Model>Config. قد يؤدي عدم التطابق إلى تعطيل التحميل وفئات auto والتوقعات المطوَّرة.

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

TRF002

يتحقق من أن base_model_prefix هو حرف ثابت غير فارغ وخالي من مسافات. قد تؤدي البادئات غير الصالحة إلى كسر خرائط مفاتيح تحميل الأوزان والوصول إلى النموذج الأساسي.

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

TRF003

يُشير إلى نمط if not return_dict: return (x,) القديم في forward. ففرع return_dict اليدوي مُطوّل وسهل الوقوع في أخطائه. دع @capture_output أو @can_return_tuple يتكفل بذلك.

-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

يتحقق من عدم تعريف أي فئة نموذج لطريقة tie_weights. يؤدي تجاوز tie_weights إلى تعطيل التحميل، وحساب device_map، والحفظ. يجب إعلان الأوزان المرتبطة في خاصية الفئة _tied_weights_keys.

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

TRF005

يتحقق من شكل _no_split_modules عند وجوده. قد تؤدي القيم غير الصالحة إلى تعطيل تقسيم خريطة الأجهزة والتجزئة.

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

TRF006

يتحقق من أن حُجج الكاش في التوقيع الأمامي (forward) مستخدمة في الجسم. يشير حُجج الكاش غير المستخدمة إلى دعم غير مكتمل للكاش وسلوك غير متسق لواجهة البرمجة (API).

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

TRF007

يتحقق من إسنادات خاصية self بعد استدعاء self.post_init() في init. فإن تعديل بنية النموذج بعد post_init يتجاوز مهام التهيئة والإنهاء الخاصة به.

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

TRF008

يتحقق من أن add_start_docstrings على فئة النموذج تستقبل حجة غير فارغة. فالحجة الفارغة تنتج توثيقات API مولّدة غير واضحة ومنخفضة الجودة.

-@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

يتحقق من أن فئات *.py و modular*.py المباشرة الموروثة من PreTrainedConfig/PretrainedConfig تحمل الزخرفة @strict(accept_kwargs=True). فبدونها، تفقد الإعدادات الجديدة عقد التحقق من الأنواع وقت التشغيل الخاص بالمستودع، وتبتعد عن معيار الإعدادات القائم على dataclass.

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

TRF011

في دالة forward() لفئات PreTrainedModel الفرعية، لن تكون عمليات الوصول إلى سمات الوحدات الفرعية من نوع torch.nn.Identity متاحة: على متغيرات الحلقات الخاصة بـ self.layers، وعلى self.<submodule>.<attr> حيث <attr> ليس سمة قياسية لـ nn.Module. قد يستبدل توازي الأنابيب أي وحدة فرعية بـ torch.nn.Identity، لذا فإن قراءة سمة مخصصة (مثل decoder_layer.attention_type) منها تثير AttributeError وقت التشغيل. اقرأ بيانات وصفية لكل طبقة من 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

Flags in-place ops (.normal(), .zero(), …) on module weights inside _init_weights. Parameters carry internal flags tracking whether they still need re-initialization; in-place ops bypass them. Use the init primitives.

+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

يتحقق من أن كل فئة فرعية من PreTrainedModel تعرّف init تستدعي self.post_init(). في الملفات المعيارية، تُحتسب super().init() لأنها تنقل post_init الخاص بالفئة الأم. يؤدي post_init إلى إتمام مهمّات أساسية (تهيئة الأوزان، إعداد نقطة التفتيش للتدرّجات، …)؛ وإهماله يسبب أخطاءً خفية وقت التشغيل.

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

TRF014

أعلام trust_remote_code المستخدمة أو المُمرَّرة (مثلًا كحجج اسمية) في ملفات دمج النماذج الأصلية. يُحمِّل trust_remote_code أكوادًا عشوائية، بما في ذلك الملفات الثنائية — وهي ميزة قوية للمستخدمين، وليست شيئًا يمكن لدمج أصلي أن يعتمد عليه، لأن الكود البعيد لا يمكن مراجعته أو صيانته داخل 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

عندما يحدد فرع من PreTrainedModel قيمة غير فارغة للمفتاح _tied_weights_keys، يتحقق من وجود حقل tie_word_embeddings في ملف الإعداد المرافق. وبدون هذا الحقل، لا يستطيع المستخدمون التحكم في ربط الأوزان: إذ يقوم النموذج بالربط بشكل غير مشروط، مما يكسر عمليات التحويل المتبادلة للتسلسل (serialization round-trips) والتدريب الدقيق باستخدام رؤوس غير مرتبطة.

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

TRF016

عندما تُعلن فئة معالجة.py أو فيديو معالجة.py عن سمات منطقية (boolean) بصيغة do_* (مثل do_resize وdo_rescale وdo_normalize وdo_convert_rgb، ...) وتُعيد تعريف preprocess() أو _preprocess()، يتحقق من أن كل علم (flag) لا يزال يُستهلك هناك: إما بالإشارة إليه مباشرةً، أو بالتفويض عبر super().preprocess/_preprocess(…, **kwargs)، أو — لمعالجات الصور فقط — بالتحويل عبر _preprocess_image_like_inputs/_prepare_image_like_inputs. يُستثنى do_sample_frames: تستهلكه preprocess() الأساسية قبل تشغيل _preprocess(). أي do_X لا تشير إليه إعادة التعريف أبداً يكون معطلاً: فإعداد do_X=False لا يُحدث أي تأثير، وتُنفذ العملية على أي حال، وتُكسر إعادة التعريف لكل استدعاء بصمت.

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

في الفئات التي تحمل كلًا من @auto_docstring و@dataclass، يتم التحقق من أن @auto_docstring يأتي أولًا. تُطبَّق المُزيِّنات من الأسفل إلى الأعلى، لذا فإن @dataclass في الأعلى يُنفِّذ @auto_docstring أولًا، على فئة لا تحتوي بعد على init مُولَّدة: ثم يعدِّل doc الخاص بـ init للفئة الأم بدلًا من فئة الفرع.

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

TRF018

يتحقق من أن كل فئة فرعية من _init_weights(self, module, ...) تستدعي الفئة الأساسية عبر super()._init_weights(...). كما تقبل الملفات المعيارية القيم المرجعية PreTrainedModel._init_weights(self, module) وPreTrainedModel._init_weights(module) وraise AttributeError(...). إذا كان الهدف هو تجاوز كامل متعمد، فقم بتعطيل التحذير بوضع # trf-ignore: TRF018 فوق الدالة. تغطي الدالة الأساسية _init_weights أنواع الوحدات القياسية (Linear، Embedding، LayerNorm، RotaryEmbedding، ...). إذا تم تجاوزها، ستبقى جميع الوحدات الفرعية التي لا تغطيها الدالة المخصصة غير مهيأة — وهو ما يمر عبر الاختبارات ثم يظهر لاحقًا كخطأ دقيق في تهيئة الأوزان (انظر 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

يُصدر تحذيرًا عند وجود _defaults غير فارغ في قواميس *ProcessorKwargs من نوع TypedDict في ملفات processing_*.py. النماذج التي تم إصدارها قبل تاريخ الحد النهائي معفاة من هذا الشرط. إن تثبيت قيم _defaults يدويًا يؤدي إلى تشتيت إعدادات المعالج عبر مصادر Python، ويجعل تجاوزها من ملف الإعداد أمرًا غير مريح، كما يُثقل الكود. أما في processor_config.json على المنصة، فتنتقل هذه الإعدادات مع نقطة التفتيش ويمكن تحديثها دون الحاجة إلى أي تغيير في الكود.

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

TRF020

في المجلدات التي تحتوي على نماذج يُعلن فيها ملف الإعداد عن kv_lora_rank (انتباه كامن متعدد الرؤوس)، يتم فحص فئة الانتباه التي تمتلك إسقاط توسيع KV LoRA (kv_b_proj، أو أي nn.Linear(config.kv_lora_rank, ...)): يجب أن يكون التوسيع في طريقة مخصصة (مثل expand_kv) تستدعيها forward()، وليس مضمّناً في الكود. في الملفات المعيارية، تُحتسب الطريقة الموروثة من فئة أساسية مستوردة. الخوادم الخارجية (vLLM/SGLang) تتجاوز التوسيع لاستهلاك ذاكرة KV المضغوطة مباشرةً. إذا كان التوسيع مضمّناً في forward()، فلا يوجد ما يمكن تجاوزه، لذا يجب على الخادم إنشاء ذاكرة كاملة — مما يفقدنا توفير الذاكرة الذي صُمم MLA من أجله.

+    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

في نمذجة *.py و modular.py، تُستخدم الأعلام torch.tensor(<value>, ..., device=<non-cpu>) حيث <value> يُحلّ بشكل قاطع إلى قيمة قياسية بايثون — من الحروف الرقمية والحسابات عليها، حقول torch.finfo/iinfo، الدوال المدمجة التي تُرجع قيمًا قياسية واستدعاءات math.، المتغيرات المحلية المرتبطة مرة واحدة بالضبط، self.<attr> المُسندة في جسم الفئة، وحقول الإعدادات المُعلَّمة كـ int/float/bool في ملف الإعدادات المرافق (وفقًا لـ attribute_map). يُترك أي شيء قد يكون أيضًا تسلسلًا (eos_token_id: int | list[int] | None) أو لا يمكن حله كما هو. init، _init_weights، post_init و post_init معفاة: فهي لا تعمل أبدًا داخل منطقة التقاط. يُنشئ torch.tensor(<python scalar>, device=<accelerator>) القيمة على المضيف ثم ينسخها إلى الجهاز؛ يمنع التقاط مخطط CUDA ذلك النسخ، لذا لا يمكن التقاط النموذج. يملأ torch.full((), <value>, dtype=…, device=…) نفس المصفوفة ذات البُعد صفر على الجهاز بنواة قابلة للالتقاط وبدون مزامنة.

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

يتحقق من أن كل سلسلة نصية في قائمة _no_split_modules داخل ملف modeling*.py أو modular*.py تشير إلى فئة معرّفة في ذلك الملف، أو مستوردة إليه، أو معرّفة بواسطة وحدة أخت من نفس دليل النموذج. يكمل هذا الفحص TRF005 الذي يتحقق فقط من شكل القيمة. يطابق device_map هذه السلاسل النصية مع module.__class__.__name__ وقت التشغيل، لذا فإن الاسم القديم أو المكتوب بشكل خاطئ لا يطابق شيئاً ويُتجاهل بصمت — ما يعني أن الوحدة التي كان يجب إبقاؤها معاً قد تُقسَّم عبر الأجهزة. احذف العناصر التي تشير إلى فئات نموذج آخر بدلاً من تصحيحها: فـpost_init يجمع _no_split_modules من النماذج الفرعية بالفعل.

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

في نمذجة *.py و modular.py، يُعلَّم أي ثابت صحيح أكبر من 8 في حجة بُعد داخل مُنشئ torch.nn (Linear، Embedding، LayerNorm، RMSNorm، GroupNorm، BatchNorm، InstanceNorm، Convd، ConvTranspose*d، Bilinear، MultiheadAttention)، سواءً كانت الحجة موضعية أو عبر اسم المفتاح (in_features، out_features، in_channels، out_channels، num_embeddings، embedding_dim، embed_dim، normalized_shape، num_channels، hidden_size). تُتجاهل حُجج الشكل المشغّلة (kernel_size، stride، padding، num_groups)؛ وتبقى الثوابت حتى 8 نظيفةً لرؤوس القياسات، والتصنيفات الثنائية، وعدد قنوات RGB. النماذج المضافة قبل cutoff_date معفاة. تثبيت عرضٍ صلب يثبّت الوحدة على حجم نقطة تفتيش واحدة: نفس البنية عند مقياس آخر تُحمَّل مع عدم تطابق في الشكل، ولا يستطيع from_pretrained تحديد أي قيمة خاطئة لأن لا حقل في الإعداد يشير إليها. كما أنه يقسم مصدر الحقيقة، بحيث لم يعد تعديل الإعداد يغيّر النموذج الذي يُبنى.

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

في نمذجة *.py و modular.py، تُطلق الأعلام استدعاءً إلى مصنع الأقنعة (نقاط الدخول utils الخاصة بالتقنيع: create_causal_mask, create_bidirectional_mask, create_sliding_window_causal_mask, create_chunked_causal_mask, create_masks_for_generate، وأي دالة مساعدة باسم create_mask) داخل فئة لا ترث من PreTrainedModel — كتل nn.Module عادية مثل الطبقات ووحدات الانتباه والمشفّرات. بناء القناع هو عمل تعقيدُه O(مربع طول التسلسل) ولا يتغير من طبقة إلى أخرى، لذا فإن بناؤه داخل الطبقة يدفع هذا التكلفة مرة واحدة لكل طبقة، وتصبح كل طبقة مالكةً لقناعها الخاص، فلا يمكن بعد ذلك تسليم واجهات الخلفية (backends) الخاصة بالانتباه قناعًا واحدًا مُعدّ مسبقًا. ابنِ القناع مرة واحدة في النموذج ثم مرّره إلى الأسفل.

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

في نمذجة *.py و modular*.py، يُعلَّم أي فئة غير PreTrainedModel تعرّف فقط init و forward، وتُسند بالضبط خاصية واحدة self.<attr> في init، وكان جسم forward بالضبط return self.<attr>(...) لها (يتم تجاهل سلسلة التوثيق الأولية). أي طريقة أخرى، أو خاصية إضافية، أو عبارة قبل return، أو استدعاء super() في forward يعني أن الفئة تؤدي عملها الخاص. يضيف الغلاف مستوىً إلى كل اسم وزن، وإلى _no_split_modules، وخطط التوازي، وكل خرائط التحويل، دون حساب أي شيء — وعلى القراء فتح فئة إضافية واحدة لإدراك ذلك. فئات PreTrainedModel الفرعية معفاة: فهي موجودة من أجل from_pretrained والفئات التلقائية حتى عندما يقتصر forward على التفويض فقط.

-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

تُشير إلى أي عبارة assert في ملفات modeling*.py, modular.py و configuration_.py. حيث أن python -O يزيل هذه العبارات، مما يعني أن فحصًا للشكل أو الإعداد مكتوبًا كواحدة منها يختفي بصمت في عمليات التشغيل المُحسَّنة. كما أن مثل هذه العبارة تُولّد خطأ AssertionError عاريًا، بينما يمكن لخطأ ValueError تسمية القيمة المسببة للمشكلة وتحديد الإجراء المطلوب.

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

يتحقق من الأسطر الخمسة والعشرين الأولى في ملفات *.py، modular.py، configuration_.py، processing*.py، image_processing.py و videoprocessing.py بحثاً عن سطر Licensed under the <name> License يليه كل بند من فقرات الضمان القياسية، بدءاً من You may obtain a copy of the License at وصولاً إلى limitations under the License.. تُسطَّر الأسطر وتُحوَّل إلى أحرف صغيرة أولاً، لذا لا يؤثر تنسيق الأسطر أو أسلوب التعليقات. لا يتم التحقق من اسم الترخيص ولا من سطر حقوق النشر: إذ يختلفان من نموذج إلى آخر. إن تم تسليم ملف دون الترويسة، فإن ذلك يترك مصدره غامضاً، وإضافتها لاحقاً تعني تعديل ملف تم إصداره بالفعل. إن كان المطابقة تقتصر على Apache License فقط، فسيتم السماح بمرور العيوب التي تحدث فعلياً: فقرة مقطوعة في منتصفها، أو ترويسة تتوقف بعد الرابط، أو ترويسة مشوهة بسبب عملية بحث واستبدال خاطئة.

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

TRF029

في نمذجة *.py و modular*.py، يُعلَّم __init__ الذي يستقبل config إلى جانب معامل يُفهم من اسمه بوضوح أنه حقل في الإعداد (hidden_size, num_attention_heads, intermediate_size, head_dim, num_hidden_layers, embed_dim, dropout, eps, patch_size, rope_theta, …). المعامل الاختياري الذي يكون افتراضه None مستثنى من ذلك: فهو تجاوز وليس مصدرًا ثانيًا للحقيقة، وهو الطريقة التي تخدم بها فئة MLP واحدة كلًا من عرض الكثيف وعرض الخبير في نموذج MoE. أما الافتراض الصارم مثل hidden_size: int = 1024 فلا يُستثنى — فهو يتغلب على الإعداد كلما لم يمرر المستدعي شيئًا. kosmos2 مُدرج في القائمة البيضاء: فصفحة مستنداته لا يمكن اشتقاقها من اسم المجلد، لذا لا يمكن لقاعدة الاستثناء أن تغطيها. الآن أصبح لهذا الرقم مصدران للحقيقة، والمستدعي هو من يختار الفائز، لذا لم يعد تعديل الإعداد يغيّر النموذج الذي يُبنى. كما أنه يدفع معرفة البنية إلى كل موقع استدعاء، وهو مكان لا يناسبها.

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

في *.py للنمذجة وmodular*.py، تُعلَّم سلاسل السمات الجذرية عند config أو self.config التي يبلغ عمقها ثلاثة مستويات أو أكثر. config.hidden_size (قفزة واحدة) وconfig.text_config.hidden_size (قفزتان، وهو الوصول المعتاد إلى الإعداد الفرعي) مقبولان. يُحتسب انتهاك واحد لكل سطر. الوحدة التي تتصفح config.diffusion_config.atom_encoder_config.hidden_size تكون مترابطة مع هرمية الإعداد بأكملها بدلاً من شريحتها الخاصة، لذا لا يمكن إعادة استخدامها أو اختبارها أو تزويدها بإعداد فرعي مختلف. مرّر الإعداد الفرعي ذي الصلة إلى الأسفل فتتحول السلسلة إلى قفزة واحدة.

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

في *.py و modular*.py، يُصدر تحذيرًا لأي @dataclass على المستوى العلوي لا تتضمن قواعده أي شيء ينتهي بـ Output، ما لم يحتوي على حقلين إلزاميين أو أكثر — فهذه حزم حُجج داخلية، يرفضها ModelOutput وقت التشغيل. ولا يُفهرس كائن البيانات المخرجات العادي مثل المجموعة (tuple)، ولا يبقى صالحًا عند return_dict=False، وهو غير مرئي لـ @auto_docstring، لذا لا تصل حقوله أبدًا إلى توثيق الواجهة البرمجية المولّدة. أما ModelOutput فيحصل على المزايا الثلاث مجانًا.

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

TRF032

في نمذجة *.py و modular*.py، تُستدعى الأعلام fill, masked_fill، و full، و full_like، و new_full باستخدام قيمة رقمية سالبة ذات مقدار 1e3 أو أكبر. القيمة الثابتة -1e9 تتجاوز الحد الأقصى لتصبح -inf في float16، وهي ليست قريبة على الإطلاق من أصغر قيمة في float32، لذا فإن نفس القناع يتصرف بشكل مختلف حسب نوع البيانات (dtype) وقد ينتج قيم NaN بعد softmax. torch.finfo(dtype).min هو أصغر قيمة قابلة للتعبير في نوع البيانات (dtype) الفعلي قيد التشغيل.

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

TRF033

في نمذجة *.py و modular*.py، تُحدَّد الطرق التي يبدأ اسمها بـ set_، باستثناء طرق عقد PreTrainedModel وهي set_input_embeddings و set_output_embeddings و set_decoder و set_encoder و set_attn_implementation و set_default_language. تجعل طريقة التعيين (setter) السلوك يعتمد على ترتيب الاستدعاء: فالقيمة غير موجودة في الإعداد (config)، لذا لا تُحفظ، ولا تُستعاد بواسطة from_pretrained، ولا تكون مرئية لتخطيط خريطة الأجهزة (device-map) أو تخطيط التوازي. على المستخدمين أن يعرفوا بضرورة استدعائها، وإهمال ذلك يحدث بصمت.

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

تُعلَّم تعليقات # noqa، سواءً كانت تحمل أكوادًا أم لا، في ملفات modeling*.py, modular.py and configuration_.py. في ملف modular، تُقبل الأكواد F401 وF821 وF822: فهو مصدر توليد لا يُعرّف عمدًا كل الأسماء التي يستخدمها، لذا تُطلق أدوات ruff الخاصة بالأسما غير المعرّفة على كود صحيح — إدخالات __all__ التي يملؤها المحوّل، فئات موجودة في النموذج الأب، واستيرادات تُحفظ لإعادة تصديرها. تُتخطى أي علامة # noqa تُسمّي تلك الأكواد فقط؛ أما أي علامة تُسمّي أي شيء آخر فتُبلَّغ عن الأكواد المتبقية. تُبلَّغ دائمًا عن علامة # noqa عارية؛ وفي ملف modular، تطلب الرسالة ذكر الكود بدلًا من إعادة الكتابة. ملفات النموذج هي كود عادي يخضع لقواعد lint المستودع، لذا فإن أي كبت يعني أن المشكلة الكامنة تُركت في مكانها — كما أن علامة # noqa العارية تخفي أيضًا كل انتهاك مستقبلي على تلك السطر. صحّح الكود بدلًا من ذلك.

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

TRF036

في *.py و modular*.py، يُنبّه إلى أي بناء من نوع nn.Sequential(...). تُسمّي nn.Sequential(...) أبنائها حسب موضعها، فتقع الأوزان عند mlp.0.weight وmlp.2.weight: إذ تشير خريطة التحويل و_tied_weights_keys وكل خطة موازاة إلى الفهارس، وإدراج طبقة يُغيّر أسماء كل ما يليها. كما تُخفي دالة forward، فتكون عمليات تحويل dtype والبقايا بين الخطوات غير مرئية في مواضع حدوثها.

-        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

في *.py and modular*.py، تُبلّغ الأعلام عن استدعاءات einsum، مع الإبلاغ عن المعادلة عندما تكون نصيةً حرفية. تكون هذه الميزة معطّلةً افتراضيًا: فـeinsum قد تكون في بعض الأحيان الطريقة الأوضح للتعبير عن عملية انكماش، لذا فهي خيارٌ اختياري وليس قاعدةً صارمة. تُشفّر معادلة einsum الأشكال في رمزٍ يحتاج القارئ إلى فكّ تشفيره، كما تُخفي المعادلة المبنية ديناميكيًا أيّ عملية انكماش هي التي تُنفَّذ فعليًا. قم بتوسيعها إلى عمليات 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

يتحقق من أن كل ملف *.py, processing.py, imageprocessing.py, videoprocessing.py, featureextraction.py و tokenization*.py يحتوي على ملف اختبار مطابق في tests/models/<model>/test.py (modelingacme.py -> tests/models/acme/test_modeling_acme.py). الاستثناءات: configuration.py، الذي تغطيه ConfigTester في ملف الاختبار المرافق testmodeling.py، و tokenization_utils.py، وهو ملف مساعد تغطيه اختبارات مُجزّئته؛ كما أن ملف tokenization<name>_fast.py يُطابق ملف اختبار نظيره البطيء. يمكن لملف modular*.py أن يحدد عدة عائلات في آنٍ واحد، لذا تُوجَّه فئاته حسب لاحقة الاسم — XxxModel/XxxPreTrainedModel/XxxFor<Task> للنمذجة، XxxImageProcessor(Fast) لمعالجة الصور، XxxProcessor للمعالجة، XxxVideoProcessor لمعالجة الفيديو، XxxFeatureExtractor لاستخراج الميزات، XxxTokenizer(Fast) للتجزئة، و XxxConfig يُتجاهل — ويُبلَّغ عن مخالفة واحدة لكل ملف اختبار مفقود. لا يُدعم # trf-ignore: TRF038؛ استخدم allowlist_models حتى يكون الاستثناء مرئياً أثناء المراجعة. ملف المصدر الذي لا يملك ملف اختبار يفتقر إلى تغطية الانحدار: فقد يُدمج مسار أمامي معطوب، أو خرائط تحويل خاطئة، أو مُجزِّئ يتجاهل رمزاً خاصاً، ويبقى معطوباً إلى أجل غير مسمى. إعداد وهمي بأوزان عشوائية، أو مفردات صغيرة مكتوبة يدوياً، يكفي لاختبار أيٍّ منها.

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

يكتشف كتل if is_*_available(): import ... (بما في ذلك التوليفات مثل is_vision_available() and is_torch_available()) ويضع علامة على الاستيراد عندما لا يُشار إلى الاسم في أي مكان آخر من الملف، بما في ذلك داخل تلميحات الأنواع النصية وall. فحص الاستيرادات غير المستخدمة في ruff لا ينظّف هذه الكتل: فالاستيراد قابل للوصول، لذا تبدو الكتلة سليمة في حد ذاتها. بمجرد أن لم تعد إعادة الهيكلة بحاجة إلى PIL.Image أو torch أو غيرهما، يبقى الاستيراد المحمي كحمولة ميتة وإشارة مضللة حول التبعيات الفعلية للملف.

if is_vision_available():
-    from PIL import Image

TRF040

في نمذجة *.py و modular*.py، تكتشف الأعلامُ الطرقَ المُزيَّنة بكلٍّ من @capture_outputs و @can_return_tuple. تكمل هذه القاعدة القاعدة TRF003 التي تغطي تفرّع return_dict اليدوي داخل forward(). نظرًا لأن كلا المُزيِّنَين يُزيلان return_dict، فإن المُزيِّن الخارجي فقط هو الذي يرى القيمة الحقيقية. كما أن @capture_outputs يتعامل بالفعل مع التحويل إلى to_tuple، مما يجعل @can_return_tuple زائدًا.

-@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

في tests/models//testtokenization.py، يتحقق الملف من أنه يُعرّف فئة اختبار مجمّعة ترث عن TokenizerTesterMixin. تُحتسب فقط الفئات التي يجمعها المنفّذ — أي فئة الأساس TestCase، أو تسمية *Test عندما تكون الفئة الأساسية لفئة اختبار نموذج آخر — لذا تُتخطى الملفات التي تحتوي على دوال مساعدة فقط، ولا يستوفي دمج فئة مساعدة في المجموعة شرط وجود فئة اختبار حقيقية. يُتبع الوراثة عبر الفئات الأساسية في نفس الملف وإلى اختبار توكنايزر نموذج آخر مستورد بالاسم؛ ولا تُحتسب الفئة الأساسية غير القابلة للحل. يُبلَّغ عن أول فئة اختبار لا تشغّل المجموعة. auto مدرج في القائمة البيضاء: فاختبار test_tokenization_auto.py يختبر حل AutoTokenizer وليس توكنايزر. TokenizerTesterMixin هو المكان الذي تُفحص فيه فعلياً عمليات الترميز/فك الترميز ذهاباً وإياباً، والتوسيط والقطع، ومعالجة الرموز الخاصة، واستمرارية الرموز المضافة، وتكافؤ الحفظ/التحميل. اختبار يكتفي بمطابقة قوائم معرّفات رموز قليلة مكتوبة يدوياً سيمرّ بينما يكون التوكنايزر معطلاً في كل واحدة منها، ويبدو معتمداً في المراجعة.

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

TRF043

يُصدر تحذيرًا عند وجود معامل position_ids مُعلَن في توقيع forward للفئات التي تنتهي أسماءها بـ Attention. يُستهلك position_ids في المراحل اللاحقة بواسطة تدريب flash-attention بدون حشو، ويجب أن يمر عبر kwargs. تسميته في التوقيع يستهلكه قبل أن تتمكن واجهة الانتباه من قراءته؛ بينما تمرر معيار llama position_embeddings بالإضافة إلى kwargs.

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

TRF044

يُصدر تحذيرًا لأي معامل باسم position في أي دالة داخل modeling.py و modular_.py. تم حذف cache_position من جميع النماذج في الإصدار v5. إعادة تقديمه (عادةً عن طريق نسخه من مصادر ما قبل v5) يؤدي إلى تمرير معامل غير مستخدم عبر كل طبقة؛ حيث يتم تحديث الكاش عبر past_key_values.update(key_states, value_states, self.layer_idx)، دون أي تمرير للموضع.

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

يُشير إلى أن المعاملات attentions وoutput_hidden_states وreturn_dict في توقيعات forward داخل ملفات modeling.py وmodular_.py أصبحت من الإصدارات القديمة. النماذج المُساهَم بها قبل تاريخ الإيقاف معفاة من ذلك. يتولى مكدس الزخارف (decorators) التحكم في المخرجات: حيث يحلّ @capture_outputs أعلام output* مقابل الإعدادات (config) ويسجّل الموترات (tensors) عبر _can_record_outputs، بينما يتولى @can_return_tuple معالجة return_dict. الإعلان عن هذه المعاملات في التوقيع يعيد إدخال تمرير الأعلام يدوياً، مما يؤدي إلى انحراف عن سلوك الزخارف.

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

TRF046

تعيينات الأعلام إلى سمات self في طرق forward داخل ملفات modeling*.py و modular*.py. الكتابة إلى الحالة أثناء forward تُكسر معالجة الدفعات (batching) وtorch.compile والقدرة على الاستدلال حول الوحدة. تُمرَّر الحالة المحمولة بشكل صريح (كائنات الكاش، حلقة generate)؛ أما القيم التي تعتمد فقط على config أو الأشكال الثابتة فتُوضع في 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

تعيينات الأعلام إلى سمات self في preprocess، preprocess، call و post_process* methods في image_processing.py و videoprocessing.py. المعالج الذي يحمل حالة بين الاستدعاءات يكسر معالجة الدفعات من نوع preprocess-then-postprocess: فعملية preprocess الثانية تكتب فوق الحالة التي تحتاجها عملية postprocess الأولى. أعد القيمة أو مررها عبر سلسلة الدوال.

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

تُحدِّد قائمة/مجموعة/مجموعة غير مرتبة (list/tuple/set) الحرفية في إعلانات _tied_weights_keys على مستوى الفئة. في الإصدار v5، تغيّر _tied_weights_keys إلى قاموس (dict) يربط كل معامل مستهدف مرتبط (tied target parameter) بمصدره. لم تعد صيغة القائمة (list) تُبيّن أيّ معامل هو المصدر، مما يؤدي إلى سلوك خاطئ صامت في عمليات الربط (tying) وحساب device_map والحفظ.

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

TRF049

تُستدعى أعلام init في طرق init داخل ملفات modeling*.py و modular.py: بدائل nn.init. / init.* والمُهيّئات التي تُعدّل في الموقع على المعاملات الخاصة (self.weight.data.normal_()). تُنشأ النماذج على جهاز meta، لذا تُستبعد قيم المصفوفات المكتوبة في init قبل التحميل: المعامل الذي يُهيّأ فقط هناك يحتفظ بمحتوى عشوائي عند الضبط الدقيق من الصفر أو بعد إعادة تحميل جهاز meta. قسّم باستخدام torch.empty في init، وابدأ التهيئة في _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

تُوجَّه استدعاءات إلى فئة *RotaryEmbedding* في init للفئات التي تنتهي أسماؤها بـ Attention. تمتلك النموذج rotary_emb واحدة، وتبني inv_freq مرة واحدة، وتُمرِّر cos/sin إلى الأسفل على شكل position_embeddings. وجود وحدة rotary واحدة لكل طبقة انتباه يؤدي إلى تكرار المخازن المؤقتة، وإعادة حساب الترددات في كل طبقة، والانحراف عن العقد التي تنص على أن الانتباه يستقبل 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

يقارن الأعلام (Flags) مع سمة attn_implementation في modeling.py و modular_.py. وينتمي توجيه الخلفية (Backend dispatch) إلى ALL_ATTENTION_FUNCTIONS.get_interface، بينما تنتمي معالجة المصفوفات (Tensor munging) المشروطة بالخلفية (مثل الحشو وإعادة التشكيل) إلى الأغلّاف المشتركة (shared wrappers) الموجودة تحت integrations/. يؤدي التفرّع المضمّن (Inline branching) إلى إبقاء جسم النموذج واعياً بالنواة (kernel-aware)، لكنه ينكسر عند تسجيل خلفيات جديدة.

-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

تُشير إلى إسنادات على مستوى الوحدة إلى أسماء تنتهي بـ ATTENTION_CLASSES في modeling.py و modular_.py. ففئات الانتباه الخاصة بكل منصة، والتي تُختار من قاموس، هي النمط المستخدم قبل الواجهة: فالفئات المتطابقة تقريباً تنحرف عن بعضها البعض، ولا تصل نوى الانتباه المسجلة في ALL_ATTENTION_FUNCTIONS إليها أبداً. تستبدل فئة انتباه واحدة تُوجَّه عبر الواجهة هذا القاموس؛ فلا تنقلها من فئة سابقة قديمة.

-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

في *.py و modular*.py، تُعلَّم إسنادات الأعلام التي تبني logits/shift_labels (والنسخ المُزاحة عن طريق التقطيع، كما في labels[…, 1:]. استلام تسميات مُزاحة مسبقًا (shift_labels = kwargs.pop(“shift_labels”, labels)) هو التعبير الصحيح ولا يُعلَّم عليه. يقوم self.loss_function بإزاحة التسميات بنفسه، لذا فإن الإزاحة المسبقة في كود النموذج تؤدي إلى التدريب على أهداف مُزاحة مرتين أو تفرض مسار خسارة مخصصًا. تمرّ النماذج الحصرية للوحّاد التوليد (Decoder-only) التسميات الخام وتترك لدالة الخسارة إزاحتها. أما نماذج المُشفِّر-المُفكِّك (Encoder-decoder) فهي الحالة المعاكسة: فالتسميات فيها مُزاحة مسبقًا بواسطة رمز بداية المُفكِّك المُضاف في البداية، لذا تمرّر shift_labels=labels لإيقاف دالة الخسارة عن الإزاحة مرة أخرى.

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

أعلام config = SomeConfig كخاصية صنف في فئات PreTrainedModel الفرعية داخل ملفات modeling*.py و modular*.py. يستنتج PreTrainedModel.__init_subclass__ قيمة config_class من config تسمية، وذلك عبر inspect.get_annotations(cls). يؤدي التعيين إلى إنشاء خاصية صنف زائدة لا يبلغ عنها الاستدعاء، وبالتالي يحتفظ الصنف الفرعي بصمت بقيمة config_class الخاصة بالصنف الأب. أما التسمية المجردة فلا تملك قيمة وقت التشغيل، ولا تنشئ أي خاصية، وتُلتقط بشكل صحيح.

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

TRF056

في نمذجة *.py و modular*.py، تُسبّب الأعلام .item() واستدعاءات .tolist() داخل أي forward انقطاعاً في الرسم البياني. أما استدعاء .tolist() الذي يغذّي معامل حجم التقسيم في split(...) فهو مستثنى من ذلك، لأن torch.split يحتاج إلى أعداد صحيحة من نوع Python. كلا الاستدعاءين يقرآن متجه tensor ويعيدانه إلى المضيف، وهو ما لا يستطيع dynamo تتبّعه، مما يؤدي إلى انقطاع الرسم البياني.

-        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

يتحقق @auto_docstring من فئات PreTrainedModel العامة (<Model>PreTrainedModel، <Model>Model، <Model>For<Task>، والعناصر الهيكلية)، ومن فئات PreTrainedConfig، ومن فئات ModelOutput، ومعالجات الصور، ومن فئات ProcessorMixin، وكذلك من طرقها العامة forward، get_image_features، get_video_features، get_audio_features، get_text_features، preprocess و__call__. يتم فحص modular_*.py مقابل الملفات المولّدة منه. وبدونه، تُشحن الفئة دون مقدمة ودون توثيق للمعاملات، وتُشحن الطريقة دون توثيق للحجج، ودون قسم Returns، ودون مثال على الاستخدام — وكل ذلك يجب كتابته يدوياً لكل نموذج بدلاً من أن يأتي من 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

في نمذجة *.py و modular*.py، تُسجَّل استدعاءات register_buffer("<name>", ...) التي يكون اسم المخزن فيها حرفًا نصيًا ثابتًا، على أي مستقبل (self، أو وحدة أخرى مثل layer.mamba). أما الاسم المحسوب — متغير أو سلسلة f، على سبيل المثال مخزن واحد لكل طبقة داخل حلقة — فلا يوجد له مكافئ من إسناد السمات، لذا يُستثنى من ذلك. نظرًا لأن torch>=2.5 nn.Buffer يسجّل مخزنًا من خلال إسناد سمة عادي، تمامًا مثل nn.Parameter. فالمخزن الذي يُنشأ عبر استدعاء دالة لا يوجد إلا كأثر جانبي لتشغيل __init__، لذا فإن ملفًا modular يريد تعديل مخزن واحد عليه إعادة تعريف __init__ بالكامل. وبإسناده كسمة، يمكن وراثته وتجاوزه بشكل مستقل.

-        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

بالنسبة إلى مديريات النماذج التي يخصص مخطط التوازي فيها للتensors moe_tp_experts، يتحقق من أن فئات *Experts الموجَّهة تستقبل حالات المخفية، والفهارس الأعلى k، وأوزان التوجيه الأعلى k كأول ثلاثة حواجز موضعية في forward. تُقبل الأسماء المستعارة مثل selected_experts وrouting_weights. يطبّق MoeExpertsParallel تحويلًا على التدرج للحاجز الموضعي الثالث. إذا كانت التوقيع مختلفًا، فسيُطبَّق ذلك على tensor خاطئ بصمت أو يُتخطى.

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

كبت الانتهاكات

إذا كنت بحاجة إلى كبح انتهاك قاعدة، فاستخدم أحد الخيارين أدناه.

القمع المضمّن

أضِف تعليق # trf-ignore: RULE_ID على السطر المخالف. تضمّن شرحًا حتى يفهم المراجعون سبب مبرر الكبت.

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

لا تستخدم trf-ignore لإسكات المخالفات التي يجب تصحيحها في الكود.

allowlist_models

بالنسبة إلى النماذج التي تحتوي على أكواد قديمة لا يمكن إصلاحها على الفور، أضف اسم مجلد النموذج إلى قائمة allowlist_models في القاعدة ذات الصلة داخل mlinter rules.toml.

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