# Boîtes à outils de débogage de modèles

Cette page répertorie tous les outils de débogage et d’ajout de modèles utilisés par la bibliothèque, ainsi que les fonctions utilitaires qu’elle fournit à cet effet.

La plupart de ces éléments ne sont utiles que si vous ajoutez de nouveaux modèles à la bibliothèque.

## Débogageurs d'ajout de modèles

### Débogueur d'ajout de modèle - gestionnaire de contexte pour les ajouts de modèles

Ce gestionnaire de contexte est un outil destiné aux utilisateurs avancés et aux contributeurs de modèles. Il suit tous les appels forward au sein d'un forward de modèle et enregistre un extrait de chaque entrée et sortie dans un JSON imbriqué. À noter que ce gestionnaire de contexte impose `torch.no_grad()`.

### Justification

Lors du portage de modèles vers Transformers, même d’un langage Python à un autre, les contributeurs de modèles doivent souvent effectuer de nombreuses opérations manuelles, telles que l’enregistrement et le chargement de tenseurs, la comparaison des dtypes, etc. Cet outil léger devrait permettre de gagner un peu de temps.

### Utilisation

Ajoutez ce gestionnaire de contexte comme suit pour déboguer un modèle :

```python
import torch
from PIL import Image
import requests
from transformers import LlavaProcessor, LlavaForConditionalGeneration
from transformers.model_debugging_utils import model_addition_debugger_context
torch.random.manual_seed(673)

# load pretrained model and processor
model_id = "llava-hf/llava-1.5-7b-hf"
processor = LlavaProcessor.from_pretrained(model_id)
model = LlavaForConditionalGeneration.from_pretrained(model_id)

# create random image input
random_image = Image.fromarray(torch.randint(0, 256, (224, 224, 3), dtype=torch.uint8).numpy())

# prompt
prompt = "<image>Describe this image."

# process inputs
inputs = processor(text=prompt, images=random_image, return_tensors="pt")

# call forward method (not .generate!)
with model_addition_debugger_context(
    model,
    debug_path="optional_path_to_your_directory",
    do_prune_layers=False # This will output ALL the layers of a model.
):
    output = model.forward(**inputs)

```

### Lecture des résultats

Le débogueur génère deux fichiers à partir de l’appel forward, tous deux portant le même nom de base, mais se terminant par `_SUMMARY.json` ou par `_FULL_TENSORS.json`.

Le premier contiendra un résumé des valeurs et des formes des tenseurs _en entrée_ et _en sortie_ de chaque module.

```json
{
  "module_path": "MolmoForConditionalGeneration",
  "inputs": {
    "args": [],
    "kwargs": {
      "input_ids": {
        "shape": "torch.Size([1, 589])",
        "dtype": "torch.int64"
      },
      "attention_mask": {
        "shape": "torch.Size([1, 589])",
        "dtype": "torch.int64"
      },
      "pixel_values": {
        "shape": "torch.Size([1, 5, 576, 588])",
        "dtype": "torch.float32",
        "mean": "tensor(-8.9514e-01, device='cuda:0')",
        "std": "tensor(9.2586e-01, device='cuda:0')",
        "min": "tensor(-1.7923e+00, device='cuda:0')",
        "max": "tensor(1.8899e+00, device='cuda:0')"
    }
  },
  "children": [
    {
      "module_path": "MolmoForConditionalGeneration.language_model.model.embed_tokens",
      "inputs": {
        "args": [
          {
            "shape": "torch.Size([1, 589])",
            "dtype": "torch.int64"
          }
        ]
      },
      "outputs": {
        "shape": "torch.Size([1, 589, 3584])",
        "dtype": "torch.float32",
        "mean": "tensor(6.5460e-06, device='cuda:0')",
        "std": "tensor(2.3807e-02, device='cuda:0')",
        "min": "tensor(-3.3398e-01, device='cuda:0')",
        "max": "tensor(3.9453e-01, device='cuda:0')"
      }
    },
    {
      "module_path": "MolmoForConditionalGeneration.vision_tower",
      "inputs": {
        "args": [
          {
            "shape": "torch.Size([5, 1, 576, 588])",
            "dtype": "torch.float32",
            "mean": "tensor(-8.9514e-01, device='cuda:0')",
            "std": "tensor(9.2586e-01, device='cuda:0')",
            "min": "tensor(-1.7923e+00, device='cuda:0')",
            "max": "tensor(1.8899e+00, device='cuda:0')"
          }
        ],
        "kwargs": {
          "output_hidden_states": "True"
        }
      },
      "children": [
        { ... and so on
```

