Transformers documentation

Contribuição de modelo legado

You are viewing main version, which requires installation from source. If you'd like regular pip install, checkout the latest stable version (v5.22.1).
Hugging Face's logo
Join the Hugging Face community

and get access to the augmented documentation experience

to get started

Contribuição de modelo legado

Tente primeiro adicionar novos modelos com uma abordagem mais modular. Isso torna muito mais fácil contribuir com um modelo para o Transformers!

Muitos dos modelos em Transformers são contribuídos por desenvolvedores e pesquisadores. Como um projeto open-source em primeiro lugar, estamos empenhados em capacitar a comunidade a adicionar mais modelos de forma ativa e independente.

Ao adicionar um modelo ao Transformers, você aprenderá:

  • mais sobre as melhores práticas de código aberto
  • sobre a arquitetura de um modelo
  • sobre os princípios de design do Transformers
  • como testar modelos grandes de forma eficiente
  • como usar utilitários do Python, como Black e Ruff, para criar código limpo e legível

É um processo desafiador, porém recompensador.

Este guia mostrará como adicionar um modelo PyTorch de exemplo BrandNewLlama ao Transformers. Antes de começar, é uma boa ideia familiarizar-se com a biblioteca.

Visão geral do Transformers

Transformers é uma biblioteca com opiniões firmes, possuindo sua própria filosofia e escolhas de design únicas. Essas escolhas nos ajudam a escalar e manter o Transformers de forma sustentável.

Saiba mais sobre nossos princípios de design no documento Philosophy.

Algumas dessas escolhas de design são:

  • composição > sobreabstração
  • código duplicado nem sempre é ruim, se ele melhorar significativamente a legibilidade e a acessibilidade
  • os arquivos do modelo são autocontidos e todo o código necessário do modelo é encontrado no arquivo modeling_mymodel.py

Essas escolhas de design são importantes para todos que interagem com o modelo. É mais fácil de ler, entender e modificar.

Esta seção descreve como as classes de modelo e configuração interagem e o estilo de código do Transformers.

Modelo e configuração

Todos os modelos do Transformers herdam de uma classe base PreTrainedModel e PreTrainedConfig. A configuração é o plano dos modelos.

Nunca há mais de dois níveis de abstração para qualquer modelo, a fim de manter o código legível. O modelo de exemplo aqui, BrandNewLlama, herda de BrandNewLlamaPreTrainedModel e PreTrainedModel. É importante que um novo modelo dependa apenas de PreTrainedModel para que possa usar os métodos from_pretrained() e save_pretrained().

Outras funções importantes, como o método forward, são definidas no arquivo modeling.py.

Cabeças de modelo específicas (por exemplo, classificação de sequência ou modelagem de linguagem) devem chamar o modelo base no caminho de encaminhamento, em vez de herdar dele, para manter a abstração baixa.

Novos modelos exigem uma configuração, por exemplo BrandNewLlamaConfig, que é armazenada como um atributo de PreTrainedModel.

model = BrandNewLlamaModel.from_pretrained("username/brand_new_llama")
model.config

PreTrainedConfig fornece os métodos from_pretrained() e save_pretrained().

Ao usar PreTrainedModel.save_pretrained(), ele chama automaticamente PreTrainedConfig.save_pretrained() para que o modelo e a configuração sejam salvos juntos.

Um modelo é salvo em um arquivo model.safetensors e uma configuração é salva em um arquivo config.json.

Estilo de código

O Transformers prefere um código limpo e legível a um estilo de código mais abstrato. Algumas das escolhas de estilo de código incluem:

  • O código deve ser acessível a usuários que não falam inglês. Escolha nomes de variáveis descritivos e evite abreviações. Por exemplo, “activation” é preferível a “act”. Nomes de variáveis com uma única letra são fortemente desencorajados, a menos que se trate de um índice em um laço for.

  • Código explícito é preferível — mesmo que seja mais longo — a código mais curto.

  • Evite criar subclasses de nn.Sequential. Em vez disso, crie subclasses de nn.Module para que o código possa ser depurado rapidamente com declarações de impressão ou pontos de interrupção.

  • As assinaturas de função devem ter anotações de tipo. Caso contrário, use nomes de variáveis claros para que sejam mais compreensíveis.

Problema na adição de novo modelo

Abra uma issue de Adição de novo modelo para adicionar um modelo específico.

Filtre pelo rótulo New model no GitHub para visualizar e adicionar solicitações de modelos existentes.

