🚀 Low-Rank Adaptation - Microsoft 2021

LoRA & QLoRA: Fine-Tuning Eficiente de LLMs

Low-Rank Adaptation Democratiza o Treinamento de Modelos Gigantes

LoRA (Low-Rank Adaptation) e QLoRA revolucionaram fine-tuning de Large Language Models. Treine modelos de 70B parâmetros com 0.1% dos recursos originais. Democratização completa do fine-tuning de LLMs.

Low-Rank Adaptation

Entenda como LoRA democratiza fine-tuning de LLMs gigantes

ΔW = BA: A Fórmula da Democratização

Fine-tuning completo de um LLM de 70B parâmetros requer 280GB de memória GPU e dias de treinamento. Custo proibitivo para 99% das empresas. LoRA resolve isso congelando pesos originais e treinando apenas matrizes de baixo rank (low-rank).

LoRA decompõe updates de peso ΔW em duas matrizes menores: ΔW = BA, onde B ∈ ℝ^(d×r) e A ∈ ℝ^(r×k), com r << min(d,k). Para r=8 em matriz 4096×4096, treina-se apenas 65K parâmetros ao invés de 16M - redução de 250x!

QLoRA adiciona quantização 4-bit (NormalFloat4) ao modelo base congelado, reduzindo memória em 4x adicional. Llama 70B que precisava 140GB agora roda em 35GB. Double quantization e paged optimizers permitem fine-tuning em GPUs consumer.

Low-Rank Decomposition

W' = W + ΔW = W + BA

Onde W são pesos originais (congelados), B e A são matrizes de baixo rank treináveis com r << d,k

LoRA vs QLoRA vs Full Fine-Tuning

Compare diferentes abordagens de fine-tuning de LLMs

🔴 Full Fine-Tuning

Treinamento completo de todos os parâmetros do modelo

100%
Parâmetros Treináveis
280GB
Memória para Llama-70B
100%
Tempo de Treinamento
4x A100
Hardware Mínimo

🟢 LoRA/QLoRA

Adaptadores de baixo rank com quantização opcional

0.1-1%
Parâmetros Treináveis
35GB
Memória para Llama-70B
80-90%
Tempo de Treinamento
1x RTX 4090
Hardware Mínimo

Aplicações LoRA/QLoRA

Como LoRA está democratizando fine-tuning de LLMs

🏢

Fine-Tuning Domain-Specific

Empresas adaptam Llama-2, Mistral ou GPT para domínios específicos (legal, médico, financeiro) com datasets proprietários. LoRA permite múltiplos adaptadores especializados compartilhando modelo base. Custo 100x menor que treinar do zero.

🎯

Multi-Task Adaptation

Um modelo base + múltiplos adaptadores LoRA para diferentes tarefas: summarização, Q&A, código, tradução. Troca de adaptador em <1s permite servir múltiplas aplicações eficientemente. 10+ tarefas especializadas por modelo.

👤

Personalização por Usuário

Chatbots e assistentes criam adaptadores LoRA individuais por usuário/empresa, mantendo personalização sem re-treinar modelo completo. Privacy-preserving e escalável. Personalização a custo marginal.

🔬

Pesquisa e Experimentação

Pesquisadores testam hipóteses treinando dezenas de variações em GPUs consumer. QLoRA democratizou pesquisa em LLMs, antes restrita a BigTech. Democratização completa de LLM research.

💼

Startups e SMBs

Empresas pequenas podem criar LLMs customizados sem infraestrutura cara. Nível de customização antes exclusivo de grandes corporações.

🌍

Low-Resource Languages

Adaptação de modelos para idiomas com poucos dados. Fine-tuning eficiente permite preservação e revitalização de línguas minoritárias.

Impacto LoRA/QLoRA

Números que mostram a democratização do fine-tuning

250x

Redução de parâmetros treináveis

8x

Redução de memória GPU (QLoRA)

100x

Redução de custo vs treino do zero

95-99%

Qualidade mantida vs full FT

Implementação LoRA/QLoRA

Como fazer fine-tuning eficiente com HuggingFace PEFT

Fine-Tuning na Prática

Implementação completa de fine-tuning com LoRA e QLoRA usando HuggingFace PEFT. Inclui configuração de quantização 4-bit, adaptadores low-rank, e serving multi-adapter para múltiplas tarefas compartilhando modelo base.

