¿Quieres entrenar un modelo de 27B parámetros sin una granja de GPUs? Con Unsloth y QLoRA puedes hacer fine-tuning de Qwen 3.8 27B con solo 22 GB de VRAM — o incluso gratis en Kaggle.
Esta guía está basada en el anuncio oficial de Daniel Han (Co-founder, Unsloth AI), agosto 2026, y te lleva desde cero hasta exportar tu modelo a GGUF para correrlo en Ollama o llama.cpp.
TL;DR
| Característica | Detalle |
|---|---|
| Modelo base | Qwen 3.8 27B (dense, vision-language, 262K contexto) |
| Framework | Unsloth (optimizado para velocidad y memoria) |
| VRAM mínima (QLoRA) | 22 GB |
| VRAM recomendada (FFT) | 96 GB+ |
| Aceleración | ~1.5x más rápido vs Flash Attention 2 |
| Ahorro VRAM | ~50% menos que setups FA2 |
| Formatos de exportación | NVFP4, FP8, GGUF, merged (16-bit / 32-bit) |
Opciones de ejecución
Opción 1: Kaggle (gratis, recomendado para empezar)
- 30 horas de GPU gratis por semana
- 2x Tesla T4 (16 GB VRAM cada una)
- Notebooks pre-configurados listos para usar
- Ideal para experimentar sin hardware propio
Opción 2: Local (tu propio hardware)
- GPU con 24 GB+ VRAM (RTX 4090, A6000, etc.)
- O Apple Silicon con Unsloth Desktop app
- Control total del entorno
Opción 3: Unsloth Desktop App
- Disponible para macOS, Windows, Linux
- Interfaz gráfica, sin código
- Descarga desde unsloth.ai
Requisitos previos
Hardware
| Modo | VRAM requerida | GPU ejemplo |
|---|---|---|
| QLoRA 4-bit | 22 GB | RTX 3090 / 4090 (24 GB), A6000 |
| LoRA | >36 GB | A100 40GB, RTX A6000 Ada |
| Full Fine-Tuning (FFT) | 4x más que QLoRA | 2x A100 80GB, H100 |
| RL (GRPO) | 22 GB | RTX 4090, A6000 |
Software
# Python 3.10+
python --version
# PyTorch con CUDA (para NVIDIA)
pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu121
# Unsloth (instalación completa)
pip install "unsloth[colab-new] @ git+https://github.com/unslothai/unsloth.git"
# Dependencias adicionales
pip install transformers datasets trl peft accelerate bitsandbytes
Paso 1: Cargar el modelo base (QLoRA 4-bit)
from unsloth import FastModel
import torch
# Configuración
max_seq_length = 2048 # Puedes subir a 4096, 8192, etc.
dtype = None # Auto-detect (Float16 para Tesla T4, BFloat16 para Ampere+)
load_in_4bit = True # QLoRA: carga en 4-bit
# Cargar modelo y tokenizer
model, tokenizer = FastModel.from_pretrained(
model_name="unsloth/Qwen3.8-27B-unsloth-bnb-4bit",
max_seq_length=max_seq_length,
dtype=dtype,
load_in_4bit=load_in_4bit,
# token="hf_...", # Tu token de Hugging Face si es necesario
)
Nota:
unsloth/Qwen3.8-27B-unsloth-bnb-4bites la versión 4-bit pre-cuantizada de Unsloth. Ocupa ~15 GB en disco vs ~54 GB del modelo BF16 original.
Paso 2: Configurar LoRA adapters
# Añadir adapters LoRA al modelo
model = FastModel.get_peft_model(
model,
r=16, # Rank de LoRA (8, 16, 32, 64, 128)
target_modules=[
"q_proj", "k_proj", "v_proj", "o_proj",
"gate_proj", "up_proj", "down_proj",
],
lora_alpha=16, # Scaling factor (usualmente = r o 2*r)
lora_dropout=0, # 0 es óptimo para QLoRA
bias="none",
use_gradient_checkpointing="unsloth", # Ahorra 30% más VRAM
random_state=3407,
use_rslora=False, # Rank-Stabilized LoRA (opcional)
)
¿Qué es LoRA?
LoRA (Low-Rank Adaptation) entrena solo pequeñas matrices adicionales (adapters) en lugar de todos los pesos del modelo. Esto reduce el uso de VRAM de ~96 GB a ~22 GB.
| Parámetro | Efecto |
|---|---|
r=8 |
Menos parámetros, más rápido, menos expresivo |
r=16 |
Balance recomendado para la mayoría de casos |
r=32 |
Más capacidad, más VRAM, mejor para tareas complejas |
r=64+ |
Casi full fine-tuning en expresividad |
Paso 3: Preparar tu dataset
Formato requerido
Tu dataset necesita una columna text con el chat template de Qwen ya aplicado:
{
"text": "<|im_start|>system\nEres un asistente útil.<|im_end|>\n<|im_start|>user\n¿Cuál es la capital de Francia?<|im_end|>\n<|im_start|>assistant\nLa capital de Francia es París.<|im_end|>"
}
Ejemplo: cargar dataset desde Hugging Face
from datasets import load_dataset
# Dataset de ejemplo (reemplaza con el tuyo)
dataset = load_dataset("yahma/alpaca-cleaned", split="train")
# Función para formatear con el chat template de Qwen
def format_prompt(examples):
texts = []
for instruction, input_text, output in zip(
examples["instruction"],
examples["input"],
examples["output"]
):
if input_text:
prompt = f"{instruction}\n\n{input_text}"
else:
prompt = instruction
# Aplicar chat template de Qwen 3.8
messages = [
{"role": "user", "content": prompt},
{"role": "assistant", "content": output}
]
text = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=False)
texts.append(text)
return {"text": texts}
# Aplicar formateo
dataset = dataset.map(format_prompt, batched=True)
Ejemplo: dataset propio (CSV/JSON)
import pandas as pd
from datasets import Dataset
# Cargar CSV
df = pd.read_csv("mi_dataset.csv") # Columnas: instruction, input, output
dataset = Dataset.from_pandas(df)
dataset = dataset.map(format_prompt, batched=True)
Paso 4: Entrenar (SFT - Supervised Fine-Tuning)
from trl import SFTTrainer
from transformers import TrainingArguments
trainer = SFTTrainer(
model=model,
tokenizer=tokenizer,
train_dataset=dataset,
dataset_text_field="text",
max_seq_length=max_seq_length,
dataset_num_proc=2,
packing=False, # True para secuencias cortas (ahorra VRAM)
args=TrainingArguments(
per_device_train_batch_size=2,
gradient_accumulation_steps=4, # Efectivo: batch_size * grad_acc = 8
warmup_steps=5,
max_steps=60, # Total de pasos de entrenamiento
learning_rate=2e-4,
logging_steps=1,
optim="adamw_8bit", # Optimizador 8-bit (ahorra VRAM)
weight_decay=0.01,
lr_scheduler_type="linear",
seed=3407,
output_dir="outputs",
report_to="none", # Cambia a "wandb" o "tensorboard" si quieres
),
)
# ¡Entrenar!
trainer.train()
Monitoreo de VRAM durante entrenamiento
# Ver uso de GPU
!nvidia-smi
# O en código Python
import torch
print(f"VRAM usada: {torch.cuda.memory_allocated() / 1024**3:.2f} GB")
print(f"VRAM reservada: {torch.cuda.memory_reserved() / 1024**3:.2f} GB")
Paso 5: Fine-tuning con RL (GRPO)
Para entrenamiento por refuerzo con GRPO (Group Relative Policy Optimization):
from trl import GRPOConfig, GRPOTrainer
# Configuración GRPO
training_args = GRPOConfig(
learning_rate=5e-6,
per_device_train_batch_size=1,
gradient_accumulation_steps=8,
max_prompt_length=512,
max_completion_length=1024,
num_generations=4, # Tamaño del grupo GRPO
beta=0.04, # Coeficiente KL
logging_steps=1,
max_steps=100,
output_dir="outputs_grpo",
report_to="none",
)
# Función de recompensa (ejemplo: longitud de respuesta)
def reward_len(completions, **kwargs):
return [-abs(20 - len(completion)) for completion in completions]
trainer = GRPOTrainer(
model=model,
processing_class=tokenizer,
reward_funcs=[reward_len],
args=training_args,
train_dataset=dataset,
)
trainer.train()
Nota: GRPO requiere que definas una función de recompensa. Puedes usar modelos de recompensa pre-entrenados o funciones custom.
Paso 6: Guardar y exportar
Guardar solo los adapters LoRA
# Guardar adapters (pequeño, ~100-500 MB)
model.save_pretrained("lora_adapters")
tokenizer.save_pretrained("lora_adapters")
Fusionar adapters + modelo base (16-bit)
# Fusionar LoRA con el modelo base para un modelo completo
model.save_pretrained_merged(
"merged_model_16bit",
tokenizer,
save_method="merged_16bit",
)
Exportar a GGUF (para Ollama, llama.cpp)
# Exportar a GGUF con diferentes cuantizaciones
model.save_pretrained_gguf(
"gguf_model",
tokenizer,
quantization_method=[
"q4_k_m", # ~18 GB, balance calidad/tamaño
"q5_k_m", # ~20 GB, alta calidad
"q8_0", # ~30 GB, casi sin pérdida
],
)
Exportar a FP8 / NVFP4
# FP8 (para servidores con soporte Hopper/Ada)
model.save_pretrained_merged(
"merged_model_fp8",
tokenizer,
save_method="merged_8bit",
)
Paso 7: Inferencia con tu modelo fine-tuneado
# Cargar modelo fine-tuneado (adapters)
from unsloth import FastModel
model, tokenizer = FastModel.from_pretrained(
model_name="unsloth/Qwen3.8-27B-unsloth-bnb-4bit",
max_seq_length=2048,
load_in_4bit=True,
)
# Cargar adapters entrenados
model = FastModel.get_peft_model(model, r=16)
model.load_adapter("lora_adapters")
# Generar respuesta
messages = [
{"role": "user", "content": "¿Cuál es la capital de Francia?"}
]
inputs = tokenizer.apply_chat_template(messages, tokenize=True, return_tensors="pt").to("cuda")
outputs = model.generate(
input_ids=inputs,
max_new_tokens=256,
temperature=0.7,
top_p=0.95,
)
response = tokenizer.batch_decode(outputs, skip_special_tokens=True)[0]
print(response)
Comparativa: modos de entrenamiento
| Modo | VRAM | Velocidad | Calidad | Cuándo usarlo |
|---|---|---|---|---|
| QLoRA 4-bit | 22 GB | Rápido | Muy buena | La mayoría de casos. Balance ideal. |
| LoRA 16-bit | >36 GB | Media | Buena | Cuando 4-bit no es suficiente |
| FFT (Full) | ~96 GB | Lento | Máxima | Cuando necesitas cambiar todo el modelo |
| RL (GRPO) | 22 GB | Variable | Depende de recompensa | Para alinear comportamiento por refuerzo |
Opción especial: Apple Silicon (macOS)
# Instalar Unsloth para Mac
pip install unsloth
# O usar Unsloth Desktop App
# Descarga desde: https://unsloth.ai
En Mac con Apple Silicon (M1/M2/M3/M4), Unsloth usa MLX como backend para aceleración nativa. El consumo de memoria es similar pero usa RAM unificada en lugar de VRAM dedicada.
Troubleshooting
"CUDA out of memory"
# Soluciones:
# 1. Reduce max_seq_length
max_seq_length = 1024 # En lugar de 2048
# 2. Reduce batch_size
per_device_train_batch_size = 1
gradient_accumulation_steps = 8 # Mantiene batch efectivo de 8
# 3. Activa gradient checkpointing
use_gradient_checkpointing = "unsloth"
# 4. Usa QLoRA en lugar de LoRA
load_in_4bit = True
"Model not found"
# Autenticación con Hugging Face
huggingface-cli login
# O en Python:
# from huggingface_hub import login
# login(token="tu_token_aqui")
Lentitud en Kaggle
# En Kaggle, asegúrate de tener GPU activada:
# Settings → Accelerator → GPU T4 x2
# Verificar GPU disponible
import torch
print(torch.cuda.device_count()) # Debe mostrar 2
print(torch.cuda.get_device_name(0)) # Tesla T4
Recursos
- Notebooks de Unsloth (Kaggle/Colab): unsloth.ai
- Documentación: docs.unsloth.ai
- GitHub: github.com/unslothai/unsloth
-
Modelo base:
unsloth/Qwen3.8-27B-unsloth-bnb-4bit - Desktop app: unsloth.ai
Checklist final
- [ ] Acceso a GPU con 22+ GB VRAM (o Kaggle/Colab Pro)
- [ ] Python 3.10+ y PyTorch con CUDA instalados
- [ ] Unsloth instalado:
pip install "unsloth[colab-new] @ git+https://github.com/unslothai/unsloth.git" - [ ] Dataset con columna
texty chat template de Qwen aplicado - [ ] Modo elegido: QLoRA (recomendado) / LoRA / FFT / RL
- [ ] Hiperparámetros configurados (r, learning_rate, batch_size, steps)
- [ ] Plan de exportación (GGUF para Ollama, merged para vLLM, etc.)
- [ ] VRAM verificada antes de entrenar
¿Tú ya hiciste fine-tuning de un modelo de este tamaño? ¿Qué dataset usaste y qué resultados obtuviste? Cuéntame en los comentarios. 👇
Guía basada en el anuncio oficial de Daniel Han, Co-founder de Unsloth AI (agosto 2026).
Eber Cruz Fararoni is a software architect specialized in distributed systems, event-driven architectures, and applied artificial intelligence. He builds Fararoni Flow, an open-source AI agent orchestrator, on Java 25, NATS, and hexagonal architecture. He writes at ebercruz.com about the intersection of software engineering and artificial intelligence.
If you found this article useful, share it with someone navigating the complex enterprise AI landscape in 2026. And if you want to try Fararoni Flow or exchange ideas about agent orchestration: contact me.
Top comments (0)