Avanzado 25 minFine-tuning

Fine-tuning de LLM en local: ejemplo paso a paso con LoRA / QLoRA

El fine-tuning de LLM en local con LoRA y QLoRA permite adaptar un modelo de 8-9B (como Qwen 3.5 9B) a tu vocabulario profesional, tu estilo de redacción o un formato de respuesta específico —todo en una RTX 3090 de segunda mano. Ya no necesitas un clúster A100. Esta guía te lleva desde cero experiencia hasta un modelo GGUF cargado en Ollama, pasando por el formato del dataset, el notebook de Unsloth paso a paso y la exportación final.

Por Mohamed Meguedmi·Actualización 2026-08-27·Probado en Windows, macOS y Linux

#¿Por qué realizar un ajuste fino de un LLM local?

Un LLM de base es polivalente, pero imperfecto para tu uso específico. La ingeniería de prompts y el RAG resuelven el 80 % de los casos; el fine-tuning sirve para el 20 % restante. Deberías considerar el fine-tuning cuando tu modelo deba responder en un formato estricto (JSON, etiquetas, estructura interna), respetar un estilo empresarial (tono, vocabulario), dominar un ámbito especializado (médico, jurídico o técnico propio de tu actividad) o reproducir comportamientos que ningún prompt consiga estabilizar realmente.

Por el contrario, no uses el fine-tuning para introducir conocimiento factual (el RAG lo hace mejor y se mantiene actualizado), corregir una alucinación aislada (un mejor prompt basta) o competir con GPT-5 en los benchmarks (no ganarás).

i
Regla empírica
Si puedes expresar lo que quieres con menos de 5 ejemplos en un prompt, crea un prompt de sistema. Si tienes 50 o más ejemplos y el modelo sigue desviándose, haz un ajuste fino.

#LoRA vs fine-tuning completo: por qué LoRA gana

El kit de IA Local

Tu ChatGPT privado y gratuito en tu máquina en 1 hora — LM Studio, Ollama, Open WebUI, tus documentos, sin nube.

  • Espacio en línea de por vida
  • PDF + archivos
  • Reembolsado 30 j

El fine-tuning completo actualiza todos los parámetros del modelo. Para un 7B en FP16, son 14 GB de pesos + aproximadamente 30 GB para los gradientes y el optimizador Adam. Total: entre 40 y 60 GB de VRAM mínimos. Inaccesible sin A100 80GB o H100.

LoRA (adaptación de bajo rango) congela los pesos originales y entrena únicamente pequeñas matrices de adaptación de rango r, insertadas en las capas de atención. En concreto, para un 7B con r=16, entrenas aproximadamente 20 millones de parámetros en lugar de 7 mil millones, es decir, el 0,3 % del modelo. La VRAM necesaria para los gradientes y el optimizador se reduce drásticamente.

Fine-tune completo 7B
~60 GB VRAM, 2-4× A100, varias horas, archivo de salida de 14 GB.
LoRA 7B
~16 GB de VRAM, una RTX 4080 basta, de 30 minutos a 2 horas, adaptador de solo 30-200 MB.
QLoRA 7B
~6 GB de VRAM, una RTX 3060 de 12 GB es suficiente, calidad casi idéntica a la de LoRA clásico.
→
Casi equivalente en la práctica
En conjuntos de datos de tamaño moderado (< 10 000 ejemplos), LoRA alcanza un 95-99 % de la calidad de un fine-tune completo. La diferencia solo se vuelve visible en tareas muy alejadas del preentrenamiento. Para tu primer fine-tune, ni siquiera te lo plantees: LoRA.

#QLoRA: la revolución de la VRAM

QLoRA lleva la idea un paso más allá: el modelo base se carga en 4 bits (NF4, NormalFloat 4-bit) en lugar de FP16. Durante el entrenamiento, los pesos permanecen cuantizados; solo los adaptadores LoRA en FP16 reciben los gradientes. Resultado: un 7B cabe en 5-6 GB de VRAM, un 13B en 10 GB y un 70B en 48 GB.