import torch from transformers import AutoModelForCausalLM, AutoTokenizer, TrainingArguments from peft import LoraConfig, get_peft_model, prepare_model_for_kbit_training from peft import PeftModel from transformers import BitsAndBytesConfig from trl import SFTTrainer from datasets import load_dataset # Configuração QLoRA: 4-bit quantization bnb_config = BitsAndBytesConfig( load_in_4bit=True, bnb_4bit_quant_type="nf4", # NormalFloat4 bnb_4bit_compute_dtype=torch.bfloat16, bnb_4bit_use_double_quant=True, # Double quantization ) # Carrega modelo base quantizado model_id = "meta-llama/Llama-2-7b-hf" model = AutoModelForCausalLM.from_pretrained( model_id, quantization_config=bnb_config, device_map="auto", trust_remote_code=True, ) tokenizer = AutoTokenizer.from_pretrained(model_id) tokenizer.pad_token = tokenizer.eos_token # Prepara modelo para k-bit training model = prepare_model_for_kbit_training(model) # Configuração LoRA lora_config = LoraConfig( r=16, # Rank - quanto maior, mais parâmetros mas melhor qualidade lora_alpha=32, # Scaling factor (geralmente 2x o rank) target_modules=[ # Módulos do transformer para adicionar LoRA "q_proj", "k_proj", "v_proj", "o_proj", "gate_proj", "up_proj", "down_proj", ], lora_dropout=0.05, bias="none", task_type="CAUSAL_LM", ) # Adiciona adaptadores LoRA model = get_peft_model(model, lora_config) # Mostra parâmetros treináveis model.print_trainable_parameters() # Output: trainable params: 4,194,304 || all params: 6,742,609,920 || trainable%: 0.062% # Carrega dataset dataset = load_dataset("timdettmers/openassistant-guanaco") # Training arguments training_args = TrainingArguments( output_dir="./llama-7b-lora-finetuned", per_device_train_batch_size=4, gradient_accumulation_steps=4, num_train_epochs=3, learning_rate=2e-4, fp16=True, logging_steps=10, optim="paged_adamw_8bit", # Optimizer otimizado para QLoRA save_strategy="epoch", evaluation_strategy="epoch", ) # SFTTrainer: Supervised Fine-Tuning trainer = SFTTrainer( model=model, train_dataset=dataset["train"], eval_dataset=dataset["test"], peft_config=lora_config, dataset_text_field="text", max_seq_length=512, tokenizer=tokenizer, args=training_args, ) # Treina! trainer.train() # Salva apenas adaptadores LoRA (alguns MBs) model.save_pretrained("./lora-adapter") # Inference com LoRA class LoRAInference: """Carrega modelo base + adaptador LoRA para inferência""" def __init__(self, base_model_id, adapter_path): # Carrega modelo base (pode ser quantizado) self.model = AutoModelForCausalLM.from_pretrained( base_model_id, quantization_config=bnb_config, device_map="auto", ) # Carrega adaptador LoRA self.model = PeftModel.from_pretrained( self.model, adapter_path ) self.tokenizer = AutoTokenizer.from_pretrained(base_model_id) def generate(self, prompt, max_new_tokens=256): inputs = self.tokenizer(prompt, return_tensors="pt").to("cuda") with torch.no_grad(): outputs = self.model.generate( **inputs, max_new_tokens=max_new_tokens, temperature=0.7, top_p=0.9, ) return self.tokenizer.decode(outputs[0], skip_special_tokens=True) def swap_adapter(self, new_adapter_path): """Troca adaptador LoRA em <1s""" self.model.unload() self.model = PeftModel.from_pretrained( self.model.base_model, new_adapter_path ) # Multi-Adapter: Serving múltiplas tarefas class MultiAdapterServer: """Serve múltiplos adaptadores LoRA compartilhando base""" def __init__(self, base_model_id): self.base_model = AutoModelForCausalLM.from_pretrained( base_model_id, quantization_config=bnb_config, device_map="auto", ) self.tokenizer = AutoTokenizer.from_pretrained(base_model_id) self.adapters = {} def load_adapter(self, name, adapter_path): """Carrega adaptador na memória""" self.adapters[name] = adapter_path def generate(self, prompt, adapter_name="default", **kwargs): """Gera texto usando adaptador específico""" # Carrega adaptador model = PeftModel.from_pretrained( self.base_model, self.adapters[adapter_name] ) inputs = self.tokenizer(prompt, return_tensors="pt").to("cuda") outputs = model.generate(**inputs, **kwargs) # Libera adaptador model.unload() return self.tokenizer.decode(outputs[0], skip_special_tokens=True) # Uso if __name__ == "__main__": # Inferência com adaptador treinado inference = LoRAInference( "meta-llama/Llama-2-7b-hf", "./lora-adapter" ) response = inference.generate( "Explain quantum computing in simple terms:" ) print(response) # Multi-adapter serving server = MultiAdapterServer("meta-llama/Llama-2-7b-hf") server.load_adapter("medical", "./adapters/medical-lora") server.load_adapter("code", "./adapters/code-lora") server.load_adapter("legal", "./adapters/legal-lora") # Usa adaptador específico por tarefa medical_response = server.generate( "What are symptoms of diabetes?", adapter_name="medical" ) code_response = server.generate( "Write a binary search in Python:", adapter_name="code" )

🚀 Começe Agora

Linguagens Suportadas:

  • ✅ Python - HuggingFace PEFT
  • ✅ PyTorch - Framework principal
  • ✅ TensorFlow - Keras LoRA
  • ⚡ JAX - T5X e Flax

Casos de Uso Testados:

  • 🏢 Fine-tuning domain-specific (legal, médico, financeiro)
  • 🎯 Multi-task adaptation (Q&A, código, tradução)
  • 👤 Personalização por usuário/empresa
  • 🔬 Pesquisa e experimentação rápida
  • 💼 Customização para startups e SMBs
  • 🌍 Adaptação para low-resource languages