# 模型调试工具箱

此页面列出了该库使用的所有调试和模型添加工具，以及它为此提供的实用函数。

其中大部分内容仅在你向该库中添加新模型时才有用。

## 模型添加调试器

### 模型添加调试器 - 用于模型添加器的上下文管理器

此上下文管理器是面向模型添加者的进阶工具。它会跟踪模型 forward 中的所有 forward 调用，并将每个输入和输出的切片记录到嵌套的 JSON 中。请注意，此上下文管理器会强制执行 `torch.no_grad()`。

### 原理

在将模型移植到 Transformers 时，即使是从 Python 到 Python，模型添加者通常也需要执行大量手动操作，例如保存和加载张量、比较数据类型等。这个小工具希望能节省一些时间。

### 用法

按以下方式添加此上下文管理器以调试模型：

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

```

### 读取结果

调试器会根据前向调用生成两个文件，两者具有相同的基础名称，但分别以 `_SUMMARY.json` 或 `_FULL_TENSORS.json` 结尾。

第一个将包含每个模块的 _输入_ 和 _输出_ 张量值及其形状的摘要。

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

`_FULL_TENSORS.json` 文件将显示所有张量的完整视图，便于比较两个文件。

```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')"
      },
```

#### 将张量保存到磁盘

某些模型添加器可能受益于将完整张量值记录到磁盘，以支持例如跨实现的数值分析。

设置 `use_repr=False` 以使用 [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)
```

使用 `use_repr=False` 时，张量会被写入与 `_SUMMARY.json` 和 `_FULL_TENSORS.json` 文件相同的磁盘位置。`_FULL_TENSORS.json` 文件中条目的 `value` 属性将包含指向关联的 `.safetensors` 文件的相对路径引用。每个张量都会作为状态字典的 `data` 属性写入其独立的文件中。文件名以 `module_path` 为前缀，并附加若干通过递归方式构建的后缀来生成。

* 模块的输入以 `_inputs` 表示，输出以 `_outputs` 表示。
* `list` 和 `tuple` 实例（例如 `args` 或函数返回值）会在末尾添加 `_{index}`。
* `dict` 实例将附加后缀 `_{key}`。

### 比较不同实现

当调试器追踪完两个模型的前向传播后，可以比较 `json` 输出文件。如下所示：我们可以看到这两个实现的键投影层存在细微差异。输入基本相同，但并不完全一致。通过查看文件差异，更容易定位出哪一层存在问题。

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

### 局限性与范围[[transformers.model_addition_debugger_context]]

此功能仅适用于基于 torch 的模型。严重依赖外部内核调用的模型可能可以运行，但 trace 可能会遗漏部分内容。无论如何，任何旨在模拟另一实现的 Python 实现都可以被 trace 一次，而无需通过断点重新运行 N 次。

如果向模型调试器传入 `do_prune_layers=False`，则所有层都会输出到 `json` 中。否则，仅显示第一层和最后一层。当某些层（通常是交叉注意力层）仅在 N 层之后出现时，此功能非常有用。

#### 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)
```

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

# 模型添加调试器 - 用于模型添加器的上下文管理器
此上下文管理器是面向模型添加者的进阶用户工具。

它会跟踪模型前向传播中的所有前向调用，并将每个输入和输出的切片记录到一个嵌套的 JSON 文件中。如果 `use_repr=True`（默认值），JSON 文件会将张量的 `repr()` 化版本记录为字符串列表。如果 `use_repr=False`，完整的张量将存储在单独的 SafeTensors 文件中，而 JSON 文件将提供指向该文件的相对路径。

请注意，此上下文管理器会强制执行 `torch.no_grad()`。

## 用法

将上下文管理器添加到模型中以进行调试

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

## 跳过测试的分析器

### 扫描被跳过的测试 - 面向模型添加者和维护者

这个小工具是面向模型添加者和维护者的进阶工具。它会列出 `test_modeling_common.py` 中所有被各模型测试器类继承的测试方法，并扫描仓库以统计有多少测试被跳过以及涉及哪些模型。

### 原理

在将模型移植到 Transformers 时，测试失败是意料之中的，有时 `test_modeling_common` 似乎与我们全新模型的独特特性格格不入。但当我们添加一个看似无害的跳过时，如何确保自己并没有破坏一切？

此实用工具：

- 扫描所有 test_modeling_common 方法
- 查找方法被跳过的时间
- 返回一个摘要 JSON，您可以将其加载为 DataFrame 或进行查看

**例如，在编写此工具时，test_inputs_embeds 被跳过的比例高达 39%。**

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

### 用法

你可以通过以下两种方式运行跳过测试分析器：

#### 完整扫描（默认）

从 `transformers` 仓库的根目录开始，扫描所有常见的测试方法，并将结果输出到 JSON 文件（默认：`all_tests_scan_result.json`）。

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

- `--output_dir`（可选）：保存 JSON 结果的目录。默认值为当前目录。

**示例输出：**

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

```

它会生成一个`all_tests_scan_result.json`文件，你可以查看该文件。该 JSON 以方法名作为索引，每个条目遵循此模式，并标明其来源（来自`common`或`GenerationMixin`）。

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

您可以在上方使用例如 `pandas` 进行可视化。

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

```

### 扫描单个测试方法

您可以使用 `--test_method_name` 来关注特定的测试方法：

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

- `--test_method_name`：要扫描的测试方法名称（例如 `test_inputs_embeds`）。
- `--output_dir`（可选）：保存 JSON 结果的目录。

**示例输出：**

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

```

## 模块化模型检测器

### 代码相似度分析器 - 面向模型添加者

此实用工具通过分析模型实现之间的代码相似性，识别模块化优化的机会。它使用基于嵌入和基于令牌的相似度指标，将新的或现有的建模文件与库中的所有模型进行比较。

### 原理

在 transformers 中添加新模型时，许多组件（如注意力层、MLP、输出层等）可能已以相似的形式存在于其他模型中。与其从头开始实现所有内容，模型添加者可以通过模块化来识别哪些现有类是相似的，并可能加以复用。

该工具会计算两个相似度分数：

- **嵌入分数**：使用语义代码嵌入（通过 `Qwen/Qwen3-Embedding-4B`）来检测功能相似的代码，即使命名不同
- **Jaccard 分数**：通过衡量 token 集合的重叠度来识别结构相似的代码模式

得分为 1.00 表示代码完全相同。

### 用法

在 `transformers` 仓库的根目录下：

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

该工具会自动从 Hub 下载预构建的索引（需要为嵌入模型预留 RAM/VRAM）。

**示例输出：**

```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:
```

`intersection` 字段显示了同时出现在前 5 名结果中的类别，表明模块化候选项具有较高置信度。

### 构建自定义索引

要从本地代码库重建索引（在添加新模型或使用不同的嵌入模型后很有用）：

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

要将重建的索引推送到 Hub 数据集：

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

### 选项

- `--modeling-file`：要分析的建模文件的路径
- `--build`：从 `src/transformers/models/` 中的所有建模文件构建代码相似度索引
- `--push-new-index`：构建完成后，将索引推送到 Hub 数据集（需要 `--build`）
- `--hub-dataset`：用于拉取/推送索引的 Hub 数据集仓库 ID（默认值：`hf-internal-testing/transformers_code_embeddings`）

### 局限性

此工具需要 GPU/CPU 资源来运行嵌入模型（`Qwen/Qwen3-Embedding-4B`）。默认情况下，预构建的索引会从 Hub 下载，首次使用时需要互联网连接。

结果是基于代码相似度的建议，在模块化之前应进行人工审查。高相似度分数并不保证完全兼容。