La pérdida de calidad es despreciable gracias a dos técnicas: la cuantización NF4 (calibrada sobre la distribución gaussiana de los pesos) y la doble cuantización (las propias constantes de cuantización también se cuantizan). En la práctica, en la mayoría de los benchmarks, QLoRA difiere de LoRA FP16 en menos de un 1 %.

i
La opción predeterminada adecuada
Para un primer fine-tuning local, QLoRA es casi siempre la opción adecuada. Ahorras un 60-70% de VRAM con una pérdida de calidad imperceptible. Pasa a LoRA clásico solo si buscas un uso crítico en producción con una RTX 4090 o superior.

#VRAM necesaria por tamaño del modelo

Las cifras que aparecen a continuación son mínimos realistas con Unsloth, un tamaño de lote de 2, una secuencia de 2048 tokens y gradient checkpointing activado. Añade un 20% de margen para los picos y el sistema operativo.

Modelo 2-3B (Qwen 3.5 2B, Granite 4.2 3B)
QLoRA: 4 GB de VRAM · LoRA FP16: 8 GB · GTX 1660 con 6 GB o RTX 3050 son suficientes en QLoRA.
Modelo 8-9B (Granite 4.2 8B, Qwen 3.5 9B)
QLoRA: 6 GB de VRAM · LoRA FP16: 16 GB · RTX 3060 de 12 GB: permite trabajar con comodidad; RTX 3090: ideal.
Modelo 12B (Gemma 4 12B)
QLoRA: 10 GB VRAM · LoRA FP16: 28 GB · RTX 3090/4090 24 GB en QLoRA, A100 40GB en LoRA.
Modelo 27-35B (Qwen 3.8 27B, Qwen 3.6 35B-A3B)
QLoRA: 22 GB de VRAM · LoRA FP16: imposible en hardware de consumo · RTX 3090/4090 o RTX 5090 con QLoRA estricto.
Modelo 70B (denso de gama alta)
QLoRA: 48 GB VRAM · 2× RTX 3090 o 1× A100 80GB · multi-GPU obligatorio con Unsloth Pro.

Para esta guía, el objetivo es un modelo de 8-9B con QLoRA en una RTX 3090 de 24 GB. Es la combinación más universal: una RTX 3060 de 12 GB también basta, pero una RTX 3090 de segunda mano (precio variable) permite trabajar cómodamente con QLoRA con todos los tamaños hasta 12B. Una RTX 4090 o una RTX 5090 multiplica la velocidad por un factor de 1,5 a 2, pero ninguna de las dos es indispensable.

#Requisitos de hardware y software

GPU NVIDIA con CUDA 11.8+
RTX 3060 de 12 GB como mínimo; RTX 3090 recomendada. Las GPU AMD con ROCm funcionan parcialmente, pero Unsloth está optimizado para CUDA: quédate con NVIDIA para esta primera guía.
Python 3.10 o 3.11
No 3.12 (incompatibilidades con bitsandbytes al momento de la redacción). Usa conda o pyenv.
32 GB de RAM del sistema
16 GB pueden ser suficientes, pero la carga inicial del modelo y la preparación del conjunto de datos son más cómodas con 32 GB.
50 GB de espacio en disco
Modelo base + checkpoints + adaptador fusionado + exportación GGUF Q4_K_M. Reserva espacio de sobra.
Conexión adecuada
La descarga inicial del modelo 8-9B pesa 5-8 GB. Solo una vez.

En cuanto al software, se utiliza Unsloth (github.com/unslothai/unsloth), un marco que reescribe los kernels Triton para ganar 2× de velocidad y 60% de memoria en comparación con el estándar de PEFT de Hugging Face. Instalación en un entorno dedicado:

