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.2).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

模型调试工具箱

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

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

模型添加调试器

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

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

原理

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

用法

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

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 结尾。

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

{
  "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 文件将显示所有张量的完整视图,便于比较两个文件。

      "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 将张量写入磁盘。

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

局限性与范围

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

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

transformers.model_addition_debugger_context

< >

( modeldebug_path: str | None = Nonedo_prune_layers: bool = Trueuse_repr: bool = True )

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

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

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

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

用法

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

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

用法

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

完整扫描(默认)

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

python utils/scan_skipped_tests.py --output_dir path/to/output
  • --output_dir(可选):保存 JSON 结果的目录。默认值为当前目录。

示例输出:

🔬 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)。

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

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

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

扫描单个测试方法

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

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 结果的目录。

示例输出:

$ 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 仓库的根目录下:

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

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

示例输出:

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 名结果中的类别,表明模块化候选项具有较高置信度。

构建自定义索引

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

python utils/modular_model_detector.py --build

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

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 下载,首次使用时需要互联网连接。

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

Update on GitHub