Ajuste Fino de LLMs com LoRA e PEFT: Guia Prático Avançado
Ajuste Fino de LLMs com LoRA e PEFT: Guia Prático Avançado
Grandes modelos de linguagem (LLMs), como GPT, PaLM e LLaMA, revolucionaram tarefas de NLP, mas adaptá-los a novas tarefas pode ser muito custoso. Ajuste fino tradicional envolve treinar todos os bilhares de parâmetros do modelo, exigindo enorme poder de hardware e memória. Como alertam Mangrulkar e Paul (Hugging Face, 2023), treinar completamente um LLM pré-treinado já estabelecido torna-se muitas vezes inviável em hardware de consumidor (huggingface.co). Além disso, para cada tarefa diferente teríamos que armazenar uma cópia completa desse modelo de dezenas de gigabytes, o que também é muito caro.
Para contornar esses desafios, surgem técnicas de Ajuste Fino Eficiente (PEFT), que atualizam apenas uma fração dos parâmetros do LLM em vez de todos eles. Em termos simples, PEFT significa ajustar só pequenos módulos extras do modelo, mantendo o restante completamente congelado. Esse truque reduz drasticamente o uso de memória e tempo de treinamento, além de evitar o chamado esquecimento catastrófico, no qual o modelo perde conhecimento antigo ao ser ajustado completamente para uma nova tarefa (huggingface.co).
Neste guia você irá aprender:
- Desafios do ajuste fino tradicional: por que treinar um LLM completo é caro e difícil (recursos e qualidade).
- PEFT e LoRA: o que são e como funcionam, de forma intuitiva.
- Implementação prática: como usar a biblioteca
peftdo Hugging Face para aplicar LoRA em modelos de linguagem, com exemplos de código. - Extensões avançadas: dicas sobre combinações com quantização (como QLoRA) para ir além.
No final, você terá uma visão sólida de como personalizar modelos gigantes de forma econômica, obtendo desempenho próximo ao ajuste fino completo, mas gastando muito menos recursos.
Desafios do ajuste fino de LLMs
Ajustar finamente LLMs massivos enfrenta vários obstáculos práticos:
- Altos requisitos de hardware: Modelos atuais têm bilhões de parâmetros; ajustá-los exige GPUs muito potentes. Como observa Sourab Mangrulkar et al., em cenários reais treinar completamente um LLM pré-existente fica inviável em hardware de consumidor (huggingface.co). Muitas vezes não temos GPUs com memória suficiente, e até 80GB pode não bastar para um modelo de 12B parâmetros.
- Tempo de treinamento: Mesmo com hardware avançado, treinar bilhões de pesos pode levar dias. Cada época de treinamento envolve cálculos enormes, tornando a iteração muito lenta.
- Custo de armazenamento: Cada modelo fino (fine-tuned) ocupa tanto espaço quanto o modelo original. Por exemplo, se o modelo base ocupa 40 GB, uma cópia ajustada consumirá mais 40 GB. Assim, manter várias versões para tarefas diferentes vira um pesadelo de disco.
- Esquecimento catastrófico: Atualizar todos os pesos de um LLM pode fazer o modelo esquecer comportamentos ou conhecimentos anteriores ao focar na tarefa específica. Técnicas PEFT surgem para mitigar isso: congelam a maior parte do modelo base e adicionam apenas pequenos ajustes extra, preservando o currículo original do modelo (huggingface.co).
Esses desafios motivam a busca por métodos mais leves, que mantenham a maior parte do poder do LLM congelado e treinem apenas o necessário para a nova aplicação.
Ajuste Fino Eficiente (PEFT)
PEFT (Parameter-Efficient Fine-Tuning) é o nome dado a um conjunto de técnicas que adaptam grandes modelos treinando somente pequenos módulos extras, sem alterar a maior parte dos pesos originais. Em vez de reescrever o manual inteiro do modelo, como no ajuste fino tradicional, o PEFT faz algo mais parecido com adicionar notas de reforço ou complementos ao manual existente. Isso traz várias vantagens:
- Parâmetros treináveis reduzidos: Apenas uma fração dos pesos é atualizada. Por exemplo, aplicando LoRA (uma técnica de PEFT) em um grande modelo, somente ~0,2% dos parâmetros acaba sendo treinado (github.com). Assim, a maior parte do modelo fica congelada.
- Eficiência de memória: Como atualizamos poucos pesos, precisamos de muito menos GPU RAM e CPU para salvar gradientes. Até modelos de 12B, que estouram GPU de 80GB em fine-tuning completo, podem ser treinados com PEFT em cartões menores (com ajuda de técnicas de pool e offloading) (github.com).
- Economia de armazenamento: Os checkpoints resultantes são minúsculos. Em vez de salvar 40GB para cada tarefa, um adaptador PEFT é normalmente apenas alguns MB. Isso permite ter dezenas de variantes sem explodir o disco.
- Maior flexibilidade: Podemos criar várias versões leves do mesmo modelo para diferentes tarefas, sem destruir o modelo base. Por exemplo, um chatbot usa o modelo original + adaptador A (para tarefa X), enquanto outro projeto usa adaptador B (para tarefa Y), ambos partindo de um único backbone grande.
- Redução de overfitting: Congelar o modelo principal ajuda a preservar o conhecimento geral. Estima-se que ajustar finamente apenas um pequeno complemento (evitando sobreajustar todos os pesos) leva a melhor generalização em cenários de dados escassos.
Alguns métodos populares de PEFT incluem:
- Adapters: Inserem pequenos blocos de rede (feed-forward) em cada camada do transformer. Treina-se apenas esses adapters e mantém os pesos originais congelados.
- Prefix-Tuning: Adiciona um vetor (ou prefixo) treinável aos tokens de entrada, influenciando a atenção sem mudar pesos principais.
- P-Tuning/Prompt-Tuning: Ajusta apenas os vetores de embedding de tokens especiais (prompts) que guiam o modelo.
- BitFit: Treina apenas os bias (termos de enviesamento) de todas camadas, deixando demais pesos intactos.
- LoRA (Low-Rank Adaptation): Decompõe atualizações de peso em matrizes de baixa rank (foco deste post).
Em comum, todas estas abordagens introduzem poucos parâmetros extras e reduzem custos. Por exemplo, usando LoRA em bigscience/mt0-large, apenas 0,19% dos parâmetros foram treinados, economizando gigabytes de GPU (github.com). Isso reforça que só uma pequena “ajuda de praia” é suficiente para especializar o modelo.
Analogia: Pense no modelo base como um enorme livro de receita. O ajuste fino completo seria reescrever receitas inteiras, um processo demorado. As técnicas PEFT, como LoRA, funcionam mais como anotações nas margens: você não muda o texto original, apenas adiciona notas breves que ajustam a receita à tarefa. Isso é rápido e ocupa pouco espaço.
Com a biblioteca peft do Hugging Face, aplicar essas técnicas ficou ainda mais fácil, integrando-se com transformers, diffusers e accelerate para finetuning escalável. A seguir, focaremos em LoRA, um método de PEFT amplamente usado.
LoRA (Low-Rank Adaptation)
LoRA (Adaptção de Baixo Rank) é uma técnica de fine-tuning proposta por Hu et al. (2021) que reduz drasticamente o custo de treinamento. Em vez de ajustar os pesos originais $W$ de uma camada, LoRA modela a atualização desses pesos como o produto de duas matrizes pequenas. Em outras palavras, partimos do princípio de que o ajuste $\Delta W$ pode ser escrito como:
$$ \Delta W = A \times B $$
onde $A$ tem dimensão ((d \times r)) e $B$ tem dimensão ((r \times d)), sendo (r) muito menor que (d). Assim, em vez de adicionar uma matriz full-size, adicionamos duas bem menores. O antigo peso passa a vale $$W' = W + \Delta W = W + A B,$$ mas apenas (A) e (B) são treinados. A intenção é que (AB) capture a mudança necessária para a nova tarefa com muito menos parâmetros.
Como explicado em artigos recentes, LoRA usa decomposição de matriz de baixo rank para representar atualizações de peso, criando uma maneira eficiente de ajustar modelos grande escala (huggingface.co). Em termos computacionais, se o peso original $W$ tem dimensão (d\times d) (ou similar), treinar todo ele exigiria (d^2) parâmetros. Com LoRA, os parâmetros extras são (2d \times r), que podem ser centenas de vezes menores se (r) for pequeno. Por exemplo, se (d=1024) e (r=8), teríamos apenas (2 \times 1024 \times 8 = 16{,}384) parâmetros adicionais, vs (1024^2 = 1{,}048{,}576) originais. Essa ordem de grandeza menor permite treinar com GPUs modestas.
Como LoRA é aplicado no Transformer
No universo de Transformers, LoRA é tipicamente acoplado às camadas de atenção multi-cabeça. Por exemplo, considera-se as projeções de consulta (q_proj) e valor (v_proj) dentro de um bloco de atenção. Em vez de atualizar diretamente essas matrizes grandes durante o fine-tuning, LoRA insere as matrizes de baixa rank (A) e (B) nelas. Ou seja, durante o forward pass, as projeções calculam algo como:
# Simplificação ilustrativa: y = W @ x # Com LoRA: y = (W + A @ B) @ x # W original congelado, A e B treináveis
Desse modo, o modelo original W permanece intacto (camadas congeladas), e apenas os subcomponentes A e B são atualizados. Como resultado, o modelo aprende EPSILON de alterações focadas, sem prejudicar o peso principal.
Parâmetros de configuração do LoRA
Ao usar LoRA, definimos alguns hiperparâmetros básicos:
r(rank): número de colunas de (A) (e linhas de (B)). Controla a capacidade do adaptador. Tipicamente (r) varia de 4 a 16 para LLMs. Valores maiores dão mais poder de ajuste, mas aumentam parâmetros.lora_alpha: fator de escala. Multiplica as atualizações de LoRA, ajudando na convergência. Um valor comum é o próprior(por exemplo, ser=8, usarlora_alpha=16ou32é comum).lora_dropout: taxa de dropout aplicada às atualizações LoRA. Útil para regularizar e evitar overfitting no adaptador.target_modules: especifica quais camadas usar LoRA, como"q_proj","v_proj", etc. É possível ajustar apenas partes específicas do transformer.bias: define se os vieses (bias) serão ajustados ou não.
Por exemplo, ao usar a biblioteca peft do Hugging Face, fazemos algo assim (em Python) para configurar LoRA:
from peft import LoraConfig, TaskType lora_config = LoraConfig( task_type=TaskType.CAUSAL_LM, # tipo de tarefa (ex.: linguagem causal) r=8, lora_alpha=16, lora_dropout=0.05, target_modules=["q_proj", "v_proj"], # módulos de atenção bias="none" )
Esse LoraConfig informa que vamos treinar apenas as matrizes de baixa rank definidas por r, com escala e dropout especificados. O parâmetro target_modules pode variar dependendo da arquitetura (em GPT2/GPTJ, use "c_attn"; em LLaMA ou OPT, "k_proj","v_proj" etc.).
Em resumo, LoRA cria um “adaptador leve” embutido em cada camada. Quando treinamos o modelo com LoRA, apenas os novos parâmetros desse adaptador são atualizados. Isso entrega uma doce economia de recursos: estudos mostram que modelos afinados com LoRA alcançam desempenho comparável ao fine-tuning completo, mas com custos de memória e computação muito menores (huggingface.co). Além disso, como os pesos originais não mudam, não há latência extra na inferência — basta somar as matrizes LoRA no momento da predição (geralmente feito de forma transparente pela biblioteca).
Vantagens do LoRA:
- Dimensão reduzida de treinamento (ex.: 100x menos parâmetros).
- Compatível com outros métodos: pode ser usado junto com quantização (ex.: QLoRA).
- Checkpoints pequenos.
- Mantém desempenho real próximo ao full-fine-tuning (huggingface.co).
Observação: LoRA foi inicialmente criada para LLMs, mas também se tornou popular em modelos de difusão para geração de imagem (Stable Diffusion, DALL-E, etc.) (huggingface.co), graças à mesma vantagem de reduzir drasticamente o custo de ajuste. Em suma, LoRA provou ser uma solução versátil em grandes redes neurais.
Implementando LoRA na prática com 🤗PEFT
Agora que entendemos o conceito, vamos ver um exemplo prático usando o ecossistema Hugging Face. Seguem os passos básicos:
-
Instalar bibliotecas: Certifique-se de ter
transformersepeftinstalados. Por exemplo:pip install transformers datasets peft accelerateIsso traz também o
datasetseacceleratepara facilitar multigpu. -
Carregar modelo e tokenizer: Suponha que usemos o GPT-2 médio:
from transformers import AutoModelForCausalLM, AutoTokenizer model_name = "gpt2-medium" model = AutoModelForCausalLM.from_pretrained(model_name) tokenizer = AutoTokenizer.from_pretrained(model_name)Esse modelo será nosso
modelo base(que ficará congelado). -
Configurar LoRA: Usamos o objeto
LoraConfigdo PEFT:from peft import LoraConfig, get_peft_model, TaskType peft_config = LoraConfig( task_type=TaskType.CAUSAL_LM, # ajustando modelo causal LM r=8, lora_alpha=16, lora_dropout=0.1, target_modules=["c_attn"] # para GPT modelos (projeção de atenção) ) model = get_peft_model(model, peft_config) model.print_trainable_parameters() # Deve mostrar apenas uma pequena fração dos parâmetros como treináveis.O Hugging Face recomenda envolver o modelo e a configuração com
get_peft_modelpara ativar LoRA (github.com). No output, você verá algo como "treinable params: X; trainable%: 0.YYY" — tipicamente bem baixo (ex.: 0.12%). -
Preparar dados e treinar: Podemos usar o
Trainerdo transformers ou outra rotina. Exemplo rápido usando Trainer:from datasets import load_dataset from transformers import Trainer, TrainingArguments, DataCollatorForLanguageModeling # Exemplo de dataset pequeno (somente para ilustração) dataset = load_dataset("wikitext", "wikitext-2-raw-v1", split="train") def tokenize_fun(examples): return tokenizer(examples["text"], truncation=True, max_length=128, padding="max_length") dataset = dataset.map(tokenize_fun, batched=True) data_collator = DataCollatorForLanguageModeling(tokenizer=tokenizer, mlm=False) training_args = TrainingArguments( output_dir="./lora_finetune", num_train_epochs=1, per_device_train_batch_size=4, save_steps=500, logging_steps=100, learning_rate=5e-4 ) trainer = Trainer( model=model, args=training_args, train_dataset=dataset, data_collator=data_collator ) trainer.train() # Após o treino, salvamos: model.save_pretrained("./gpt2-medium-lora")Esse código treina por uma época um modelo GPT-2 com LoRA, no coração do fluxo normal do Hugging Face. Note que não alteramos a estrutura do Trainer — LoRA é integrada automaticamente.
-
Inferência com o modelo LoRA: Para utilizar o modelo afinado, carregue o modelo base e o adaptador LoRA:
from peft import PeftModel # Carrega o modelo base e o tokenizer novamente model = AutoModelForCausalLM.from_pretrained(model_name) tokenizer = AutoTokenizer.from_pretrained(model_name) # Aplica o adaptador LoRA salvo model = PeftModel.from_pretrained(model, "./gpt2-medium-lora") # Agora podemos gerar: input_ids = tokenizer("Currículo:\n", return_tensors="pt").input_ids output_ids = model.generate(input_ids, max_new_tokens=50) print(tokenizer.decode(output_ids[0], skip_special_tokens=True))Como mostra o quickstart oficial da Hugging Face, usar
PeftModel.from_pretrainedaplica as matrizes LoRA ao modelo base carregado (github.com). A geração de texto a partir daí é feita normalmente, retornando respostas específicas ajustadas.
Nesse processo, notamos as principais vantagens: em vez de treinar 329M de pesos (GPT-2 medium) completamentes, apenas alguns milhões foram atualizados. O model.save_pretrained criou um checkpoint leve (alguns MBs) contendo apenas os parâmetros LoRA. Esse checkpoint é vele para ser armazenado e carregado rapidamente.
Curiosidade: No exemplo acima usamos GPT-2, mas o mesmo código funciona para modelos maiores, como LLaMA ou BLOOM, bastando só ajustar
target_modulesconforme a arquitetura. A bibliotecapeftabstrai muito da complexidade.
Combinando LoRA com Quantização (QLoRA) e Outros Avanços
Para levar a eficiência ainda mais a níveis extremos, pode-se combinar LoRA com quantização. O método QLoRA (Quantized LoRA) aplica quantização de 4 bits aos pesos do modelo base enquanto usa LoRA para ajustá-los (huggingface.co). Isso permite, por exemplo, ajustar modelos de até 65 bilhões de parâmetros em apenas uma GPU de 48GB, mantendo desempenho semelhante ao fine-tuning tradicional de 16 bits (huggingface.co). Em prática, QLoRA tem sido usado para fazer fine-tuning de LLMs enormes sem necessitar de clusters de GPU.
Além disso, existem várias extensões e técnicas relacionadas:
- IA³ (Intrinsic Adaptations): Ajusta apenas vetores normais/escalares para cada cabeça de atenção. Extremamente eficiente em parâmetros, porém mais complexo de implementar.
- AdapterFusion, LoRA-V, LoKR, etc.: Variações de LoRA para tarefas específicas. Por exemplo, LoRA-V aplica o conceito em redes de visão, e LoKr usa produto de Kronecker em vez de matrizes tradicionais.
- Ferramentas de hardware-swino: Bibliotecas como
bitsandbyteseacceleratefacilitam treinamento zerando modelo em 8 bits ou usando CPU/TPU, aumentando escalabilidade.
A comunidade de pesquisa segue inovando nesse espaço, sempre buscando o equilíbrio entre desempenho, custo e adaptabilidade.
Conclusão
Ajustar grandes LLMs diretamente é uma tarefa árdua e custosa, mas PEFT e LoRA nos oferecem um caminho prático para tornar essa adaptação viável em recursos modestos. Com LoRA, somente adicionamos matrizes de baixo rank às camadas de atenção, treinando uma fração minúscula dos parâmetros e preservando o modelo base. Assim, obtemos resultados quase tão bons quanto o ajuste fino completo, porém com economia de memória e velocidade muito maiores (huggingface.co) (github.com). Em resumo, técnicas como o LoRA permitem que equipes menores e recursos limitados possam personalizar LLMs de última geração sem precisar reinventar o modelo inteiro.
Para próximos passos, vale explorar combinações adicionais como quantização (QLoRA), adapters multi-tarefa (AdapterFusion) e prompt tuning, dependendo da aplicação. Na prática, a sugestão é: comece testando com LoRA projetos pequenos, depois escale e ajuste hiperparâmetros. A biblioteca peft e a comunidade Hugging Face já disponibilizam diversos templates e notebooks. Em breve, veremos ainda mais recursos automatizados (e quem sabe LLMs ainda maiores!) tornando esse processo ainda mais amigável.
Em resumo: LoRA e PEFT democratizam o fine-tuning de LLMs, transformando o ajuste fino de uma proeza técnica em algo acessível. Com elas, você consegue “docar” novos skills no seu modelo gigante rapidamente e continuar no caminho da inovação em IA.
Referências: trabalhos e documentação usados neste artigo incluem os posts e repositórios oficiais do Hugging Face sobre PEFT e LoRA (huggingface.co) (github.com) (huggingface.co) (huggingface.co) (huggingface.co).
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!