Instalación de Unsloth
conda create -n unsloth python=3.11 -y
conda activate unsloth
pip install --upgrade pip
pip install "unsloth[cu121-torch240] @ git+https://github.com/unslothai/unsloth.git"
pip install --no-deps trl peft accelerate bitsandbytes
!
Versiones CUDA
Adapta cu121-torch240 a tu instalación: cu118 para CUDA 11.8, cu124 para CUDA 12.4. Comprueba la versión con nvidia-smi, en la parte superior derecha de su salida. Una versión incorrecta provocará errores crípticos en la primera importación.

#Preparar tu conjunto de datos en formato JSONL

El formato estándar para el fine-tuning de instrucciones es JSONL (un objeto JSON por línea). Tres variantes son comunes:

Formato Alpaca (el más sencillo)
{"instruction": "Traduis en français formel.", "input": "Hey, what's up?", "output": "Bonjour, comment allez-vous ?"}
{"instruction": "Résume en une phrase.", "input": "Le chat noir a sauté...", "output": "Un chat noir saute sur la table."}
Formato ShareGPT (varios turnos)
{"conversations": [
  {"from": "system", "value": "Tu es un assistant juridique."},
  {"from": "human", "value": "Qu'est-ce qu'une clause léonine ?"},
  {"from": "gpt", "value": "Une clause léonine est une disposition contractuelle..."}
]}

Para un primer fine-tune, elige Alpaca: un solo turno, un formato claro y soporte nativo de Unsloth. Algunas reglas cruciales para la calidad:

Volumen mínimo
300 ejemplos para observar un efecto, 1000-5000 para un fine-tune sólido, 10 000+ para algo serio. Menos de 300, pierdes tu tiempo.
Calidad > cantidad
100 ejemplos perfectos superan a 5000 ejemplos ruidosos. Revísalos. Pide a otra persona que los revise. El modelo aprende literalmente tu conjunto de datos, defectos incluidos.
Diversidad de entradas
Si todos tus inputs comienzan por 'Traduce', el modelo no sabrá hacer nada más. Varía las formulaciones.
Longitudes equilibradas
Si todas tus salidas tienen 2 frases, el modelo ya no sabrá producir una respuesta larga. Mezcla distintas longitudes.
Reserva un 10% para validación
Separa aleatoriamente train.jsonl y val.jsonl para medir el sobreajuste.
→
Generar un conjunto de datos sintético
¿No tienes a mano 1000 ejemplos? Usa un modelo grande (Claude, GPT-5 o Qwen 3.8 27B en local) para generar pares sintéticos de instrucción/salida a partir de tus documentos internos. Es la técnica estándar, llamada "self-instruct". Calcula entre 1 y 2 horas de generación para 1000 ejemplos.

#El cuaderno Unsloth comentado

Aquí tienes un script completo y mínimo para ajustar Qwen 3.5 9B mediante QLoRA con tu conjunto de datos. Ejecutar como archivo .py o en un notebook de Jupyter.

finetune.py — paso 1: carga
from unsloth import FastLanguageModel
import torch

MODEL = "unsloth/Qwen3.5-9B-Instruct-bnb-4bit"
MAX_SEQ = 2048

model, tokenizer = FastLanguageModel.from_pretrained(
    model_name = MODEL,
    max_seq_length = MAX_SEQ,
    dtype = None,           # auto : bf16 sur Ampere+, fp16 sinon
    load_in_4bit = True,    # QLoRA : modèle quantifié 4-bit
)

Unsloth aloja versiones precuantizadas a 4 bits de la mayoría de los modelos populares en Hugging Face (prefijo unsloth/). Descarga más rápida, inicio inmediato. La primera ejecución descarga aproximadamente 5 GB.

