Voltar ao blog

Fine-Tuning de Modelos de Linguagem com Hugging Face Transformers: Guia Avançado

Fine-Tuning de Modelos de Linguagem com Hugging Face Transformers: Guia Avançado

Introdução

O ajuste fino (fine-tuning) de modelos de linguagem pré-treinados tornou-se uma prática fundamental em NLP. Em vez de treinar um modelo do zero – algo que exige imensos dados e poder computacional – adaptamos um modelo já pré-treinado a uma tarefa específica. Como explica a Hugging Face, “fine-tuning adapts a pretrained model to a specific task with a smaller specialized dataset”, exigindo bem menos dados e recursos do que treinar do início (huggingface.co). É como ter um aluno que já sabe matemática e só precisa aprender física avançada: o modelo já tem conhecimento geral (como linguagem ou sintaxe) e só precisa “aprender” o foco específico da sua tarefa.

Neste guia, veremos passo a passo como preparar os dados, configurar o modelo e realizar o treinamento usando a biblioteca Hugging Face Transformers. Abordaremos também técnicas avançadas – como uso de precisão mista e treinamento distribuído – para otimizar e acelerar o fine-tuning. Por fim, mostraremos como avaliar, otimizar e implantar seu modelo numa aplicação real.

Preparação de Dados

A qualidade dos dados de treinamento é crucial para um bom fine-tuning. Em muitas tarefas de NLP (classificação de texto, análise de sentimentos, NER, etc.), você precisa de um conjunto anotado de exemplos de entrada-saída. A Hugging Face oferece a biblioteca Datasets, que contém diversos datasets populares pré-formatados. Veja um exemplo simples com o conjunto IMDb de análise de sentimentos (strong>0: negativo, 1: positivo):

from datasets import load_dataset # Carrega o dataset IMDb (treino e teste) dataset = load_dataset("imdb") print(dataset) # mostra divisões: train, test

O retorno típico indica as divisões dos dados:

DatasetDict({
    train: Dataset(features: ['text', 'label'], num_rows: 25000),
    test:  Dataset(features: ['text', 'label'], num_rows: 25000)
})

Cada entrada possui uma chave "text" (o review) e "label" (0 ou 1). Para tarefas reais, se seu dado não estiver nesse formato, você deve converter para algo parecido: uma lista de dicionários onde cada item tem um texto e rótulo. Se for outro tipo de tarefa (ex.: SQuAD para QA), o formato muda (texto + pergunta + resposta), mas o conceito é similar.

Tokenização e Data Collator

Após carregar e organizar o dataset, o próximo passo é tokenizar o texto, convertendo-o em IDs de tokens entendíveis pelo modelo. Para isso usamos um tokenizer pré-treinado compatível com o modelo escolhido. Por exemplo, para um BERT básico:

from transformers import AutoTokenizer, DataCollatorWithPadding tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased") def tokenize_function(examples): return tokenizer(examples["text"], padding="max_length", truncation=True) # Aplica tokenização a todo o dataset em lotes tokenized_datasets = dataset.map(tokenize_function, batched=True) # DataCollator fará o padding dinâmico durante o treinamento data_collator = DataCollatorWithPadding(tokenizer=tokenizer)

Neste exemplo, dataset.map() aplica a função de tokenização a cada exemplo, preenchendo (padding) ou cortando (truncation) os textos para um tamanho máximo padrão do modelo. Se quiser flexibilizar o tamanho do batch, DataCollatorWithPadding empacota dinamicamente os exemplos. Assim, as entradas (token IDs e máscaras de atenção) ficam prontas para o modelo consumir.

Dica: Sempre confira se as colunas do dataset estão corretas. Após mapear, as colunas típicas de entrada esperadas pelo modelo serão input_ids, attention_mask (e possivelmente token_type_ids para alguns modelos). A coluna alvo (labels ou label) deve ser mantida para supervisão.

Configuração do Modelo

Escolha do Modelo Pré-Treinado

