Ajuste fino de LLMs com LoRA e PyTorch: guia técnico completo
Ajuste fino de LLMs com LoRA e PyTorch: guia técnico completo
Introdução
O ajuste fino (fine-tuning) de modelos de linguagem de grande porte (LLMs) é essencial para adaptar modelos pré-treinados a tarefas específicas. No entanto, ajustar todos os parâmetros dessas redes massivas requer recursos computacionais elevados (muita memória de GPU e tempo) (www.datacamp.com). A técnica LoRA (Low-Rank Adaptation) permite personalizar LLMs de forma eficiente, adicionando apenas um pequeno conjunto de parâmetros treináveis ao modelo pré-treinado (www.cloudflare.com) (huggingface.co).
Neste guia técnico didático, exploraremos desde a preparação dos dados e do ambiente de desenvolvimento até a aplicação prática do LoRA com PyTorch e a biblioteca Transformers da Hugging Face. Mostraremos exemplos de código para ilustrar cada etapa, incluindo configuração do modelo, treinamento e otimização de hiperparâmetros. Ao final, você compreenderá os conceitos fundamentais do LoRA e estará apto a aplicar essa técnica para maximizar o desempenho de seu modelo.
O que é LoRA e por que utilizá-lo?
LoRA é um método de adaptação de baixa classificação (low-rank) para modelos de linguagem grandes. Em vez de modificar todo o modelo, o LoRA adiciona pequenas adaptadores matriciais de baixa dimensão aos pesos originais. Durante o treinamento, apenas esses novos parâmetros A e B (matrizes de baixa rank) são atualizados, enquanto os pesos originais do modelo permanecem congelados (www.cloudflare.com) (www.cloudflare.com). Isso equivale a anexar um módulo extra ao modelo base, ajustando seu comportamento sem re-treinar tudo.
Por analogia, pensar em LoRA é como colocar um filtro colorido em um foco de luz: o holofote original (modelo pré-treinado) continua o mesmo, mas a cor da luz muda de acordo com o novo filtro (www.cloudflare.com). No contexto de LLMs, esses “filtros” são as matrizes de baixa rank, que alteram o resultado do modelo sem exigir o treino de bilhões de parâmetros. Em palavras simples, LoRA adapta rapidamente modelos enormes a novos usos adicionando partes leves (pequenas matrizes) em vez de mudar todo o modelo (www.cloudflare.com) (www.cloudflare.com).
Vantagens do LoRA
- Eficiência de parâmetros: O LoRA reduz drasticamente o número de parâmetros treináveis. Por exemplo, usando LoRA em um modelo GPT-3 (175 bilhões de parâmetros) pode diminuir os parâmetros treináveis em um fator de 10.000, ficando perto de 17,5 milhões (www.datacamp.com). Menos parâmetros treináveis significam menor demanda por memória e hardware.
- Manutenção da qualidade: Apesar de treinar menos parâmetros, o desempenho final do modelo costuma ser alinhado ao do ajuste fino completo. A técnica preserva quase toda a capacidade do modelo original, sem derrubar sua qualidade ou velocidade de inferência (huggingface.co) (www.cloudflare.com).
- Tamanho de checkpoint: Como só são salvas as matrizes adicionais, o arquivo de modelo ajustado fica muito menor. Isso facilita compartilhar e carregar modelos finos com LoRA.
- Nenhuma latência extra: No momento da inferência, as matrizes de LoRA podem ser mescladas (merged) aos pesos originais, de modo que não há perda de velocidade na geração de texto. Em resumo, LoRA não torna a inferência mais lenta.
Essas características tornam o LoRA ideal para equipes que desejam fine-tuning eficiente em recursos limitados. A Hugging Face reúne LoRA em sua biblioteca PEFT (Parameter-Efficient Fine-Tuning), que inclui métodos como LoRA, prefix tuning e outros (www.philschmid.de). Com isso, usar LoRA em modelos tradicionais do Transformers é direto e bem suportado.
Preparação do ambiente e dados
Instalação das bibliotecas
Antes de começar, configure um ambiente Python com as principais bibliotecas: PyTorch, Hugging Face Transformers, Datasets e PEFT (biblioteca de adaptação). Por exemplo, você pode instalar via pip:
pip install torch transformers datasets peft
Isso garante que temos o PyTorch (nível de GPU), a biblioteca de modelos Transformers (para carregar LLMs), a biblioteca de datasets para manipulação de dados, e o pacote PEFT para usar o LoRA. Após a instalação, importe no seu script ou notebook:
import torch from transformers import AutoTokenizer, AutoModelForCausalLM, Trainer, TrainingArguments from peft import LoraConfig, get_peft_model from datasets import load_dataset
Certifique-se de ter também uma GPU CUDA disponível. Para GPUs menores, pode ser útil reduzir o batch ou usar técnicas adicionais (quantização 8-bit ou 4-bit), mas neste guia focaremos no LoRA puro.
Preparação dos dados
Em paralelo, precisamos de um dataset de texto para treinar (fine-tuning). Você pode usar tanto dados públicos (por exemplo, o WikiText, BooksCorpus, ou conjuntos específicos como IMDB para análise de sentimentos) quanto seu próprio arquivo de textos. O importante é que os dados estejam no formato esperado pelo modelo (texto para modelos de linguagem causal ou pares texto/label para tarefas específicas, etc.).
Como exemplo ilustrativo, usaremos o dataset "wikitext-2-raw-v1" do Hugging Face Datasets, que contém passagens em inglês. Carregue e processe assim:
# Carrega o dataset de exemplo dataset = load_dataset("wikitext", "wikitext-2-raw-v1") print(dataset)
Após isso, devemos tokenizar o texto usando o Tokenizer correspondente ao modelo escolhido. Por exemplo, se formos usar uma versão do GPT-2:
tokenizer = AutoTokenizer.from_pretrained("gpt2") tokenizer.pad_token = tokenizer.eos_token # GPT-2 não tem token de padding definido def tokenize_batch(examples): # Tokeniza sob truncamento padrão do modelo return tokenizer(examples["text"], truncation=True, max_length=512) # Aplica tokenização e setamos o formato de PyTorch tokenized_dataset = dataset.map(tokenize_batch, batched=True) tokenized_dataset.set_format("torch", columns=["input_ids", "attention_mask"])
Nesse código, limitamos a 512 tokens cada sequência. Dependendo do seu GPU, você pode reduzir tamanho ou batch posteriormente. Em seguida, crie DataLoaders (ou use direto no Trainer, para isso basta passar tokenized_dataset["train"] e tokenized_dataset["validation"]).
Dica: em cenários reais, considere pré-processar seus dados para remover ruído, distinguir sentenças etc., mas aqui focamos apenas na tokenização básica.
Aplicando LoRA ao modelo
Com o ambiente pronto e os dados carregados, passamos ao modelo. O fluxo básico é:
- Carregar o modelo pré-treinado e o tokenizer correspondente.
- Definir a configuração do LoRA (
LoraConfig) indicando parâmetros como rank, alpha, target_modules, etc. - Envolver o modelo com LoRA usando
get_peft_model. Isso congela o modelo original e injeta adaptadores LoRA.
Por exemplo, usando o GPT-2 small (como demonstração):
model_name = "gpt2" model = AutoModelForCausalLM.from_pretrained(model_name) tokenizer = AutoTokenizer.from_pretrained(model_name) tokenizer.pad_token = tokenizer.eos_token # Define configuração do LoRA lora_config = LoraConfig( r=8, # rank baixo (número de colunas de A/B) lora_alpha=32, # escala (importante para polivalência) target_modules=["q_proj", "v_proj"], # módulos-alvo: projeções Q e V na atenção lora_dropout=0.1, # dropout para regularização bias="none", # deixar bias original (nenhum bias em LoRA) task_type="CAUSAL_LM" # tipo de tarefa (modelo causal) ) # Aplica LoRA ao modelo; os pesos originais ficam congelados model = get_peft_model(model, lora_config)
Neste bloco, destacamos alguns hiperparâmetros de LoRA importantes:
- r (rank): dimensão da matriz de baixa rank. Uma valor pequeno economiza muito parâmetros, mas pode limitar adaptabilidade. Em geral, r=4,8 ou 16 são comuns.
- lora_alpha: fator de escala multiplicativo. Geralmente definido igual ou próximo a r.
- target_modules (módulos-alvo): indica onde inserir LoRA. Em LLMs, normalmente escolhemos as camadas de atenção (as matrizes query e value). Algumas implementações podem usar
"q_proj"e"v_proj"para GPT-2, ou os nomes equivalentes em outros modelos. - lora_dropout: probabilidade de dropout aplicado nas matrizes LoRA, ajuda a evitar overfitting.
- bias: controla se adaptadores para bias são adicionados; aqui mantivemos apenas o bias original.
Ao final desse passo, model é um PeftModel treinável: apenas as novas matrizes A e B serão atualizadas durante o otimização. Esse processo reduz enormemente o número de parâmetros treináveis, mantendo a estrutura base inalterada (www.cloudflare.com) (www.datacamp.com).
Treinamento e ajuste de hiperparâmetros
Com o modelo configurado, podemos treinar (fine-tune) usando o Trainer do Hugging Face, que gerencia laço de treinamento, avaliação e logging. Primeiro definimos TrainingArguments, por exemplo:
training_args = TrainingArguments( output_dir="./lora-model", per_device_train_batch_size=4, per_device_eval_batch_size=4, gradient_accumulation_steps=8, # acumula gradientes para simular maior batch efetivo num_train_epochs=3, learning_rate=5e-5, warmup_steps=100, logging_steps=50, save_steps=500, evaluation_strategy="steps" ) trainer = Trainer( model=model, args=training_args, train_dataset=tokenized_dataset["train"], eval_dataset=tokenized_dataset["validation"], data_collator=lambda data: {'input_ids': torch.stack([f[0] for f in data]), 'attention_mask': torch.stack([f[1] for f in data]), 'labels': torch.stack([f[0] for f in data])} ) trainer.train()
Esse exemplo usa batch size de 4 (por GPU) e acumula gradientes para simular 32 no total. Ajuste num_train_epochs, taxa de aprendizado (learning_rate), entre outros, de acordo com seu problema e recursos. A função data_collator acima é necessária para fornecer as entradas e máscaras de atenção ao modelo causal.
Durante o treinamento, o Trainer atualiza apenas os parâmetros LoRA. Você verá que a quantidade efetiva de parâmetros otimizados é muito menor – note as mensagens de log ou use model.print_trainable_parameters() para verificar.
Hiperparâmetros principais
Existem dois grupos de hiperparâmetros a considerar:
- Hiperparâmetros do LoRA:
- Rank (r): maior
rpermite uma adaptação mais rica (mais parâmetros), mas requer mais memória. Em modelos de maior porte,rentre 8 e 32 costuma ser eficaz. - Alpha:
lora_alpharegula o peso dos novos parâmetros. Em muitos casos, define-sealpha = rpara equilibrar escalonamento. - Dropout: altas taxas (p.ex. 0.1-0.3) aumentam regularização, útil se o conjunto de dados for pequeno.
- Target modules: às vezes limita-se LoRA a certos módulos ajuda (por exemplo, apenas a Q e V em cada camada).
- Rank (r): maior
- Hiperparâmetros de treinamento:
- Batch size / acumulação de gradiente: ajuste para caber na memória GPU.
- Número de épocas: suficiente para convergir sem overfitting. LoRA costuma precisar de menos épocas que fine-tuning completo.
- Taxa de aprendizado: taxas iniciais típicas são ~1e-4 a 5e-5.
- Scheduler de LR: aquecimento (warmup) e decaimento ajudam a estabilizar.
- Regularização: droputs adicionais, weight decay, etc.
Avalie constantemente o loss ou a perplexidade em um conjunto de validação. Se o modelo não melhorar ou apresentar overfitting, ajuste os hiperparâmetros – por exemplo, incrementar r ou alpha para maior capacidade, ou usar mais dropout para maior robustez.
Exemplos práticos
Para ilustrar tudo acima na prática, veja um exemplo resumido de fine-tuning com LoRA em código:
from transformers import AutoTokenizer, AutoModelForCausalLM from peft import LoraConfig, get_peft_model # 1. Carregar modelo e tokenizer model_name = "gpt2" tokenizer = AutoTokenizer.from_pretrained(model_name) model = AutoModelForCausalLM.from_pretrained(model_name) # 2. Configurar LoRA lora_config = LoraConfig( r=8, lora_alpha=32, target_modules=["q_proj", "v_proj"], lora_dropout=0.1, bias="none", task_type="CAUSAL_LM" ) model = get_peft_model(model, lora_config) # 3. Treinar com Trainer (veja seção anterior para args e datasets)
Após o treinamento, você terá um model onde o ajuste fino foi realizado apenas nos adaptadores LoRA. Para usar ou compartilhar o modelo adaptado, é comum salvar somente os pesos LoRA, pois o modelo base está amplamente disponível. Por exemplo:
model.save_pretrained("lora-adpt-model")
Isso salva os pesos LoRA (as matrizes A e B) em um diretório. Para carregar depois, basta recarregar o modelo base e aplicar PeftModel.from_pretrained(...) com o diretório ou identificador do adaptador.
Conclusão e próximos passos
Neste guia, vimos como realizar fine-tuning eficiente com LoRA usando PyTorch e Hugging Face Transformers. Entendemos que o LoRA permite adaptar grandes LLMs adicionando poucos parâmetros extras, preservando os pesos originais do modelo (www.cloudflare.com) (www.datacamp.com). Discutimos a configuração de ambiente, preparação dos dados, o processo de incorporação do LoRA no modelo e o ajuste de hiperparâmetros importantes.
Com LoRA, equipes podem maximizar o desempenho de modelos de linguagem usando menos recursos. Como próximos passos, você pode explorar:
- Quantização: combinar LoRA com quantização de 4/8 bits (por exemplo, usando bitsandbytes) para reduzir ainda mais a memória.
- Outras técnicas PEFT: além de LoRA, existem prefix tuning e P-tuning no Hugging Face PEFT (www.philschmid.de), que podem ser mais adequadas para tarefas específicas.
- Ajuste de hiperparâmetros: experimente diferentes valores de
r,alphae regularizações para seu caso de uso, acompanhando métricas de validação. - Avaliação do modelo: teste o modelo ajustado em tarefas reais (geração de texto, completamento, etc.) para verificar se o fine-tuning melhorou conforme esperado.
Em resumo, LoRA simplifica o fine-tuning de grandes modelos e abre caminho para que até equipes menores possam personalizar LLMs com eficácia. Experimente essas técnicas em seus projetos e aproveite a flexibilidade do PyTorch e Transformers para incendiar suas soluções de NLP!
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!