finetune.py — paso 2: configuración de LoRA
model = FastLanguageModel.get_peft_model(
    model,
    r = 16,                  # rang de la décomposition LoRA
    target_modules = [
        "q_proj", "k_proj", "v_proj", "o_proj",
        "gate_proj", "up_proj", "down_proj",
    ],
    lora_alpha = 16,
    lora_dropout = 0,
    bias = "none",
    use_gradient_checkpointing = "unsloth",  # économie VRAM
    random_state = 42,
)

El rango r=16 es un buen valor predeterminado. Auméntalo a 32 o 64 si tienes muchos datos (>10k) y un dominio muy alejado del preentrenamiento. Un rango más alto = más parámetros entrenables = mayor capacidad, pero también mayor riesgo de sobreajuste.

finetune.py — paso 3: conjunto de datos
from datasets import load_dataset

ALPACA_PROMPT = """### Instruction:
{}

### Input:
{}

### Réponse:
{}"""

EOS = tokenizer.eos_token

def format_prompt(ex):
    texts = [
        ALPACA_PROMPT.format(i, inp or "", out) + EOS
        for i, inp, out in zip(ex["instruction"], ex["input"], ex["output"])
    ]
    return {"text": texts}

ds = load_dataset("json", data_files="train.jsonl", split="train")
ds = ds.map(format_prompt, batched=True)
!
No olvides el token EOS
Sin EOS al final de cada ejemplo, el modelo aprende a no detenerse nunca. Síntoma típico: tu modelo ajustado mediante fine-tuning genera sin fin, repite o continúa con una nueva pregunta inventada. Comprueba siempre que tokenizer.eos_token se haya añadido correctamente.
finetune.py — paso 4: entrenamiento
from trl import SFTTrainer
from transformers import TrainingArguments

trainer = SFTTrainer(
    model = model,
    tokenizer = tokenizer,
    train_dataset = ds,
    dataset_text_field = "text",
    max_seq_length = MAX_SEQ,
    args = TrainingArguments(
        per_device_train_batch_size = 2,
        gradient_accumulation_steps = 4,    # batch effectif = 8
        warmup_steps = 10,
        num_train_epochs = 3,
        learning_rate = 2e-4,
        fp16 = not torch.cuda.is_bf16_supported(),
        bf16 = torch.cuda.is_bf16_supported(),
        logging_steps = 10,
        optim = "adamw_8bit",
        weight_decay = 0.01,
        lr_scheduler_type = "linear",
        seed = 42,
        output_dir = "outputs",
    ),
)

trainer.train()

En una RTX 3090, con un conjunto de datos de 1000 ejemplos y 3 épocas, calcula entre 20 y 40 minutos. En una RTX 4090, entre 12 y 25 minutos. En una RTX 5090, aproximadamente entre 8 y 15 minutos. Vigila la pérdida: debe bajar de forma regular y luego estabilizarse. Si vuelve a subir en la validación, estás sobreajustando el modelo.

→
Cuándo detenerse
3 épocas es un buen valor por defecto para un conjunto de datos de 1k a 5k ejemplos. Más allá de 5 épocas, el riesgo de sobreajuste se vuelve serio: el modelo recita de memoria en lugar de generalizar. Si tienes más de 20k ejemplos, reduce a 1-2 épocas.

#Exportar GGUF hacia Ollama

Tu adaptador LoRA está en outputs/. Es un archivo de unas pocas decenas de MB, separado del modelo base. Para usarlo en Ollama, hay dos pasos: fusionar el adaptador con el modelo y luego convertirlo a GGUF cuantizado.

finetune.py — paso 5: exportar GGUF Q4_K_M
model.save_pretrained_gguf(
    "mon-modele-gguf",
    tokenizer,
    quantization_method = "q4_k_m",  # le défaut recommandé
)

Unsloth se encarga de todo: fusión adaptador + base, conversión mediante llama.cpp, cuantización Q4_K_M. El resultado: un archivo mon-modele-gguf/unsloth.Q4_K_M.gguf de aproximadamente 5,3 GB para un 9B. Q5_K_M y Q8_0 están disponibles si deseas más precisión (y más peso).

