O ajuste fino completo de um modelo de 7B requer 56GB de VRAM. Você não tem isso. A maioria das empresas também não. O LoRA permite que você faça o ajuste fino do mesmo modelo em 6GB, treinando menos de 1% dos parâmetros. Isso não é um meio-termo — ele iguala a qualidade do ajuste fino completo na maioria das tarefas. Todo o ecossistema de ajuste fino de código aberto funciona com base nesse único truque.
Tipo: Build
Idiomas: Python
Pré-requisitos: Fase 10, Lição 06 (Instruction Tuning / SFT)
Tempo: ~75 minutos
Relacionado: A Fase 10 cobre os loops SFT/DPO do zero. Esta lição integra-os com os toolkits de PEFT de 2026 (PEFT, TRL, Unsloth, Axolotl, LLaMA-Factory).
Objetivos de Aprendizado
Implementar o LoRA injetando matrizes adaptadoras de baixo posto (A e B) nas camadas de atenção de um modelo pré-treinado
Calcular a economia de parâmetros do LoRA vs ajuste fino completo: rank r com dimensões d_model treina 2rd parâmetros em vez de d^2
Fazer o ajuste fino de um modelo usando QLoRA (base quantizada em 4 bits + adaptadores LoRA) para caber na memória de GPU de consumo
Mesclar os pesos do LoRA de volta no modelo base para implantação e comparar a velocidade de inferência com e sem adaptadores
O Problema
Você tem um modelo base. Llama 3 8B. Você quer que ele responda a chamados de suporte ao cliente no tom de voz da sua empresa. O SFT é a solução. Mas o SFT tem um problema de custo.
O ajuste fino completo atualiza todos os parâmetros do modelo. O Llama 3 8B possui 8 bilhões de parâmetros. Em fp16, cada parâmetro ocupa 2 bytes. Isso dá 16GB apenas para carregar os pesos. Durante o treinamento, você também precisa de gradientes (16GB), estados do otimizador para Adam (32GB para momentum + variância) e ativações. Total: aproximadamente 56GB de VRAM para um único modelo 8B.
Uma A100 80GB mal consegue acomodar isso. Duas A100s custam $3-4/hora em provedores de nuvem. O treinamento por 3 épocas em 50.000 exemplos leva de 6 a 10 horas. Isso representa $30-40 por experimento. Execute 10 experimentos para acertar os hiperparâmetros e você terá gasto $400 antes de implantar qualquer coisa.
Dimensione isso para o Llama 3 70B e os números ficam absurdos. 140GB apenas para os pesos. Você precisa de um cluster. Mais de
00 por experimento.
Há também um problema mais profundo. O ajuste fino completo modifica todos os pesos do modelo. Se você fizer o ajuste fino em dados de suporte ao cliente, poderá degradar as capacidades gerais do modelo. Isso é chamado de esquecimento catastrófico (catastrophic forgetting). O modelo fica melhor na sua tarefa e pior em tudo o resto.
Você precisa de um método que treine menos parâmetros, use menos memória e não destrua o conhecimento existente do modelo.
O Conceito
LoRA: Low-Rank Adaptation
Edward Hu e seus colegas na Microsoft publicaram o LoRA em junho de 2021. O insight do artigo: as atualizações de peso durante o ajuste fino têm posto intrínseco baixo. Você não precisa atualizar todos os 16,7 milhões de parâmetros em uma matriz de pesos de 4096x4096. A informação útil na atualização pode ser capturada por uma matriz de posto 16 ou 32.
Aqui está a matemática. Uma camada linear padrão calcula:
y = Wx
Onde W é uma matriz d_out x d_in. Para uma projeção de atenção de 4096x4096, são 16.777.216 parâmetros.
O LoRA congela W e adiciona uma decomposição de baixo posto:
y = Wx + BAx
Onde B é (d_out x r) e A é (r x d_in). O posto (rank) r é muito menor que d — tipicamente 8, 16 ou 32.
Para r=16 em uma camada 4096x4096:
Parâmetros originais: 4096 x 4096 = 16.777.216
Parâmetros do LoRA: (4096 x 16) + (16 x 4096) = 65.536 + 65.536 = 131.072
Redução: 131.072 / 16.777.216 = 0,78%
Você está treinando 0,78% dos parâmetros e obtendo de 95% a 100% da qualidade.
graph LR
X["Entrada x"] --> W["W Congelado (d x d)"]
X --> A["A (r x d)"]
A --> B["B (d x r)"]
W --> Plus["+ (mesclar)"]
B --> Plus
Plus --> Y["Saída y"]
style W fill:#1a1a2e,stroke:#e94560,color:#fff
style A fill:#0f3460,stroke:#16213e,color:#fff
style B fill:#0f3460,stroke:#16213e,color:#fff
A é inicializado com uma Gaussiana aleatória. B é inicializado como zero. Isso significa que a contribuição do LoRA começa em zero — o modelo inicia o treinamento a partir do seu comportamento original e gradualmente aprende a adaptação.
O Fator de Escala: Alpha
O LoRA introduz um fator de escala alpha que controla o quanto a atualização de baixo posto afeta a saída:
y = Wx + (alpha / r) * BAx
Quando alpha = r, a escala é de 1x. Quando alpha = 2r (o padrão comum), a escala é de 2x. Esse hiperparametro controla a taxa de aprendizado do caminho do LoRA independentemente da taxa de aprendizado base.
Diretrizes práticas:
alpha = 2 * rank é uma convenção comum da comunidade (o artigo original usou alpha = rank na maioria dos experimentos)
alpha = rank fornece escala de 1x, conservadora mas estável
Um alpha mais alto significa atualizações maiores por passo, o que pode acelerar a convergência ou causar instabilidade
Onde Aplicar o LoRA
Um transformer possui muitas camadas lineares. Você não precisa adicionar o LoRA a todas elas. O artigo original testou diferentes combinações:
Camadas Alvo
Parâmetros Treináveis (7B)
Qualidade
apenas q_proj
4.7M
Boa
q_proj + v_proj
9.4M
Melhor
q_proj + k_proj + v_proj + o_proj
18.9M
Melhor para atenção
Todas lineares (atenção + MLP)
37.7M
Ganho marginal, 2x parâmetros
O ponto ideal para a maioria das tarefas: q_proj + v_proj. Isso visa as projeções de consulta (query) e valor (value) na auto-atenção, que controlam para onde o modelo direciona a atenção e quais informações ele extrai. Adicionar camadas MLP ajuda em tarefas complexas, como geração de código, mas dobra a contagem de parâmetros para retornos decrescentes em tarefas mais simples.
Seleção de Posto (Rank)
O posto r controla a expressividade da adaptação:
Posto (Rank)
Parâmetros Treináveis (por camada)
Ideal Para
4
32,768
Classificação simples, sentimento
8
65,536
P&R de domínio único, sumarização
16
131,072
Tarefas de múltiplos domínios, seguimento de instruções
32
262,144
Raciocínio complexo, geração de código
64
524,288
Retornos decrescentes para a maioria das tarefas
128
1,048,576
Raramente justificado
Hu et al. mostraram que r=4 já captura a maior parte da adaptação para tarefas simples. r=8 e r=16 são as escolhas mais comuns na prática. Ir além de r=64 raramente melhora a qualidade e começa a perder a vantagem de memória do LoRA.
QLoRA: Quantização de 4 Bits + LoRA
Tim Dettmers e seus colegas na Universidade de Washington publicaram o QLoRA em maio de 2023. A ideia: quantizar o modelo base congelado para a precisão de 4 bits e, em seguida, adicionar adaptadores LoRA em fp16 por cima.
Isso altera a equação de memória drasticamente:
Método
Memória dos Pesos (7B)
Memória de Treinamento (7B)
GPU Necessária
Ajuste fino completo (fp16)
14GB
~56GB
1x A100 80GB
LoRA (base fp16)
14GB
~18GB
1x A100 40GB
QLoRA (base 4 bits)
3.5GB
~6GB
1x RTX 3090 24GB
O QLoRA traz três contribuições técnicas:
NF4 (Normal Float 4-bit): Um novo tipo de dados projetado especificamente para pesos de redes neurais. Os pesos de redes neurais seguem uma distribuição aproximadamente normal. O NF4 posiciona seus 16 níveis de quantização nos quantis de uma distribuição normal padrão. Isso é ideal do ponto de vista da teoria da informação para dados distribuídos normalmente. Ele perde menos informação do que a quantização uniforme de 4 bits (INT4) ou Float4 padrão.
Double quantization (Quantização dupla): As próprias constantes de quantização ocupam memória. Cada bloco de 64 pesos precisa de um fator de escala fp32 (4 bytes). Para um modelo de 7B, isso representa 0,4GB extras. A quantização dupla quantiza essas constantes para fp8, reduzindo o overhead para 0,1GB. É pouco, mas faz diferença no total.
Paged optimizers (Otimizadores paginados): Durante o treinamento, os estados do otimizador (momentum e variância do Adam) podem exceder a memória da GPU em sequências longas. Os otimizadores paginados usam a memória unificada da NVIDIA para paginar automaticamente os estados do otimizador para a memória RAM da CPU quando a memória da GPU estiver esgotada, e trazê-los de volta quando necessário. Isso evita travamentos por falta de memória (OOM), ao custo de alguma taxa de processamento (throughput).
A Questão da Qualidade
Reduzir parâmetros ou quantizar a base prejudica a qualidade? Os resultados de múltiplos artigos:
Método
MMLU (5-shot)
MT-Bench
HumanEval
Ajuste fino completo (Llama 2 7B)
48.3
6.72
14.6
LoRA r=16
47.9
6.68
14.0
QLoRA r=16 (NF4)
47.5
6.61
13.4
QLoRA r=64 (NF4)
48.1
6.70
14.2
O LoRA com r=16 fica a menos de 1% do ajuste fino completo na maioria dos benchmarks. O QLoRA com r=16 perde mais uma fração de percentual. O QLoRA com r=64 essencialmente iguala o ajuste fino completo enquanto usa 90% menos memória.
Custos no Mundo Real
Ajuste fino do Llama 3 8B em 50.000 exemplos (3 épocas):
Método
GPU
Tempo
Custo
Ajuste fino completo
2x A100 80GB
8 horas
~$32
LoRA r=16
1x A100 40GB
4 horas
~$8
QLoRA r=16
1x RTX 4090 24GB
6 horas
~$5
QLoRA r=16 (Unsloth)
1x RTX 4090 24GB
2.5 horas
~
QLoRA r=16
1x T4 16GB
12 hours
~$4
O QLoRA em uma única GPU de consumo custa menos que um almoço. É por isso que a comunidade de ajuste fino de pesos abertos explodiu em 2023 e por que cada framework de treinamento abaixo vem com suporte ao QLoRA por padrão em 2026.
O ecossistema de PEFT em 2026
Framework
O que é
Escolha quando
Hugging Face PEFT
A biblioteca canônica para LoRA/QLoRA/DoRA/IA3
Você quer controle bruto e seu loop de treinamento já está no transformers.Trainer
TRL
Treinadores de aprendizado por reforço baseados em feedback da HF (SFT, DPO, GRPO, PPO, ORPO)
Você precisa de DPO/GRPO após SFT; construído sobre o PEFT
Unsloth
Reescrita em kernel Triton das passagens forward/backward
Você quer aceleração de 2-5x + metade da VRAM sem perda de precisão; famílias Llama/Mistral/Qwen
Axolotl
Wrapper de configuração YAML sobre PEFT + TRL + DeepSpeed + Unsloth
Você quer execuções de treinamento reproduzíveis e com controle de versão
LLaMA-Factory
GUI/CLI/API sobre PEFT + TRL
Você deseja ajuste fino sem código; mais de 100 famílias de modelos suportadas
torchtune
Receitas PyTorch nativas, sem dependência de transformers
Você quer o mínimo de dependências e sua organização já padroniza no PyTorch
Regra geral: pesquisa ou experimento único → PEFT. Pipeline de produção repetível → Axolotl com kernels Unsloth habilitados. Prototipagem rápida e descartável → LLaMA-Factory.
Mesclando Adaptadores
Após o treinamento, você tem duas coisas: o modelo base congelado e um pequeno adaptador LoRA (tipicamente 10-100MB). Você pode:
Mantê-los separados: Carregar o modelo base e carregar o adaptador por cima. Trocar adaptadores para tarefas diferentes. É assim que você serve múltiplas variantes com ajuste fino a partir de um único modelo base.
Mesclá-los permanentemente: Calcular W' = W + (alpha/r) * BA e salvar o resultado como um novo modelo completo. O modelo mesclado tem o mesmo tamanho do original. Sem overhead de inferência. Sem adaptador para gerenciar.
Para atender a múltiplas tarefas (adaptador de suporte ao cliente, adaptador de código, adaptador de tradução), mantenha-os separados. Para implantar um único modelo especializado, mescle-os.
Técnicas avançadas de mesclagem para combinar múltiplos adaptadores:
TIES-Merging (Yadav et al. 2023): Remove parâmetros de pequena magnitude, resolve conflitos de sinal e então faz a mesclagem. Reduz a interferência entre adaptadores.
DARE (Yu et al. 2023): Descarta aleatoriamente parâmetros do adaptador antes de mesclar e redimensiona o restante. Surpreendentemente eficaz para combinar capacidades.
Aritmética de tarefas (Task arithmetic): Simplesmente somar ou subtrair pesos de adaptadores. Somar um adaptador de "código" e um adaptador de "matemática" frequentemente gera um modelo bom em ambos.
Quando NÃO Fazer Ajuste Fino
O ajuste fino é a terceira opção, não a primeira.
Primeiro: engenharia de prompt (prompt engineering). Escreva um prompt de sistema melhor. Adicione exemplos de poucos disparos (few-shot). Use cadeia de pensamento (chain-of-thought). Isso não custa nada e leva minutos. Se o prompt levar você a 80% do caminho, você provavelmente não precisa de ajuste fino.
Segundo: RAG. Se o modelo precisar conhecer seus dados específicos (documentos, base de conhecimento, catálogo de produtos), a recuperação é mais barata e mais fácil de manter do que gravar essas informações nos pesos do modelo. Veja a Lição 06.
Terceiro: ajuste fino. Use isso quando você precisar que o modelo adote um estilo, formato ou padrão de raciocínio específico que não possa ser alcançado por meio de prompts. Quando precisar de saída estruturada de forma consistente. Quando precisar destilar um modelo maior em um menor. Quando a latência for importante e você não puder arcar com o custo de tokens extras de prompts few-shot.
graph TD
Start["Precisa de melhor comportamento do modelo?"] --> PE["Tente engenharia de prompt"]
PE -->|"Funciona"| Done["Publique-o"]
PE -->|"Não é suficiente"| RAG["Precisa de conhecimento externo?"]
RAG -->|"Sim"| RAGBuild["Construa pipeline RAG"]
RAG -->|"Não, preciso mudar estilo/formato"| FT["Ajuste fino com LoRA/QLoRA"]
RAGBuild -->|"Funciona"| Done
RAGBuild -->|"Também preciso mudar estilo"| FT
FT --> Done
style Start fill:#1a1a2e,stroke:#e94560,color:#fff
style Done fill:#0f3460,stroke:#16213e,color:#fff
Construa
Implementamos o LoRA do zero em PyTorch puro. Sem bibliotecas. Sem mágica. Você construirá a camada LoRA, a injetará em um modelo, o treinará e mesclará os pesos de volta.
A é inicializado com valores aleatórios escalados. B é inicializado como zero. O produto BA começa em zero, então o modelo inicia com seu comportamento original.
Passo 2: Camada Linear Envelopada com LoRA
class LinearWithLoRA(nn.Module):
def __init__(self, linear, rank=8, alpha=16):
super().__init__()
self.linear = linear
self.lora = LoRALayer(
linear.in_features, linear.out_features, rank, alpha
)
for param in self.linear.parameters():
param.requires_grad = False
def forward(self, x):
return self.linear(x) + self.lora(x)
A camada linear original é congelada. Apenas os parâmetros do LoRA (A e B) são treináveis.
Passo 3: Injetar LoRA em um Modelo
def inject_lora(model, target_modules, rank=8, alpha=16):
for param in model.parameters():
param.requires_grad = False
lora_layers = {}
for name, module in model.named_modules():
if isinstance(module, nn.Linear):
if any(t in name for t in target_modules):
parent_name = ".".join(name.split(".")[:-1])
child_name = name.split(".")[-1]
parent = dict(model.named_modules())[parent_name]
lora_linear = LinearWithLoRA(module, rank, alpha)
setattr(parent, child_name, lora_linear)
lora_layers[name] = lora_linear
return lora_layers
Primeiro, congele todos os parâmetros do modelo. Em seguida, percorra a árvore do modelo, encontre as camadas lineares correspondentes aos seus nomes de destino e substitua-as por versões envelopadas com LoRA. As matrizes LoRA A e B são os únicos parâmetros treináveis em todo o modelo.
Passo 4: Contar Parâmetros
def count_parameters(model):
total = sum(p.numel() for p in model.parameters())
trainable = sum(p.numel() for p in model.parameters() if p.requires_grad)
frozen = total - trainable
return {
"total": total,
"trainable": trainable,
"frozen": frozen,
"trainable_pct": 100 * trainable / total if total > 0 else 0
}
Passo 5: Mesclar Pesos de Volta
def merge_lora_weights(model):
for name, module in model.named_modules():
if isinstance(module, LinearWithLoRA):
with torch.no_grad():
merged = (
module.lora.A @ module.lora.B
) * module.lora.scaling
module.linear.weight.data += merged.T
parent_name = ".".join(name.split(".")[:-1])
child_name = name.split(".")[-1]
if parent_name:
parent = dict(model.named_modules())[parent_name]
else:
parent = model
setattr(parent, child_name, module.linear)
Após a mesclagem, as camadas LoRA desaparecem. O modelo passa a ter o mesmo tamanho do original, com a adaptação gravada diretamente nos pesos. Sem overhead de inferência.
Isso simula a quantização de 4 bits mapeando os pesos em 16 níveis discretos dentro de blocos de 64. O QLoRA em produção utiliza a biblioteca bitsandbytes para obter NF4 real em GPU.
Passo 7: Loop de Treinamento
def train_lora(model, data, epochs=5, lr=1e-3, batch_size=4):
optimizer = torch.optim.AdamW(
[p for p in model.parameters() if p.requires_grad], lr=lr
)
criterion = nn.MSELoss()
losses = []
for epoch in range(epochs):
epoch_loss = 0.0
n_batches = 0
indices = torch.randperm(len(data["inputs"]))
for i in range(0, len(indices), batch_size):
batch_idx = indices[i:i + batch_size]
x = data["inputs"][batch_idx]
y = data["targets"][batch_idx]
output = model(x)
loss = criterion(output, y)
optimizer.zero_grad()
loss.backward()
optimizer.step()
epoch_loss += loss.item()
n_batches += 1
avg_loss = epoch_loss / n_batches
losses.append(avg_loss)
return losses
A demonstração cria um modelo pequeno, injeta o LoRA em duas camadas, treina-o e mescla os pesos de volta. A contagem de parâmetros cai de totalmente treinável para aproximadamente 1% treinável durante o treinamento do LoRA, e depois retorna à arquitetura original após a mesclagem.
Use
Com o ecossistema da Hugging Face, aplicar o LoRA em um modelo real leva cerca de 20 linhas:
from transformers import AutoModelForCausalLM, AutoTokenizer
from peft import LoraConfig, get_peft_model, TaskType
model = AutoModelForCausalLM.from_pretrained("meta-llama/Llama-3.1-8B")
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-3.1-8B")
lora_config = LoraConfig(
task_type=TaskType.CAUSAL_LM,
r=16,
lora_alpha=32,
lora_dropout=0.05,
target_modules=["q_proj", "v_proj"],
)
model = get_peft_model(model, lora_config)
model.print_trainable_parameters()
Para o QLoRA, adicione a quantização do bitsandbytes:
from transformers import BitsAndBytesConfig
bnb_config = BitsAndBytesConfig(
load_in_4bit=True,
bnb_4bit_quant_type="nf4",
bnb_4bit_compute_dtype=torch.bfloat16,
bnb_4bit_use_double_quant=True,
)
model = AutoModelForCausalLM.from_pretrained(
"meta-llama/Llama-3.1-8B",
quantization_config=bnb_config,
device_map="auto",
)
model = get_peft_model(model, lora_config)
É isso. Mesmo loop de treinamento. Mesmo pipeline de dados. O modelo base agora vive em 4 bits, os adaptadores LoRA são treinados em fp16 e todo o conjunto cabe em 6GB.
O adaptador salvo possui entre 10 e 100MB. O modelo base permanece inalterado. Você pode compartilhar adaptadores no Hugging Face Hub sem redistribuir o modelo completo.
Publique
Esta lição produz:
outputs/prompt-lora-advisor.md — um prompt que ajuda a decidir o posto (rank) do LoRA, módulos de destino e hiperparâmetros para sua tarefa específica
outputs/skill-fine-tuning-guide.md — uma habilidade que ensina aos agentes a árvore de decisão de quando e como fazer o ajuste fino
Exercícios
Estudo de ablação do posto (Rank ablation study). Execute a demonstração com os postos 2, 4, 8, 16, 32 e 64. Plote a perda final vs. posto. Encontre o ponto de retornos decrescentes onde dobrar o posto não reduz mais a perda pela metade. Para uma tarefa de classificação simples em recursos de 256 dimensões, isso deve ser em torno de r=8-16.
Comparação de módulos de destino. Modifique inject_lora para segmentar apenas a camada "0", apenas a camada "2", apenas a camada "4", e todas as três. Treine cada variante por 20 épocas. Compare a velocidade de convergência e a perda final. Isso reflete a decisão real de atingir q_proj vs v_proj vs todas as camadas lineares.
Análise de erro de quantização. Obtenha as matrizes de pesos do modelo treinado antes e depois de quantize_to_nf4 / dequantize_from_nf4. Calcule o erro quadrático médio, o erro absoluto máximo e a correlação entre os pesos originais e reconstruídos. Experimente com valores de block_size de 32, 64, 128 e 256.
Atendimento com múltiplos adaptadores (Multi-adapter serving). Treine dois adaptadores LoRA em diferentes subconjuntos de dados (índices pares vs. ímpares). Salve ambos os adaptadores. Carregue o modelo base uma vez, depois troque os adaptadores e verifique se cada um produz saídas diferentes para a mesma entrada. É assim que sistemas em produção servem múltiplos modelos com ajuste fino a partir de uma única base.
Inferência mesclada vs. não mesclada. Compare a saída do modelo LoRA antes e depois de merge_lora_weights nas mesmas 100 entradas. Verifique se as saídas são idênticas (dentro da tolerância de ponto flutuante de 1e-5). Em seguida, avalie a velocidade de inferência para ambos — a versão mesclada deve ser ligeiramente mais rápida, pois é uma multiplicação de matriz única em vez de duas.
Termos-Chave
Termo
O que dizem
O que realmente significa
LoRA
"Ajuste fino eficiente"
Low-Rank Adaptation: congela os pesos da base, treina duas matrizes pequenas A e B cujo produto aproxima a atualização completa dos pesos
QLoRA
"Ajuste fino em um laptop"
Quantized LoRA: carrega o modelo base em NF4 de 4 bits, treina adaptadores LoRA em fp16 por cima, permitindo o ajuste fino de 7B em 6GB de VRAM
Posto / Rank (r)
"O quanto o modelo pode aprender"
A dimensão interna das matrizes A e B; controla a expressividade vs. contagem de parâmetros
Alpha
"Taxa de aprendizado do LoRA"
Fator de escala aplicado à saída do LoRA; alpha/r dimensiona a contribuição da adaptação para a saída final
NF4
"Quantização de 4 bits"
Normal Float 4: um tipo de dados de 4 bits com níveis de quantização nos quantis da distribuição normal, ideal para pesos de redes neurais
Adaptador
"A parte pequena treinada"
As matrizes LoRA A e B salvas como um arquivo separado (10-100MB), carregáveis por cima de qualquer cópia do modelo base
Módulos de destino
"Quais camadas aplicar LoRA"
As camadas lineares específicas (q_proj, v_proj, etc.) onde os adaptadores LoRA são injetados
Mesclagem (Merging)
"Gravar no modelo"
Calcular W + (alpha/r) * BA e substituir o peso original, eliminando o overhead do adaptador na inferência
Otimizadores paginados
"Evitar OOM durante o treino"
Descarregar os estados do otimizador (momentum e variância do Adam) para a CPU quando a memória da GPU estiver esgotada
Esquecimento catastrófico
"O ajuste fino quebrou tudo"
Quando a atualização de todos os pesos faz com que o modelo perca capacidades aprendidas anteriormente
Leituras Adicionais
Hu et al., "LoRA: Low-Rank Adaptation of Large Language Models" (2021) — o artigo original que introduz o método de decomposição de baixo posto, testado no GPT-3 175B com posto tão baixo quanto 4
Dettmers et al., "QLoRA: Efficient Finetuning of Quantized Language Models" (2023) — apresenta NF4, quantização dupla e otimizadores paginados, permitindo o ajuste fino de 65B em uma única GPU de 48GB
Documentação da biblioteca PEFT (huggingface.co/docs/peft) — a biblioteca padrão para LoRA, QLoRA e outros métodos de eficiência de parâmetros no ecossistema Hugging Face
Yadav et al., "TIES-Merging: Resolving Interference When Merging Models" (2023) — técnicas para combinar múltiplos adaptadores LoRA sem degradação de qualidade
Documentação do TRL — referência oficial para SFTTrainer, DPOTrainer, KTOTrainer e a superfície de integração com PEFT/bitsandbytes/Unsloth.
Documentação do Unsloth — kernels fundidos que dobram o rendimento de ajuste fino e reduzem a memória pela metade; a camada de desempenho sob o TRL.
Documentação do Axolotl — treinador de SFT/DPO/QLoRA multi-GPU configurado por YAML; a alternativa de configuração como código para scripts escritos à mão.