Le fichier `_FULL_TENSORS.json` affiche une vue complète de tous les tenseurs, ce qui est utile pour comparer deux fichiers.

```json
      "pixel_values": {
        "shape": "torch.Size([1, 5, 576, 588])",
        "dtype": "torch.float32",
        "value": [
          "tensor([[[[-1.7923e+00, -1.7521e+00, -1.4802e+00,  ..., -1.7923e+00, -1.7521e+00, -1.4802e+00],",
          "          [-1.7923e+00, -1.7521e+00, -1.4802e+00,  ..., -1.7923e+00, -1.7521e+00, -1.4802e+00],",
          "          [-1.7923e+00, -1.7521e+00, -1.4802e+00,  ..., -1.7923e+00, -1.7521e+00, -1.4802e+00],",
          "          ...,",
          "          [-1.7923e+00, -1.7521e+00, -1.4802e+00,  ..., -1.7923e+00, -1.7521e+00, -1.4802e+00],",
          "          [-1.7923e+00, -1.7521e+00, -1.4802e+00,  ..., -1.7923e+00, -1.7521e+00, -1.4802e+00],",
          "          [-1.7923e+00, -1.7521e+00, -1.4802e+00,  ..., -1.7923e+00, -1.7521e+00, -1.4802e+00]],",
          "",
          "         [[-1.7923e+00, -1.7521e+00, -1.4802e+00,  ..., -1.7923e+00, -1.7521e+00, -1.4802e+00],",
          "          [-1.7923e+00, -1.7521e+00, -1.4802e+00,  ..., -1.7923e+00, -1.7521e+00, -1.4802e+00],",
          "          [-1.7923e+00, -1.7521e+00, -1.4802e+00,  ..., -1.7923e+00, -1.7521e+00, -1.4802e+00],",
          "          ...,",
          "          [-1.4857e+00, -1.4820e+00, -1.2100e+00,  ..., -6.0979e-01, -5.9650e-01, -3.8527e-01],",
          "          [-1.6755e+00, -1.7221e+00, -1.4518e+00,  ..., -7.5577e-01, -7.4658e-01, -5.5592e-01],",
          "          [-7.9957e-01, -8.2162e-01, -5.7014e-01,  ..., -1.3689e+00, -1.3169e+00, -1.0678e+00]],",
          "",
          "         [[-1.7923e+00, -1.7521e+00, -1.4802e+00,  ..., -1.7923e+00, -1.7521e+00, -1.4802e+00],",
          "          [-1.7923e+00, -1.7521e+00, -1.4802e+00,  ..., -1.7923e+00, -1.7521e+00, -1.4802e+00],",
          "          [-1.7923e+00, -1.7521e+00, -1.4802e+00,  ..., -1.7923e+00, -1.7521e+00, -1.4802e+00],",
          "          ...,",
          "          [-3.0322e-01, -5.0645e-01, -5.8436e-01,  ..., -6.2439e-01, -7.9160e-01, -8.1188e-01],",
          "          [-4.4921e-01, -6.5653e-01, -7.2656e-01,  ..., -3.4702e-01, -5.2146e-01, -5.1326e-01],",
          "          [-3.4702e-01, -5.3647e-01, -5.4170e-01,  ..., -1.0915e+00, -1.1968e+00, -1.0252e+00]],",
          "",
          "         [[-1.1207e+00, -1.2718e+00, -1.0678e+00,  ..., 1.2013e-01, -1.3126e-01, -1.7197e-01],",
          "          [-6.9738e-01, -9.1166e-01, -8.5454e-01,  ..., -5.5050e-02, -2.8134e-01, -4.2793e-01],",
          "          [-3.4702e-01, -5.5148e-01, -5.8436e-01,  ..., 1.9312e-01, -8.6235e-02, -2.1463e-01],",
          "          ...,",
          "          [-1.7923e+00, -1.7521e+00, -1.4802e+00,  ..., -1.7923e+00, -1.7521e+00, -1.4802e+00],",
          "          [-1.7923e+00, -1.7521e+00, -1.4802e+00,  ..., -1.7923e+00, -1.7521e+00, -1.4802e+00],",
          "          [-1.7923e+00, -1.7521e+00, -1.4802e+00,  ..., -1.7923e+00, -1.7521e+00, -1.4802e+00]],",
          "",
          "         [[-1.0039e+00, -9.5669e-01, -6.5546e-01,  ..., -1.4711e+00, -1.4219e+00, -1.1389e+00],",
          "          [-1.0039e+00, -9.5669e-01, -6.5546e-01,  ..., -1.7193e+00, -1.6771e+00, -1.4091e+00],",
          "          [-1.6317e+00, -1.6020e+00, -1.2669e+00,  ..., -1.2667e+00, -1.2268e+00, -8.9720e-01],",
          "          ...,",
          "          [-1.7923e+00, -1.7521e+00, -1.4802e+00,  ..., -1.7923e+00, -1.7521e+00, -1.4802e+00],",
          "          [-1.7923e+00, -1.7521e+00, -1.4802e+00,  ..., -1.7923e+00, -1.7521e+00, -1.4802e+00],",
          "          [-1.7923e+00, -1.7521e+00, -1.4802e+00,  ..., -1.7923e+00, -1.7521e+00, -1.4802e+00]]]], device='cuda:0')"
        ],
        "mean": "tensor(-8.9514e-01, device='cuda:0')",
        "std": "tensor(9.2586e-01, device='cuda:0')",
        "min": "tensor(-1.7923e+00, device='cuda:0')",
        "max": "tensor(1.8899e+00, device='cuda:0')"
      },
```

