Fine-tuning de Modelos de Linguagem com Hugging Face e PyTorch: Crie seu Próprio Chatbot
Fine-tuning de Modelos de Linguagem com Hugging Face e PyTorch: Crie seu Próprio Chatbot
Introdução
Nos últimos anos, modelos de linguagem pré-treinados como GPT-2, BERT e seus derivados têm revolucionado o processamento de linguagem natural (PLN). Esses modelos são como cérebros artificiais que já aprenderam muito sobre a língua lendo enormes quantidades de texto. Ao invés de treinar um modelo do zero, podemos aproveitar um modelo pré-treinado e refiná-lo (fazer fine-tuning) numa tarefa específica — como criar um chatbot que converse em português.
Neste artigo, vamos mostrar passo a passo como ajustar um modelo de linguagem pré-treinado usando a biblioteca Transformers do Hugging Face integrada ao PyTorch. Vamos abordar desde a preparação dos dados de conversas até o treinamento e a avaliação do modelo final. Ao término do tutorial, você terá uma base para criar seu próprio assistente conversacional personalizado, treinado em dados que você escolher.
Escolhendo o Modelo Pré-treinado e Bibliotecas
Antes de tudo, precisamos escolher que modelo de linguagem iremos usar como base. A Hugging Face oferece centenas de modelos pré-treinados para tarefas diversas. Para um chatbot, fazemos sentido usar um modelo gerativo de linguagem causal, que consegue prever a próxima palavra em uma sequência (como GPT-2 ou DialoGPT). Esses modelos já foram treinados em muito texto (por exemplo, páginas da internet) e podem gerar respostas coerentes a perguntas.
Vale ressaltar que usar um modelo pré-treinado tem várias vantagens:
- Menos custo computacional: não precisamos treinar do zero, só refinamos o que já existe.
- Menos dados necessários: o modelo já sabe muita coisa, basta ajustar detalhes com dados específicos.
- Menos emissões de carbono: menos tempo de GPU = menor impacto ambiental.
Para este tutorial, usaremos o PyTorch e o pacote Transformers do Hugging Face. Você pode instalar as bibliotecas com o seguinte comando (é recomendável usar um ambiente virtual, como virtualenv ou conda):
pip install transformers torch
Com as bibliotecas instaladas, escolhemos um modelo pré-treinado. Vamos exemplificar com o GPT-2 (um modelo de tamanho médio da OpenAI, disponível no Hugging Face) ou o DialoGPT (uma versão do GPT-2 especializada em conversação, da Microsoft). Por simplicidade, usaremos o GPT-2. Veja como carregar o modelo e o tokenizador correspondente:
from transformers import AutoModelForCausalLM, AutoTokenizer model_name = "gpt2" # você pode trocar para "microsoft/DialoGPT-small" ou outro tokenizer = AutoTokenizer.from_pretrained(model_name) model = AutoModelForCausalLM.from_pretrained(model_name)
Esse código faz o download do modelo e do tokenizador. O tokenizador converte texto em números (tokens) que o modelo entende. O modelo em si é um AutoModelForCausalLM, ou seja, um gerador de texto (Language Model) para tarefas causais (prevendo a próxima palavra).
Dica: Caso use o GPT-2, é comum definir o token de padding para evitar erros em sequências de tamanho variável. Por exemplo:
tokenizer.pad_token = tokenizer.eos_token model.resize_token_embeddings(len(tokenizer))
Preparação dos Dados
Agora que temos o modelo pré-treinado, precisamos preparar nossos dados de conversação para o fine-tuning. O ideal é ter um conjunto de diálogos estruturados, onde cada exemplo contenha uma pergunta e uma resposta (ou trechos de diálogo).
Exemplo de Dados Conversacionais
Para ilustrar, vamos criar manualmente poucos exemplos. Em um caso real, você buscaria um dataset maior (como o DailyDialog ou conversas coletadas do seu domínio). Aqui, apenas dois exemplos em português:
conversations = [ {"pergunta": "Qual é a capital da França?", "resposta": "A capital da França é Paris."}, {"pergunta": "Qual vers\u00e3o do Python estamos usando?", "resposta": "Estamos usando o Python 3."}, # Adicione mais pares como desejar... ]
Cada entrada é um dicionário simples com pergunta e resposta. Para treinar, vamos unir a pergunta e a resposta em um único texto, separando-os com rótulos como "Usuário:" e "Bot:". Essa abordagem ajuda o modelo a distinguir quem está falando. Por exemplo:
Usuário: Qual é a capital da França?
Bot: A capital da França é Paris.
Tokenização
Em seguida, precisamos transformar esse texto em tensores numéricos. Para isso, usamos o tokenizador do modelo. Veja um código que monta uma lista de exemplos preparados e tokeniza-os:
texts = [] for conv in conversations: texto = f"Usu\u00e1rio: {conv['pergunta']}\nBot: {conv['resposta']}" texts.append(texto) # Converta o texto para tokens encoded = tokenizer(texts, return_tensors="pt", padding=True, truncation=True) input_ids = encoded["input_ids"] attention_mask = encoded["attention_mask"]
- Aqui,
tokenizer(texts, return_tensors="pt", padding=True, truncation=True)gera um dicionário em PyTorch cominput_ids(os tokens) eattention_mask. - Definimos
padding=Truepara igualar o comprimento das sequências ao tamanho máximo necessário, etruncation=Truepara cortar sequências muito longas (caso existam).
Cada input_ids[i] representa um diálogo inteiro (pergunta + resposta).
Para facilitar o uso no PyTorch, podemos criar um Dataset personalizado. Exemplo usando torch.utils.data.Dataset:
from torch.utils.data import Dataset import torch class ChatDataset(Dataset): def __init__(self, tokenized_inputs): self.input_ids = tokenized_inputs["input_ids"] self.attention_mask = tokenized_inputs["attention_mask"] def __len__(self): return self.input_ids.size(0) def __getitem__(self, idx): return { "input_ids": self.input_ids[idx], "attention_mask": self.attention_mask[idx], "labels": self.input_ids[idx] # Usamos o mesmo texto como label para LM } dataset = ChatDataset(encoded)
Nesse dataset, o labels recebe o mesmo input_ids porque queremos treinar o modelo para prever cada próximo token em relação aos tokens de entrada. Em outras palavras, o modelo de linguagem receberá a sequência completa como entrada e tentará reconstruí-la palavra por palavra (tarefa de Language Modeling).
Fine-tuning do Modelo
DataLoader e Otimizador
Com os dados preparados e o modelo carregado, partimos para o treinamento. Primeiro, criamos um DataLoader para iterar sobre os dados em lotes (batches). Também montamos o otimizador (por exemplo, AdamW) conforme boas práticas de fine-tuning:
from torch.utils.data import DataLoader from torch.optim import AdamW batch_size = 4 train_loader = DataLoader(dataset, batch_size=batch_size, shuffle=True) optimizer = AdamW(model.parameters(), lr=5e-5)
Opcionalmente, você poderia definir um agendador de taxa de aprendizado (learning rate scheduler) após alguns passos, mas para um tutorial inicial, mostraremos um loop simples.
Loop de Treinamento
O processo de treinamento (fine-tuning) envolve percorrer várias épocas (passes pelos dados) e otimizar o modelo. Veja um exemplo de loop de treinamento com PyTorch puro:
device = torch.device("cuda" if torch.cuda.is_available() else "cpu") model = model.to(device) model.train() num_epochs = 3 # Ajuste conforme seus recursos de hardware e tamanho do dataset for epoch in range(num_epochs): total_loss = 0 for batch in train_loader: optimizer.zero_grad() # Mova dados para o dispositivo (CPU ou GPU) input_ids = batch["input_ids"].to(device) attention_mask = batch["attention_mask"].to(device) labels = batch["labels"].to(device) # Executa o modelo sobre a batch outputs = model(input_ids=input_ids, attention_mask=attention_mask, labels=labels) loss = outputs.loss loss.backward() # Calcula gradientes optimizer.step() # Atualiza os pesos total_loss += loss.item() avg_loss = total_loss / len(train_loader) print(f"Época {epoch+1}/{num_epochs} - Loss médio: {avg_loss:.4f}")
Explicação do loop:
- Definimos
model.train()para colocá-lo em modo de treinamento (Hugging Face utiliza PyTorch por baixo dos panos). - Para cada batch, zeramos os gradientes (
optimizer.zero_grad()), movemos os tensores para o GPU (se disponível) e em seguida chamamosmodel(...)passandoinput_ids,attention_maskelabels. - O modelo retorna um objeto cujo atributo
lossé a loss (perda) da batch. Usamosloss.backward()para computar gradiente eoptimizer.step()para ajustar os pesos do modelo. - Ao final de cada época, imprimimos a loss média. Geralmente, a loss deve diminuir a cada época se tudo estiver funcionando bem.
Para modelos grandes, é essencial ter uma GPU, caso contrário o treinamento será muito lento. Se você não tem GPU, considere usar serviços em nuvem (Colab, Paperspace etc) ou treinar em um modelo menor (como distilgpt2).
Avaliação e Teste do Chatbot
Gerando Respostas com o Modelo Ajustado
Após o treinamento, colocamos o modelo em modo de avaliação (model.eval()) e testamos como ele responde a novas perguntas. Neste exemplo, vamos perguntar algo e deixar o modelo completar:
model.eval() prompt = "Usu\u00e1rio: Oi, quem \u00e9 voc\u00ea?\nBot:" input_ids = tokenizer.encode(prompt, return_tensors="pt").to(device) # Gera continuação da conversa output_ids = model.generate( input_ids, max_length=input_ids.shape[1] + 50, # limite de tokens gerados pad_token_id=tokenizer.eos_token_id # garante terminação adequada ) response = tokenizer.decode(output_ids[0], skip_special_tokens=True) print(response)
Esse código formata a frase inicial com "Usuário: ... \nBot:" e deixa o modelo completar a resposta. A função generate produz novos tokens até atingir max_length ou encontrar um token especial de parada. Definimos pad_token_id como o token de fim de texto (eos_token_id) do modelo para evitar avisos do PyTorch.
Um exemplo de saída pode ser:
Usuário: Oi, quem é você?
Bot: Eu sou um assistente virtual treinado para responder perguntas em português!
Lembre-se: como nosso conjunto de dados de exemplo foi muito pequeno, o desempenho do chatbot real ficará limitado. Com mais dados variados, ele aprenderá a responder perguntas diferentes.
Métrica de Avaliação (Perplexidade)
Para medir quantitativamente o desempenho, podemos calcular a perplexidade do modelo sobre um conjunto de validação. A perplexidade é uma métrica comum em modelagem de linguagem, que indica quão bem o modelo prevê o texto. Em geral, uma perplexidade menor significa um modelo melhor.
import math model.eval() with torch.no_grad(): total_loss = 0 for batch in train_loader: # em prática, use um conjunto de validação separado input_ids = batch["input_ids"].to(device) attention_mask = batch["attention_mask"].to(device) labels = batch["labels"].to(device) outputs = model(input_ids=input_ids, attention_mask=attention_mask, labels=labels) loss = outputs.loss total_loss += loss.item() avg_loss = total_loss / len(train_loader) perplexity = math.exp(avg_loss) print(f"Perplexidade do modelo: {perplexity:.2f}")
Esse código recalcula a loss média (idealmente num conjunto de validação) e computa a perplexidade como a exponencial da loss média. Em tarefas práticas, você compararia essa perplexidade com outros modelos ou checkpoints.
Conclusão
Neste tutorial, aprendemos como realizar fine-tuning de um modelo de linguagem pré-treinado usando a biblioteca Transformers do Hugging Face com PyTorch. Vimos que, ao usar um modelo base como o GPT-2, é possível criar rapidamente um chatbot básico em português. Resumindo os passos:
- Escolhemos um modelo pré-treinado generativo (GPT-2) e carregamos o tokenizador e modelo correspondentes.
- Preparamos os dados de conversação, formatando pares de pergunta e resposta e convertendo-os em tokens.
- Criamos um DataLoader e otimizador (AdamW) para o treinamento.
- Executamos o loop de treinamento (fine-tuning), fazendo o modelo ajustar-se às nossas conversas.
- Avaliamos o modelo gerando respostas e (opcionalmente) calculando sua perplexidade em dados de prova.
Como próximos passos, você pode expandir esse projeto treinando com um conjunto de dados maior e mais variado, ajustar hiperparâmetros (como taxa de aprendizado e número de épocas), ou experimentar modelos distintos (como GPT-3 via APIs ou OpenAI GPT, caso disponível). Também é possível aprimorar a limpeza dos dados, adicionar técnicas de transfer learning mais avançadas (LoRA, distinção de estilo, prompts complexos etc.) e integrar o chatbot a interfaces de usuário.
Espero que este guia didático tenha dado uma visão clara de como customizar um modelo de linguagem para seu próprio chatbot. Com prática e experimentação, você poderá criar assistentes conversacionais cada vez mais inteligentes e adaptados às suas necessidades!
Quer aprender mais sobre Programação?
Você acabou de ganhar 1 hora com a minha equipe para uma call exclusiva! Vamos entender o seu momento e te mostrar o caminho para se tornar um programador de sucesso. Clique no botão abaixo e agende agora mesmo!