LoRA (Adaptação de Baixa Ordem)
O que é LoRA?
LoRA (adaptação de baixo posto, Low-Rank Adaptation) é um método de ajuste fino eficiente em parâmetros (parameter-efficient fine-tuning, PEFT) que adapta redes neurais grandes (large neural networks)—especialmente modelos de Arquitetura Transformer—ao injetar pequenas matrizes treináveis de baixo posto em camadas de pesos selecionadas enquanto mantém os pesos do modelo original (“base”) congelados.
Em vez de atualizar bilhões de parâmetros durante o Ajuste Fino, o LoRA aprende um conjunto comparativamente pequeno de parâmetros (frequentemente <1% do modelo base). Isso torna o ajuste fino:
- Mais barato (menos memória de GPU e estado do otimizador (optimizer state))
- Mais rápido (menos parâmetros treináveis)
- Mais fácil de armazenar e compartilhar (checkpoints de adaptador pequenos)
- Menos destrutivo para as capacidades de base (pesos de base inalterados)
O LoRA é amplamente usado para:
- modelos de linguagem grandes (large language models, LLMs) (ajuste por instruções (instruction tuning), adaptação de domínio (domain adaptation), estilo/persona, uso de ferramentas)
- modelos de difusão / geração de imagens (diffusion/image generation models) (por exemplo, LoRAs de estilo/personagem/assunto para a UNet do Stable Diffusion)
Também é comumente combinado com quantização (quantization) (por exemplo, QLoRA) para reduzir ainda mais a memória.
Ideia central: aprender uma atualização de baixo posto em vez de mudar a matriz de pesos inteira
Considere uma camada linear (linear layer) com matriz de pesos (W \in \mathbb{R}^{d_{\text{out}} \times d_{\text{in}}}). No ajuste fino completo, você atualizaria todas as entradas de (W).
O LoRA mantém (W) congelada e aprende uma atualização (\Delta W) restrita a ter baixo posto:
[ W' = W + \Delta W ] [ \Delta W = \frac{\alpha}{r} , B A ]
Onde:
- (A \in \mathbb{R}^{r \times d_{\text{in}}})
- (B \in \mathbb{R}^{d_{\text{out}} \times r})
- (r) é o posto (rank) (um número pequeno como 4, 8, 16, 32)
- (\alpha) é um fator de escala (scaling factor) (o “alpha” do LoRA)
Como (\Delta W) é fatorada em duas matrizes “finas”, o número de parâmetros treináveis se torna:
[ \text{params}(\Delta W) = r(d_{\text{in}} + d_{\text{out}}) ]
Em vez de (d_{\text{in}} d_{\text{out}}). Para camadas grandes, isso é uma redução drástica.
Intuição: por que baixo posto?
Muitas tarefas de adaptação (mudança de domínio (domain shift), seguimento de instruções, transferência de estilo (style transfer)) não exigem mudanças arbitrárias em todas as dimensões dos pesos. Empiricamente, a atualização “útil” muitas vezes reside em um subespaço de baixa dimensionalidade—o LoRA codifica essa suposição ao só permitir atualizações que possam ser expressas como uma matriz de posto-(r).
Um modelo mental útil:
- Ajuste fino completo: “Posso mover os pesos da camada para qualquer lugar em um espaço enorme.”
- LoRA: “Posso mover os pesos da camada, mas apenas ao longo de (r) direções aprendidas.”
Onde o LoRA é aplicado em modelos modernos
LoRA em modelos de linguagem grandes (Transformers)
Em Transformers, o LoRA é mais comumente acoplado a matrizes de projeção de atenção (attention) e, às vezes, às de rede MLP (multilayer perceptron, MLP).
Módulos-alvo (target modules) típicos incluem:
- Projeções de atenção: (W_q, W_k, W_v, W_o)
- Às vezes apenas (W_q) e (W_v) (um padrão comum)
- Projeções da MLP (varia por arquitetura): matrizes “up”, “down”, “gate”
O LoRA é popular para:
- Ajuste Fino Supervisionado (ajuste por instruções)
- Adaptação de domínio (jurídico, médico, jargão específico de base de código)
- Ajuste de estilo/persona
- Configurações com múltiplos adaptadores (trocar adaptadores por tarefa)
LoRA em modelos de difusão / geração de imagens
Em modelos de difusão (por exemplo, Stable Diffusion), o LoRA comumente tem como alvo:
- camadas de atenção da UNet (atenção cruzada (cross-attention) e autoatenção (self-attention))
- Às vezes a atenção/MLP do codificador de texto (text encoder) (para mudanças na interpretação de prompts)
- Ocasionalmente blocos convolucionais (convolutional blocks) (menos comum; depende das ferramentas)
Usos práticos:
- Aprender um estilo (por exemplo, “aquarela”, “iluminação film noir”)
- Aprender um personagem/assunto (um rosto/criatura/objeto específico)
- Aprender padrões de composição ou o “visual” de um conjunto de dados
No ecossistema de difusão, o LoRA é popular porque produz arquivos pequenos que são fáceis de misturar, compartilhar e aplicar na inferência (inference).
Como o LoRA funciona durante a passagem direta
Para a saída de uma camada linear:
[ y = Wx ]
O LoRA modifica para:
[ y = Wx + \frac{\alpha}{r} B(Ax) ]
Pontos-chave:
- A base (W) não muda e pode ser quantizada ou armazenada em menor precisão.
- Apenas (A) e (B) recebem gradientes.
- O custo computacional extra costuma ser pequeno porque (r) é pequeno.
Mesclando LoRA para inferência
Uma propriedade útil: após o treinamento, você pode mesclar (merge) o LoRA no peso base para implantação (deployment):
[ W_{\text{merged}} = W + \frac{\alpha}{r}BA ]
Isso produz um modelo padrão sem módulos de adaptador extras (embora você perca a capacidade de trocar LoRAs, a menos que os mantenha separados).
Em ferramentas de difusão, as pessoas frequentemente:
- Mantêm os LoRAs separados e os aplicam com um multiplicador (multiplier) em tempo de execução (runtime) (por exemplo, 0.6–1.2)
- Ou mesclam em um checkpoint por conveniência
Principais hiperparâmetros (e como escolhê-los)
Posto \(r\)
O posto controla a capacidade de adaptação e a contagem de parâmetros.
- Posto baixo (por exemplo, 2–8): menor, mais rápido, menos sobreajuste (overfitting), às vezes subajusta (underfits) tarefas difíceis
- Posto médio (por exemplo, 16–32): um “ponto doce” comum para muitas tarefas de modelos de linguagem grandes e de difusão
- Posto alto (64+): mais capacidade, mais memória/compute, pode se aproximar da qualidade do ajuste fino completo em alguns casos
Heurísticas práticas:
- Ajuste por instruções em modelos de linguagem grandes: r = 8–32 costuma funcionar bem
- Adaptação de domínio com mudança significativa: r = 16–64 pode ajudar
- LoRA de estilo em difusão: r = 4–16 é comum; LoRAs de assunto/personagem podem se beneficiar de 16–32
Alpha \(\alpha\) (escala do LoRA)
O LoRA normalmente usa a escala (\alpha/r). Aumentar (\alpha) aumenta a magnitude efetiva da atualização aprendida.
Regras gerais:
- Muitas configurações usam (\alpha = r) (então a escala é 1)
- Se o treinamento estiver instável ou “sobrepor” o modelo base, reduza (\alpha) (ou reduza a taxa de aprendizado)
- Se o efeito do adaptador parecer fraco demais, aumente (\alpha) ou o posto
Em UIs de difusão, você frequentemente verá um multiplicador de inferência separado (às vezes chamado de “strength” ou “weight”) que escala o efeito do adaptador em tempo de execução. Conceitualmente isso é parecido, mas não idêntico, a (\alpha).
Módulos-alvo (onde você injeta LoRA)
Essa escolha pode ser tão importante quanto o posto.
Escolhas comuns em modelos de linguagem grandes:
- Apenas q_proj, v_proj: eficiente e frequentemente forte
- q_proj, k_proj, v_proj, o_proj: mais capacidade
- Adicionar projeções da MLP para mudanças mais difíceis (mas mais parâmetros)
Escolhas comuns em difusão:
- Blocos de atenção cruzada da UNet (um padrão forte)
- Opcionalmente incluir o codificador de texto se você precisar de mudanças na interpretação do prompt
Dropout do LoRA
Algumas implementações incluem dropout do LoRA (LoRA dropout) no caminho do adaptador. Isso pode ajudar a regularizar conjuntos de dados pequenos, especialmente para LoRAs de assunto em difusão ou corpora de domínio muito restrito.
Valores típicos: 0.0 a 0.1.
Taxa de aprendizado e otimizador
Como apenas os parâmetros do adaptador treinam, o LoRA frequentemente tolera uma taxa de aprendizado (learning rate) mais alta do que o ajuste fino completo (mas isso depende da qualidade dos dados e da tarefa).
Padrões típicos:
- LoRA em modelos de linguagem grandes: LR em torno de 1e-4 a 5e-4 (muito dependente de modelo/dados)
- LoRA em difusão: frequentemente 1e-4 a 1e-5 dependendo do tamanho do batch e do conjunto de dados
O LoRA reduz a memória do otimizador (optimizer) porque você só armazena o estado do otimizador para os pesos do adaptador—não para todos os pesos de base.
Fluxo de trabalho prático de treinamento
Um fluxo de trabalho típico de ajuste fino com LoRA é:
- Carregar o modelo base pré-treinado (pesos congelados)
- Inserir módulos LoRA em camadas selecionadas
- Treinar apenas os parâmetros do LoRA (e às vezes layer norms ou vieses, dependendo da variante)
- Salvar os pesos do adaptador (checkpoint pequeno)
- Para inferência:
- Carregar base + adaptador(es), ou
- Mesclar o adaptador nos pesos base
Isso é comumente combinado com:
- Quantização do modelo base (por exemplo, 4-bit) para caber em GPUs menores
- Precisão mista (mixed precision) (fp16/bf16) para velocidade e memória
Exemplo: ajuste fino com LoRA de um modelo de linguagem grande com Hugging Face PEFT (conceitual)
Abaixo está um exemplo representativo usando APIs no estilo da biblioteca PEFT (os nomes exatos variam entre versões, mas a estrutura é estável):
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import LoraConfig, get_peft_model
model_name = "meta-llama/Llama-2-7b-hf" # example
tokenizer = AutoTokenizer.from_pretrained(model_name)
base_model = AutoModelForCausalLM.from_pretrained(
model_name,
torch_dtype="auto",
device_map="auto",
)
lora_config = LoraConfig(
r=16,
lora_alpha=32,
target_modules=["q_proj", "v_proj"],
lora_dropout=0.05,
bias="none",
task_type="CAUSAL_LM",
)
model = get_peft_model(base_model, lora_config)
# Now train `model` with your SFT loop / Trainer
# Only LoRA parameters will have requires_grad=True
model.save_pretrained("my-lora-adapter")
tokenizer.save_pretrained("my-lora-adapter")
Notas práticas:
- Comece mirando em q_proj/v_proj antes de expandir para mais módulos.
- Se você observar subajuste, aumente o posto ou mire em mais módulos.
- Se as saídas ficarem estranhas ou excessivamente “estilizadas”, reduza LR, posto, alpha ou passos de treino.
Exemplo: LoRA em difusão (estilo Stable Diffusion) no treinamento e na inferência
Em fluxos de trabalho de difusão, um LoRA frequentemente é treinado contra as camadas de atenção da UNet para capturar estilo ou aparência de um assunto. Na inferência, usuários tipicamente aplicam com um peso:
- “Aplicar LoRA de estilo em 0.7”
- “Aplicar LoRA de personagem em 0.9”
- Misturar múltiplos LoRAs (por exemplo, 0.5 estilo + 0.8 assunto)
Conceitualmente:
pipe = load_base_diffusion_pipeline(...)
pipe.load_lora_weights("my_style_lora.safetensors")
pipe.fuse_lora(lora_scale=0.7) # apply at chosen strength
image = pipe(prompt="portrait photo, cinematic lighting").images[0]
Notas práticas:
- LoRAs de estilo frequentemente toleram postos menores.
- LoRAs de assunto/personagem são sensíveis à curadoria do dataset e às legendas; sobreajuste é comum.
- Muitas cadeias de ferramentas (toolchains) suportam segmentação por bloco (per-block targeting) e variantes avançadas, mas um LoRA básico de atenção da UNet é uma baseline forte.
Tradeoffs: LoRA vs ajuste fino completo
Vantagens do LoRA
- Memória de treinamento muito menor
- Você não armazena o estado do otimizador para todos os pesos de base.
- Artefatos menores
- Compartilhar um adaptador de 5–200 MB é mais fácil do que um checkpoint de múltiplos GB.
- Modularidade
- Manter um modelo base e muitos adaptadores (por cliente, domínio, estilo).
- Risco reduzido de esquecimento catastrófico (catastrophic forgetting)
- Como os pesos de base estão congelados, as capacidades centrais são preservadas de forma mais confiável (embora as saídas ainda mudem).
Desvantagens / limitações
- Teto mais baixo em algumas tarefas
- Algumas tarefas realmente se beneficiam de atualizar muitos pesos (especialmente mudanças profundas de distribuição (distribution shifts)).
- Sensibilidade ao módulo-alvo
- A escolha de onde acoplar o LoRA importa; uma mira subótima pode ter desempenho inferior.
- Ajuste do posto
- Posto baixo demais subajusta; posto alto demais aumenta custo e pode sobreajustar.
- Interações entre adaptadores
- Combinar múltiplos LoRAs pode produzir resultados imprevisíveis (especialmente em difusão), exigindo escalonamento cuidadoso.
Quando o ajuste fino completo ainda é preferível
O ajuste fino completo pode valer a pena quando:
- Você tem um dataset grande e de alta qualidade e quer o melhor desempenho possível
- Você precisa de mudanças comportamentais profundas que o LoRA não consegue capturar com um posto razoável
- Você está treinando um modelo para uma única implantação fixa (sem necessidade de adaptadores modulares)
Ainda assim, muitos sistemas do mundo real usam LoRA como padrão, porque iterar é muito mais barato.
LoRA vs outros métodos de ajuste fino eficiente em parâmetros
O LoRA faz parte de uma família mais ampla de técnicas de Ajuste Fino Eficiente em Parâmetros. Comparações comuns:
Adaptadores (módulos MLP de gargalo, bottleneck MLP modules)
Adaptadores (adapters) inserem pequenos blocos neurais entre camadas. Assim como o LoRA, eles são modulares e eficientes.
- Prós: boa capacidade, não se limitam a atualizações lineares de baixo posto
- Contras: podem adicionar latência de inferência (inference latency) porque inserem camadas extras (nem sempre são facilmente mescláveis)
Ajuste de prompt / ajuste de prefixo
Esses métodos aprendem “prompts suaves (soft prompts)” treináveis ou “vetores de prefixo (prefix vectors)” que direcionam o modelo sem alterar pesos internos.
- Prós: eficiência extrema em parâmetros; simples de armazenar
- Contras: às vezes mais fracos do que LoRA para tarefas que exigem mudanças mais profundas; podem ser sensíveis ao comprimento e ao posicionamento do prompt
IA³ (adaptador infundido ao inibir e amplificar ativações internas, Infused Adapter by Inhibiting and Amplifying Inner Activations)
IA³ aprende vetores de gateamento (gating vectors) multiplicativos sobre as ativações (activations).
- Prós: contagem de parâmetros muito pequena
- Contras: pode ser menos expressivo do que LoRA em tarefas desafiadoras
BitFit (ajuste fino apenas de vieses, bias-only fine-tuning)
Treina apenas parâmetros de viés (bias).
- Prós: contagem de parâmetros minúscula
- Contras: capacidade limitada; frequentemente tem desempenho inferior ao LoRA
Na prática, o LoRA é frequentemente preferido como um equilíbrio forte de: qualidade ↔ eficiência ↔ facilidade de implantação.
Orientação prática e armadilhas comuns
Escolhendo alvos em modelos de linguagem grandes
Uma receita inicial sólida:
- target_modules:
["q_proj", "v_proj"] - r: 8 ou 16
- alpha: 16 ou 32
- dropout: 0.0–0.05
Se os resultados forem fracos:
- Adicione
k_projeo_proj - Aumente o posto para 32
- Considere adicionar projeções da MLP para mudanças fortes de domínio
Qualidade dos dados importa mais do que o posto
O LoRA vai memorizar alegremente datasets pequenos se legendas/instruções forem ruidosas. Problemas comuns:
- LoRAs de assunto em difusão treinados com poucas imagens quase duplicadas → sobreajuste, generalização ruim
- LoRAs de instruções treinados com pares instrução/resposta de baixa qualidade ou inconsistentes → queda de utilidade
Cuidado com o “adaptador sobrepondo o modelo”
Sintomas:
- O modelo de linguagem grande fica excessivamente estilizado, repetitivo ou recusa tarefas não relacionadas
- Saídas de difusão colapsam para o estilo do LoRA mesmo com prompts não relacionados
Mitigações:
- Reduzir passos de treino
- Diminuir a taxa de aprendizado
- Diminuir alpha ou o posto
- Usar dropout
- Usar uma força de inferência menor (difusão)
Mesclagem e preocupações com precisão
Se você mesclar o LoRA em uma base quantizada, os resultados podem diferir de manter adaptadores separados (por efeitos de quantização). Muitas implantações:
- Mantêm a base quantizada
- Aplicam adaptadores em tempo de execução (especialmente para sistemas multitenant (multi-tenant systems))
Variantes e extensões (breve)
O LoRA inspirou um grande ecossistema de refinamentos. Você pode encontrar:
- QLoRA: LoRA sobre um modelo base quantizado em 4 bits (reduz a memória drasticamente)
- AdaLoRA: adapta a alocação de posto durante o treinamento (mais capacidade onde necessário)
- DoRA: decompõe atualizações para controlar melhor magnitude/direção (pode melhorar a qualidade em algumas tarefas)
- Variantes focadas em difusão: parametrizações alternativas de baixo posto e controle por bloco
Elas se baseiam na mesma ideia central: adaptar modelos grandes com componentes pequenos e treináveis.
Resumo
O LoRA é um método amplamente usado e prático para adaptar modelos grandes de forma eficiente ao aprender atualizações de baixo posto para matrizes de pesos selecionadas, mantendo a base pré-treinada congelada. Ele se tornou uma abordagem padrão para ajuste fino tanto de modelos de linguagem grandes quanto de modelos de difusão / geração de imagens devido ao seu bom desempenho, baixo custo e implantação modular.
Principais conclusões:
- O LoRA aprende (\Delta W = \frac{\alpha}{r}BA) em vez de atualizar (W) diretamente.
- Os principais controles são posto (r), alpha e módulos-alvo.
- Em geral, é mais barato e mais fácil do que ajuste fino completo, com apenas um tradeoff modesto de qualidade em muitas tarefas.
- Integra-se bem ao ferramental moderno, incluindo treinamento/inferência quantizados e composição de adaptadores.