Para cargarlo en Ollama que escucha en localhost:11434, crea un archivo Modelfile mínimo y guarda el modelo:

Modelfile
cat > Modelfile <<EOF
FROM ./mon-modele-gguf/unsloth.Q4_K_M.gguf

TEMPLATE """### Instruction:
{{ .Prompt }}

### Réponse:
"""

PARAMETER temperature 0.7
PARAMETER stop "### Instruction:"
EOF

ollama create mon-modele -f Modelfile
ollama run mon-modele
i
La plantilla debe coincidir
La plantilla de Ollama debe reproducir exactamente el formato utilizado durante el entrenamiento (aquí Alpaca con ### Instruction: y ### Réponse:). De lo contrario, el modelo recibe prompts que nunca ha visto y genera alucinaciones. Si has usado ShareGPT o ChatML, adapta la plantilla en consecuencia.

#Consejos y solución de problemas

OutOfMemoryError al iniciar
Reduce per_device_train_batch_size a 1 y aumenta gradient_accumulation_steps en proporción. Reduce max_seq_length a 1024 si tus ejemplos son cortos.
Pérdida que no disminuye
Tasa de aprendizaje demasiado baja (prueba con 5e-4) o formato del prompt incorrecto. Imprime 2-3 ejemplos de ds[0]["text"] y comprueba visualmente que se parezcan a lo que quieres.
La pérdida se dispara (NaN)
Tasa de aprendizaje demasiado alta. Vuelve a 1e-4. O activa bf16 si estabas usando fp16 en Ampere+: fp16 tiende a provocar desbordamientos numéricos.
Modelo que repite infinitamente
Token EOS faltante en el entrenamiento, o parámetro stop ausente en el Modelfile. Ambos problemas suelen ocurrir juntos.
Sobreajuste visible
La pérdida de entrenamiento baja, la pérdida de validación sube. Detente en la época en que divergen. Reduce epochs o aumenta lora_dropout a 0.05.
La exportación a GGUF falla
Unsloth descarga llama.cpp automáticamente la primera vez; asegúrate de tener git, cmake y build-essential instalados en Linux. En Mac, ejecuta xcode-select --install.
Modelo que ignora las nuevas instrucciones
Dataset con poca variedad o rango LoRA demasiado bajo. Establece r=32 y lora_alpha=64 y vuelve a ejecutar.
→
Mantener el adaptador separado
Para iterar rápidamente, mantén también una versión no fusionada del adaptador (~100 MB) con model.save_pretrained("adapter"). Puedes reutilizarla más tarde con otro modelo base o combinarla con otros adaptadores sin tener que descargar todo de nuevo.

#Para ir más allá

Tienes un primer modelo ajustado mediante fine-tuning que funciona en Ollama. Tres vías naturales para profundizar:

Elegir la cuantización adecuada
Has exportado en Q4_K_M por defecto. La guía para elegir la cuantización compara Q4_K_M, Q5_K_M, Q8_0 y FP16; resulta útil para decidir si la calidad de tu modelo ajustado merece Q5 o Q8.
RAG en lugar de fine-tuning para incorporar conocimientos
Si quieres que el modelo conozca tus documentos (y no solo un estilo), la guía de introducción al RAG local explica por qué el RAG es casi siempre preferible al fine-tuning para incorporar hechos.
Hardware para pasar a modelos grandes
Para realizar un ajuste fino de modelos de 32B o 70B con QLoRA, se necesitan 24 GB y 48 GB de VRAM, respectivamente. La guía «Qué LLM elegir para 24 GB de VRAM» detalla lo que es posible con RTX 3090, 4090 y RX 7900 XTX.
¿Esta guía te ha ayudado?

¿Un comentario, un error, una precisión? Avísanos, eso mejora la guía para todos.