#### Enregistrer des tenseurs sur le disque

Certains créateurs de modèles peuvent bénéficier de la journalisation des valeurs complètes des tenseurs sur le disque pour prendre en charge, par exemple, l’analyse numérique entre différentes implémentations.

Définissez `use_repr=False` pour écrire les tenseurs sur le disque à l'aide de [SafeTensors](https://huggingface.co/docs/safetensors/en/index).

```python
with model_addition_debugger_context(
    model,
    debug_path="optional_path_to_your_directory",
    do_prune_layers=False,
    use_repr=False,   # Defaults to True
):
    output = model.forward(**inputs)
```

Lors de l'utilisation de `use_repr=False`, les tenseurs sont écrits à l'emplacement disque des fichiers `_SUMMARY.json` et `_FULL_TENSORS.json`. La propriété `value` des entrées du fichier `_FULL_TENSORS.json` contiendra une référence de chemin relatif vers le fichier `.safetensors` associé. Chaque tenseur est écrit dans son propre fichier en tant que propriété `data` du dictionnaire d'état. Les noms de fichiers sont construits en utilisant `module_path` comme préfixe, avec quelques suffixes possibles construits de manière récursive.

* Les entrées des modules sont notées `_inputs` et les sorties `_outputs`.
* Les instances de `list` et de `tuple`, telles que `args` ou les valeurs de retour de fonctions, seront suivies de `_{index}`.
* Les instances de `dict` seront suivies de `_{key}`.

### Comparaison entre les implémentations

