Transformers documentation
모델 구조 규칙
모델 구조 규칙
Transformers는 모든 modeling_*.py, modular_*.py, configuration_*.py 파일에 대해 일련의 정적 규칙을 적용합니다. mlinter 패키지가 체크어 엔진을 제공하며, 저장소는 활성 규칙 집합을 utils/rules.toml에 유지합니다. 이 로컬 TOML을 사용하면 새로운 transformers-mlinter 릴리스를 기다리지 않고도 규칙을 빠르게 활성화, 비활성화하거나 조정할 수 있습니다.
완전한 checker 참고 자료 및 사용법에 대해서는 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 branch 대비 수정한 파일만 검사합니다. 래퍼 대신 mlinter를 직접 실행하는 경우, 로컬 오버라이드가 적용되도록 --rules-toml utils/rules.toml를 전달하세요.
위반 사항 수정
규칙 위반이 감지되면 오류는 다음과 같이 표시됩니다:
src/transformers/models/acme/modeling_acme.py:18: TRF013: AcmeModel.__init__ does not call self.post_init().rules reference에서 rule ID를 사용하여 수정 방법을 확인합니다. PreTrainedModel 하위 클래스가 self.post_init()을 호출하지 않을 때 TRF013이 트리거됩니다. 해당 메서드는 필수적인 최종 처리 단계를 수행하며, 이를 생략하면 런타임 버그가 발생합니다.
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
<Model>PreTrainedModel의 config_class가 <Model>Config으로 이름이 지어졌는지 확인합니다. 이름이 일치하지 않으면 로딩, auto 클래스, 개발자의 기대치에 문제가 생길 수 있습니다.
class AcmePreTrainedModel(PreTrainedModel):
- config_class = WileConfig
+ config_class = AcmeConfigTRF002
base_model_prefix가 공백이 없는 비어 있지 않은 문자열 리터럴인지 확인합니다. 잘못된 접두사는 가중치 로딩 키 매핑 및 베이스 모델 접근을 깨뜨릴 수 있습니다.
class AcmePreTrainedModel(PreTrainedModel):
- base_model_prefix = ""
+ base_model_prefix = "model"TRF003
forward에서 구식 if not return_dict: return (x,) 패턴을 감지합니다. 수동 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의 shape를 확인합니다. 잘못된 값은 device-map 파티셔닝과 샤딩을 깨뜨릴 수 있습니다.
-_no_split_modules = [SomeLayerClass, ""]
+_no_split_modules = ["AcmeDecoderLayer", "AcmeAttention"]TRF006
forward 시그니처의 cache 인자가 본문에서 사용되었는지 확인합니다. 사용되지 않는 cache 인자는 캐싱 지원이 불완전하고 API 동작이 일관되지 않음을 시사합니다.
def forward(self, x, past_key_values=None, use_cache=False):
+ if use_cache:
+ ...
return xTRF007
init에서 self.post_init() 호출 이후에 self 속성이 할당되는지 확인합니다. 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
configuration*.py와 modular*.py에 있는 PreTrainedConfig/PretrainedConfig의 직접 하위 클래스에 @strict(accept_kwargs=True)가 있는지 확인합니다. 이 데코레이터가 없으면 새로운 config가 저장소의 런타임 타입 검증 계약에서 누락되고, dataclass 기반 config 표준에서 벗어납니다.
+@strict(accept_kwargs=True)
class AcmeConfig(PreTrainedConfig):
...TRF011
PreTrainedModel 하위 클래스의 forward()에서, self.layers에 대한 반복 변수 및 self.<submodule>.<attr> (여기서 <attr>가 표준 nn.Module 속성이 아닌 경우)에 대한 플래그 서브모듈 속성 접근은 torch.nn.Identity가 갖지 않는 접근입니다. 파이프라인 병렬화는 어떤 서브모듈이든 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
init를 정의하는 모든 PreTrainedModel 하위 클래스가 self.post_init()을 호출하는지 확인합니다. 모듈러 파일에서는 super().init()도 해당되며, 이는 부모의 post_init을 전파하기 때문입니다. post_init은 가중치 초기화, gradient checkpointing 설정 등 필수적인 최종 처리를 수행하므로, 이를 건너뛰면 미묘한 런타임 버그가 발생합니다.
class AcmeModel(AcmePreTrainedModel):
def __init__(self, config):
super().__init__(config)
self.layers = nn.ModuleList(...)
+ self.post_init()TRF014
네이티브 모델 통합 파일에서 사용되거나 전달(예: kwarg로)되는 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 필드가 있는지 동반 구성 파일(companion configuration file)을 검사합니다. 이 필드가 없으면 사용자가 가중치 연결(weight tying)을 제어할 수 없으며, 모델은 무조건적으로 가중치를 연결하게 되어 직렬화 왕복(serialization round-trip)과 연결되지 않은 헤드(untied heads)를 사용한 미세 조정(fine-tuning)이 깨집니다.
# configuration_foo.py
@strict(accept_kwargs=True)
class FooConfig(PreTrainedConfig):
hidden_size: int = 768
+ tie_word_embeddings: bool = TrueTRF016
이미지 processing.py 또는 비디오 processing.py 클래스가 do_* 부울 속성(do_resize, do_rescale, do_normalize, do_convert_rgb, …)을 선언하고 preprocess() 또는 _preprocess()를 오버라이드하는 경우, 각 플래그가 해당 메서드에서 여전히 소비되는지 확인합니다. 직접 참조되거나, 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
와 를 모두 가진 클래스의 경우, 가 먼저 적용되는지 확인합니다. 데코레이터는 아래에서 위로 적용되므로, 위에 있는 는 아직 합성된 init가 없는 클래스에서 를 먼저 실행합니다. 그러면 서브클래스의 init 대신 부모 클래스의 doc를 수정하게 됩니다.
-@dataclass
@auto_docstring(
custom_intro="""
Output type of `AcmeForPreTraining`.
"""
)
+@dataclass
class AcmeForPreTrainingOutput(ModelOutput):
...TRF018
모든 _init_weights(self, module, ...)를 오버라이드하는 PreTrainedModel 하위 클래스가 super()._init_weights(...)를 통해 체이닝되는지 확인합니다. 모듈러 파일은 PreTrainedModel._init_weights(self, module), PreTrainedModel._init_weights(module), raise AttributeError(...)라는 센티널도 허용합니다. 의도적인 전체 오버라이드의 경우, 해당 메서드 위에 # trf-ignore: TRF018를 추가하여 억제할 수 있습니다. 기본 _init_weights는 표준 모듈 유형(Linear, Embedding, LayerNorm, RotaryEmbedding, …)을 처리합니다. 이를 건너뛰면 오버라이드가 놓친 모든 서브모듈이 초기화되지 않은 상태로 남아, 테스트는 통과하지만 이후에 미묘한 weight-init 버그로 드러나는 문제가 발생합니다(참조: 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
processing_*.py의 *ProcessorKwargs TypedDict에서 비어 있지 않은 _defaults를 플래그로 표시합니다. 기준일 이전에 릴리스된 모델은 예외로 처리됩니다. 하드코딩된 _defaults는 프로세서 설정을 Python 소스에 분산시켜 config에서 오버라이드하기 어렵고, 코드 부피를 증가시킵니다. 허브의 processor_config.json에는 체크포인트와 함께 저장되어 코드 변경 없이 업데이트할 수 있습니다.
class Gemma4ProcessorKwargs(ProcessingKwargs, total=False):
- _defaults = {
- "text_kwargs": {"padding": False},
- "images_kwargs": {"return_tensors": "pt"},
- }
images_kwargs: Gemma4ImageProcessorKwargsTRF020
kv_lora_rank가 선언된 설정을 가진 모델 디렉터리에서, KV LoRA 확장 투영(kv_b_proj 또는 nn.Linear(config.kv_lora_rank, ...))을 소유하는 attention 클래스를 검사합니다. 확장 로직은 forward()가 호출하는 전용 메서드(예: expand_kv)에 포함되어야 하며, 인라인으로 작성되어서는 안 됩니다. 모듈러 파일의 경우, 가져온 베이스 클래스에서 상속받은 메서드도 해당됩니다. 외부 백엔드(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
modeling*.py와 modular.py에서, <value>가 Python 스칼라로 증명 가능하게 해석되는 경우 torch.tensor(<value>, ..., device=<non-cpu>) 플래그를 설정합니다. 여기에는 숫자 리터럴 및 이에 대한 산술 연산, torch.finfo/iinfo 필드, 스칼라를 반환하는 내장 함수 및 math. 호출, 정확히 한 번만 바인딩되는 로컬 변수, 클래스 본문에서 할당되는 self.<attr>, 그리고 동반 구성 파일에서 int/float/bool로 주석 처리된 config 필드(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=…)는 동일한 0-d 텐서를 캡처 가능한 커널과 동기화 없이 디바이스에서 채웁니다.
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
modeling*.py 또는 modular*.py 파일의 _no_split_modules 목록에 포함된 모든 문자열이 해당 파일에서 정의된 클래스, 해당 파일로 가져온 클래스, 또는 동일한 모델 디렉터리의 형제 모듈에서 정의된 클래스를 가리키는지 확인합니다. 값의 형태만 검사하는 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 = 24TRF024
modeling*.py와 modular.py에서, torch.nn 생성자(Linear, Embedding, LayerNorm, RMSNorm, GroupNorm, BatchNorm, InstanceNorm, Convd, ConvTranspose*d, Bilinear, MultiheadAttention)의 차원 인자에서 8보다 큰 정수 리터럴을 플래그합니다. 이는 위치 인자나 키워드 인자(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 이전에 추가된 모델은 예외입니다. 하드코딩된 폭은 모듈을 하나의 체크포인트 크기에 고정시킵니다. 동일한 아키텍처를 다른 스케일로 로드하면 shape mismatch가 발생하며, from_pretrained는 어떤 값이 잘못된지 알 수 없습니다. 왜냐하면 해당 값을 가리키는 config 필드가 없기 때문입니다. 또한 진실의 원천이 분리되어, config를 수정해도 실제로 빌드되는 모델이 변경되지 않습니다.
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
modeling*.py와 modular.py에서, PreTrainedModel을 상속하지 않는 클래스(레이어, 어텐션 모듈, 인코더와 같은 일반 nn.Module 블록) 내부에서 마스크 팩토리를 호출하는 플래그입니다(마스킹 utils 엔트리포인트인 create_causal_mask, create_bidirectional_mask, create_sliding_window_causal_mask, create_chunked_causal_mask, create_masks_for_generate 및 기타 create_mask 헬퍼 포함). 마스크 생성은 시퀀스 길이의 제곱에 비례하는 O(n^2) 작업이며 레이어마다 변하지 않으므로, 레이어에서 생성하면 레이어마다 해당 비용을 한 번씩 지불하게 됩니다. 또한 각 레이어가 자체 마스크를 소유하게 되어 어텐션 백엔드에 단일 준비된 마스크를 전달할 수 없게 됩니다. 모델에서 한 번만 생성한 후 아래로 전달하세요.
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
modeling*.py와 modular*.py에서, init과 forward만 정의하고 init에서 정확히 하나의 self.<attr>를 할당하며, forward 본문이 해당 속성에 대해 정확히 return self.<attr>(...)인(선두 docstring은 무시됨) PreTrainedModel이 아닌 클래스를 플래그합니다. forward에서 다른 메서드, 추가 속성, return 이전의 문, 또는 super() 호출이 있으면 해당 클래스가 자체 작업을 수행한다고 간주됩니다. 래퍼는 모든 가중치 이름, _no_split_modules, 병렬화 계획 및 모든 변환 매핑에 한 수준의 계층을 추가하지만 아무것도 계산하지 않으며, 이를 확인하려면 독자가 한 개 더 클래스를 열어야 합니다. PreTrainedModel 하위 클래스는 예외입니다. forward가 위임만 하더라도 from_pretrained와 auto 클래스를 위해 존재하기 때문입니다.
-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
Flags any assert statement in modeling*.py, modular.py and configuration_.py. python -O strips asserts, so a shape or config check written as one silently disappears in optimised runs. An assert also gives a bare AssertionError, where a ValueError can name the offending value and say what to do about it.
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
modeling*.py, modular.py, configuration_.py, processing*.py, image_processing.py, videoprocessing.py 파일의 첫 25줄을 검사하여 Licensed under the <name> License 줄 뒤에 표준 보증 문단의 모든 조항이 You may obtain a copy of the License at부터 limitations under the License.까지 연속으로 존재하는지 확인합니다. 줄은 먼저 평탄화되고 소문자로 변환되므로 줄바꿈이나 주석 스타일은 검사 대상이 아닙니다. 라이선스 이름과 저작권 줄은 모델마다 달라지므로 검사하지 않습니다. 헤더 없이 배포된 파일은 출처가 불분명해지며, 나중에 헤더를 추가하려면 이미 릴리스된 파일을 수정해야 합니다. Apache License만 일치하도록 검사하면 실제로 발생하는 결함을 놓치게 됩니다. 예를 들어 문단이 중간에 잘리거나, 헤더가 URL 이후로 중단되거나, 잘못된 검색 및 치환으로 손상된 경우 등이 해당됩니다.
+# Copyright 2026 The HuggingFace Team. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# ...
"""PyTorch Acme model."""TRF029
modeling*.py와 modular*.py에서, __init__가 config와 함께 이름이 명확하게 config 필드(hidden_size, num_attention_heads, intermediate_size, head_dim, num_hidden_layers, embed_dim, dropout, eps, patch_size, rope_theta, …)인 인수를 받는 경우를 플래그로 표시합니다. 기본값이 None인 선택적 파라미터는 예외입니다. 이는 오버라이드이지 두 번째 진실의 원천이 아니며, 하나의 MLP 클래스가 MoE 모델의 dense 폭과 expert 폭을 모두 처리하는 방식입니다. hidden_size: int = 1024와 같은 하드코딩된 기본값은 예외가 아닙니다. 호출자가 아무것도 전달하지 않으면 config보다 우선 적용됩니다. kosmos2는 허용 목록에 포함되어 있습니다. 해당 문서 페이지가 디렉터리 이름에서 유도될 수 없으므로, 기준 시점 이전의 예외 적용(cutoff) 대상이 될 수 없습니다. 이제 동일한 값에 두 개의 진실의 원천이 존재하며 호출자가 어느 쪽이 우선하는지 선택하게 되므로, config를 수정해도 실제로 빌드되는 모델은 더 이상 변경되지 않습니다. 또한 아키텍처 지식을 그 자리에 속하지 않는 모든 호출 지점으로 밀어냅니다.
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_sizeTRF030
modeling*.py와 modular*.py에서 config 또는 self.config를 루트로 하는 3단계 이상의 속성 체인을 플래그합니다. config.hidden_size(1단계)과 config.text_config.hidden_size(2단계, 일반적인 서브-설정 접근)는 허용됩니다. 한 줄당 위반 사항이 하나만 표시됩니다. config.diffusion_config.atom_encoder_config.hidden_size를 탐색하는 모듈은 자신의 슬라이스가 아닌 전체 설정 계층에 결합되어 있어 재사용, 테스트 또는 다른 서브-설정 적용이 불가능합니다. 관련 서브-설정을 하위로 전달하면 체인이 1단계로 축소됩니다.
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
modeling*.py와 modular*.py에서, 상위 레벨 @dataclass의 베이스 클래스에 Output로 끝나는 이름이 하나도 없고 필수 필드가 2개 미만인 경우를 플래그로 표시합니다. 이러한 클래스는 내부 인자 번들이며, ModelOutput은 런타임에서 이를 거부합니다. 일반 출력 dataclass는 튜플처럼 인덱싱할 수 없고, return_dict=False를 유지하지 못하며, @auto_docstring에 의해 감지되지 않으므로 해당 필드가 생성된 API 문서에 반영되지 않습니다. ModelOutput은 이 세 가지 장점을 모두 자동으로 제공합니다.
@auto_docstring
@dataclass
-class AcmeStructureOutput:
+class AcmeStructureOutput(ModelOutput):
positions: torch.Tensor
confidence: Optional[torch.Tensor] = NoneTRF032
modeling*.py와 modular*.py에서, 절댓값이 1e3 이상인 부정(負定) 숫자 리터럴로 호출된 maskedfill, masked_fill, full, full_like, new_full 플래그가 감지되었습니다. 하드코딩된 -1e9는 float16에서 -inf로 오버플로우되며, float32의 최소값과는 매우 거리가 멀기 때문에 동일한 마스크가 dtype별로 다르게 동작하고 softmax 이후 NaN을 생성할 수 있습니다. 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
modeling*.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_sizeTRF034
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, configuration_.py 파일에서 감지합니다. modular 파일에서는 F401, F821, F822가 허용됩니다. modular 파일은 의도적으로 사용하는 모든 이름을 정의하지 않는 생성 소스이므로, ruff의 undefined-name 계열 규칙이 올바른 코드에서 발동할 수 있습니다. 예를 들어 컨버터가 채워 넣는 __all__ 항목, 부모 모델에 정의된 클래스, 재수출을 위해 유지되는 import 등이 해당됩니다. 이러한 코드만 명시한 # noqa는 건너뛰고, 다른 코드를 명시한 경우 남은 코드에 대해 보고합니다. 코드 없이 단순히 # noqa만 있는 경우 항상 보고하며, modular 파일에서는 재작성 대신 해당 코드를 명시하도록 안내합니다. 모델 파일은 저장소의 lint 규칙이 적용되는 일반 코드이므로, 억제(suppression)는 근본적인 문제가 그대로 남아 있음을 의미합니다. 또한 단순한 # noqa는 해당 줄의 모든 향후 위반 사항도 숨깁니다. 대신 코드를 수정하세요.
-from ...modeling_utils import PreTrainedModel # noqa: F401
+from ...modeling_utils import PreTrainedModelTRF036
modeling*.py와 modular*.py에서 nn.Sequential(...) 구성을 모두 플래그 처리합니다. 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
modeling*.py와 modular*.py에서, einsum 호출을 플래그로 표시하며, 식이 리터럴인 경우 해당 식을 보고합니다. 기본적으로 비활성화되어 있습니다: einsum이 수축을 표현하는 가장 명확한 방법이 될 수 있으므로, 이는 강제 규약이 아닌 선택적(opt-in) 기능입니다. einsum 식은 독자가 해석해야 하는 표기로 형태(shape)를 인코딩하며, 동적으로 생성된 식은 어떤 수축이 실행되는지 가립니다. 이를 명시적인 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
모든 modeling*.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는 동반 testmodeling.py의 ConfigTester에 의해 커버되며, tokenization_utils.py는 해당 tokenizer를 통해 커버되는 헬퍼입니다. tokenization<name>_fast.py는 느린 버전의 테스트 파일에 매핑됩니다. 모듈형*.py는 여러 family를 동시에 정의할 수 있으므로, 클래스는 이름 접미사에 따라 라우팅됩니다 — XxxModel/XxxPreTrainedModel/XxxFor<Task> modeling, XxxImageProcessor(Fast) image processing, XxxProcessor processing, XxxVideoProcessor video processing, XxxFeatureExtractor feature extraction, XxxTokenizer(Fast) tokenization, XxxConfig는 건너뜀 — 그리고 누락된 테스트 파일마다 위반 사항이 하나씩 보고됩니다. # trf-ignore: TRF038은 지원되지 않으며, 예외가 리뷰에서 보이도록 allowlist_models를 사용하십시오. 테스트 파일이 없는 소스 파일은 회귀 테스트 커버리지가 없습니다: 손상된 forward pass, 잘못된 변환 매핑, 특수 토큰을 누락하는 tokenizer 등이 머지되고 영구적으로 손상된 상태로 남아 있을 수 있습니다. 무작위 가중치를 가진 더미 config나 작은 수작업 어휘만으로도 이 중 어떤 것을든 검증할 수 있습니다.
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.pyTRF039
if is_*_available(): import ... 블록(예: is_vision_available() and is_torch_available()과 같은 조합 포함)을 찾아, 해당 이름이 파일 내 다른 어디에도 참조되지 않는 경우(문자열 타입 힌트 및 all 내부 포함) 해당 import를 플래깅합니다. ruff의 미사용 import 체크는 이를 정리하지 않습니다. import가 도달 가능하기 때문에 해당 블록 자체는 정상적으로 보입니다. 리팩토링 이후 PIL.Image, torch 등이 더 이상 필요 없게 되면, 가드된 import는 불필요한 잔재로 남아 파일의 실제 의존성에 대해 오해를 유발하는 신호가 됩니다.
if is_vision_available():
- from PIL import ImageTRF040
modeling*.py와 modular*.py에서 @capture_outputs와 @can_return_tuple로 모두 장식된 플래그 메서드를 감지합니다. forward()에서 return_dict에 대한 수동 분기를 다루는 TRF003를 보완합니다. 두 장식자 모두 return_dict를 pop하므로, 가장 바깥쪽 장식자만 실제 값을 확인합니다. @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 removedTRF042
tests/models//testtokenization.py 파일에서, 해당 파일이 TokenizerTesterMixin을 상속하는 수집 대상 테스트 클래스를 정의하는지 확인합니다. 러너가 수집하는 클래스만 해당됩니다. 즉, TestCase 기반 클래스이거나, 기반 클래스가 다른 모델의 테스트 클래스인 경우 *Test 네이밍 규칙을 따르는 클래스입니다. 따라서 헬퍼 전용 파일은 건너뛰며, 스위트가 믹스인된 헬퍼만으로는 실제 테스트 클래스에 대한 규칙을 충족하지 못합니다. 상속은 같은 파일 내의 기반 클래스를 따라가며, 이름으로 가져온 다른 모델의 토크나이저 테스트까지 추적합니다. 해석할 수 없는 기반 클래스는 절대 계산되지 않습니다. 스위트를 실행하지 않는 첫 번째 테스트 클래스에 대해 보고됩니다. auto는 허용 목록에 포함되어 있습니다. test_tokenization_auto.py는 토크나이저가 아니라 AutoTokenizer의 해석을 테스트하기 때문입니다. TokenizerTesterMixin은 encode/decode 왕복, 패딩 및 잘라내기, 특수 토큰 처리, 추가된 토큰의 영속성, 저장/로드 동등성이 실제로 확인되는 곳입니다. 몇 개의 수동으로 작성된 토큰 ID 목록만 단언하는 테스트는, 토크나이저가 그 모든 항목에서 깨져 있더라도 통과하며, 리뷰에서는 테스트가 된 것처럼 보입니다.
-class AcmeTokenizationTest(unittest.TestCase):
+class AcmeTokenizationTest(TokenizerTesterMixin, unittest.TestCase):
+ tokenizer_class = AcmeTokenizer
+ test_slow_tokenizer = TrueTRF043
이름이 Attention으로 끝나는 클래스의 forward 시그니처에서 선언된 position_ids 파라미터를 플래그합니다. position_ids는 flash-attention의 패딩 없는 학습(downstream)에서 소비되며 kwargs를 통해 전달되어야 합니다. 시그니처에 이름을 명시하면 attention 인터페이스가 읽기 전에 소모됩니다. 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
Flags a parameter named cacheposition on any function in modeling.py and modular_.py. cache_position was removed from all models in v5. Reintroducing it (usually copied from pre-v5 sources) threads a dead argument through every layer; the cache update is past_key_values.update(key_states, value_states, self.layer_idx), with no position threading.
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
Flags the legacy outputattentions, output_hidden_states and return_dict parameters in forward signatures in modeling.py and modular_.py. Models contributed before the cutoff date are exempt. The decorator stack owns output control: @captureoutputs resolves the output* flags against the config and records tensors via _can_record_outputs, and @can_return_tuple handles return_dict. Declaring them in the signature reintroduces manual flag threading that drifts from the decorators.
+@capture_outputs
def forward(
self,
input_ids,
- output_attentions=None,
- output_hidden_states=None,
- return_dict=None,
**kwargs: Unpack[TransformersKwargs],
):TRF046
modeling*.py와 modular*.py의 forward 메서드에서 self 속성에 플래그를 할당하지 마세요. forward 중에 상태를 기록하면 배치 처리, torch.compile, 모듈에 대한 추론이 깨집니다. 전달되는 상태는 명시적으로 전달해야 하며(cache 객체, generate 루프 등), config나 정적 shape에만 의존하는 값은 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
preprocess, preprocess, call 및 post_process* 메서드에서 self 속성에 대한 플래그 할당을 수행하는 image_processing.py 및 videoprocessing.py 파일. 호출 사이에 상태를 유지하는 프로세서가 preprocess를 여러 번 수행한 후 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
클래스 레벨의 _tied_weights_keys 선언에서 list/tuple/set 리터럴을 사용합니다. v5에서는 _tied_weights_keys가 각 tied 대상 파라미터를 소스로 매핑하는 dict로 변경되었습니다. list 형식은 더 이상 어떤 파라미터가 소스인지 명시하지 않으므로, tying, device_map 계산 및 저장 과정에서 오류가 조용히 발생합니다.
class AcmeForCausalLM(AcmePreTrainedModel):
- _tied_weights_keys = ["lm_head.weight"]
+ _tied_weights_keys = {"lm_head.weight": "model.embed_tokens.weight"}TRF049
modeling*.py and modular.py: nn.init. / init.* primitives and in-place initializers on own parameters (self.weight.data.normal_()) in init methods: Models instantiate on the meta device, so tensor values written in init are discarded before loading: a parameter initialized only there holds random content when fine-tuning from scratch or after a meta-device reload. Allocate with torch.empty in init, initialize in _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
이름이 Attention으로 끝나는 클래스의 init에서 *RotaryEmbedding 클래스에 대한 호출을 플래그 처리합니다. Model은 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
모델링의 attn_implementation 속성과 modeling.py 및 modular_.py를 비교하는 플래그입니다. 백엔드 디스패치는 ALL_ATTENTION_FUNCTIONS.get_interface에 속하며, 백엔드 조건부 텐서 조작(패딩, 리셰이핑)은 integrations/의 공유 래퍼에 속합니다. 인라인 분기는 모델 본문을 커널 인식 상태로 유지하지만, 새로운 백엔드가 등록되면 깨집니다.
-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
Flags module-level assignments to names ending in ATTENTION_CLASSES in modeling.py and modular_.py. Per-backend attention classes picked from a dict are the pre-interface idiom: near-identical classes drift apart, and hub attention kernels registered into ALL_ATTENTION_FUNCTIONS never reach them. One attention class dispatching through the interface replaces the dict; do not propagate it from a legacy parent.
-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
modeling*.py와 modular*.py에서, labels[…, 1:]와 같이 슬라이싱을 통해 shiftlogits/shift_labels(그리고 shifted 변형)를 구성하는 플래그 할당을 수행합니다. 이미 shift된 labels를 받는 것(shift_labels = kwargs.pop("shift_labels", labels))은 올바른 관용 표현이며 플래그가 지정되지 않습니다. self.loss_function은 labels를 스스로 shift하므로, modeling 코드에서 미리 shift하면 이중으로 shift된 target으로 학습하거나 맞춤형 loss 경로를 강제하게 됩니다. Decoder-only 모델은 원본 labels를 전달하고 loss가 shift하도록 합니다. Encoder-decoder 모델은 반대 경우입니다. 이 모델의 labels는 앞에 붙은 decoder start token에 의해 이미 shift되어 있으므로, loss가 다시 shift하는 것을 막기 위해 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
Flags config = SomeConfig as a class attribute on PreTrainedModel subclasses in modeling*.py and modular*.py. PreTrainedModel.__init_subclass__ derives config_class from a config annotation, via inspect.get_annotations(cls). An assignment creates a stray class attribute that call does not report, so the subclass silently keeps the parent’s config_class. A bare annotation has no runtime value, creates no attribute, and is picked up correctly.
class Gemma4VisionModel(Gemma4PreTrainedModel):
"""The Gemma 4 Vision Encoder."""
- config = Gemma4VisionConfig
+ config: Gemma4VisionConfigTRF056
modeling*.py와 modular*.py에서 forward 내부의 .item() 및 .tolist() 호출을 플래그합니다. split(...)의 split-size 인수로 전달되는 .tolist()는 예외입니다. torch.split은 Python int를 필요로 하기 때문입니다. 두 호출 모두 텐서를 호스트로 다시 읽기 때문에 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
공개 PreTrainedModel 하위 클래스(<Model>PreTrainedModel, <Model>Model, <Model>For<Task>, backbones), PreTrainedConfig 하위 클래스, ModelOutput 하위 클래스, 이미지 프로세서 및 ProcessorMixin 하위 클래스, 그리고它们的 공개 메서드 forward, get_image_features, get_video_features, get_audio_features, get_text_features, preprocess 및 __call__에 대해 @auto_docstring을 확인합니다. 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
modeling*.py와 modular*.py에서, 수신자(self 또는 layer.mamba와 같은 다른 모듈)의 버퍼 이름이 문자열 리터럴인 register_buffer("<name>", ...) 호출을 플래그로 표시합니다. 변수나 f-string과 같이 계산된 이름(예: 루프 내에서 레이어당 하나의 버퍼)은 속성 할당과 동등한 표현이 없으므로 예외로 처리됩니다. 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
tensor-parallel plan에서 moe_tp_experts가 할당된 모델 디렉터리의 경우, 라우팅된 *Experts 클래스가 hidden states, top-k indices, top-k routing weights를 forward의 첫 세 개의 위치 인자로 받는지 확인합니다. selected_experts나 routing_weights와 같은 별칭도 허용됩니다. MoeExpertsParallel은 위치 인자 3에 gradient transform을 적용합니다. 서명이 다르면 잘못된 텐서에 적용되거나 적용이 건너뛰어질 수 있습니다.
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
즉시 수정할 수 없는 레거시 코드가 있는 모델의 경우, 해당 모델의 디렉터리 이름을 mlinter rules.toml의 관련 규칙 allowlist_models 목록에 추가하세요.
[rules.TRF004]
allowlist_models = ["existing_model", "your_model_name"]