Agora é um bom momento para se familiarizar com o BrandNewLlama. É útil ler o paper de pesquisa de um modelo para entender seu design técnico e implementação. Você não precisa se preocupar muito com os detalhes teóricos. Em vez disso, foque nos detalhes práticos. Use as perguntas abaixo para orientar sua leitura.

  • Que tipo de modelo é o BrandNewLlama? É um modelo encoder, decoder ou encoder-decoder?
  • Para quais tarefas o BrandNewLlama pode ser utilizado?
  • O que torna o BrandNewLlama diferente de outros modelos?
  • Quais modelos em Transformers são mais semelhantes ao BrandNewLlama?
  • Qual tokenizer o BrandNewLlama usa?

Além de aprender mais sobre o seu modelo, use as dicas abaixo para ajudá-lo a adicionar um modelo mais rapidamente.

Cada contribuidor tem um estilo e um fluxo de trabalho únicos para adicionar modelos ao Transformers. Como exemplo, veja como o Gemma foi adicionado.

  • Não reinvente a roda! Tire um tempo para explorar modelos e tokenizadores existentes para ver o que você pode copiar e reutilizar. Grep e ripgrep são ótimas ferramentas para isso.
  • Isso é mais um desafio de engenharia do que de ciência. Foque nos aspectos mais práticos (por exemplo, configurar um ambiente de depuração eficiente) em vez dos aspectos teóricos do modelo.
  • Não tenha vergonha de pedir ajuda! Estamos aqui para te apoiar. 🤗

Ambiente de desenvolvimento

Clique no botão Fork no repositório Transformers para criar sua própria cópia para trabalhar. Clone o repositório para o seu disco local e adicione o repositório base como remoto.

git clone https://github.com/[your Github handle]/transformers.git
cd transformers
git remote add upstream https://github.com/huggingface/transformers.git

Crie um ambiente virtual e execute uma instalação editável da biblioteca com as dependências “dev” ou de desenvolvimento.

python -m venv .env
source .env/bin/activate
pip install -e ".[dev]"

Devido ao número crescente de dependências opcionais do Transformers, esse comando pode falhar. Nesse caso, instale as dependências de “quality”. Certifique-se também de que um framework de deep learning está instalado.

pip install -e ".[quality]"

Volte para o diretório pai e clone e instale o repositório original BrandNewLlama.

git clone https://github.com/org_that_created_brand_new_llama_org/brand_new_llama.git
cd brand_new_bert
pip install -e .

Volte para o seu clone do Transformers para começar a portar o BrandNewLlama.

cd transformers

Existem dois ambientes de depuração possíveis para executar o modelo original: um notebook (Google Colab ou Jupyter) ou um script Python local.

Não recomendamos configurar um ambiente de GPU para executar o modelo original, pois isso pode ser caro. Em vez disso, trabalhe primeiro em um ambiente de CPU para verificar se o modelo funciona no Transformers. Depois que ele funcionar, você pode verificá-lo em uma GPU.

Notebooks são ótimos para executar código célula por célula, o que pode ajudar a separar componentes lógicos uns dos outros. Também podem acelerar os ciclos de depuração, pois os resultados intermediários podem ser armazenados. Você também pode compartilhar notebooks ao trabalhar com outros colaboradores.

A desvantagem é que, se você não está acostumado a eles, pode levar algum tempo para se adaptar.

Se a arquitetura do modelo for idêntica à de um modelo existente, pule para converter o checkpoint, pois você pode reutilizar a arquitetura do modelo existente.

Execute o comando abaixo para iniciar e concluir o questionário com algumas informações básicas sobre o novo modelo. Este comando agiliza o processo, gerando automaticamente parte do código do modelo que você precisará adaptar.

transformers add-new-model-like

Crie um pull request

Antes de começar a adaptar o código, crie um pull request para acompanhar seu progresso e receber feedback da equipe do Transformers. Dê ao seu pull request o título [WIP] Add BrandNewLlama para deixar claro que se trata de um trabalho em andamento.

Crie um branch com um nome descritivo a partir do seu branch principal.

git checkout -b add_brand_new_bert

Commita o código e, em seguida, faça o fetch e o rebase na branch principal.

git add .
git commit
git fetch upstream
git rebase upstream/main

Envie as alterações para o seu branch e clique em Compare & pull request para abrir um pull request no GitHub. Abra o pull request como draft para indicar que está em andamento.

git push -u origin a-descriptive-name-for-my-changes

Inclua os membros relevantes da equipe do Hugging Face adicionando seus handles do GitHub na pull request para perguntas, feedback, comentários e revisões. Direcione os membros da equipe para partes específicas do código que você deseja ao clicar na aba Files changed e, em seguida, clicar em + à esquerda do número da linha para adicionar um comentário. Quando uma pergunta ou problema for resolvido, clique em Resolve para indicar que a questão foi resolvida. Isso mantém a conversa organizada e limpa.

Lembre-se de fazer commit e push do seu trabalho periodicamente e de atualizá-lo com a branch main atual.

