📚 Módulo 10: Guardar, Cargar y Fusionar Adaptadores LoRA/QLoRA

10.1 ¿Por Qué No Guardar el Modelo Completo?

Una de las mayores ventajas de PEFT es que solo necesitas guardar los parámetros del adaptador (LoRA), no el modelo base completo. Esto tiene enormes implicaciones prácticas:

  • Un adaptador LoRA para un modelo de 7B puede ocupar menos de 10 MB, comparado con 14+ GB para el modelo completo en FP16.
  • Puedes entrenar múltiples adaptadores para diferentes tareas y cargarlos en el mismo modelo base según sea necesario.
  • Facilita el intercambio, versionado y almacenamiento de modelos especializados.

10.2 Guardar el Adaptador Entrenado

Después del entrenamiento, el adaptador LoRA se guarda como pesos adicionales. El modelo base permanece intacto.

# Guardar el adaptador LoRA
model.save_pretrained("./lora_adapter")

# Guardar tokenizer (si se modificó, aunque es raro)
tokenizer.save_pretrained("./lora_adapter")

Esto crea un directorio ./lora_adapter con archivos como:

  • adapter_config.json — Configuración LoRA (r, alpha, target_modules, etc.)
  • adapter_model.bin — Pesos LoRA (A y B)
  • README.md (opcional) — Metadatos

Importante: El modelo base no se guarda aquí. Debes mantener acceso al modelo base original (por ejemplo, desde Hugging Face Hub) para cargar el adaptador posteriormente.

10.3 Cargar un Adaptador LoRA en un Modelo Base

Para usar el adaptador entrenado:

  1. Cargar el modelo base (con o sin cuantización).
  2. Cargar el adaptador LoRA sobre él.
from transformers import AutoModelForCausalLM, AutoTokenizer, BitsAndBytesConfig
from peft import PeftModel
import torch

# Configuración de cuantización (opcional para inferencia eficiente)
bnb_config = BitsAndBytesConfig(
    load_in_4bit=True,
    bnb_4bit_quant_type="nf4",
    bnb_4bit_compute_dtype=torch.bfloat16
)

# Cargar 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)

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

# El modelo ahora tiene comportamiento especializado
model.eval()  # Establecer en modo de evaluación para inferencia

10.4 Fusionar el Adaptador con el Modelo Base (merge_and_unload)

Aunque la carga dinámica de adaptadores es flexible, para despliegue en producción o inferencia más rápida, fusionar pesos LoRA en el modelo base es útil. Esto crea un modelo completo especializado que no requiere infraestructura PEFT durante la inferencia.

# Fusionar adaptador LoRA con modelo base
model = model.merge_and_unload()

# Ahora el modelo es un modelo completo con pesos actualizados
# Guardar como modelo Hugging Face estándar
model.save_pretrained("./merged_model")
tokenizer.save_pretrained("./merged_model")

Advertencia:

  • Una vez fusionado, no puedes recargar otro adaptador sin recargar el modelo base original.
  • El modelo fusionado ocupa el mismo espacio en disco que el modelo base original (~1GB para Qwen2.5-0.5B en FP16).
  • La fusión solo es posible si el modelo está en precisión completa (FP16/BF16). Si está cuantizado a 4 bits, primero descuantiza (requiere más memoria).

Fusionar con un Modelo Cuantizado:

# Si el modelo es de 4 bits, primero descuantiza (requiere más VRAM)
model = model.dequantize()  # Convierte pesos a BF16/FP16

# Luego fusiona
model = model.merge_and_unload()

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

10.5 Cargar el Modelo Fusionado para Inferencia

Una vez fusionado y guardado, el modelo se comporta como cualquier modelo Hugging Face estándar:

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)

# Listo para inferencia sin PEFT!

Course Info

Course: AI-course3

Language: ES

Lesson: Module10