Fine-tuning — Módulo 4: elegir el modelo base y el hardware
Cuarto módulo del Curso de Fine-tuning práctico y cierre del bloque básico: base vs instruct y cuándo cada uno, qué tamaño pide tu tarea, licencias de Llama, Qwen, Mistral y Gemma, la cuenta completa de VRAM paso a paso (pesos, gradientes, optimizador y activaciones), la tabla de qué cabe con full FT, LoRA y QLoRA, y dónde entrenar con costos por hora.
☰ Índice del contenido
- La tesis del módulo
- Base vs instruct
- Tamaño: qué pide realmente tu tarea
- Licencias: la parte aburrida que decide el proyecto
- La cuenta de VRAM, completa y explícita
- El ejemplo numérico, paso a paso
- Qué cabe en qué
- Las tres perillas cuando no cabe
- Dónde entrenar
- Errores comunes
- Cierre del bloque básico
- La frase para llevarse
- Qué viene
- Material relacionado
Con el módulo 3 ya sabes qué secuencia de tokens va a ver el modelo y sobre cuáles se calcula el gradiente. Falta la pregunta material: qué modelo y dónde lo entrenas. Y es la pregunta donde más gente se estrella contra un CUDA out of memory a los cuarenta minutos de haber empezado, porque las cuentas de VRAM del fine-tuning no se parecen a las de la inferencia. En inferencia dimensionas pesos más caché; en entrenamiento hay tres pasajeros más — gradientes, estados del optimizador y activaciones — y entre los tres suelen pesar más que el modelo. Este módulo cierra el bloque básico con esa cuenta explícita, la tabla de qué cabe en qué, y el mapa de dónde arrendar el fierro.
La tesis del módulo
En fine-tuning, la VRAM no la define el modelo: la define el método. El mismo 8B necesita más de 100 GB con full fine-tuning, cabe en 24 GB con LoRA y cabe en 12 GB con QLoRA. Elegir el método es una decisión de arquitectura con consecuencias de presupuesto de un orden de magnitud, y se toma antes de arrendar nada.
Base vs instruct
Todo modelo abierto se publica en dos sabores, y elegir mal el punto de partida es un error caro porque se descubre tarde.
Un modelo base (glosar: el modelo tal como salió del pre-entrenamiento; sabe muchísimo del mundo pero solo hace una cosa: continuar texto — no obedece instrucciones ni tiene formato de conversación) es materia prima. Un modelo instruct (glosar: el mismo modelo después de instruction tuning y alineamiento; ya sabe conversar, seguir instrucciones y negarse a cosas dañinas) ya trae comportamiento de asistente. El recorrido completo entre uno y otro está en los módulos 7 y 8 del Curso de Transformers.
| Modelo base | Modelo instruct | |
|---|---|---|
| Sabe conversar | No | Sí |
| Chat template | Puede no traer | Sí, definido |
| Ejemplos que necesitas | Miles | Cientos |
| Comportamiento de seguridad | Ninguno | Ya alineado |
| Riesgo de conflicto de estilo | Nulo: es hoja en blanco | Real: peleas con su alineamiento |
| Cuándo elegirlo | Formato muy propio, dataset grande | Casi siempre, sobre todo el primero |
La recomendación práctica es partir por el instruct. El razonamiento: ya trae gratis la mayor parte del comportamiento que tú tendrías que enseñar (formato de conversación, seguir instrucciones, no responder cualquier cosa), así que tu dataset de 800 ejemplos rinde mucho más. El escenario en que el base gana es cuando tu formato de salida es tan particular que el alineamiento del instruct se convierte en un estorbo — el modelo insiste en agregar advertencias, en explicar lo que hizo o en suavizar el tono — y tienes suficientes ejemplos para enseñar todo desde cero.
Ojo: los modelos base con frecuencia no traen
chat_templateen su tokenizer. Si eliges base, tienes que definir tú una plantilla y usarla idéntica en entrenamiento y en inferencia. Es exactamente el punto del módulo 3 y una fuente clásica de “el modelo entrenó bien y en producción responde raro”.
Tamaño: qué pide realmente tu tarea
La intuición de “más grande siempre mejor” es cara y con frecuencia falsa en fine-tuning, porque un modelo chico afinado en una tarea estrecha compite bien con uno grande genérico.
| Tarea | Tamaño razonable | Por qué |
|---|---|---|
| Clasificación en N categorías fijas | 0.5B – 3B | La tarea no necesita razonar |
| Extracción de campos estructurados | 1B – 8B | Necesita entender el texto, no razonar |
| Reescritura, estilo y tono | 3B – 8B | Fluidez importa; razonamiento no tanto |
| Asistente de dominio conversacional | 7B – 14B | Necesita conocimiento y coherencia |
| Razonamiento multi-paso, código | 14B – 70B | Aquí el tamaño sí compra capacidad |
| Agente con uso de herramientas | 14B+ | Planificar es lo que más pide tamaño |
La regla operativa: empieza con el modelo más chico que podría funcionar, mide, y sube solo si el número lo pide. Es más rápido de iterar (un ciclo de entrenamiento de 20 minutos en vez de 6 horas), más barato de servir en producción, y si el chico ya cumple el umbral, el grande solo agrega costo por token para siempre.
Licencias: la parte aburrida que decide el proyecto
Que un modelo esté en Hugging Face y se descargue con un comando no significa que puedas usarlo comercialmente. Las licencias de los pesos varían y hay que leerlas antes de construir un producto encima.
| Familia | Licencia | Uso comercial | Letra chica relevante |
|---|---|---|---|
| Llama (Meta) | Licencia comunitaria propia | Sí | Límite de usuarios activos mensuales; obligación de atribución y de nombrar el modelo derivado con el prefijo de la familia |
| Qwen (Alibaba) | Apache 2.0 en la mayoría de tamaños | Sí | Algunos tamaños puntuales han usado licencias distintas: verifica el modelo exacto |
| Mistral (los abiertos) | Apache 2.0 | Sí | Los modelos “grandes” son de licencia comercial cerrada, no confundir |
| Gemma (Google) | Términos de uso de Gemma | Sí | Política de uso prohibido que se hereda a los derivados |
| DeepSeek | Licencia propia / MIT según modelo | Sí | Varía por versión: revisar el repositorio |
Tres reglas que ahorran problemas:
- Lee la licencia del modelo exacto, no de la familia. Dentro de una misma familia conviven licencias distintas por tamaño y por versión.
- La licencia se hereda a tu adaptador. Un LoRA entrenado sobre un modelo con restricciones queda sujeto a esas restricciones, y también el modelo fusionado.
- Los datos con que entrenaste también tienen licencia. Es el punto de la destilación del módulo 1: si generaste el dataset con las salidas de un modelo comercial, los términos de ese proveedor pueden prohibir usarlo para entrenar.
Cuidado: las licencias cambian con cada release y las tablas de internet envejecen rápido. Lo que está arriba es un mapa de orientación, no asesoría legal. Antes de comprometer un producto, abre el archivo
LICENSEdel repositorio del modelo que vas a usar y léelo. Toma cinco minutos.
La cuenta de VRAM, completa y explícita
Esta es la sección que evita el CUDA out of memory. En GPU módulo 2 vimos la cuenta de inferencia: pesos más KV cache. Entrenar suma tres pasajeros que en inferencia no existen.
LOS CUATRO PASAJEROS DE LA VRAM AL ENTRENAR (full FT)
┌──────────────────┬───────────────────┬───────────┐
│ pesos │ ████ │ P x 2 B │
├──────────────────┼───────────────────┼───────────┤
│ gradientes │ ████ │ E x 2 B │
├──────────────────┼───────────────────┼───────────┤
│ optimizador Adam │ ████████████████ │ E x 8 B │
├──────────────────┼───────────────────┼───────────┤
│ activaciones │ ██ │ batch,seq │
└──────────────────┴───────────────────┴───────────┘
P = parámetros totales. E = parámetros ENTRENABLES.
Full FT: E = P y el optimizador pesa 4x los pesos.
LoRA: E es una fracción minúscula de P.
Vamos pieza por pieza. P es el número de parámetros del modelo.
1. Pesos. En BF16 (glosar: bfloat16, formato de 16 bits usado como estándar en entrenamiento moderno — mismo tamaño que FP16 pero con más rango de exponente, que lo hace más estable; ver el módulo 4 de GPU), cada parámetro son 2 bytes.
pesos = P × 2 bytes
2. Gradientes. Cada parámetro entrenable necesita guardar su gradiente, del mismo tamaño que el parámetro. Aquí E es el número de parámetros entrenables, que en full fine-tuning es P y en LoRA es una fracción minúscula.
gradientes = E × 2 bytes
3. Estados del optimizador. Adam (glosar: el optimizador estándar en deep learning; además del gradiente actual mantiene dos promedios móviles por parámetro — el primer momento, que es la dirección suavizada, y el segundo momento, que es la magnitud — para ajustar el paso de cada peso individualmente) guarda dos estados por parámetro entrenable, y por estabilidad numérica los guarda en FP32, es decir 4 bytes cada uno.
optimizador Adam = E × 2 estados × 4 bytes = E × 8 bytes
Este es el pasajero que sorprende: en full fine-tuning, el optimizador ocupa el cuádruple de lo que ocupan los pesos en BF16. Es la razón principal de que full fine-tuning sea tan caro.
4. Activaciones. Los resultados intermedios de cada capa que hay que conservar para el paso hacia atrás. Dependen del batch_size, del largo de secuencia, del número de capas y del tamaño oculto — no del método de fine-tuning. Es el único término que puedes bajar con perillas: batch más chico, secuencia más corta, o gradient checkpointing (glosar: técnica que descarta las activaciones intermedias y las recalcula durante el paso hacia atrás; baja mucho la memoria a cambio de aproximadamente un 30% más de tiempo de cómputo).
El ejemplo numérico, paso a paso
Modelo de 8.000 millones de parámetros (P = 8e9), BF16, Adam.
Full fine-tuning (E = P = 8e9):
pesos 8e9 x 2 bytes = 16 GB
gradientes 8e9 x 2 bytes = 16 GB
optimizador 8e9 x 8 bytes = 64 GB
──────────────────────────────────────────
subtotal estático = 96 GB
activaciones (batch 4, 2k tok) ~ 8 GB
overhead CUDA y fragmentación ~ 4 GB
──────────────────────────────────────────
TOTAL ~ 108 GB
Ciento ocho gigabytes. Eso no cabe en una H100 de 80 GB: necesitas varias GPUs y una estrategia de repartición del estado del optimizador entre ellas.
LoRA sobre el mismo modelo, con r=16 sobre atención y MLP (all-linear, el default que recomienda el módulo 5). Los parámetros entrenables quedan alrededor del 0.5% de P, digamos E ≈ 42 millones:
pesos (congelados) 8e9 x 2 bytes = 16 GB
gradientes 42e6 x 2 bytes ~ 0.08 GB
optimizador 42e6 x 8 bytes ~ 0.34 GB
──────────────────────────────────────────────
subtotal estático ~ 16.4 GB
activaciones (batch 2, 2k tok) ~ 4 GB
overhead ~ 2 GB
──────────────────────────────────────────────
TOTAL ~ 22 GB
De 108 GB a 22 GB. Y fíjate dónde está ahora el gasto: los pesos congelados son más del 70% del total. Gradientes y optimizador se volvieron ruido estadístico. Eso es lo que hace a LoRA una idea tan buena, y por eso el siguiente paso obvio es atacar el término que quedó dominando.
QLoRA, que es exactamente eso: cargar los pesos base en 4 bits (0.5 bytes por parámetro) y entrenar el LoRA encima:
pesos en 4 bits 8e9 x 0.5 bytes ~ 4 GB
gradientes 42e6 x 2 bytes ~ 0.08 GB
optimizador 42e6 x 8 bytes ~ 0.34 GB
──────────────────────────────────────────────
subtotal estático ~ 4.4 GB
activaciones (batch 2, 2k tok) ~ 4 GB
overhead ~ 2 GB
──────────────────────────────────────────────
TOTAL ~ 10.4 GB
Poco más de diez gigabytes. Un 8B afinándose en una tarjeta de consumidor. Ese salto — de necesitar un clúster a necesitar una RTX — es la razón de que el fine-tuning haya dejado de ser cosa de laboratorios.
"""Estimador de VRAM para fine-tuning. Órdenes de magnitud, no exacto.
La estimación de activaciones es aproximada por diseño: depende de la
arquitectura, de la implementación de atención y de si hay gradient
checkpointing. Úsala para dimensionar, no para prometer.
"""
def estimar_vram(
parametros_b: float, # en miles de millones (8 = 8B)
metodo: str = "qlora", # "full" | "lora" | "qlora"
frac_entrenable: float = 0.005, # LoRA r=16 all-linear ~0.5%
batch_size: int = 2,
seq_len: int = 2048,
capas: int = 32,
hidden: int = 4096,
gradient_checkpointing: bool = True,
) -> dict:
P = parametros_b * 1e9
GB = 1e9 # GB decimales, como en el texto
# 1) Pesos: 2 bytes en BF16, 0.5 bytes cuantizados a 4 bits.
bytes_peso = 0.5 if metodo == "qlora" else 2
pesos = P * bytes_peso / GB
# 2) y 3) Gradientes y Adam solo sobre lo ENTRENABLE.
E = P if metodo == "full" else P * frac_entrenable
gradientes = E * 2 / GB
optimizador = E * 8 / GB # 2 estados x 4 bytes (FP32)
# 4) Activaciones. Con checkpointing se guardan ~1 tensor por capa
# en vez de la decena de intermedios del bloque Transformer.
factor = 2 if gradient_checkpointing else 16
activaciones = batch_size * seq_len * hidden * capas * 2 * factor / GB
overhead = 2.0 # contexto CUDA, fragmentación
total = pesos + gradientes + optimizador + activaciones + overhead
return {
"pesos_gb": round(pesos, 2),
"gradientes_gb": round(gradientes, 2),
"optimizador_gb": round(optimizador, 2),
"activaciones_gb": round(activaciones, 2),
"overhead_gb": overhead,
"TOTAL_gb": round(total, 1),
# Margen de seguridad: nunca dimensiones al 100% de la tarjeta.
"gpu_minima_gb": round(total * 1.2, 1),
}
if __name__ == "__main__":
for metodo in ("full", "lora", "qlora"):
print(metodo.ljust(6), estimar_vram(8, metodo=metodo))
# full TOTAL ~ 100+ GB -> multi-GPU
# lora TOTAL ~ 22 GB -> A6000 / A100 40GB
# qlora TOTAL ~ 10 GB -> RTX 3090 / 4090 / A10
Qué cabe en qué
Con la fórmula anterior, la tabla de referencia. Los valores suponen BF16, Adam, seq_len 2048, batch chico y gradient checkpointing activado. Son órdenes de magnitud para dimensionar, no garantías.
| Modelo | Full FT | LoRA (r=16) | QLoRA (4 bits) |
|---|---|---|---|
| 1B – 3B | ~24 – 40 GB | ~8 – 12 GB | ~5 – 8 GB |
| 7B – 8B | ~100 – 120 GB | ~20 – 24 GB | ~10 – 14 GB |
| 13B – 14B | ~180 – 200 GB | ~34 – 40 GB | ~16 – 20 GB |
| 32B – 34B | ~430 – 470 GB | ~75 – 85 GB | ~28 – 36 GB |
| 70B | ~900 GB+ | ~160 – 180 GB | ~48 – 60 GB |
Traducido a tarjetas concretas:
| GPU | VRAM | Qué alcanza con QLoRA | Qué alcanza con LoRA |
|---|---|---|---|
| RTX 3060 12GB | 12 GB | Hasta 7B – 8B, justo | Hasta 3B |
| RTX 3090 / 4090 | 24 GB | Hasta 14B cómodo, 32B apretado | Hasta 8B |
| A10G / L4 | 24 GB | Igual que arriba, más lento | Hasta 8B |
| A100 40GB | 40 GB | Hasta 32B | Hasta 14B |
| A100 / H100 80GB | 80 GB | 70B apretado | Hasta 32B |
| 2 × H100 80GB | 160 GB | 70B cómodo | 70B |
Ojo: las activaciones crecen linealmente con el largo de secuencia, y el largo lo definiste en el módulo 3 con el p99 de tus longitudes. Pasar de
seq_len2048 a 8192 cuadruplica ese término, y en QLoRA — donde los pesos ya son chicos — las activaciones pasan a ser el gasto dominante. Si tu dataset tiene ejemplos largos, la tabla de arriba se corre un escalón.
Las tres perillas cuando no cabe
Cuando el CUDA out of memory aparece, este es el orden de intervención, de menos a más costoso en calidad:
- Gradient checkpointing. Baja muchísimo las activaciones a cambio de ~30% más de tiempo. Cuesta tiempo, no calidad. Enciéndelo casi siempre.
- Bajar el batch y subir
gradient_accumulation_steps. Acumular gradientes de varios micro-batches antes de actualizar (glosar: procesar 4 batches de 2 acumulando el gradiente y actualizar una vez, lo que es matemáticamente casi equivalente a un batch de 8 pero con la memoria de uno de 2). Es la perilla más segura: el batch efectivo se mantiene. - Bajar
max_seq_length. Solo hasta donde no truncar respuestas. Más allá, estás degradando el dataset, que es lo que el módulo 3 advirtió que no hay que hacer.
Recién si nada de eso alcanza: bajar de LoRA a QLoRA, o bajar de tamaño de modelo.
Dónde entrenar
Cinco opciones, con la que conviene en cada caso. Los precios son órdenes de magnitud a la fecha de escritura y varían mucho por región, disponibilidad y tipo de contrato — trátalos como referencia para comparar entre opciones, no como cotización.
| Opción | Costo aprox./hora | Ventaja | Desventaja |
|---|---|---|---|
| Tu GPU | Solo electricidad | Sin límite de tiempo, datos en casa | Techo de VRAM fijo, cero elasticidad |
| Google Colab | Gratis a ~10 USD/mes | Cero fricción para empezar | Sesiones que se cortan, GPU variable |
| RunPod / Vast.ai | ~0.3 – 3 USD | Lo más barato por GPU-hora | Sin garantías, hay que administrar |
| Lambda / Paperspace | ~1 – 3 USD | Más estable, buen soporte | Disponibilidad intermitente |
| Vertex AI / SageMaker | ~3 – 12 USD | Auditoría, IAM, VPC, cumplimiento | El más caro; complejidad de setup |
La recomendación por etapa:
- Aprendiendo y prototipando: Colab con un modelo de 1B a 3B en QLoRA. Cero costo, ciclo de iteración corto, y todo lo que aprendes se traslada igual a modelos grandes.
- Entrenamiento serio de un proyecto: RunPod o Vast con una 4090 o una A100. Es donde está la mejor relación precio-rendimiento por lejos, y para un entrenamiento de 3 horas la falta de garantías importa poco.
- Empresa con datos sensibles o requisitos de auditoría: Vertex AI o SageMaker. Pagas varias veces más por GPU-hora y estás comprando control de acceso, registro de auditoría, red privada y un contrato. Si tus datos no pueden salir de un perímetro, no hay alternativa barata.
- Uso continuo y predecible: comprar la tarjeta. Una 4090 se paga contra el arriendo en algunos meses de uso intensivo, y además queda para inferencia local.
Cuidado: cuando arriendes por hora, el primer gasto real no es entrenar sino descargar el modelo y subir el dataset. Un 14B en BF16 son unos 28 GB que hay que bajar cada vez que levantas una instancia nueva. Usa el almacenamiento persistente del proveedor o una caché de Hugging Face montada, o vas a pagar veinte minutos de GPU ociosa en cada corrida. Y apaga la instancia: la GPU se cobra encendida, no ocupada.
Errores comunes
- Dimensionar la VRAM con la cuenta de inferencia. Entrenar suma gradientes, estados del optimizador y activaciones. Con full fine-tuning, Adam solo pesa el cuádruple de los pesos en BF16.
- Olvidar que Adam guarda dos estados en FP32. Son 8 bytes por parámetro entrenable, el ítem más grande del presupuesto en full fine-tuning y la razón principal de que sea inviable en una sola GPU.
- Elegir el modelo más grande que cabe. Cabe para entrenar y después hay que servirlo en producción, todos los días, pagando cada token. Empieza por el más chico que podría funcionar y sube solo si la métrica lo exige.
- Partir de un modelo base cuando el instruct servía. El instruct ya trae la mayor parte del comportamiento gratis, así que tus 800 ejemplos rinden mucho más. El base es para casos donde el alineamiento estorba.
- Usar un modelo base sin definir el chat template. Muchos no traen uno, y armar la plantilla al vuelo distinto en entrenamiento y en inferencia produce un modelo que entrenó bien y responde mal.
- Asumir que “está en Hugging Face” es “puedo usarlo comercialmente”. Las licencias varían por familia, por tamaño y por versión, y se heredan al adaptador y al modelo fusionado. Lee el
LICENSEdel modelo exacto. - No activar gradient checkpointing. Es la perilla que más memoria libera y solo cuesta tiempo de cómputo. Debería estar encendida por defecto en casi todo entrenamiento.
- Bajar
max_seq_lengthpara que quepa, sin mirar el p99. Estás truncando respuestas y enseñándole al modelo a cortarse. Baja el batch y acumula gradientes antes de tocar la longitud. - Dejar la instancia arrendada encendida. Se cobra por hora encendida, no por hora útil. La factura del fin de semana olvidado es la anécdota más repetida del rubro.
Cierre del bloque básico
Con estos cuatro módulos ya tienes el marco completo antes de entrenar nada: cuándo corresponde hacer fine-tuning y cuándo la respuesta es prompt o RAG (M1), cómo se construye el dataset que es la mayor parte del resultado (M2), cómo se convierte ese dataset en tokens y máscaras que el modelo pueda aprovechar (M3), y qué modelo y qué fierro hacen falta para ejecutarlo (M4). Ninguna de estas cuatro decisiones se arregla después con hiperparámetros. Todo lo que viene en el bloque intermedio se apoya en que estas estén bien tomadas.
La frase para llevarse
“La GPU que necesitas no la define el modelo, la define el método: el mismo 8B pide 108 GB con full fine-tuning y 10 con QLoRA. Aprende a hacer la cuenta y dejas de arrendar de más.”
Qué viene
Módulo 5: LoRA y QLoRA a fondo, y con esto arranca el bloque intermedio. Hasta ahora LoRA fue una caja negra que baja la VRAM; toca abrirla. Vamos a ver la descomposición de bajo rango que hay detrás — por qué dos matrices flacas pueden reemplazar el ajuste de una gigante —, qué significan de verdad r y lora_alpha y cómo se eligen, en qué módulos conviene enchufar los adaptadores (q_proj, v_proj, o todas las lineales, y qué cambia), el dropout, y la mecánica interna de QLoRA: la cuantización NF4, el doble cuantizado y los optimizadores paginados. Con la configuración de peft explicada línea por línea y el criterio para saber cuándo r=8 alcanza y cuándo tu tarea de verdad pide r=64.
Material relacionado
- Módulo 3: chat templates, tokens especiales y loss masking
- Módulo 1: cuándo hacer fine-tuning y cuándo no
- GPU Módulo 2: VRAM, la memoria manda
- GPU Módulo 4: precisión y cuantización
- Transformers Módulo 7: fine-tuning e instruction tuning
- Glosario de GPU e IA local
- Glosario de Fine-tuning y adaptación de modelos
Preguntas sobre este curso
Regístrate gratis para hacer preguntas y guardar tu progreso.
✉ Entérate de lo nuevo
Recibe un aviso cuando publique nuevos cursos, workshops o investigaciones. Sin spam.