git fetch upstream
git merge upstream/main

Checkpoint original

Tome um tempo para trabalhar na implementação original do modelo primeiro, a fim de entender como ele funciona.

Isso pode ser difícil se o repositório original do modelo estiver com documentação insuficiente ou se a base de código for complexa. Mas você deve usar isso como motivação para implementar o modelo no Transformers. Sua contribuição torna o modelo mais acessível e fácil de usar para todos!

Oriente-se pelo repositório original executando as seguintes etapas.

  • Localize os pesos pré-treinados.
  • Descubra como carregar pesos pré-treinados no modelo.
  • Descubra como executar o tokenizer de forma independente do modelo.
  • Faça o rastreamento de uma passagem para frente para entender quais classes e funções são necessárias. Provavelmente, estas são as únicas classes e funções que você terá que implementar.
  • Localize todos os componentes importantes (classe do modelo, subclasses do modelo, camada de autoatenção, etc.) do modelo.
  • Descubra como depurar o modelo no repositório original. Adicione declarações de impressão, use depuradores interativos como ipdb ou um ambiente de desenvolvimento integrado (IDE) eficiente como PyCharm.

O último ponto é especialmente importante, pois você precisará de um entendimento aprofundado do que está acontecendo dentro do modelo original antes de poder reimplementá-lo no Transformers. Sinta-se à vontade para abrir issues e pull requests no repositório original caso encontre algum problema.

Um bom primeiro passo é carregar um checkpoint pré-treinado pequeno e tentar reproduzir uma única passagem direta com um vetor de exemplo de entradas inteiras. Por exemplo, em pseudocódigo, isso poderia se parecer com o seguinte.

model = BrandNewLlamaModel.load_pretrained_checkpoint("/path/to/checkpoint/")
input_ids = [0, 4, 5, 2, 3, 7, 9]  # vector of input ids
original_output = model.generate(input_ids)

Depuração

Se você encontrar problemas, precisará escolher uma das seguintes estratégias de depuração, dependendo da base de código dos modelos originais.

subcomponentes
modelo e tokenizer

Essa estratégia se baseia em dividir o modelo original em subcomponentes menores, como quando o código pode ser executado facilmente em modo eager. Embora seja mais difícil, essa abordagem apresenta algumas vantagens.

  1. É mais fácil, depois, comparar o modelo original com a sua implementação. Você pode verificar automaticamente se cada componente individual corresponde ao seu componente correspondente na implementação do Transformers. Isso é melhor do que depender de uma comparação visual baseada em declarações de impressão.
  2. É mais fácil portar componentes individuais em vez do modelo inteiro.
  3. É mais fácil entender como um modelo funciona ao dividi-lo em partes menores.
  4. É mais fácil prevenir regressões em uma etapa posterior, ao modificar seu código, graças aos testes por componente.

Consulte as verificações de integração do ELECTRA para um bom exemplo de como decompor um modelo em componentes menores.

Independentemente da estratégia escolhida, recomenda-se depurar primeiro as camadas iniciais e por último as camadas finais. Obtenha a saída, seja por meio de instruções de impressão ou de funções de subcomponentes, das seguintes camadas, nesta ordem.

  1. ids de entrada passados para o modelo
  2. embeddings de palavras
  3. entrada da primeira camada do Transformer
  4. saída da primeira camada do Transformer
  5. saída das seguintes n-1 camadas do Transformer
  6. saída do modelo inteiro

Os ids de entrada devem ser apenas um array de inteiros, como input_ids = [0, 4, 4, 3, 2, 4, 1, 7, 19].

As saídas de camadas geralmente consistem em arrays de ponto flutuante multidimensionais.

[[
 [-0.1465, -0.6501,  0.1993,  ...,  0.1451,  0.3430,  0.6024],
 [-0.4417, -0.5920,  0.3450,  ..., -0.3062,  0.6182,  0.7132],
 [-0.5009, -0.7122,  0.4548,  ..., -0.3662,  0.6091,  0.7648],
 ...,
 [-0.5613, -0.6332,  0.4324,  ..., -0.3792,  0.7372,  0.9288],
 [-0.5416, -0.6345,  0.4180,  ..., -0.3564,  0.6992,  0.9191],
 [-0.5334, -0.6403,  0.4271,  ..., -0.3339,  0.6533,  0.8694]]],

A saída de cada modelo do Transformers deve ter uma precisão ou tolerância de erro de 1e-3. Isso leva em conta as diferenças de saída que surgem do uso de um framework de biblioteca diferente. Compare as saídas intermediárias do modelo original com a implementação do Transformers para garantir que sejam praticamente idênticas. Ter um ambiente de depuração eficiente é crucial para essa etapa.

