模型调试工具箱
此页面列出了该库使用的所有调试和模型添加工具,以及它为此提供的实用函数。
其中大部分内容仅在你向该库中添加新模型时才有用。
模型添加调试器
模型添加调试器 - 用于模型添加器的上下文管理器
此上下文管理器是面向模型添加者的进阶工具。它会跟踪模型 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 输出文件。见下文:我们可以看到这两个实现的键投影层存在细微差异。输入基本相同,但并不完全一致。查看文件差异有助于精确定位出错的层。

局限性与范围
此功能仅适用于基于 torch 的模型。严重依赖外部内核调用的模型可能可以运行,但 trace 很可能会遗漏一些内容。无论如何,任何旨在模拟另一实现的 Python 实现都可以被 trace 一次,而无需通过断点重新运行 N 次。
如果向模型调试器传入 do_prune_layers=False,则所有层都会输出到 json 中。否则,仅显示第一层和最后一层。当某些层(通常是交叉注意力层)仅在 N 层之后出现时,此功能非常有用。
transformers.model_addition_debugger_context
< source >( 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%。

用法
你可以通过以下两种方式运行跳过测试分析器:
完整扫描(默认)
从 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