A Hugging Face oferece inúmeros modelos pré-treinados (BERT, GPT-2, RoBERTa, T5, etc.), cada um com variantes (tamanho, idioma, etc.). A escolha depende da tarefa:

  • BERT/RoBERTa/DistilBERT: ótimos para classificação de texto, NER, análise de sentimentos.
  • GPT-2/CPM: voltados a geração de texto (linguagem causal).
  • T5/BART: adaptáveis a tarefas de geração e interpretação (tradução, sumarização, QA).

Para este guia, focaremos em classificação de texto (por exemplo, análise de sentimentos). Usaremos o modelo bert-base-uncased como exemplo.

Carregando o Modelo e o Tokenizer

Uma vez escolhido o modelo, carregamos tanto o tokenizer quanto a arquitetura apropriada com a cabeça final para nossa tarefa. Para classificação de 2 classes:

from transformers import AutoModelForSequenceClassification model_name = "bert-base-uncased" num_labels = 2 tokenizer = AutoTokenizer.from_pretrained(model_name) model = AutoModelForSequenceClassification.from_pretrained( model_name, num_labels=num_labels )

Isso carrega o modelo BERT base com uma cabeça de classificação adicionada após o último encoder, configurada para num_labels rótulos. Note que essa nova cabeça é inicializada aleatoriamente, pois o BERT original não foi pré-treinado em classificação específica. Por isso a mensagem de aviso:

“Some weights of BertForSequenceClassification were not initialized from the model checkpoint [...] You should probably TRAIN this model on a down-stream task...” (huggingface.co).

Em outras palavras, o corpo do modelo (os layers do transformador) mantém os pesos pré-treinados, enquanto a última camada (ê cabeça) é nova e precisa ser ajustada via treinamento (fine-tuning) para oferecer boas previsões na sua tarefa. É por isso que todo o passo de treinamento a seguir é indispensável.

Adaptando a Cabeça de Saída

Caso a tarefa não fosse de classificação, mas de outro tipo (por exemplo, AutoModelForTokenClassification para NER, ou AutoModelForQuestionAnswering para QA), usaríamos classes de modelo diferentes. A ideia geral é a mesma: carregamos uma arquitetura pré-treinada com a cabeça certa para o que precisamos (validação de número de labels, etc.). Lembre-se de sempre passar num_labels correto ao carregar modelos de classificação.

Resumo até agora: organizamos os dados, tokenizamos e carregamos o modelo pré-treinado adequado (com seu tokenizer). Agora estamos prontos para configurar o treinamento.

Treinamento e Fine-Tuning

Hugging Face Transformers facilita muito o treinamento com o utilitário Trainer. O Trainer é um loop de treinamento otimizado que esconde a maior parte do boilerplate. Ele permite ajustar um modelo pré-treinado dando acesso a vários recursos, como acumulação de gradientes, precisão mista, gerenciamento de checkpoints, etc (huggingface.co). Em essência, enfileiramos todos os elementos principais: modelo, tokenizer, datasets e hiperparâmetros, e chamamos trainer.train().

Definindo Hiperparâmetros

Primeiro definimos os hiperparâmetros de treinamento por meio de TrainingArguments. Alguns parâmetros comuns:

  • output_dir: pasta onde checkpoints serão salvos.
  • num_train_epochs: número de épocas (passagens completas) pelo conjunto de treino.
  • per_device_train_batch_size e per_device_eval_batch_size: tamanho do batch por GPU/dispositivo.
  • learning_rate: taxa de aprendizado.
  • evaluation_strategy: indica quando avaliar no conjunto de validação ("epoch", ou "steps").
  • save_strategy: frequência de salvamento dos modelos.
  • fp16: (True/False) ativa a precisão mista 16-bit, útil para GPUs modernas.
  • logging_steps, save_total_limit e outros para métricas/logging.

Um exemplo de configuração básica:

from transformers import TrainingArguments training_args = TrainingArguments( output_dir="my_model", num_train_epochs=3, per_device_train_batch_size=16, per_device_eval_batch_size=16, warmup_steps=500, learning_rate=2e-5, evaluation_strategy="epoch", save_strategy="epoch", logging_dir="logs", logging_steps=50, )