Aqui estão algumas dicas para um ambiente de depuração eficiente.

  • Para depurar resultados intermediários, depende do framework de machine learning que o repositório original do modelo está usando. Para PyTorch, você deve escrever um script para decompor o modelo original em subcomponentes menores para recuperar os valores intermediários.

  • É mais rápido depurar com um checkpoint pré-treinado menor do que com um checkpoint maior, no qual a passagem para frente leva mais de 10 segundos. Se apenas checkpoints grandes estiverem disponíveis, crie um modelo fictício com pesos inicializados aleatoriamente e salve esses pesos para comparar com a implementação do Transformers.

  • Encontre a maneira mais simples de chamar a passagem para frente do modelo. Idealmente, essa função (que pode ser chamada de predict, evaluate, forward ou __call__) deve chamar a passagem para frente apenas uma vez. É mais difícil depurar uma função que chama a passagem para frente várias vezes.

  • Separe a tokenização do passo de propagação direta. Localize onde a entrada em string é convertida em ids de entrada no passo de propagação direta e comece por aqui. Você pode precisar criar um script pequeno ou modificar o código original para inserir diretamente os ids de entrada em vez de uma string de entrada.

  • Certifique-se de que o modelo não esteja em modo de treinamento. Isso pode gerar saídas aleatórias devido a múltiplas camadas de dropout no modelo. A passagem direta no seu ambiente de depuração deve ser determinística para que as camadas de dropout não sejam utilizadas.

Assim que você conseguir executar o checkpoint original, estará pronto para começar a adaptar o código do modelo para o Transformers.

Adapte o código do modelo

O comando transformers add-new-model-like deve ter gerado um arquivo de modelo e configuração.

  • src/transformers/models/brand_new_llama/modeling_brand_new_llama.py
  • src/transformers/models/brand_new_llama/configuration_brand_new_llama.py

O código gerado automaticamente no arquivo modeling.py tem a mesma arquitetura do Llama, se você respondeu que é um modelo somente de decodificador, ou terá a mesma arquitetura do BART, se você respondeu que é um modelo codificador-decodificador. O código gerado é apenas um ponto de partida. Com base na sua pesquisa sobre o novo modelo, você precisará implementar essas mudanças específicas adaptando o código gerado. Isso pode envolver alterações na camada de autoatenção, na ordem da camada de normalização, entre outras.

Inicialização do modelo

Neste ponto, seu código não precisa ser limpo ou até mesmo totalmente correto. É mais eficiente criar rapidamente um primeiro rascunho e, em seguida, melhorá-lo de forma iterativa. O mais importante é que seu modelo possa ser instanciado a partir do Transformers. O comando abaixo cria um modelo a partir da configuração com pesos aleatórios, verificando se o método __init__ funciona.

from transformers import BrandNewLlama, BrandNewLlamaConfig
model = BrandNewLlama(BrandNewLlamaConfig())

A inicialização aleatória ocorre no método _init_weights de BrandNewLlamaPreTrainedModel. Todos os módulos folha são inicializados com base nas variáveis da configuração. Use os auxiliares do módulo transformers.initialization para definir os valores, em vez de chamar operações in-place diretamente em um peso ou em seu .data.

from transformers import initialization as init

def _init_weights(self, module):
    """Initialize the weights"""
    if isinstance(module, nn.Linear):
        init.normal_(module.weight, mean=0.0, std=self.config.initializer_range)
        if module.bias is not None:
            init.zeros_(module.bias)
    elif isinstance(module, nn.Embedding):
        init.normal_(module.weight, mean=0.0, std=self.config.initializer_range)
        if module.padding_idx is not None:
            init.zeros_(module.weight[module.padding_idx])
    elif isinstance(module, nn.LayerNorm):
        init.zeros_(module.bias)
        init.ones_(module.weight)

Sempre inicialize os pesos por meio dos auxiliares de transformers.initialization. Operações in-place, como module.bias.zero_() ou qualquer coisa que toque em module.weight.data, ignoram o _is_hf_initialized, que sinaliza quais parâmetros já foram carregados. O from_pretrained() executa o _init_weights após carregar o checkpoint, de modo que operações in-place sobrescrevem silenciosamente os pesos carregados com valores aleatórios. Isso é imposto para modelos do Transformers pela regra TRF012.

Durante o from_pretrained(), os modelos geralmente são inicializados no dispositivo meta. O loader recria os buffers não persistentes como tensores vazios, de modo que os valores atribuídos em __init__ não são preservados. Inicialize esses buffers em _init_weights para que tenham os valores corretos após o carregamento. Para um exemplo, veja como o BERT inicializa seus IDs de posição e IDs de tipo de token.

