Modelos
Nesta seção, vamos analisar mais de perto a criação e a utilização de um modelo. Vamos utilizar a classe AutoModel
, que é útil quando você quer instanciar qualquer modelo a partir de um checkpoint.
A classe AutoModel
e todas as classes filhas são na verdade simples wrapper sobre a grande variedade de modelos disponíveis na biblioteca. É um wrapper inteligente, pois pode automaticamente “adivinhar” a arquitetura apropriada do modelo para seu checkpoint, e então instancia um modelo com esta arquitetura.
Entretanto, se você conhece o tipo de modelo que deseja usar, pode usar diretamente a classe que define sua arquitetura. Vamos dar uma olhada em como isto funciona com um modelo BERT.
Criando um Transformer
A primeira coisa que precisamos fazer para inicializar um modelo BERT é carregar um objeto de configuração:
from transformers import BertConfig, BertModel
# Construindo a configuração
config = BertConfig()
# Construindo o modelo a partir da configuração
model = BertModel(config)
A configuração contém muitos atributos que são usados para construir o modelo:
print(config)
BertConfig {
[...]
"hidden_size": 768,
"intermediate_size": 3072,
"max_position_embeddings": 512,
"num_attention_heads": 12,
"num_hidden_layers": 12,
[...]
}
Embora você ainda não tenha visto o que todos esses atributos fazem, você deve reconhecer alguns deles: o atributo hidden_size
define o tamanho do vetor hidden_states
, e o num_hidden_layers
define o número de camadas que o Transformer possui.
Diferentes métodos de inicializar o modelo
A criação de um modelo a partir da configuração padrão o inicializa com valores aleatórios:
from transformers import BertConfig, BertModel
config = BertConfig()
model = BertModel(config)
# O modelo é inicializado aleatoriamente!
O modelo pode ser utilizado neste estado, mas produzirá saídas errôneas; ele precisa ser treinado primeiro. Poderíamos treinar o modelo a partir do zero na tarefa em mãos, mas como você viu em Capítulo 1, isto exigiria muito tempo e muitos dados, e teria um impacto ambiental não negligenciável. Para evitar esforços desnecessários e duplicados, normalmente é possível compartilhar e reutilizar modelos que já foram treinados.
Carregar um Transformer já treinado é simples - podemos fazer isso utilizando o método from_pretrained()
:
from transformers import BertModel
model = BertModel.from_pretrained("bert-base-cased")
Como você viu anteriormente, poderíamos substituir o BertModel
pela classe equivalente ao AutoModel
. Faremos isto de agora em diante, pois isto produz um código generalista a partir de um checkpoint; se seu código funciona para checkpoint, ele deve funcionar perfeitamente com outro. Isto se aplica mesmo que a arquitetura seja diferente, desde que o checkpoint tenha sido treinado para uma tarefa semelhante (por exemplo, uma tarefa de análise de sentimento).
No exemplo de código acima não utilizamos BertConfig
, e em vez disso carregamos um modelo pré-treinado através do identificador bert-base-cased
. Este é um checkpoint do modelo que foi treinado pelos próprios autores do BERT; você pode encontrar mais detalhes sobre ele em seu model card.
Este modelo agora é inicializado com todos os pesos do checkpoint. Ele pode ser usado diretamente para inferência sobre as tarefas nas quais foi treinado, e também pode ser fine-tuned (aperfeiçoado) em uma nova tarefa. Treinando com pesos pré-treinados e não do zero, podemos rapidamente alcançar bons resultados.
Os pesos foram baixados e armazenados em cache (logo, para as futuras chamadas do método from_pretrained()
não será realizado o download novamente) em sua respectiva pasta, que tem como padrão o path ~/.cache/huggingface/transformers. Você pode personalizar sua pasta de cache definindo a variável de ambiente HF_HOME
.
O identificador usado para carregar o modelo pode ser o identificador de qualquer modelo no Model Hub, desde que seja compatível com a arquitetura BERT. A lista completa dos checkpoints BERT disponíveis podem ser encontrada [aqui].https://huggingface.co/models?filter=bert).
Métodos para salvar/armazenar o modelo
Salvar um modelo é tão fácil quanto carregar um - utilizamos o método save_pretrained()
, que é análogo ao método from_pretrained()
:
model.save_pretrained("path_no_seu_computador")
Isto salva dois arquivos em seu disco:
ls path_no_seu_computador
config.json pytorch_model.bin
Se você der uma olhada no arquivo config.json, você reconhecerá os atributos necessários para construir a arquitetura modelo. Este arquivo também contém alguns metadados, como a origem do checkpoint e a versão 🤗 Transformers que você estava usando quando salvou o checkpoint pela última vez.
O arquivo pytorch_model.bin é conhecido como o dicionário de estado; ele contém todos os pesos do seu modelo. Os dois arquivos andam de mãos dadas; a configuração é necessária para conhecer a arquitetura de seu modelo, enquanto os pesos do modelo são os parâmetros de seu modelo.
Usando um modelo de Transformer para inferência
Agora que você sabe como carregar e salvar um modelo, vamos tentar usá-lo para fazer algumas predições. Os Transformers só podem processar números - números que o tokenizer gera. Mas antes de discutirmos os tokenizers, vamos explorar quais entradas o modelo aceita.
Os Tokenizers podem se encarregar de lançar as entradas nos tensores da estrutura apropriada, mas para ajudá-lo a entender o que está acontecendo, vamos dar uma rápida olhada no que deve ser feito antes de enviar as entradas para o modelo.
Digamos que temos um par de sequências:
sequences = ["Hello!", "Cool.", "Nice!"]
O tokenizer os converte em índices de vocabulário que são normalmente chamados de IDs de entrada. Cada sequência é agora uma lista de números! A saída resultante é:
encoded_sequences = [
[101, 7592, 999, 102],
[101, 4658, 1012, 102],
[101, 3835, 999, 102],
]
Esta é uma lista de sequências codificadas: uma lista de listas. Os tensores só aceitam shapes (tamanhos) retangulares (pense em matrizes). Esta “matriz” já é de forma retangular, portanto, convertê-la em um tensor é fácil:
import torch
model_inputs = torch.tensor(encoded_sequences)
Usando os tensores como entradas para o modelo
Fazer uso dos tensores com o modelo é extremamente simples - chamamos apenas o modelo com os inputs:
output = model(model_inputs)
Embora o modelo aceite muitos argumentos diferentes, apenas os IDs de entrada são necessários. Explicaremos o que os outros argumentos fazem e quando eles são necessários mais tarde, mas primeiro precisamos olhar mais de perto os tokenizers que constroem as entradas que um Transformer pode compreender.