Transformers documentation
Escrevendo testes de modelo
Escrevendo testes de modelo
A suíte de testes do Transformers usa uma arquitetura baseada em mixins para gerar automaticamente mais de 100 testes a partir de um código mínimo. Você escreve uma pequena quantidade de código específico do modelo, e os mixins cuidam de salvar/carregar, geração, pipelines, treinamento e paralelismo de tensores.
Execute os testes do seu modelo com os seguintes comandos.
# run your model's tests
pytest tests/models/mymodel/test_modeling_mymodel.py -v
# run a specific test
pytest tests/models/mymodel/test_modeling_mymodel.py::MyModelTest::test_model
# run tests matching a keyword pattern (useful to run all integration tests)
pytest tests/models/mymodel/ -k integration -v
# include slow integration tests
RUN_SLOW=1 pytest tests/models/mymodel/ -vO CI do Hugging Face executa os testes de modelo sem @slow em cada pull request, e os testes lentos são executados em uma agenda noturna (veja Verificações de pull request para saber o que o CI valida).
Escolha uma classe de teste base
Três classes base cobrem as famílias de modelos mais comuns. Escolha a que corresponde à modalidade do seu modelo.
| Classe base | Usar para | Mixins |
|---|---|---|
CausalLMModelTest | Modelos de linguagem causais | ModelTesterMixin, GenerationTesterMixin, PipelineTesterMixin, TrainingTesterMixin, TensorParallelTesterMixin |
VLMModelTest | Modelos de visão e linguagem | ModelTesterMixin, GenerationTesterMixin, PipelineTesterMixin |
ALMModelTest | Modelos de linguagem de áudio | ModelTesterMixin, GenerationTesterMixin, PipelineTesterMixin |
VLMModelTest e ALMModelTest compartilham um pai comum, MultiModalModelTest, que aninha sub-configs em um config de nível superior composto e posiciona tokens placeholder de modalidade em input_ids junto com os recursos brutos da modalidade (áudio ou visão). CausalLMModelTest não usa o pai multimodal. Ele se baseia nos três mixins compartilhados e adiciona TrainingTesterMixin e TensorParallelTesterMixin para cobertura de treinamento e paralelismo de tensor.
Para arquiteturas que não se encaixam em nenhuma das três (somente codificador, codificador-decodificador, etc.), construa a infraestrutura de testes diretamente a partir do padrão de duas classes e dos mixins de teste descritos abaixo.
CausalLMModelTest
CausalLMModelTest é a classe base recomendada para testar modelos de linguagem causais. Ela herda de cinco test mixins e gera automaticamente testes para save/load, geração, pipelines, treinamento e paralelismo de tensor.
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 = MyModelTesterEssas duas classes oferecem cobertura completa de testes para MyModel e todas as suas classes de cabeçalho (MyModelForCausalLM, MyModelForSequenceClassification, etc.). Consulte tests/models/llama/test_modeling_llama.py para um exemplo real.
CausalLMModelTester requer apenas base_model_class. O tester remove o sufixo Model para obter um nome base (LlamaModel se torna Llama), em seguida, adiciona sufixos como Config ou ForCausalLM para descobrir classes relacionadas. Se uma classe não existir no módulo, o atributo permanece None e o tester ignora os testes correspondentes.
Sobrescrevendo os padrões no CausalLMTester
Se o seu modelo não segue a nomenclatura padrão ou você precisa personalizar o comportamento, sobrescreva os atributos no tester ou na classe de teste.
class MyModelTester(CausalLMModelTester):
if is_torch_available():
base_model_class = MyModel
# override if the class name doesn't follow the convention
causal_lm_class = MyCustomCausalLM
@require_torch
class MyModelTest(CausalLMModelTest, unittest.TestCase):
model_tester_class = MyModelTester
# disable embedding resize tests
test_resize_embeddings = FalsePara modelos que precisam de parâmetros de construtor personalizados no tester, sobrescreva __init__ e chame super().__init__(parent=parent) antes de definir atributos extras. Veja tests/models/youtu/test_modeling_youtu.py para um exemplo real.
class YoutuModelTester(CausalLMModelTester):
if is_torch_available():
base_model_class = YoutuModel
def __init__(self, parent, kv_lora_rank=16, q_lora_rank=32):
super().__init__(parent=parent)
self.kv_lora_rank = kv_lora_rank
self.q_lora_rank = q_lora_rankVLMModelTest
VLMModelTest é a classe base para modelos de visão-linguagem. Ela herda de três mixins (ModelTesterMixin, GenerationTesterMixin, PipelineTesterMixin) e define _is_composite = True para lidar com múltiplos submodelos.
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 = MyVLMTesterSobrescrevendo os padrões no VLMModelTester
Quando o VLM precisa de parâmetros de visão personalizados ou valores de configuração não padrão, sobrescreva __init__. Defina os padrões com setdefault antes de chamar super().__init__(parent, **kwargs). O exemplo abaixo mostra os primeiros padrões de 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)
# ... more defaults
super().__init__(parent, **kwargs)Os testes de VLM diferem do CausalLMModelTest em alguns aspectos.
- Você deve definir
config_class,text_config_class,vision_config_classeconditional_generation_classno tester. VLMModelTestnão incluiTrainingTesterMixinnemTensorParallelTesterMixin.- O
__init__do tester aceita parâmetros de visão (image_size,patch_size,num_channels,num_image_tokens) a partir de**kwargsesetdefault(). ConfigTesterusahas_text_modality=Falseporque a configuração de nível superior é uma configuração composta, e não uma configuração de modelo de texto.
ALMModelTest
ALMModelTest é a classe base para modelos de áudio-linguagem (ALMs), como Qwen2Audio, AudioFlamingo3 e GraniteSpeech. Ela espelha o padrão de VLM, com o mesmo pai MultiModalModelTest e descoberta automática de classes de head. A mecânica do lado da visão é substituída por recursos de áudio, uma subconfiguração de áudio e uma estratégia de posicionamento de tokens de áudio.
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 = MyALMTesterSobrescrevendo os padrões no ALMModelTester
O __init__ do tester define padrões específicos do ALM (feat_seq_length=128, num_mel_bins=80, audio_token_id=0). Substitua-os com setdefault antes de chamar super().__init__(parent, **kwargs).
Dois atributos de classe informam ao tester como o seu modelo nomeia as coisas.
audio_mask_key: o nome do kwarg que seu modelo espera para a máscara de áudio ("feature_attention_mask","input_features_mask", etc.). Deixe comoNonese seu modelo não consumir uma máscara de áudio separada.audio_config_key: o nome do atributo que a sua configuração de nível superior usa para aninhar a subconfiguração de áudio. O padrão é"audio_config", mas modelos como GraniteSpeech usam"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)ALMModelTester exige que você sobrescreva um hook, get_audio_embeds_mask(audio_mask), e expõe alguns outros opcionais para personalização.
get_audio_embeds_mask(audio_mask): retorna a máscara por lote das posições dos embeddings de áudio após o downsampling do encoder. O tester usa a soma por linha para decidir quantos placeholdersaudio_token_idinserir eminput_ids, portanto a contagem deve corresponder ao que seu encoder emite.create_audio_features(): retorna o tensor de recursos de áudio. A forma padrão é[batch_size, num_mel_bins, feat_seq_length]. Substitua quando seu modelo, como o GraniteSpeech, espera recursos com o tempo em primeiro ([batch_size, feat_seq_length, num_mel_bins]).create_audio_mask(): retorna a máscara de atenção em nível de áudio. O padrão constrói uma região válida contígua aleatória por linha no lote. Substitua por uma máscara de comprimento total determinística se seus testes compararem duas chamadas deprepare_config_and_inputs_for_common()entre si, ou se seu codificador de áudio for despachado para um backend que rejeita máscaras não nulas.place_audio_tokens(input_ids, config, num_audio_tokens): posiciona os tokens de espaço reservado de áudio de forma contígua após oBOS. Substitua apenas se o seu modelo exigir um layout diferente.get_audio_feature_key(): retorna a chave do dicionário de entradas para recursos de áudio (padrão:"input_features").
Além dos testes multimodais herdados, ALMModelTest adiciona test_mismatching_num_audio_tokens. O teste afirma que o modelo levanta um ValueError claro quando o número de recursos de áudio não corresponde ao número de tokens de placeholder de áudio em input_ids, e verifica que um prompt com múltiplos segmentos de áudio ainda é processado com sucesso.
Escreva testes para outras arquiteturas
Para arquiteturas apenas de codificador, codificador-decodificador, de áudio ou outras não padrão, construa a infraestrutura de testes diretamente a partir do padrão de duas classes e dos mixins de teste descritos abaixo.
ModelTester e ModelTest
Todo arquivo de teste de modelo segue a mesma estrutura.
ModelTester(classe simples) cria configurações pequenas e entradas fictícias para testes e também pode conter pequenos testes de regressão específicos do modelo.ModelTest(unittest.TestCase+ mixins) herda testes gerados automaticamente e os executa contra todas as variantes de modelo.
ModelTest chama prepare_config_and_inputs_for_common() no tester para obter uma tupla (config, inputs_dict). Todos os mixins dependem de prepare_config_and_inputs_for_common() para os dados de teste.
Testes de mixins
Escolha os mixins de que o seu modelo precisa.
| Mixin | Arquivo de origem | O que ele testa |
|---|---|---|
ModelTesterMixin | tests/test_modeling_common.py | Salvar/carregar, checkpointing de gradiente, assinatura do forward, atributos comuns |
GenerationTesterMixin | tests/generation/test_utils.py | Guloso, amostragem, busca em feixe, decodificação assistida |
PipelineTesterMixin | tests/test_pipeline_mixin.py | Um teste por tarefa de pipeline |
TrainingTesterMixin | tests/test_training_mixin.py | Sobreajuste em um lote pequeno |
TensorParallelTesterMixin | tests/test_tensor_parallel_mixin.py | Paralelismo de tensor distribuído |
Escrevendo um teste de modelo
Veja tests/models/modernbert/test_modeling_modernbert.py para um exemplo completo e funcional. As etapas principais estão descritas abaixo.
A classe
ModelTestercria configurações pequenas e entradas fictícias. Mantenha as dimensões pequenas para que os testes terminem em segundos na CPU. Use os três auxiliares de tensor abaixo para construir as entradas.ids_tensor(shape, vocab_size): Tensor de inteiros aleatórios em[0, vocab_size). Use parainput_ids,token_type_idse tensores de rótulos.random_attention_mask(shape): Tensor binário (0s e 1s) em que o primeiro token é sempre 1. Use paraattention_mask.floats_tensor(shape, scale=1.0): Tensor de números flutuantes aleatório. Use para entradas contínuas, comopixel_valuesouinputs_embeds.
O tester deve implementar
get_config(),prepare_config_and_inputs()eprepare_config_and_inputs_for_common(). Adicione métodoscreate_and_check_*para cada head de tarefa (modelo base, classificação de sequência, classificação de token, etc.).Herdar dos mixins que seu modelo precisa, definir
all_model_classesepipeline_model_mapping, e definirsetUp(). Escrever métodostest_*que delegam para os métodoscreate_and_check_*do tester.Para cada head de tarefa, adicione um método
create_and_check_*no tester que instancie o modelo, execute uma passagem para frente e afirme as formas de saída. Em seguida, adicione um métodotest_*correspondente na classe de teste.
Organização de arquivos
Os arquivos de teste ficam em tests/models/mymodel/, seguindo a estrutura mostrada abaixo.
tests/models/mymodel/ ├── __init__.py ├── test_modeling_mymodel.py # model tests (required) ├── test_tokenization_mymodel.py # tokenizer tests (if custom tokenizer) ├── test_image_processing_mymodel.py # image processor tests (if vision model) ├── test_feature_extraction_mymodel.py # feature extractor tests (if audio/speech model) └── test_processing_mymodel.py # processor tests (if multimodal)
Os testes de tokenizador seguem o mesmo padrão. Herde TokenizerTesterMixin de tests/test_tokenization_common.py, defina alguns atributos e obtenha testes gerados automaticamente. Consulte tests/models/llama/test_tokenization_llama.py para um exemplo.
Testes de configuração
ConfigTester verifica se uma classe de configuração trata a serialização, o save/load e as propriedades padrão corretamente. CausalLMModelTest e VLMModelTest incluem testes de configuração automaticamente. Para o caminho geral com ModelTester e ModelTest, defina o tester de configuração manualmente em setUp().
from tests.test_configuration_common import ConfigTester
def setUp(self):
self.config_tester = ConfigTester(self, config_class=MyModelConfig, hidden_size=32)
def test_config(self):
self.config_tester.run_common_tests()run_common_tests() executa várias verificações.
- Verifica se propriedades comuns como
hidden_size,num_attention_headsenum_hidden_layersexistem (evocab_sizesehas_text_modality=True). - Testa a serialização JSON com
to_json_string()eto_json_file(). - Faz o round-trip de
save_pretrained()efrom_pretrained(). - Confirma a consistência entre
id2labelelabel2id. - Cria uma configuração sem argumentos para validar a inicialização padrão.
- Define kwargs comuns como
output_hidden_statese confirma que eles são armazenados corretamente.
Passe has_text_modality=False para modelos apenas de visão que não possuem vocab_size e passe **kwargs extras para sobrescrever os padrões da configuração.
self.config_tester = ConfigTester(
self, config_class=MyVisionConfig, has_text_modality=False, hidden_size=64
)Testes de integração e modelos pequenos
Os testes de mixin usam configurações pequenas com pesos aleatórios para verificar rapidamente o comportamento do modelo. Os testes de integração executam inferência com pesos pré-treinados reais para validar a correção da saída. Os modelos pequenos no Hub são pequenos o suficiente para um CI rápido, mas estruturados como checkpoints reais.
Escrevendo testes de integração
Coloque os testes de integração em uma classe de teste separada, marque-os com @slow e inclua MemoryCleanupMixin. Cada teste baixa pesos reais, executa inferência e verifica as saídas em relação aos valores esperados. O mixin libera a memória que cada teste ocupa e executa os métodos de teste sob torch.no_grad() — consulte Liberando memória entre testes para saber o que ele faz e como desativá-lo.
import torch
from transformers import AutoTokenizer
from transformers.testing_utils import MemoryCleanupMixin, require_torch, slow, torch_device
class MyModelIntegrationTest(MemoryCleanupMixin, unittest.TestCase):
@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_tensors="pt").to(torch_device)
outputs = model(**inputs)
# check against expected values
expected_slice = torch.tensor([[-0.1234, 0.5678, -0.9012]])
torch.testing.assert_close(outputs.logits[0, :1, :3], expected_slice, atol=1e-4, rtol=1e-4)Marque qualquer teste com @slow se ele baixar pesos, carregar um conjunto de dados grande ou levar mais do que alguns segundos. O CI do pull request ignora testes lentos, mas a agenda noturna os executa.
Liberando memória entre testes
Os testes de integração carregam checkpoints reais, portanto, um teste que mantém uma referência ao seu modelo pode esgotar a memória de tudo o que for executado em seguida. MemoryCleanupMixin, em tests/test_memory_cleanup_mixin.py, trata isso para a classe inteira:
- Executa
cleanup(torch_device, gc_collect=True)antes e depois de cada teste para que sobras não passem de um para o outro. - Remove os atributos que o teste adicionou em
self(incluindo caches de@cached_property) e na classe (por exemplo,cls.model = ...emsetUpClass). O Pytest mantém as instâncias de teste vivas durante a sessão, portantogc.collect()não consegue liberar os objetos que essas referências ainda mantêm. - Executa os métodos de teste sob
torch.no_grad()para que uma passagem de frente não retenha ativações.
Classes que treinam ou chamam backward() definem run_under_no_grad = False. Uma classe que mistura ambos coloca @with_grad ou @with_no_grad nos métodos de teste individuais.
Os atributos atribuídos no corpo da classe são mantidos, e tudo o que for adicionado posteriormente é descartado. Se você sobrescrever setUp, chame super().setUp(): o mixin faz um snapshot dos atributos da instância ali e gera um erro sem essa chamada.
MemoryCleanupTestCasecombina o mixin comTestCasePlus, para testes que também desejam diretórios temporários removidos automaticamente.
Para encontrar um teste com vazamento de memória, defina TRANSFORMERS_TEST_MEMORY_LEAK_MIB com um orçamento em MiB; qualquer teste que deixe mais do que esse valor alocado no dispositivo será reportado com seu uso de pico. Adicione TRANSFORMERS_TEST_MEMORY_LEAK_MODE=error para falhar em vez de apenas emitir um aviso. Essa verificação está desativada por padrão e deve ser ativada ao procurar por resíduos. O teardown usual do mixin já executa gc.collect, o que pode limpar a alocação que você gostaria de inspecionar ao reproduzir um vazamento.
Testes de integração de geração
Use do_sample=False nos testes de geração para que a saída seja determinística entre execuções e hardware. Para modelos Mixture-of-Experts, chame também model.set_experts_implementation("eager") antes de gerar, para forçar um caminho de despacho de especialistas estável. Sem isso, pequenas diferenças numéricas no roteador podem alterar qual especialista processa um token e mudar a saída.
@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_tensors="pt").to(torch_device)
# model.set_experts_implementation("eager") # uncomment for MoE models
generated_ids = model.generate(**inputs, max_new_tokens=20, do_sample=False)
output = tokenizer.batch_decode(generated_ids, skip_special_tokens=True)
self.assertEqual(output, ["Hello, world! This is the expected continuation..."])Expectativas específicas de hardware
A CI do Transformers executa testes lentos em uma NVIDIA A10. Os resultados numéricos podem variar ligeiramente entre gerações de GPU, por isso os testes de integração usam a classe Expectations para registrar valores esperados por dispositivo. O Expectations seleciona a melhor correspondência para o hardware atual com base nas chaves SM (device_type, (major, minor)) e recorre a um valor padrão quando nada corresponde.
Execute torch.cuda.get_device_capability() para exibir a versão local do SM (por exemplo, (8, 6) para um A10, (9, 0) para H100).
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)Criando modelos minúsculos
Modelos minúsculos com pesos aleatórios estão hospedados no Hub sob a organização hf-internal-testing. Os testes de pipeline dependem de modelos minúsculos quando necessitam de um checkpoint hospedado no Hub, mas não se importam com a qualidade da saída. Testes de fumaça rápidos também carregam modelos minúsculos para verificar as formas da passagem direta, sem baixar checkpoints grandes.
Modelos minúsculos são uma última opção para testes de integração. Use-os apenas quando o menor checkpoint disponível exceder ~24 GB de VRAM. Sempre que possível, utilize os pesos pré-treinados originais para detectar regressões numéricas reais.
O script utils/create_dummy_models.py gera modelos minúsculos a partir de ModelTester.get_config(). O script extrai hiperparâmetros minúsculos do seu tester, constrói um modelo com pesos aleatórios e faz o upload do resultado para o Hub.
Gere modelos pequenos localmente.
python utils/create_dummy_models.py output_dir -m your_model_type
Faça o upload deles para o Hub.
python utils/create_dummy_models.py output_dir -m your_model_type --upload --organization hf-internal-testing
Cada modelo usa o nome hf-internal-testing/tiny-random-{ModelClassName} e é registrado em tests/utils/tiny_model_summary.json. Um workflow de CI (.github/workflows/check_tiny_models.yml) regenera os modelos pequenos diariamente.
Controle o que é testado
As bandeiras booleanas em ModelTesterMixin alternam os testes gerados automaticamente. Sobrescreva qualquer bandeira na sua classe de teste para habilitar ou desabilitar verificações específicas.
class MyModelTest(CausalLMModelTest, unittest.TestCase):
model_tester_class = MyModelTester
test_resize_embeddings = False
test_all_params_have_gradient = False # when not all parameters are activated in every forward pass| Flag | Padrão | O que ele controla |
|---|---|---|
test_resize_embeddings | True | Redimensionamento da camada de incorporação |
test_resize_position_embeddings | False | Redimensionamento de incorporação de posição |
test_mismatched_shapes | True | Tratamento de formatos de entrada/saída incompatíveis |
test_missing_keys | True | Avisos de chaves ausentes ao carregar |
test_torch_exportable | True | Compatibilidade com torch.export |
test_all_params_have_gradient | True | Todos os parâmetros recebem gradientes (defina False quando nem todos os parâmetros são ativados em cada passagem de frente, como nos especialistas de MoE) |
is_encoder_decoder | False | Testes específicos de encoder-decoder |
has_attentions | True | Testes de saída de atenção |
_is_composite | False | Tratamento de modelos compostos/multimodais |
model_split_percents | [0.5, 0.7, 0.9] | Percentagens de divisão para testes de paralelismo de modelo |
Próximas etapas
- Consulte a documentação do pytest para obter mais informações sobre seleção de testes, fixtures, logging e muito mais.