O esquema de inicialização pode ter um aspecto diferente se você precisar adaptá-lo ao seu modelo. Um submódulo com seu próprio método reset_parameters pode chamá-lo diretamente. Por exemplo, Wav2Vec2ForPreTraining inicializa nn.Linear em suas duas últimas camadas lineares. A chamada é segura porque from_pretrained() aplica patches nas funções subjacentes de torch.nn.init para respeitar a flag _is_hf_initialized enquanto executa _init_weights, de modo que os pesos carregados não sejam sobrescritos.

def _init_weights(self, module):
    """Initialize the weights"""
    if isinstance(module, Wav2Vec2ForPreTraining):
        module.project_hid.reset_parameters()
        module.project_q.reset_parameters()

Converter checkpoints para Transformers

O checkpoint original deve ser convertido para um checkpoint compatível com o Transformers.

Tente procurar por um script de conversão existente para copiar, adaptar e reutilizar para o seu modelo!

  • Se você está portando um modelo do TensorFlow para o PyTorch, um bom ponto de partida pode ser o script de conversão do BERT.
  • Se você está portando um modelo do PyTorch para o PyTorch, um bom ponto de partida pode ser o script de conversão do BART.

Verifique se todos os pesos necessários foram inicializados e imprima todos os pesos do checkpoint que não foram usados para a inicialização, a fim de garantir que o modelo foi convertido corretamente.

Você pode encontrar declarações de forma incorretas ou atribuições de nomes erradas durante a conversão. Isso provavelmente se deve a parâmetros incorretos em BrandNewLlamaConfig, à arquitetura errada, a um bug no método init da sua implementação, ou à necessidade de transpor um dos pesos do checkpoint.

Continue iterando na seção Adaptar o código do modelo até que todos os pesos do checkpoint sejam carregados corretamente. Assim que você conseguir carregar um checkpoint no seu modelo, salve-o em uma pasta. Essa pasta deve conter um arquivo model.safetensors e um arquivo config.json.

model.save_pretrained("/path/to/converted/checkpoint/folder")

Para auxiliar na conversão, a próxima seção descreve brevemente como os modelos do PyTorch armazenam e definem os pesos e os nomes das camadas.

Pesos e nomes das camadas do PyTorch

É útil criar um modelo básico do PyTorch para entender como os nomes das camadas são definidos e como os pesos são inicializados.

from torch import nn

class SimpleModel(nn.Module):
    def __init__(self):
        super().__init__()
        self.dense = nn.Linear(10, 10)
        self.intermediate = nn.Linear(10, 10)
        self.layer_norm = nn.LayerNorm(10)

Os nomes das camadas do PyTorch são definidos pelo nome do atributo da classe da camada (dense, intermediate, layer_norm). Crie uma instância de SimpleModel para preencher todas as camadas com pesos aleatórios.

model = SimpleModel()
print(model)
SimpleModel(
  (dense): Linear(in_features=10, out_features=10, bias=True)
  (intermediate): Linear(in_features=10, out_features=10, bias=True)
  (layer_norm): LayerNorm((10,), eps=1e-05, elementwise_affine=True)
)

Os valores de peso de uma camada específica são inicializados aleatoriamente.

print(model.dense.weight.data)
tensor([[-0.0818,  0.2207, -0.0749, -0.0030,  0.0045, -0.1569, -0.1598,  0.0212,
         -0.2077,  0.2157],
        [ 0.1044,  0.0201,  0.0990,  0.2482,  0.3116,  0.2509,  0.2866, -0.2190,
          0.2166, -0.0212],
        [-0.2000,  0.1107, -0.1999, -0.3119,  0.1559,  0.0993,  0.1776, -0.1950,
         -0.1023, -0.0447],
        [-0.0888, -0.1092,  0.2281,  0.0336,  0.1817, -0.0115,  0.2096,  0.1415,
         -0.1876, -0.2467],
        [ 0.2208, -0.2352, -0.1426, -0.2636, -0.2889, -0.2061, -0.2849, -0.0465,
          0.2577,  0.0402],
        [ 0.1502,  0.2465,  0.2566,  0.0693,  0.2352, -0.0530,  0.1859, -0.0604,
          0.2132,  0.1680],
        [ 0.1733, -0.2407, -0.1721,  0.1484,  0.0358, -0.0633, -0.0721, -0.0090,
          0.2707, -0.2509],
        [-0.1173,  0.1561,  0.2945,  0.0595, -0.1996,  0.2988, -0.0802,  0.0407,
          0.1829, -0.1568],
        [-0.1164, -0.2228, -0.0403,  0.0428,  0.1339,  0.0047,  0.1967,  0.2923,
          0.0333, -0.0536],
        [-0.1492, -0.1616,  0.1057,  0.1950, -0.2807, -0.2710, -0.1586,  0.0739,
          0.2220,  0.2358]]).

