Transformers 模型测试编写指南基于 Mixin 架构的自动化测试体系实战【免费下载链接】transformers Transformers: the model-definition framework for state-of-the-art machine learning models in text, vision, audio, and multimodal models, for both inference and training.项目地址: https://gitcode.com/GitHub_Trending/tra/transformers导读本文围绕 Transformers 仓库中 docs/source/en/testing.md 的完整内容系统讲解如何为新增模型编写高质量测试从运行测试的 pytest 命令、三大模型家族基类CausalLMModelTest/VLMModelTest/ALMModelTest、通用的ModelTesterModelTest双类模式与测试 Mixin到配置测试、集成测试与 tiny model 的生成再到如何用布尔开关精确控制测试范围。文章同时结合仓库源码如 tests/causal_lm_tester.py、tests/vlm_tester.py、tests/alm_tester.py、tests/multimodal_tester.py 等深入剖析其底层实现原理帮助读者理解这套写最少代码、自动生成 100 测试的测试体系。核心架构Mixin 驱动的测试自动生成Transformers 测试套件采用基于 Mixin 的架构从极少的模型专属代码自动生成 100 多个测试。开发者只需编写一小段模型专属代码Mixin 即可自动覆盖保存/加载、生成、Pipeline、训练和 Tensor Parallel 等能力。运行模型测试的命令# 运行某个模型的全部测试 pytest tests/models/mymodel/test_modeling_mymodel.py -v # 运行单个指定测试 pytest tests/models/mymodel/test_modeling_mymodel.py::MyModelTest::test_model # 按关键字模式筛选测试例如运行所有 integration 测试 pytest tests/models/mymodel/ -k integration -v # 包含慢速集成测试RUN_SLOW1 环境变量 RUN_SLOW1 pytest tests/models/mymodel/ -v其中RUN_SLOW环境变量在 src/transformers/testing_utils.py 中被解析_run_slow_tests parse_flag_from_env(RUN_SLOW, defaultFalse)即默认跳过slow测试只有显式设置为真值时才会运行。Hugging Face CI 会在每次 Pull Request 中运行不带slow的模型测试而慢速测试则由夜间nightly调度执行PR 检查的具体验证内容参见 pull request checks。选择一个基础测试类三大基础测试类覆盖最常见的模型家族按模型模态选择即可基础类适用场景继承的 MixinCausalLMModelTest因果语言模型ModelTesterMixin、GenerationTesterMixin、PipelineTesterMixin、TrainingTesterMixin、TensorParallelTesterMixinVLMModelTest视觉-语言模型ModelTesterMixin、GenerationTesterMixin、PipelineTesterMixinALMModelTest音频-语言模型ModelTesterMixin、GenerationTesterMixin、PipelineTesterMixinVLMModelTest与ALMModelTest共享共同的父类MultiModalModelTest它将各子配置嵌套进一个复合的顶层配置中并在input_ids中放置模态占位 token同时提供原始的模态特征音频或视觉。CausalLMModelTest不使用多模态父类它在三个共享 Mixin 的基础上额外加入TrainingTesterMixin和TensorParallelTesterMixin以获得训练与张量并行的测试覆盖。对于无法归入上述三类的架构仅编码器、编码器-解码器等可以直接基于 ModelTester 与 ModelTest 双类模式 和 测试 Mixin 自行搭建测试基础设施。CausalLMModelTest因果语言模型的推荐基类CausalLMModelTest是测试因果语言模型的推荐基类继承自五个测试 Mixin自动生成保存/加载、生成、Pipeline、训练和张量并行测试。import unittest from transformers.testing_utils import require_torch from transformers import is_torch_available from ...causal_lm_tester import CausalLMModelTest, CausalLMModelTester if is_torch_available(): from transformers import MyModel class MyModelTester(CausalLMModelTester): if is_torch_available(): base_model_class MyModel require_torch class MyModelTest(CausalLMModelTest, unittest.TestCase): model_tester_class MyModelTester这两个类即可为MyModel及其所有 head 类MyModelForCausalLM、MyModelForSequenceClassification等提供完整测试覆盖。真实示例可参考 tests/models/llama/test_modeling_llama.py。从源码看CausalLMModelTester._verify_and_infer_model_attributes()见 tests/causal_lm_tester.py承担了关键的属性校验与推断逻辑base_model_class是强制属性必须设置为有效的模型类PreTrainedModel的子类否则直接抛出ValueError若config_class、causal_lm_class等属性未显式设置测试器会从基础类名推断base_model_class.__name__.replace(Model, )得到基础名如LlamaModel变为Llama再通过_COMMON_MODEL_NAMES_MAP拼接Config、ForCausalLM等后缀从模块中发现关联类若类不存在属性保持None对应的测试会被跳过同时还会防御性地检查不允许在测试器上把未登记的属性设置为模型类防止拼写错误。覆盖 CausalLMTester 中的默认值如果模型命名不符合标准约定或需要自定义行为可覆盖测试器或测试类上的属性class MyModelTester(CausalLMModelTester): if is_torch_available(): base_model_class MyModel # 类名不符合约定时覆盖 causal_lm_class MyCustomCausalLM require_torch class MyModelTest(CausalLMModelTest, unittest.TestCase): model_tester_class MyModelTester # 禁用 embedding resize 测试 test_resize_embeddings False对于需要在测试器上传递自定义构造参数如新架构的专属超参的模型可覆盖__init__并在设置额外属性之前先调用super().__init__(parentparent)。真实示例可参考 tests/models/youtu/test_modeling_youtu.pyclass YoutuModelTester(CausalLMModelTester): if is_torch_available(): base_model_class YoutuModel def __init__(self, parent, kv_lora_rank16, q_lora_rank32): super().__init__(parentparent) self.kv_lora_rank kv_lora_rank self.q_lora_rank q_lora_rank补充一点源码细节CausalLMModelTester.__init__还会为_TEXT_MODEL_TESTER_DEFAULTS中的共享文本模型默认值如batch_size、seq_length、vocab_size、hidden_size等赋初值并额外设置 Mamba 系参数mamba_n_groups、mamba_d_state等与tie_word_embeddings False这些均可被子类通过 kwargs 覆盖见 tests/causal_lm_tester.py。VLMModelTest视觉-语言模型基类VLMModelTest是视觉-语言模型的基类继承三个 MixinModelTesterMixin、GenerationTesterMixin、PipelineTesterMixin并设置_is_composite True以处理多子模型场景。import unittest from transformers.testing_utils import require_torch from transformers import is_torch_available from ...vlm_tester import VLMModelTest, VLMModelTester if is_torch_available(): from transformers import ( MyVLMConfig, MyVLMModel, MyVLMTextConfig, MyVLMVisionConfig, MyVLMForConditionalGeneration, ) class MyVLMTester(VLMModelTester): if is_torch_available(): base_model_class MyVLMModel config_class MyVLMConfig text_config_class MyVLMTextConfig vision_config_class MyVLMVisionConfig conditional_generation_class MyVLMForConditionalGeneration require_torch class MyVLMTest(VLMModelTest, unittest.TestCase): model_tester_class MyVLMTester覆盖 VLMModelTester 中的默认值当 VLM 需要自定义视觉参数或非默认配置值时覆盖__init__在调用super().__init__(parent, **kwargs)之前用setdefault设置默认值。下面的示例展示了 tests/models/qianfan_ocr/test_modeling_qianfan_ocr.py 中的部分默认值class QianfanOCRVisionText2TextModelTester(VLMModelTester): base_model_class QianfanOCRModel config_class QianfanOCRConfig text_config_class Qwen3Config vision_config_class QianfanOCRVisionConfig conditional_generation_class QianfanOCRForConditionalGeneration def __init__(self, parent, **kwargs): kwargs.setdefault(image_token_id, 1) kwargs.setdefault(image_size, 32) kwargs.setdefault(patch_size, 4) kwargs.setdefault(num_channels, 3) # ... 更多默认值 super().__init__(parent, **kwargs)VLM 测试与 CausalLMModelTest 的差异VLM 测试与CausalLMModelTest在几个方面存在差异必须在测试器上设置config_class、text_config_class、vision_config_class和conditional_generation_classVLMModelTest不包含TrainingTesterMixin或TensorParallelTesterMixin测试器的__init__通过**kwargs与setdefault()接收视觉参数image_size、patch_size、num_channels、num_image_tokensConfigTester使用has_text_modalityFalse因为顶层配置是复合配置而非文本模型配置。从源码看VLMModelTester.__init__tests/vlm_tester.py会通过setdefault设置一整套 VLM 专属默认值seq_length由7 num_image_tokens计算得出num_image_tokens默认等于(image_size // patch_size) ** 2以及image_token_id3、image_size8、patch_size4、num_channels3、projection_dim32、vision_feature_select_strategydefault、vision_feature_layer-1等。其pipeline_model_mapping属性将feature-extraction映射到base_model_class、image-text-to-text映射到conditional_generation_class。此外place_image_tokens()会先把输入中误出现的image_token_id清理为bos_token_id再在序列开头放置num_image_tokens个图像占位 token见 tests/vlm_tester.py。ALMModelTest音频-语言模型基类ALMModelTest是音频-语言模型ALM的基类适用于 Qwen2Audio、AudioFlamingo3、GraniteSpeech 等模型。它复刻 VLM 模式使用相同的MultiModalModelTest父类与 head 类自动发现机制但将视觉相关机制替换为音频特征、音频子配置与音频 token 放置策略。class MyALMTester(ALMModelTester): config_class MyALMConfig text_config_class MyALMTextConfig audio_config_class MyALMAudioConfig conditional_generation_class MyALMForConditionalGeneration audio_mask_key feature_attention_mask class MyALMTest(ALMModelTest, unittest.TestCase): model_tester_class MyALMTester覆盖 ALMModelTester 中的默认值测试器的__init__设置 ALM 专属默认值feat_seq_length128、num_mel_bins80、audio_token_id0可通过setdefault在调用super().__init__(parent, **kwargs)之前覆盖。两个类属性告知测试器模型如何命名相关组件audio_mask_key模型期望接收音频掩码的 kwarg 名feature_attention_mask、input_features_mask等。如果模型不消费独立的音频掩码保持None。audio_config_key顶层配置中嵌套音频子配置的属性名。默认是audio_config但 GraniteSpeech 等模型使用encoder_config。class Qwen2AudioModelTester(ALMModelTester): def __init__(self, parent, **kwargs): kwargs.setdefault(feat_seq_length, 60) kwargs.setdefault(max_source_positions, kwargs[feat_seq_length] // 2) super().__init__(parent, **kwargs)ALM 测试器的 Hook 机制ALMModelTester要求覆盖一个 hookget_audio_embeds_mask(audio_mask)此外还提供若干可选 hook 用于自定义get_audio_embeds_mask(audio_mask)返回编码器下采样后各批次音频嵌入位置的掩码。测试器用它的行和决定向input_ids中插入多少个audio_token_id占位符因此返回数量必须与编码器实际输出的音频嵌入数量一致对应 tests/alm_tester.py 中的raise NotImplementedError。create_audio_features()返回音频特征张量默认形状为[batch_size, num_mel_bins, feat_seq_length]。当模型如 GraniteSpeech期望时间优先的特征[batch_size, feat_seq_length, num_mel_bins]时需覆盖。create_audio_mask()返回音频级注意力掩码。默认实现为批次中每一行生成随机但连续的合法区域如[0, 0, 1, 1, 1, 0, 0]并固定至少一个批次为全长掩码如果测试需要对两次prepare_config_and_inputs_for_common()调用做对比或音频编码器分发到的后端拒绝非空掩码应覆盖为确定性的全长掩码。place_audio_tokens(input_ids, config, num_audio_tokens)将音频占位 token 连续放置在BOS之后。只有模型需要不同布局时才需覆盖。get_audio_feature_key()返回输入字典中音频特征的键名默认为input_features。从源码看tests/alm_tester.py_prepare_modality_inputs的完整链路为create_audio_features()生成特征 →create_audio_mask()生成音频掩码 →get_audio_embeds_mask(audio_mask)得到嵌入掩码并求和得到num_audio_tokens→place_audio_tokens()将占位 token 写入input_ids→ 最后按audio_mask_key是否为空决定是否把音频掩码写入输入字典。ALM 专属测试音频 token 数量不匹配除了继承的多模态测试外ALMModelTest额外增加了test_mismatching_num_audio_tokens。该测试断言当音频特征数量与input_ids中的音频占位 token 数量不一致时模型会抛出明确的ValueError同时验证包含多个音频段的 prompt 仍能成功前向见 tests/alm_tester.py。其测试策略包括删减一个音频但保留文本中的音频 token、追加一个音频、沿序列维度复制文本使音频 token 翻倍均期望ValueError以及同时复制文本与音频特征期望前向成功。为其他架构编写测试对于仅编码器、编码器-解码器、音频或其他非标准架构可基于以下双类模式与测试 Mixin 直接搭建测试基础设施。ModelTester 与 ModelTest 双类模式每个模型测试文件都遵循相同的结构ModelTester普通类创建微小的配置与虚拟输入用于测试也可以承载模型专属的小型回归测试。ModelTestunittest.TestCase Mixin继承自动生成的测试并针对每个模型变体运行。ModelTest调用测试器上的prepare_config_and_inputs_for_common()获取(config, inputs_dict)元组。所有 Mixin 都依赖prepare_config_and_inputs_for_common()提供测试数据。以CausalLMModelTester的实现为例tests/causal_lm_tester.pyprepare_config_and_inputs()用ids_tensor生成input_ids可选的下三角attention_mask、token_type_ids与三类标签get_config()通过反射config_class.__init__的签名并匹配attribute_map来收集 kwargs并强制加入pad_token_idprepare_config_and_inputs_for_common()则返回(config, {input_ids: ..., attention_mask: ...})。测试 Mixin 一览按模型需求选择所需的 MixinMixin源文件测试内容ModelTesterMixintests/test_modeling_common.py保存/加载、梯度检查点、前向签名、通用属性GenerationTesterMixintests/generation/test_utils.pyGreedy、Sampling、Beam Search、Assisted DecodingPipelineTesterMixintests/test_pipeline_mixin.py每个 Pipeline 任务一个测试TrainingTesterMixintests/test_training_mixin.py小批量上的过拟合TensorParallelTesterMixintests/test_tensor_parallel_mixin.py分布式张量并行编写一个模型测试完整的可运行示例见 tests/models/modernbert/test_modeling_modernbert.py。关键步骤如下ModelTester类构建微小的配置与虚拟输入。保持维度足够小使测试能在 CPU 上数秒内完成。使用下面三个张量辅助函数构建输入ids_tensor(shape, vocab_size)在[0, vocab_size)范围内的随机整数张量用于input_ids、token_type_ids与标签张量。其实现tests/test_modeling_common.py基于全局 RNG 生成torch.long张量。random_attention_mask(shape)二进制张量0/1第一个 token 恒为 1保证因果掩码下每批至少有一个被注意的 token用于attention_masktests/test_modeling_common.py。floats_tensor(shape, scale1.0)随机浮点张量用于连续输入如pixel_values或inputs_embedstests/test_modeling_common.py。测试器必须实现get_config()、prepare_config_and_inputs()和prepare_config_and_inputs_for_common()并为每个任务 head基础模型、序列分类、token 分类等添加create_and_check_*方法。继承模型所需的 Mixin设置all_model_classes与pipeline_model_mapping定义setUp()。编写委托给测试器create_and_check_*方法的test_*方法。为每个任务 head 在测试器上添加create_and_check_*方法实例化模型、执行前向、断言输出形状再在测试类上添加对应的test_*方法。文件组织测试文件位于tests/models/mymodel/目录结构如下tests/models/mymodel/ ├── __init__.py ├── test_modeling_mymodel.py # 模型测试必需 ├── test_tokenization_mymodel.py # 分词器测试如有自定义分词器 ├── test_image_processing_mymodel.py # 图像处理器测试如是视觉模型 ├── test_feature_extraction_mymodel.py # 特征提取器测试如是音频/语音模型 └── test_processing_mymodel.py # 处理器测试如是多模态模型分词器测试遵循同样的模式从 tests/test_tokenization_common.py 继承TokenizerTesterMixin设置少量属性即可获得自动生成的测试。示例见 tests/models/llama/test_tokenization_llama.py。配置测试ConfigTesterConfigTester验证配置类是否正确处理序列化、保存/加载与标准属性。CausalLMModelTest和VLMModelTest已自动包含配置测试对于使用ModelTesterModelTest的通用路径需要在setUp()中手动定义配置测试器。from tests.test_configuration_common import ConfigTester def setUp(self): self.config_tester ConfigTester(self, config_classMyModelConfig, hidden_size32) def test_config(self): self.config_tester.run_common_tests()run_common_tests()执行多项检查检查通用属性如hidden_size、num_attention_heads、num_hidden_layers是否存在若has_text_modalityTrue还需检查vocab_size用to_json_string()与to_json_file()测试 JSON 序列化对save_pretrained()与from_pretrained()做往返测试确认id2label与label2id的一致性无参数创建配置验证默认初始化设置output_hidden_states等通用 kwargs 并确认被正确存储。对于缺少vocab_size的纯视觉模型传入has_text_modalityFalse还可以传入额外**kwargs覆盖配置默认值。self.config_tester ConfigTester( self, config_classMyVisionConfig, has_text_modalityFalse, hidden_size64 )值得注意多模态测试基类MultiModalModelTest.setUp中创建ConfigTester时固定使用has_text_modalityFalse见 tests/multimodal_tester.py这正是因为多模态顶层配置是复合配置不直接拥有vocab_size属性。集成测试与 tiny modelsMixin 测试使用带随机权重的小型配置快速验证模型行为集成测试则用真实预训练权重运行推理以验证输出正确性。Hub 上的 tiny models 足够小、适合快速 CI但结构上与真实 checkpoint 一致。编写集成测试将集成测试放在独立的测试类中并用slow标记。每个测试下载真实权重、运行推理并将输出与期望值比对。在setUp和tearDown中调用cleanup(torch_device, gc_collectFalse)以避免内存残留。import torch from transformers import AutoTokenizer from transformers.testing_utils import cleanup, require_torch, slow, torch_device class MyModelIntegrationTest(unittest.TestCase): def setUp(self): cleanup(torch_device, gc_collectFalse) def tearDown(self): cleanup(torch_device, gc_collectFalse) slow require_torch def test_inference(self): model MyModelForCausalLM.from_pretrained(myorg/mymodel-base).to(torch_device) tokenizer AutoTokenizer.from_pretrained(myorg/mymodel-base) inputs tokenizer(Hello, world, return_tensorspt).to(torch_device) with torch.no_grad(): outputs model(**inputs) # 与期望值比对 expected_slice torch.tensor([[-0.1234, 0.5678, -0.9012]]) torch.testing.assert_close(outputs.logits[0, :1, :3], expected_slice, atol1e-4, rtol1e-4)任何需要下载权重、加载大数据集或耗时超过数秒的测试都应标记slow。PR CI 会跳过慢速测试夜间调度会运行它们。生成Generation集成测试生成测试中使用do_sampleFalse保证输出在不同运行与不同硬件间可复现。对于 Mixture-of-ExpertsMoE模型还需在生成前调用model.set_experts_implementation(eager)以强制走稳定的专家分发路径否则 router 中微小的数值差异可能导致某个 token 落到不同的专家上从而改变输出。slow require_torch def test_generate(self): model MyModelForCausalLM.from_pretrained(myorg/mymodel-base).to(torch_device) tokenizer AutoTokenizer.from_pretrained(myorg/mymodel-base) inputs tokenizer(Hello, world, return_tensorspt).to(torch_device) # model.set_experts_implementation(eager) # MoE 模型需取消注释 generated_ids model.generate(**inputs, max_new_tokens20, do_sampleFalse) output tokenizer.batch_decode(generated_ids, skip_special_tokensTrue) self.assertEqual(output, [Hello, world! This is the expected continuation...])硬件相关的期望值Transformers CI 在 NVIDIA A10 上运行慢速测试。不同 GPU 代际间的数值结果可能略有差异因此集成测试使用Expectations类定义于 src/transformers/testing_utils.py注册按设备区分的期望值。Expectations基于(device_type, (major, minor))的 SM 键为当前硬件挑选最佳匹配没有匹配时回退到默认值。其评分规则为设备类型匹配得 1 分、major 匹配再加 1 分、minor 匹配再加 1 分而默认期望(None, None)固定得 0.5 分见score()方法。运行torch.cuda.get_device_capability()可打印本机 SM 版本例如 A10 为(8, 6)H100 为(9, 0)。from transformers.testing_utils import Expectations expected_texts Expectations( { (cuda, (8, 6)): [Hello, world! This is the A10 continuation...], (cuda, (9, 0)): [Hello, world! This is the H100 continuation...], } ).get_expectation() self.assertEqual(output, expected_texts)创建 tiny models带随机权重的 tiny models 存放在 Hub 的 hf-internal-testing 组织下。Pipeline 测试在需要 Hub 托管的 checkpoint 但不关心输出质量时依赖 tiny models快速冒烟测试也加载 tiny models 验证前向形状而无需下载大 checkpoint。tiny models 是集成测试的最后手段仅当最小可用 checkpoint 超过约 24 GB 显存时才使用。只要可能应使用原始预训练权重以捕捉真实的数值回归。utils/create_dummy_models.py 脚本基于ModelTester.get_config()生成 tiny models从测试器中提取微小超参数、构建随机权重模型并上传到 Hub。本地生成 tiny modelspython utils/create_dummy_models.py output_dir -m your_model_type上传到 Hubpython utils/create_dummy_models.py output_dir -m your_model_type --upload --organization hf-internal-testing每个模型使用hf-internal-testing/tiny-random-{ModelClassName}命名并被记录在 tests/utils/tiny_model_summary.json 中。CI 工作流每天重新生成 tiny models。控制测试范围布尔开关ModelTesterMixin上的布尔标志用于开关自动生成的测试。在测试类上覆盖任意标志即可启用或禁用对应检查。class MyModelTest(CausalLMModelTest, unittest.TestCase): model_tester_class MyModelTester test_resize_embeddings False test_all_params_have_gradient False # 当并非所有参数在每次前向中都激活时标志默认值控制内容test_resize_embeddingsTrue嵌入层尺寸调整test_resize_position_embeddingsFalse位置嵌入尺寸调整test_mismatched_shapesTrue输入/输出形状不匹配处理test_missing_keysTrue加载时的缺失键警告test_torch_exportableTruetorch.export兼容性test_all_params_have_gradientTrue所有参数接收梯度当并非所有参数在每次前向中都激活时设为False如 MoE 专家is_encoder_decoderFalse编码器-解码器专属测试has_attentionsTrue注意力输出测试_is_compositeFalse复合/多模态模型处理model_split_percents[0.5, 0.7, 0.9]模型并行测试的切分百分比例如多模态基类MultiModalModelTest直接将_is_composite True设为类属性tests/multimodal_tester.py而测试类中设置test_resize_embeddings False则会在继承层级中覆盖 Mixin 的默认值这一机制正是继承 覆盖实现按模型定制测试范围的精髓。下一步浏览 pytest 官方文档了解更多关于测试选择、fixture、日志记录等特性。【免费下载链接】transformers Transformers: the model-definition framework for state-of-the-art machine learning models in text, vision, audio, and multimodal models, for both inference and training.项目地址: https://gitcode.com/GitHub_Trending/tra/transformers创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考 SEO 优化官网定制响应式建站教育培训建站