Saltar al contenido
Lección 10 de 12

Fusion y Exportacion de Modelos

6 min read

De Adaptador LoRA a Modelo Desplegable

Despues del entrenamiento, tienes un adaptador LoRA — un archivo pequeno (10-100MB) que contiene solo los cambios de pesos aprendidos. Para desplegar, necesitas servir el adaptador junto al modelo base o fusionarlo en un modelo independiente. Esta leccion cubre todas las estrategias de fusion, formatos de exportacion y flujos de publicacion que necesitas.

Estrategias de Fusion LoRA

Fusion Basica: merge_and_unload

El enfoque mas simple y comun. Agrega los pesos LoRA a los pesos del modelo base y elimina las capas LoRA.

from peft import PeftModel
from transformers import AutoModelForCausalLM, AutoTokenizer
import torch

# Cargar modelo base (en precision completa para fusion)
base_model = AutoModelForCausalLM.from_pretrained(
    "meta-llama/Llama-3.1-8B-Instruct",
    torch_dtype=torch.float16,
    device_map="auto",
)
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-3.1-8B-Instruct")

# Cargar adaptador LoRA
model = PeftModel.from_pretrained(base_model, "./output/lora-adapter")

# Fusionar y descargar
merged_model = model.merge_and_unload()

# Guardar el modelo fusionado
merged_model.save_pretrained("./output/merged-model")
tokenizer.save_pretrained("./output/merged-model")

Importante: Al fusionar un adaptador QLoRA, primero debes descuantizar el modelo base. Cargar el modelo base en float16 (no en 4 bits) antes de fusionar evita artefactos de cuantizacion en el modelo final.

Fusion TIES

TIES (TrIM, Elect Sign & Merge) es una estrategia avanzada de fusion para combinar multiples adaptadores LoRA. Resuelve conflictos entre adaptadores recortando valores pequenos, resolviendo conflictos de signo y fusionando los parametros restantes.

from peft import PeftModel, PeftConfig
import torch

# Cargar modelo base
base_model = AutoModelForCausalLM.from_pretrained(
    "meta-llama/Llama-3.1-8B-Instruct",
    torch_dtype=torch.float16,
    device_map="auto",
)

# Fusion TIES con multiples adaptadores
adapters = ["./adapter-legal", "./adapter-medical", "./adapter-finance"]
weights = [0.4, 0.3, 0.3]  # Ponderar la contribucion de cada adaptador
density = 0.5  # Mantener el 50% superior de parametros por magnitud

model = PeftModel.from_pretrained(base_model, adapters[0], adapter_name="legal")
model.load_adapter(adapters[1], adapter_name="medical")
model.load_adapter(adapters[2], adapter_name="finance")

# Fusionar usando TIES
model.add_weighted_adapter(
    adapters=["legal", "medical", "finance"],
    weights=weights,
    adapter_name="merged",
    combination_type="ties",
    density=density,
)
model.set_adapter("merged")
merged = model.merge_and_unload()

Fusion DARE

DARE (Drop And REscale) elimina aleatoriamente parametros antes de fusionar, lo cual puede mejorar la generalizacion al combinar multiples adaptadores:

model.add_weighted_adapter(
    adapters=["legal", "medical", "finance"],
    weights=weights,
    adapter_name="merged",
    combination_type="dare_ties",  # DARE combinado con TIES
    density=0.5,
)

Cuando usar fusion multi-adaptador: Cuando has entrenado adaptadores LoRA separados para diferentes tareas o dominios y quieres crear un modelo unico que combine todas las capacidades.

Formatos de Exportacion

Safetensors (Por Defecto)

El formato estandar para modelos de Hugging Face. Seguro, rapido de cargar y soporta mapeo de memoria:

# Ya se guarda en formato safetensors por defecto
merged_model.save_pretrained("./output/merged-model")
# Crea: model-00001-of-00002.safetensors, model-00002-of-00002.safetensors, etc.

GGUF (para llama.cpp y Ollama)

GGUF es el formato usado por llama.cpp, Ollama, LM Studio y otras herramientas de inferencia local. Incluye cuantizacion integrada para inferencia eficiente en CPU y GPU.

Metodo 1: Usando Unsloth (mas facil)

# Si entrenaste con Unsloth
model.save_pretrained_gguf(
    "output/gguf",
    tokenizer,
    quantization_method="q4_k_m",  # Buen balance de calidad y tamano
)

Metodo 2: Usando llama.cpp directamente

# Clonar llama.cpp
git clone https://github.com/ggerganov/llama.cpp
cd llama.cpp

# Convertir de formato HF a GGUF
python convert_hf_to_gguf.py ../output/merged-model --outfile model-f16.gguf --outtype f16

# Cuantizar a tamanos mas pequenos
./llama-quantize model-f16.gguf model-q4_k_m.gguf Q4_K_M
./llama-quantize model-f16.gguf model-q5_k_m.gguf Q5_K_M
./llama-quantize model-f16.gguf model-q8_0.gguf Q8_0

Metodos de cuantizacion GGUF explicados:

| Metodo | Tamano (7B) | Calidad | Velocidad | Caso de Uso | |--------|------------|---------|-----------|-------------| | Q4_K_M | ~4.1 GB | Buena | Rapida | Mejor balance para la mayoria | | Q5_K_M | ~4.8 GB | Mejor | Rapida | Cuando necesitas mayor calidad | | Q6_K | ~5.5 GB | Muy buena | Media | Tareas sensibles a calidad | | Q8_0 | ~7.2 GB | Casi FP16 | Mas lenta | Cuando la calidad es critica | | F16 | ~14 GB | Referencia | La mas lenta | Referencia / desarrollo |

AWQ (Activation-aware Weight Quantization)

AWQ produce modelos cuantizados a 4 bits optimizados para inferencia en GPU con vLLM:

from awq import AutoAWQForCausalLM

model = AutoAWQForCausalLM.from_pretrained(
    "./output/merged-model",
    device_map="auto",
)
tokenizer = AutoTokenizer.from_pretrained("./output/merged-model")

# Dataset de calibracion (usa una muestra representativa)
quant_config = {
    "zero_point": True,
    "q_group_size": 128,
    "w_bit": 4,
    "version": "GEMM",
}

model.quantize(tokenizer, quant_config=quant_config)
model.save_quantized("./output/merged-model-awq")
tokenizer.save_pretrained("./output/merged-model-awq")

GPTQ

Similar a AWQ pero usa un algoritmo de cuantizacion diferente:

from transformers import GPTQConfig

gptq_config = GPTQConfig(
    bits=4,
    dataset="c4",  # Dataset de calibracion
    tokenizer=tokenizer,
)

model = AutoModelForCausalLM.from_pretrained(
    "./output/merged-model",
    quantization_config=gptq_config,
    device_map="auto",
)

model.save_pretrained("./output/merged-model-gptq")

Publicando en Hugging Face Hub

Creando un Model Card

Cada modelo publicado debe tener un model card (README.md) que documente su proposito, detalles de entrenamiento y limitaciones:

card_content = """
---
license: llama3.1
base_model: meta-llama/Llama-3.1-8B-Instruct
tags:
  - fine-tuned
  - legal
  - LoRA
---

# Asistente Legal - Llama 3.1 8B Fine-Tuned

## Descripcion del Modelo
Llama 3.1 8B con fine-tuning para analisis de documentos legales, entrenado con 2,000
ejemplos de revision de contratos, resumen de clausulas y evaluacion de riesgos.

## Detalles de Entrenamiento
- **Modelo base:** meta-llama/Llama-3.1-8B-Instruct
- **Metodo:** QLoRA (4-bit, rango 16, alpha 32)
- **Dataset:** 2,000 ejemplos legales curados
- **Epocas:** 3
- **Hardware:** 1x RTX 3090

## Uso Previsto
Analisis de documentos legales para contratos y acuerdos en ingles.

## Limitaciones
- No sustituye el asesoramiento legal profesional
- Entrenado principalmente en derecho contractual de EE.UU.
- Puede no generalizar a otros sistemas legales
"""

Subiendo al Hub

from huggingface_hub import HfApi

api = HfApi()

# Crear repositorio y subir modelo fusionado
api.create_repo("tu-usuario/legal-llama3-8b", private=False)

merged_model.push_to_hub("tu-usuario/legal-llama3-8b")
tokenizer.push_to_hub("tu-usuario/legal-llama3-8b")

# Subir GGUF separadamente (opcional)
api.upload_file(
    path_or_fileobj="output/gguf/model-q4_k_m.gguf",
    path_in_repo="legal-llama3-8b-q4_k_m.gguf",
    repo_id="tu-usuario/legal-llama3-8b-gguf",
)

Consideraciones de Licencia

Al publicar modelos fine-tuned, debes cumplir con la licencia del modelo base:

  • Llama 3.1: Licencia Comunitaria de Meta. Uso comercial permitido. Debe incluir atribucion. Se debe aceptar la licencia para usar.
  • Mistral: Apache 2.0. Muy permisiva. Uso comercial permitido.
  • Qwen: Apache 2.0 o Licencia Qwen dependiendo de la version.
  • Gemma: Terminos de Uso de Google. Uso comercial permitido con restricciones.

Siempre verifica la licencia del modelo base antes de publicar tu version fine-tuned. Tu modelo fine-tuned hereda las restricciones de licencia del modelo base.

Control de Versiones para Modelos

Rastrea tus modelos sistematicamente:

models/
  legal-assistant/
    v1.0/
      adapter/         # Archivos del adaptador LoRA
      merged/          # Modelo fusionado completo
      gguf/            # Versiones cuantizadas GGUF
      eval_results.json  # Puntuaciones de evaluacion
      training_config.json  # Hiperparametros usados
    v1.1/
      ...

Almacena configuraciones de entrenamiento junto a los modelos para poder reproducir cualquier version:

import json

config = {
    "base_model": "meta-llama/Llama-3.1-8B-Instruct",
    "lora_rank": 16,
    "lora_alpha": 32,
    "learning_rate": 2e-4,
    "epochs": 3,
    "dataset": "legal-contracts-v2",
    "dataset_size": 2000,
    "eval_loss": 0.847,
    "eval_win_rate_vs_base": 0.78,
}

with open("models/legal-assistant/v1.0/training_config.json", "w") as f:
    json.dump(config, f, indent=2)

En la proxima leccion, tomaremos tu modelo exportado y lo desplegaremos con motores de inferencia de produccion — vLLM para alto rendimiento y Ollama para despliegue local.