No script de conversão, os pesos aleatórios devem ser substituídos pelos pesos exatos da camada correspondente no checkpoint original.

# retrieve matching layer weights with recursive algorithm
layer_name = "dense"
pretrained_weight = array_of_dense_layer

model_pointer = getattr(model, "dense")
model_pointer.weight.data = torch.from_numpy(pretrained_weight)

Verifique se os pesos inicializados aleatoriamente e os pesos do checkpoint pré-treinado correspondente têm a mesma forma e o mesmo nome. Adicione declarações assert para a forma e imprima os nomes dos pesos do checkpoint.

assert (
    model_pointer.weight.shape == pretrained_weight.shape
), f"Pointer shape of random weight {model_pointer.shape} and array shape of checkpoint weight {pretrained_weight.shape} mismatched"

logger.info(f"Initialize PyTorch weight {layer_name} from {pretrained_weight.name}")

Quando a forma ou o nome não correspondem, é possível que você tenha atribuído o peso de checkpoint incorreto a uma camada inicializada aleatoriamente. Uma forma incorreta pode ocorrer porque os parâmetros do BrandNewLlama não correspondem exatamente aos parâmetros dos modelos originais. Mas também pode ser que a implementação da camada do PyTorch exija que os pesos sejam transpostos primeiro.

Implemente a passagem direta

O passo de propagação para frente (forward pass) deve ser implementado em seguida, caso o modelo seja carregado corretamente. Ele recebe algumas entradas e retorna a saída do modelo.

model = BrandNewLlamaModel.from_pretrained("/path/to/converted/checkpoint/folder")
input_ids = [0, 4, 4, 3, 2, 4, 1, 7, 19]
output = model.generate(input_ids).last_hidden_states

Não se desanime se o seu forward pass não for idêntico à saída do modelo original ou se retornar um erro. Verifique se o forward pass não lança nenhum erro. Isso geralmente ocorre porque as dimensões estão incorretas ou porque o tipo de dados errado é usado (torch.long em vez de torch.float32).

A saída deve ter uma precisão de 1e-3. Certifique-se de que as formas da saída e os valores de saída sejam idênticos. Razões comuns para as saídas não serem idênticas incluem:

  • Algumas camadas não foram adicionadas (camada de ativação ou conexão residual).
  • A matriz de incorporação de palavras não está vinculada.
  • Os embeddings posicionais incorretos são usados porque a implementação original inclui um deslocamento.
  • O Dropout é aplicado durante a passagem direta. Corrija esse erro garantindo que model.training seja False e passando self.training para torch.nn.functional.dropout.

Compare o forward pass do modelo original com a sua implementação para verificar se há alguma diferença. Idealmente, depure e imprima as saídas intermediárias de ambas as implementações do forward pass para identificar onde a implementação original difere da sua.

  1. Certifique-se de que o input_ids codificado em ambos os implementações sejam idênticos.
  2. Verifique se as saídas da primeira transformação de input_ids (geralmente os embeddings de palavras) são idênticas e prossiga até a última camada.

Qualquer diferença entre as duas implementações deve indicar um bug na sua implementação.

Uma das melhores estratégias é adicionar muitos comandos de impressão nas mesmas posições em ambas as implementações e, em seguida, removê-los sucessivamente quando eles exibirem valores idênticos para as saídas intermediárias.

Quando ambas as implementações produzem a mesma saída, verifique se as saídas estão dentro de uma precisão de 1e-3.

torch.allclose(original_output, output, atol=1e-3)

Esta é, tipicamente, a parte mais difícil do processo. Parabéns se você chegou até aqui!

E, se você estiver travado ou com dificuldade nessa etapa, não hesite em pedir ajuda no seu pull request.

Adicionar testes de modelo

Enquanto o modelo funciona, você ainda precisa adicionar testes para garantir que ele seja compatível com o Transformers. Os testes são importantes porque ajudam os usuários a entender seu trabalho ao examinar testes específicos e porque impedem que seu modelo quebre no futuro caso sejam feitas alterações.

O Cookiecutter deve ter adicionado um arquivo de teste para o seu modelo. Execute o arquivo de teste abaixo para garantir que todos os testes comuns sejam aprovados.

pytest tests/models/brand_new_llama/test_modeling_brand_new_llama.py

Os testes de integração devem ser adicionados primeiro, pois cumprem a mesma função dos scripts de depuração que você usou anteriormente para implementar o novo modelo no Transformers. Um modelo para esses testes de modelo, BrandNewLlamaModelIntegrationTests, foi adicionado pelo Cookiecutter e deve ser preenchido. Para garantir que ele seja aprovado, execute o seguinte comando.