Ajuste esses valores conforme seu caso: batch size pequeno pode estabilizar o treinamento em GPUs limitadas; taxa de aprendizado menor (como 1e-5 a 5e-5) geralmente funciona bem para Bert-like; épocas típicas entre 2-5 em fine-tuning, dependendo do tamanho do conjunto. O framework Trainer aceita muito mais opções (veja a documentação de TrainingArguments para detalhes).

Criando o Trainer

Com o modelo, tokenizer, datasets e argumentos prontos, instanciamos o Trainer. Além do modelo e do tokenizer, precisamos informar os datasets de treino e validação e opcionalmente o data_collator. Por exemplo:

from transformers import Trainer trainer = Trainer( model=model, args=training_args, train_dataset=tokenized_datasets["train"], eval_dataset=tokenized_datasets["test"], data_collator=data_collator, tokenizer=tokenizer, )

Aqui, train_dataset e eval_dataset são objetos do Hugging Face Datasets já tokenizados. Passamos o data_collator (para padding dinâmico) e o tokenizer para que o Trainer finalize o empacotamento correto dos inputs.

Iniciando o Fine-Tuning

Finalmente, para iniciar o treinamento, usamos:

trainer.train()

Isso executa o loop de treinamento. A cada iteração/época o Trainer alimenta batches ao modelo, calcula a perda (loss), faz backpropagation e atualiza os pesos. O processo mostra na saída diversos logs de perda (loss) e métricas (se configurado). Como resultado final, a cabeça de classificação do modelo já estará ajustada à sua tarefa específica.

Em resumo, o Trainer cuida de todo o fluxo: forward pass, backward pass, otimização, agendamento de learning rate, avaliação periódica (se definido) e checkpoints. Graças a isso, podemos focar em preparar nossos dados e ajustar somente os hyperparâmetros relevantes (huggingface.co).

Técnicas Avançadas de Treinamento

Além do fluxo básico, há várias técnicas para acelerar e melhorar o fine-tuning, especialmente quando trabalhamos com modelos grandes ou recursos limitados.

Precisão Mista (Mixed Precision)

Mixed precision (treinar usando 16 bits em vez de 32 bits em parte do cálculo) é suportado diretamente pelo Trainer. Basta ativar o flag fp16=True em TrainingArguments. Isso acelera o treinamento e reduz o uso de GPU sem perda significativa de precisão. Por exemplo:

training_args = TrainingArguments( ..., per_device_train_batch_size=8, fp16=True, # ativa mixed precision ... )

Assim, definindo fp16=True, o modelo usa metade da precisão (FP16) nos tensores onde é seguro (huggingface.co). Essa mudança muitas vezes duplica a velocidade em GPUs compatíveis, particularmente com arquiteturas NVIDIA Ampere ou superiores.

Acumulação de Gradientes (Gradient Accumulation)

Se a GPU não comporta batches grandes, é possível simular um batch maior acumulando gradientes em vários passos. Em TrainingArguments, defina gradient_accumulation_steps=N. Por exemplo, se per_device_train_batch_size=8 e gradient_accumulation_steps=4, o efeito é semelhante a usar batch de tamanho 32 (8×4). O Trainer automaticamente acumula gradientes por 4 passos antes de fazer um passo de otimização. Isso permite aumentar “virtualmente” o batch size sem usar mais memória.

Checkpoint de Gradiente (Gradient Checkpointing)

Quando o modelo é muito grande, podemos ativar o gradient_checkpointing. Essa técnica computa parte do modelo várias vezes no lugar de armazenar todos os estados intermediários, reduzindo muito a memória usada, ao custo de mais tempo de computação. Para usar:

model.gradient_checkpointing_enable() training_args = TrainingArguments(..., fp16=True, gradient_checkpointing=True)

Com gradient_checkpointing=True e fp16=True, mesmo modelos enormes (como GPT-2 grande ou T5-3B) podem ser ajustados em GPUs com memória limitadas. A Hugging Face comprova que com isso é possível treinar modelos maiores gastando menos memória (huggingface.co) (huggingface.co).

Treinamento Distribuído e Multi-GPU

O Trainer integra-se ao PyTorch Lightning ou ao próprio PyTorch DDP para facilitar multi-GPU. No geral, se você executa o script em uma máquina com várias GPUs e configura os argumentos corretamente (e.g. per_device_train_batch_size por GPU), o Trainer calibrará automaticamente o uso paralelo.

Para casos multi-máquina ou uso de TPUs, a Hugging Face fornece a biblioteca Accelerate, que abstrai essa complexidade. Com o accelerate, basta um comando para rodar em múltiplos GPUs/TPUs sem alterar quase nada no código. Por exemplo:

accelerate config          # configurações iniciais
accelerate launch train.py

Internamente, ele ajusta as estratégias de paralelismo. Assim, você pode usar vários dispositivos sem modificar manualmente o laço de treinamento.

Hiperparâmetros Avançados e Regularização

Para melhorar ainda mais:

  • Taxa de aprendizado e Scheduler: além do valor fixo, use agendadores (warmup_linear, cosine, etc.) para variar o learning rate durante as épocas.
  • # Warmup steps: permite começar com LR menor antes de subir lentamente.
  • Weight decay: aplica regularização L2 (previne overfitting). Configure em TrainingArguments(weight_decay=0.01).
  • Dropout: modelos como BERT já têm dropout em alguns layers; em fine-tuning, pode-se ajustar essa taxa para evitar overfitting se o dataset for pequeno.
  • Early Stopping: interrompe treino quando a métrica de validação não melhora após várias épocas. Pode ser adicionado via callbacks no Trainer.

Em suma, use listas e bullet points para organizar seus hiperparâmetros críticos. Teste diferentes configurações e use conjuntos de validação para monitorar quando parar.

Avaliação e Implantação do Modelo

Avaliação e Métricas

É fundamental medir a qualidade do modelo após o fine-tuning. Para isso, criamos uma função de métricas (p. ex. acurácia, F1) e passamos ao Trainer. Usamos a biblioteca evaluate da Hugging Face para simplificar. Exemplo de função de avaliação para classificação binária:

import numpy as np from evaluate import load accuracy_metric = load("accuracy") f1_metric = load("f1") def compute_metrics(eval_pred): logits, labels = eval_pred preds = np.argmax(logits, axis=-1) acc = accuracy_metric.compute(predictions=preds, references=labels) f1 = f1_metric.compute(predictions=preds, references=labels) return {"accuracy": acc["accuracy"], "f1": f1["f1"]}

Após adicionar essa função ao Trainer(compute_metrics=compute_metrics), o framework exibirá essas métricas no final de cada avaliação. Por exemplo, em um experimento típico de MRPC, poderíamos obter algo assim:

{'accuracy': 0.8578, 'f1': 0.8996}

Esses valores indicam a precisão do modelo no conjunto de validação (huggingface.co). Escolha métricas apropriadas para sua tarefa (ex.: F1 para classes desbalanceadas, exatidão para dados balanceados, BLEU/ROUGE para geração de texto, etc.).

Salvando e Carregando o Modelo

Quando satisfeito com o treinamento, salve o modelo e o tokenizer para uso futuro. Por exemplo:

model.save_pretrained("meu_modelo_finetuned") tokenizer.save_pretrained("meu_modelo_finetuned")

Isso cria arquivos (geralmente em um diretório) com os pesos e configurações. Para recarregar depois:

from transformers import AutoModelForSequenceClassification, AutoTokenizer model = AutoModelForSequenceClassification.from_pretrained("meu_modelo_finetuned") tokenizer = AutoTokenizer.from_pretrained("meu_modelo_finetuned")

Se desejar, a Hugging Face permite publicar o modelo no seu Hugging Face Hub. Basta incluir push_to_hub=True em TrainingArguments ou chamar trainer.push_to_hub("nome_usuario/nome_modelo"). Assim, outros podem baixar ou até usar a API de inferência diretamente.

Otimização e Quantização

Para um modelo pronto para produção, considerar otimizações é importante:

  • Quantização: reduz a precisão dos pesos (por exemplo, de 32-bit para 8-bit) para diminuir tamanho e acelerar inferência. A Hugging Face fornece o pacote Optimum ONNX Runtime para essa tarefa. Com ele, é possível aplicar quantização a vários modelos da Hugging Face Hub usando o ONNX Runtime (huggingface.co). Em poucas palavras: transforme seu modelo PyTorch em ONNX e aplique quantização para obter uma versão menor (e.g. menor latência em CPU).

  • Distilação (Distillation): treine um modelo student menor para imitar o output do modelo teacher original, reduzindo ainda mais o custo de inferência (vamos do grande para o pequeno). Há scripts de distilação na comunidade, e o Hub já oferece alguns modelos distilados.

  • JIT/ONNX export: você pode exportar o modelo para TorchScript ou ONNX e otimizar com ferramentas externas. Isso normalmente aumenta drasticamente a velocidade de execução em produção.

Por exemplo, após a quantização (via Optimum CLI ou código), você carregaria o modelo quantizado no ONNX Runtime para fazer inferência mais leve. Toda essa otimização visa tornar seu modelo rápido o suficiente para atender usuários em tempo real.

Implantação com Transformers

Para fazer inferência, a maneira mais simples pós-treinamento é usar o pipeline do Transformers. Suponha que salvamos o modelo em "meu_modelo_finetuned":

from transformers import pipeline # Carrega modelo/tokenizer atualizados do diretório salvo classifier = pipeline("text-classification", model="meu_modelo_finetuned", tokenizer="meu_modelo_finetuned") # Usa o pipeline para classificar um texto novo exemplo = "O filme foi ótimo e divertido!" result = classifier(exemplo) print(result) # Saída: [{'label': 'POSITIVE', 'score': 0.986}]

O pipeline gerencia todos os passos (tokenização, modelo, decodificação) para uma predicação rápida. Alternativamente, você pode usar APIs web da Hugging Face ou integrar o modelo em um serviço REST (por exemplo, FastAPI ou SageMaker). O importante é que, após o fine-tuning, o modelo agora esteja capaz de responder às entradas específicas de sua aplicação.

Conclusão

Neste guia avançado, vimos todo o processo de fine-tuning de um modelo de linguagem pré-treinado com a biblioteca Hugging Face Transformers. Abordamos a preparação dos dados (carregando e tokenizando), a escolha e configuração do modelo pré-treinado, e o treinamento usando o Trainer. Destacamos que o fine-tuning permite adaptar um modelo geral a uma tarefa específica usando muito menos dados e tempo de computação do que treinar do zero (huggingface.co).

Também exploramos técnicas avançadas para otimizar recursos: desde mixed precision com fp16 e acumulação de gradientes, até estratégias de paralelismo multi-GPU. Vimos como usar recursos do Transformers para avaliação consistente (métricas de acurácia e F1) e discutimos dicas de implantação – como salvar o modelo, quantização via Optimum e o uso do pipeline para inferência em produção.

Para ganhar fluência, recomendamos praticar em diversos cenários: por exemplo, ajuste fino de modelos em Português (há modelos BERT específicos para PT), ou até tentar geração de texto com GPT-2. Consulte sempre a documentação oficial e tutoriais da Hugging Face, e acompanhe novidades como técnicas de ajuste eficiente (adapters, LoRA, etc.) que vêm ganhando força. Com esses conhecimentos, você está bem equipado para criar soluções de NLP personalizadas e escaláveis com Transformers.

Próximos passos: experimente outras tarefas (tradução, sumarização, QA), teste variações de hiperparâmetros e explore bibliotecas como 🤗Evaluate para métricas detalhadas. O universo de IA está em constante evolução: investir em fine-tuning avançado o coloca na vanguarda da criação de modelos inteligentes 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!