Transformers documentation

Escrevendo testes de modelo

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

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/ -v

O 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 baseUsar paraMixins
CausalLMModelTestModelos de linguagem causaisModelTesterMixin, GenerationTesterMixin, PipelineTesterMixin, TrainingTesterMixin, TensorParallelTesterMixin
VLMModelTestModelos de visão e linguagemModelTesterMixin, GenerationTesterMixin, PipelineTesterMixin
ALMModelTestModelos de linguagem de áudioModelTesterMixin, 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 = MyModelTester

Essas 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 = False

Para 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_rank

VLMModelTest

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 = MyVLMTester

Sobrescrevendo 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_class e conditional_generation_class no tester.
  • VLMModelTest não inclui TrainingTesterMixin nem TensorParallelTesterMixin.
  • O __init__ do tester aceita parâmetros de visão (image_size, patch_size, num_channels, num_image_tokens) a partir de **kwargs e setdefault().
  • ConfigTester usa has_text_modality=False porque 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 = MyALMTester

Sobrescrevendo 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 como None se 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 placeholders audio_token_id inserir em input_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 de prepare_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 o BOS. 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.

  1. 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.
  2. 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.

MixinArquivo de origemO que ele testa
ModelTesterMixintests/test_modeling_common.pySalvar/carregar, checkpointing de gradiente, assinatura do forward, atributos comuns
GenerationTesterMixintests/generation/test_utils.pyGuloso, amostragem, busca em feixe, decodificação assistida
PipelineTesterMixintests/test_pipeline_mixin.pyUm teste por tarefa de pipeline
TrainingTesterMixintests/test_training_mixin.pySobreajuste em um lote pequeno
TensorParallelTesterMixintests/test_tensor_parallel_mixin.pyParalelismo 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.

  1. A classe ModelTester cria 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 para input_ids, token_type_ids e tensores de rótulos.
    • random_attention_mask(shape): Tensor binário (0s e 1s) em que o primeiro token é sempre 1. Use para attention_mask.
    • floats_tensor(shape, scale=1.0): Tensor de números flutuantes aleatório. Use para entradas contínuas, como pixel_values ou inputs_embeds.

    O tester deve implementar get_config(), prepare_config_and_inputs() e prepare_config_and_inputs_for_common(). Adicione métodos create_and_check_* para cada head de tarefa (modelo base, classificação de sequência, classificação de token, etc.).

  2. Herdar dos mixins que seu modelo precisa, definir all_model_classes e pipeline_model_mapping, e definir setUp(). Escrever métodos test_* que delegam para os métodos create_and_check_* do tester.

  3. 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étodo test_* 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_heads e num_hidden_layers existem (e vocab_size se has_text_modality=True).
  • Testa a serialização JSON com to_json_string() e to_json_file().
  • Faz o round-trip de save_pretrained() e from_pretrained().
  • Confirma a consistência entre id2label e label2id.
  • Cria uma configuração sem argumentos para validar a inicialização padrão.
  • Define kwargs comuns como output_hidden_states e 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 = ... em setUpClass). O Pytest mantém as instâncias de teste vivas durante a sessão, portanto gc.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.

MemoryCleanupTestCase combina o mixin com TestCasePlus, 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
FlagPadrãoO que ele controla
test_resize_embeddingsTrueRedimensionamento da camada de incorporação
test_resize_position_embeddingsFalseRedimensionamento de incorporação de posição
test_mismatched_shapesTrueTratamento de formatos de entrada/saída incompatíveis
test_missing_keysTrueAvisos de chaves ausentes ao carregar
test_torch_exportableTrueCompatibilidade com torch.export
test_all_params_have_gradientTrueTodos 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_decoderFalseTestes específicos de encoder-decoder
has_attentionsTrueTestes de saída de atenção
_is_compositeFalseTratamento 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.
Update on GitHub