macOS
Windows
RUN_SLOW=1 pytest -sv tests/models/brand_new_llama/test_modeling_brand_new_llama.py::BrandNewLlamaModelIntegrationTests

Todos os recursos exclusivos do BrandNewLlama devem ser testados em um teste separado sob BrandNewLlamaModelTester/BrandNewLlamaModelTest. Esse teste costuma ser negligenciado, mas é extremamente importante porque:

  • ela ajuda a transferir para a comunidade o conhecimento adquirido durante o processo, demonstrando como os recursos inovadores do modelo funcionam
  • contribuidores futuros podem testar rapidamente as alterações no modelo executando esses testes especiais

Implementar tokenizer

Recomendamos adicionar um tokenizer rápido (PreTrainedTokenizerFast) para oferecer aos usuários o melhor desempenho. Sinta-se à vontade para marcar @ArthurZucker ou @itazap na sua PR para obter ajuda sobre como adicionar PreTrainedTokenizerFast.

Com o modelo resolvido, é hora de focar no tokenizer. O tokenizer deve ser idêntico ou muito semelhante a um tokenizer existente em Transformers.

Encontre e carregue o arquivo original do tokenizer na sua implementação. Crie um script no repositório original que recebe uma string e retorna os input_ids. O pseudocódigo deve ser semelhante ao código abaixo.

input_str = "This is a long example input string containing special characters .$?-, numbers 2872 234 12 and words."
model = BrandNewLlamaModel.load_pretrained_checkpoint("/path/to/checkpoint/")
input_ids = model.tokenize(input_str)

Você pode precisar procurar no repositório original para encontrar a função de tokenizer correta ou modificar o tokenizer existente em seu clone do repositório original para que ele retorne apenas input_ids. O script para o seu tokenizer deve se parecer com o seguinte.

from transformers import BrandNewLlamaTokenizer

input_str = "This is a long example input string containing special characters .$?-, numbers 2872 234 12 and words."
tokenizer = BrandNewLlamaTokenizer.from_pretrained("/path/to/tokenizer/folder/")
input_ids = tokenizer(input_str).input_ids

Quando ambas as implementações possuem o mesmo input_ids, adicione um arquivo de teste do tokenizer. Este arquivo é análogo aos arquivos de teste de modelagem. Os arquivos de teste do tokenizer devem conter alguns testes de integração com valores fixos.

Implementar processador de imagem

Os processadores de imagem agora utilizam uma arquitetura baseada em backends. O backend padrão é o TorchvisionBackend, que utiliza a biblioteca torchvision e pode executar o processamento de imagens na GPU. Um backend alternativo PIL/NumPy (PilBackend) também é fornecido. Ambos os backends são importados de image_processing_backends. Sinta-se à vontade para marcar @yonigozlan para obter ajuda.

Embora este exemplo não inclua um processador de imagens, você pode precisar implementar um caso seu modelo exija entradas de imagem. O processador de imagens é responsável por converter as imagens em um formato adequado para o seu modelo. Antes de implementar um novo, verifique se um processador de imagens existente na biblioteca Transformers pode ser reutilizado, pois muitos modelos compartilham técnicas semelhantes de processamento de imagens. Observe que você também pode usar modular para processadores de imagens a fim de reutilizar componentes existentes.

Se você precisar implementar um novo processador de imagem, cada modelo possui dois arquivos de processador:

  • image_processing_<model>.py: o default processador baseado em torchvision (<Model>ImageProcessor), herdando de TorchvisionBackend. Isso substitui o antigo processador “fast”.
  • image_processing_pil_<model>.py: o processador alternativo PIL/NumPy (<Model>ImageProcessorPil), que herda de PilBackend. Ele substitui o antigo processador “slow”.

O arquivo de backend do torchvision também define qualquer classe de kwargs personalizada que o arquivo PIL importa. Ambos os arquivos usam o decorador @auto_docstring — não adicione docstrings de classe manuais. Consulte o IMAGE_PROCESSOR_REFACTORING_GUIDE.md para um passo a passo e exemplos completos.

Adicione testes para o processador de imagens em tests/models/your_model_name/test_image_processing_your_model_name.py. Esses testes devem ser semelhantes aos de outros processadores de imagens e devem verificar se o processador de imagens trata corretamente as entradas de imagem. Se o seu processador de imagens incluir recursos ou métodos de processamento exclusivos, certifique-se de adicionar testes específicos para eles também.

Implementar processador

Se o seu modelo aceita múltiplas modalidades, como texto e imagens, você precisa adicionar um processador. O processador centraliza o pré-processamento das diferentes modalidades antes de passá-las ao modelo.