Une fois que les passes avant de deux modèles ont été tracées par le débogueur, on peut comparer les fichiers de sortie `json`. Voir ci-dessous : on observe de légères différences entre la couche de projection des clés de ces deux implémentations. Les entrées sont quasi identiques, mais pas tout à fait. Examiner les différences entre les fichiers facilite l’identification de la couche défaillante.

![download-icon](https://huggingface.co/datasets/huggingface/documentation-images/resolve/main/transformers/files_difference_debugging.png)

### Limites et périmètre[[transformers.model_addition_debugger_context]]

Cette fonctionnalité ne fonctionnera que pour les modèles basés sur torch. Les modèles qui s’appuient fortement sur des appels de noyaux externes peuvent fonctionner, mais la trace manquera probablement certains éléments. Quoi qu’il en soit, toute implémentation en python visant à imiter une autre implémentation peut être tracée une seule fois au lieu d’être exécutée N fois avec des points d’arrêt.

Si vous passez `do_prune_layers=False` à votre débogueur de modèle, toutes les couches seront exportées dans `json`. Sinon, seules la première et la dernière couche seront affichées. Cela est utile lorsque certaines couches (généralement l'attention croisée) n'apparaissent qu'après N couches.

#### transformers.model_addition_debugger_context[[transformers.model_addition_debugger_context]]

```python
transformers.model_addition_debugger_context(model, debug_path: str | None = None, do_prune_layers: bool = True, use_repr: bool = True)
```

[Source](https://github.com/huggingface/transformers/blob/main/src/transformers/model_debugging_utils.py#L391)

# Débogueur d'ajout de modèle - gestionnaire de contexte pour les ajouts de modèles
Ce gestionnaire de contexte est un outil destiné aux utilisateurs avancés, en particulier aux personnes qui ajoutent des modèles.

Il suit tous les appels de propagation avant au sein d'une propagation avant de modèle et enregistre un extrait de chaque entrée et sortie dans un fichier JSON imbriqué. Si `use_repr=True` (par défaut), le fichier JSON enregistre une version `repr()` des tenseurs sous forme de liste de chaînes de caractères. Si `use_repr=False`, les tenseurs complets sont stockés dans des fichiers SafeTensors distincts et le fichier JSON fournit un chemin relatif vers ce fichier.

À noter, ce gestionnaire de contexte impose `torch.no_grad()`.

## Utilisation

ajouter le gestionnaire de contexte à un modèle pour le débogage

```python
import torch

from PIL import Image
from transformers import LlavaProcessor, LlavaForConditionalGeneration, model_addition_debugger_context

torch.random.manual_seed(673)

# load pretrained model and processor
model_id = "llava-hf/llava-1.5-7b-hf"
processor = LlavaProcessor.from_pretrained(model_id)
model = LlavaForConditionalGeneration.from_pretrained(model_id)

# create random image input
random_image = Image.fromarray(torch.randint(0, 256, (224, 224, 3), dtype=torch.uint8).numpy())

# prompt
prompt = "<image>Describe this image."

# process inputs
inputs = processor(text=prompt, images=random_image, return_tensors="pt")

# call forward method (not .generate!)
with model_addition_debugger_context(model, debug_path="Your_debug_path", do_prune_layers=False):
    output = model.forward(**inputs)
```

## Analyseur des tests ignorés

### Analyser les tests ignorés - pour les contributeurs et les mainteneurs de modèles

Cet utilitaire discret est un outil destiné aux utilisateurs avancés, en particulier aux contributeurs et mainteneurs de modèles. Il répertorie toutes les méthodes de test présentes dans `test_modeling_common.py`, héritées par toutes les classes de test de modèles, et analyse le dépôt pour mesurer le nombre de tests ignorés et pour quels modèles.

### Justification

Lors du portage de modèles vers Transformers, les tests échouent comme il se doit, et parfois `test_modeling_common` semble irréconciliable avec les particularités de notre tout nouveau modèle. Mais comment être certains de ne pas tout casser en ajoutant un skip apparemment inoffensif ?

Cette utilitaire :

- scanne toutes les méthodes de test_modeling_common
- recherche les moments où une méthode est ignorée
- renvoie un résumé au format JSON que vous pouvez charger en tant que DataFrame/inspecter

**Par exemple, test_inputs_embeds est ignoré dans une proportion impressionnante de 39 % au moment de la rédaction de cet utilitaire.**

![download-icon](https://huggingface.co/datasets/huggingface/documentation-images/resolve/f7f671f69b88ce4967e19179172c248958d35742/transformers/tests_skipped_visualisation.png)

### Utilisation

Vous pouvez exécuter l'analyseur de tests ignorés de deux manières :

#### Analyse complète (par défaut)

Depuis la racine du dépôt `transformers`, scanne toutes les méthodes de test courantes et affiche les résultats dans un fichier JSON (par défaut : `all_tests_scan_result.json`).

```bash
python utils/scan_skipped_tests.py --output_dir path/to/output
```

- `--output_dir` (facultatif) : répertoire où les résultats JSON seront enregistrés. Par défaut, le répertoire courant est utilisé.

**Exemple de sortie :**

```text
🔬 Parsing 331 model test files once each...
📝 Aggregating 224 tests...
  (224/224) test_update_candidate_strategy_with_matches_1es_3d_is_nonecodet_schedule_fa_kwargs
✅ Scan complete.

📄 JSON saved to /home/pablo/git/transformers/all_tests_scan_result.json

```

Il génère le fichier `all_tests_scan_result.json` que vous pouvez inspecter. Le JSON est indexé par nom de méthode, et chaque entrée suit ce schéma, indiquant également l’origine (depuis `common` ou `GenerationMixin`.)

```json
{
  "<method_name>": {
    "origin": "<test suite>"
    "models_ran": ["<model_name>", ...],
    "models_skipped": ["<model_name>", ...],
    "skipped_proportion": <float>,
    "reasons_skipped": ["<model_name>: <reason>",
      ...
    ]
  },
  ...
}
```

Ce que vous pouvez visualiser comme ci-dessus avec, par exemple, `pandas`

```python
df = pd.read_json('all_tests_scan_result.json').T
df.sort_values(by=['skipped_proportion'], ascending=False)

```

### Analyser une seule méthode de test

Vous pouvez cibler une méthode de test spécifique à l’aide de `--test_method_name` :

```bash
python utils/scan_skipped_tests.py --test_method_name test_inputs_embeds --output_dir path/to/output
```

- `--test_method_name` : Nom de la méthode de test à analyser (par exemple, `test_inputs_embeds`).
- `--output_dir` (facultatif) : répertoire où le résultat JSON sera enregistré.

**Exemple de sortie :**

```bash
$ python utils/scan_skipped_tests.py --test_method_name test_inputs_embeds

🔬 Parsing 331 model test files once each...

== test_inputs_embeds ==

Ran    : 199/323
Skipped : 124/323 (38.4%)
 - aimv2: Aimv2 does not use inputs_embeds
 - align: Inputs_embeds is tested in individual model tests
 - altclip: Inputs_embeds is tested in individual model tests
 - audio_spectrogram_transformer: AST does not use inputs_embeds
 - beit: BEiT does not use inputs_embeds
 - bit: Bit does not use inputs_embeds
 - blip: Blip does not use inputs_embeds
 - blip_2: Inputs_embeds is tested in individual model tests
 - bridgetower:
 - canine: CANINE does not have a get_input_embeddings() method.
 - ...

📄 JSON saved to /home/pablo/git/transformers/scan_test_inputs_embeds.json

```

## Détecteur de modèles modulaire

### Analyseur de similarité de code - pour les ajouts de modèles

Cet utilitaire analyse les similarités de code entre les implémentations de modèles afin d’identifier les opportunités de modularisation. Il compare un fichier de modélisation nouveau ou existant à tous les modèles de la bibliothèque à l’aide de métriques de similarité basées sur les embeddings et sur les jetons.

### Justification

Lors de l’ajout d’un nouveau modèle à transformers, de nombreux composants (couches d’attention, MLP, sorties, etc.) peuvent déjà exister sous une forme similaire dans d’autres modèles. Plutôt que d’implémenter tout à partir de zéro, les ajouts de modèles peuvent identifier quelles classes existantes sont similaires et potentiellement réutilisables grâce à la modularisation.

L'outil calcule deux scores de similarité :

- **Score d'embedding** : Utilise des embeddings de code sémantiques (via `Qwen/Qwen3-Embedding-4B`) pour détecter du code fonctionnellement similaire, même avec des noms différents
- **Score de Jaccard** : Mesure le chevauchement des ensembles de jetons pour identifier des motifs de code structurellement similaires

Un score de 1.00 signifie que le code est identique.

### Utilisation

Depuis la racine du dépôt `transformers` :

```bash
python utils/modular_model_detector.py --modeling-file path/to/modeling_file.py
```

L'outil téléchargera automatiquement l'index préconstruit depuis le Hub (nécessite de la RAM/VRAM pour le modèle d'embedding).

**Exemple de sortie :**

```text
Loading checkpoint shards: 100%|████████████████████| 2/2 [00:00<00:00, 33.62it/s]
encoding 21 query definitions with Qwen/Qwen3-Embedding-4B (device=cuda, batch=16, max_length=4096)

stuff.py::Beit3ImageTextMatchingOutput:
embedding:
    blip_2::Blip2ImageTextMatchingModelOutput (0.9994)
    chinese_clip::ChineseCLIPOutput (0.9818)
    owlvit::OwlViTOutput (0.9818)
jaccard:
    owlv2::Owlv2Output (0.9667)
    metaclip_2::MetaClip2Output (0.9667)
    altclip::AltCLIPOutput (0.9667)
intersection:
    blip::BlipOutput
    owlvit::OwlViTOutput

stuff.py::Beit3MLP:
embedding:
    efficientloftr::EfficientLoFTRMLP (0.9718)
    seggpt::SegGptMlp (0.9650)
jaccard:
    chinese_clip::ChineseCLIPTextSelfOutput (0.5294)
    bert::BertSelfOutput (0.5294)
intersection:
```

Le champ `intersection` affiche les classes qui apparaissent dans les deux résultats top-5, indiquant une forte confiance pour les candidats à la modularisation.

### Créer un index personnalisé

Pour reconstruire l'index à partir de votre base de code locale (utile après l'ajout de nouveaux modèles ou l'utilisation d'un modèle d'embedding différent) :

```bash
python utils/modular_model_detector.py --build
```

Pour pousser l'index reconstruit vers un jeu de données du Hub :

```bash
python utils/modular_model_detector.py --build --push-new-index --hub-dataset your-org/your-dataset
```

### Options

- `--modeling-file` : Chemin vers le fichier de modélisation à analyser
- `--build` : Construit l'index de similarité du code à partir de tous les fichiers de modélisation dans `src/transformers/models/`
- `--push-new-index` : après la construction, pousse l’index vers un jeu de données du Hub (nécessite `--build`)
- `--hub-dataset` : identifiant du dépôt de jeu de données sur le Hub à partir duquel tirer/pousser l'index (par défaut : `hf-internal-testing/transformers_code_embeddings`)

### Limites

Cet outil nécessite des ressources GPU/CPU pour exécuter le modèle d'embedding (`Qwen/Qwen3-Embedding-4B`). L'index préconstruit est téléchargé depuis le Hub par défaut, ce qui requiert une connexion internet lors de la première utilisation.

Les résultats sont des suggestions basées sur la similarité du code et doivent être examinées manuellement avant la modularisation. Des scores de similarité élevés ne garantissent pas une compatibilité parfaite.

