📚 Módulo 10: Salvar, Carregar e Fundir Adaptadores LoRA/QLoRA

10.1 Por Que Não Salvar o Modelo Completo?

Uma das maiores vantagens do PEFT é que você só precisa salvar os parâmetros do adaptador (LoRA), não o modelo base completo. Isso tem enormes implicações práticas:

  • Um adaptador LoRA para um modelo de 7B pode ocupar menos de 10 MB, comparado a 14+ GB para o modelo completo em FP16.
  • Você pode treinar múltiplos adaptadores para diferentes tarefas e carregá-los no mesmo modelo base conforme necessário.
  • Facilita o compartilhamento, versionamento e armazenamento de modelos especializados.

10.2 Salvar o Adaptador Treinado

Após o treinamento, o adaptador LoRA é salvo como pesos adicionais. O modelo base permanece intacto.

# Salvar o adaptador LoRA
model.save_pretrained("./lora_adapter")

# Salvar tokenizer (se modificado, embora raro)
tokenizer.save_pretrained("./lora_adapter")

Isso cria um diretório ./lora_adapter com arquivos como:

  • adapter_config.json — Configuração LoRA (r, alpha, target_modules, etc.)
  • adapter_model.bin — Pesos LoRA (A e B)
  • README.md (opcional) — Metadados

Importante: O modelo base não é salvo aqui. Você deve manter acesso ao modelo base original (por exemplo, do Hugging Face Hub) para carregar o adaptador posteriormente.

10.3 Carregar um Adaptador LoRA em um Modelo Base

Para usar o adaptador treinado:

  1. Carregar o modelo base (com ou sem quantização).
  2. Carregar o adaptador LoRA sobre ele.
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
from peft import PeftModel
import torch

# Configuração de quantização (opcional para inferência eficiente)
bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_compute_dtype=torch.bfloat16
)

# Carregar modelo base
model_name = "Qwen/Qwen2.5-0.5B-Instruct"
model = AutoModelForCausalLM.from_pretrained(
    model_name,
    quantization_config=bnb_config,
    device_map="auto",
    trust_remote_code=True
)

tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True)

# Carregar adaptador LoRA
model = PeftModel.from_pretrained(model, "./lora_adapter")

# O modelo agora tem comportamento especializado
model.eval()  # Definir para modo de avaliação para inferência

10.4 Fundir o Adaptador com o Modelo Base (merge_and_unload)

Embora o carregamento dinâmico de adaptadores seja flexível, para implantação em produção ou inferência mais rápida, fundir pesos LoRA no modelo base é útil. Isso cria um modelo completo especializado que não requer infraestrutura PEFT durante a inferência.

# Fundir adaptador LoRA com modelo base
model = model.merge_and_unload()

# Agora o modelo é um modelo completo com pesos atualizados
# Salvar como modelo Hugging Face padrão
model.save_pretrained("./merged_model")
tokenizer.save_pretrained("./merged_model")

Aviso:

  • Uma vez fundido, você não pode recarregar outro adaptador sem recarregar o modelo base original.
  • O modelo fundido ocupa o mesmo espaço em disco que o modelo base original (~1GB para Qwen2.5-0.5B em FP16).
  • A fusão só é possível se o modelo estiver em precisão completa (FP16/BF16). Se estiver quantizado para 4 bits, primeiro desquantize (requer mais memória).

Fundir com um Modelo Quantizado:

# Se o modelo é de 4 bits, primeiro desquantize (requer mais VRAM)
model = model.dequantize()  # Converte pesos para BF16/FP16

# Depois funde
model = model.merge_and_unload()

# Salvar
model.save_pretrained("./merged_model_full_precision")

10.5 Carregar o Modelo Fundido para Inferência

Uma vez fundido e salvo, o modelo se comporta como qualquer modelo Hugging Face padrão:

from transformers import AutoModelForCausalLM, AutoTokenizer

model = AutoModelForCausalLM.from_pretrained(
    "./merged_model",
    device_map="auto",
    trust_remote_code=True
)

tokenizer = AutoTokenizer.from_pretrained("./merged_model", trust_remote_code=True)

# Pronto para inferência sem PEFT!

Course Info

Course: AI-course3

Language: PT

Lesson: Module10