O processador deve chamar os processadores específicos de cada modalidade dentro da função __call__ para tratar cada tipo de entrada corretamente. Certifique-se de verificar os processadores existentes na biblioteca para entender sua estrutura esperada. O Transformers usa a seguinte convenção na assinatura da função __call__.

def __call__(
    self,
    images: ImageInput = None,
    text: Union[TextInput, PreTokenizedInput, list[TextInput], list[PreTokenizedInput]] = None,
    audio=None,
    videos=None,
    **kwargs: Unpack[YourModelProcessorKwargs],
) -> BatchFeature:
    ...

YourModelProcessorKwargs é um TypedDict que inclui todos os argumentos de processamento típicos e quaisquer argumentos extras que um processador específico possa exigir.

Adicione testes para o processador em tests/models/your_model_name/test_processor_your_model_name.py. Esses testes devem ser semelhantes aos de outros processadores e devem verificar se o processador trata corretamente as diferentes modalidades.

Testes de integração

Agora que você tem um modelo e um tokenizer, adicione testes de integração de ponta a ponta para o modelo e o tokenizer em tests/models/brand_new_llama/test_modeling_brand_new_llama.py.

O teste deve fornecer um exemplo significativo de texto-para-texto para demonstrar que o modelo funciona como esperado. Por exemplo, você pode incluir um par de tradução de origem para destino, um par de artigo para resumo ou um par de pergunta para resposta.

Se o checkpoint não foi ajustado em uma tarefa de nível inferior, os testes do modelo são suficientes.

Por fim, tente garantir que seus testes possam ser executados em uma GPU adicionando declarações .to(self.device) aos tensores internos dos modelos. Se você não tiver acesso a uma GPU, podemos cuidar disso por você.

Adicionar documentação

Seu modelo só é útil se os usuários souberem como usá-lo. Por isso, é importante adicionar documentação e docstrings. O Cookiecutter adicionou um arquivo de modelo, docs/source/model_doc/brand_new_llama.md, que você pode preencher com informações sobre o seu modelo.

Isso geralmente é a primeira interação do usuário com um modelo, portanto, a documentação deve ser clara e concisa. Frequentemente, é muito útil adicionar exemplos de como o modelo deve ser usado.

Certifique-se de que docstrings sejam adicionadas a src/transformers/models/brand_new_llama/modeling_brand_new_llama.py e incluam todas as entradas e saídas necessárias. Consulte nosso guia para a criação de documentação e docstrings.

Refatorar

É hora de organizar as coisas e garantir que o estilo do código esteja consistente com o restante da biblioteca. Execute o comando a seguir para corrigir automaticamente estilos incorretos.

make style

Para verificar se o estilo do código passa nos controles de qualidade, execute o comando abaixo.

make check-repo

Há a possibilidade de existirem outros testes ou verificações com falha (docstring ausente ou nomenclatura incorreta) no seu pull request devido aos testes de design estritos do Transformers. Podemos ajudá-lo com esses problemas caso você esteja travado.

Depois de garantir que o código é executado corretamente, você pode querer refatorá-lo para torná-lo mais legível ou limpo.

Enviar para o Hub

Converta e envie todos os checkpoints para o Hub. Adicione um model card para fornecer mais transparência e contexto sobre o modelo. O model card deve destacar características específicas de um checkpoint, como o modelo foi treinado e exemplos de código de como usá-lo.

Em muitos casos, adicionar um notebook interativo que os usuários podem executar é uma ótima maneira de demonstrar como usar o modelo para inferência ou ajustá-lo para uma tarefa específica. Embora não seja obrigatório, incluir um notebook pode impulsionar a adoção do seu modelo.

Você também deve consultar a equipe do Transformers para decidir um nome adequado para o modelo e obter as permissões de acesso necessárias para fazer o upload do modelo.

Use o método push_to_hub() para enviar o modelo.

brand_new_bert.push_to_hub("brand_new_llama")

Consulte o guia Compartilhamento para obter mais informações sobre o envio de modelos para o Hub.

Mergulhe seu modelo

Você está finalmente pronto para mesclar sua pull request e adicionar oficialmente o modelo ao Transformers! Certifique-se de que todos os testes estão passando e de que todos os comentários e feedbacks foram tratados.

Parabéns por adicionar um novo modelo ao Transformers! 🥳

Esta é uma contribuição muito significativa. Seu trabalho torna o Transformers mais acessível para desenvolvedores e pesquisadores ao redor do mundo. Você deve se orgulhar da sua contribuição e compartilhar sua conquista com a comunidade!

Veja também

  • Regras de estrutura do modelo — regras estáticas aplicadas a todos os arquivos modeling_*.py e configuration_*.py. Execute make typing para verificá-las antes de abrir um PR.
  • Verificações de pull request — referência completa sobre as verificações de CI executadas no seu PR e como aprová-las.
Update on GitHub