← Volver al Blog

Nanochat de Karpathy: cuánto cuesta hoy entrenar un modelo tipo GPT-2 (era $43,000 en 2019)

En 2019, entrenar OpenAI GPT-2 costaba $43,000 USD. El dato aparece en el dev/LEADERBOARD.md del propio repositorio de nanochat: 32 TPU v3, 168 horas, la tarifa de entonces de $8/hora. Salió el paper de Radford et al., el mundo aprendió qué era un modelo de lenguaje grande, y para empezar a jugar hacía falta poco menos que una ronda semilla.

Siete años más tarde, Andrej Karpathy publicó nanochat: 8,159 líneas de Python y shell (yo mismo pasé el wc -l) que reproducen un modelo con la misma capacidad — mismo CORE score de 0.256 — en 2 horas y $48 USD en un nodo 8xH100. En spot, se queda en $15.

En este artículo repaso speedrun.sh paso a paso y desgloso de dónde salen esos números. Si viste pasar el tuit del “reproduce GPT-2 en un finde” y sospechaste que había letra pequeña, sí que la hay. Yo la pongo aquí encima.

$43,000 vs $48: la comparación no es apples-to-apples, pero sí es honesta

Antes de entrar en el código, hay que estabilizar la comparación. Estos son los dos puntos.

ConceptoGPT-2 original (2019)nanochat speedrun (2026)
CORE score0.25650.2585 (leve mejora)
Hardware32× TPU v38× H100 SXM
Tiempo de entrenamiento168 horas~2 horas
Costo on-demand~$43,000 USD~$48 USD
Costo spotn/d~$15 USD
Parámetros del modelo~1.5B (GPT-2 large)561M (depth=24, dim=1536)
Vocabulario50,25732,768

Fuentes: dev/LEADERBOARD.md del repo, README.md, Karpathy Oct 2024 X thread anunciando nanochat.

Dos advertencias antes de entusiasmarnos. Primera: la comparación de CORE score valida que se resuelve la misma tarea con calidad equivalente, pero el modelo de nanochat tiene ~1/3 de los parámetros del GPT-2 original. Karpathy optimizó por “capacidad medida” antes que por “conteo de parámetros idéntico”. Segunda: el precio de $48 asume que ya tienes acceso a un nodo 8xH100. En 2026 eso es más fácil que hace dos años (RunPod, Lambda Labs, Vast.ai listan H100 SXM entre $2 y $6/hora on-demand, ~$1.50/hora spot desde LatAm con VPN a la región US-East), pero sigue siendo una barrera real si no tienes tarjeta de crédito internacional.

De dónde viene la reducción: no es una sola cosa

La tentación es pensar “las GPU se abarataron y ya”. Es parte de la respuesta, pero solo parte. El LEADERBOARD.md de nanochat rastrea las mejoras acumuladas desde 168h hasta 1.65h:

#TiempoCORECambioFecha
0168 h0.2565Baseline GPT-2 OpenAI2019
13.04 h0.2585Baseline d24 en 8xH1002026-01-29
22.91 h0.2578d26 + fp82026-02-02
32.76 h0.2602Batch total → 1M tokens2026-02-05
42.02 h0.2571Dataset: NVIDIA ClimbMix2026-03-04
51.80 h0.2690Autoresearch round 12026-03-09
61.65 h0.2626Autoresearch round 22026-03-14

Del 168h original al 3.04h del baseline moderno, la mejora es ~55x y viene del salto de TPU v3 a H100 más siete años de progreso en la pila de entrenamiento (Flash Attention 3, torch.compile, mejores optimizadores). Del 3.04h al 1.65h actual, la mejora es ~1.8x adicional y viene de decisiones específicas del repositorio: FP8 en las capas lineales, ajuste del batch total, cambio de dataset a NVIDIA ClimbMix, autoresearch (búsqueda de hiperparámetros automatizada).

Traducido a plata: los $43,000 originales bajan a ~$800 solo por hardware moderno, y de ahí a $48 por las optimizaciones del repo. El repo aporta un factor 16x sobre lo que ya te daba tirar el mismo experimento con GPU actuales sin ajustar nada.

Qué hace realmente speedrun.sh (79 líneas)

El corazón de nanochat es runs/speedrun.sh, un script de 79 líneas que ejecuta el pipeline completo. Lo repasamos por bloques.

Bloque 1: setup del entorno (líneas 1-30)

export OMP_NUM_THREADS=1
export NANOCHAT_BASE_DIR="$HOME/.cache/nanochat"
mkdir -p $NANOCHAT_BASE_DIR

command -v uv &> /dev/null || curl -LsSf https://astral.sh/uv/install.sh | sh
[ -d ".venv" ] || uv venv
uv sync --extra gpu
source .venv/bin/activate

Instala uv (gestor de paquetes moderno de Astral), crea el venv, instala dependencias con extras de GPU. Nada exótico. La única decisión de diseño que importa: todo el estado intermedio (checkpoints, tokenizer, dataset shards) vive en ~/.cache/nanochat, así que puedes borrar y reintentar sin ensuciar el repo.

Bloque 2: dataset y tokenizer en paralelo (líneas 30-45)

python -m nanochat.dataset -n 8
python -m nanochat.dataset -n 170 &
DATASET_DOWNLOAD_PID=$!
python -m scripts.tok_train
python -m scripts.tok_eval

Aquí hay un truco simple y elegante. Descarga primero 8 shards (~2,000 millones de caracteres) para entrenar el tokenizer, y en paralelo lanza la descarga de los 170 shards del dataset completo. El tokenizer tarda unos minutos en entrenarse; mientras tanto, el disco se llena con los datos que vas a necesitar después. Wallclock ahorrado: ~15 minutos. Es el tipo de detalle que separa “código académico” de “código pensado para correr”.

Bloque 3: pretraining y evaluación (líneas 60-70)

wait $DATASET_DOWNLOAD_PID
torchrun --standalone --nproc_per_node=8 -m scripts.base_train -- \
    --depth=24 --target-param-data-ratio=8 --device-batch-size=16 --fp8 --run=$WANDB_RUN
torchrun --standalone --nproc_per_node=8 -m scripts.base_eval -- --device-batch-size=16

El comando de una sola línea que dispara el entrenamiento completo. --depth=24 es “la perilla” — un solo entero define la cantidad de capas, la dimensión del modelo, la cantidad de heads, el learning rate, el tamaño del batch y la cantidad de tokens que se van a leer. Esto no es magia; es que build_model_meta y get_scaling_params en scripts/base_train.py derivan todo lo demás mecánicamente:

base_dim = depth * args.aspect_ratio          # 24 * 64 = 1536
num_heads = model_dim // args.head_dim        # 1536 / 128 = 12
target_tokens = ratio * num_scaling_params    # 8 * ~561M = 4.5B tokens

Bloque 4: SFT (líneas 71-79)

torchrun --standalone --nproc_per_node=8 -m scripts.chat_sft -- --run=$WANDB_RUN
torchrun --standalone --nproc_per_node=8 -m scripts.chat_eval -- -i sft

Después del pretraining viene el SFT (Supervised Fine-Tuning): le enseña al modelo el formato de conversación, los tokens especiales, cómo responder preguntas de opción múltiple. RLHF no está en el speedrun — es un paso opcional en chat_rl.py que suma precisión solo en GSM8K (matemática de primaria) y sigue siendo experimental.

¿Se puede entrenar esto en tu laptop? Depende de qué llames “esto”

Aquí voy a discutir con la lectura entusiasta del anuncio original. La respuesta corta: no, un GPT-2 completo no. La respuesta larga es más interesante.

El speedrun oficial pide 8×H100 SXM y 4e19 FLOPs de cómputo total. Ese cálculo no te lo hace ninguna laptop. Ni siquiera una Mac Studio con M3 Ultra: la memoria unificada de 192 GB alcanza, pero la potencia de cómputo efectiva para bf16/fp8 es 15-20x menor que un H100 SXM. Un H100 SXM entrega ~989 TFLOPS bf16; el M3 Ultra ronda 50 TFLOPS bf16 según benchmarks públicos de Georgi Gerganov (llama.cpp). Traducción: lo que en 8xH100 tarda 2 horas, en una Mac Studio M3 Ultra tardaría del orden de 150-200 horas si es que la runtime aguanta sin OOM.

Pero puedes correr una versión más chica del mismo pipeline en tu laptop, cambiando la perilla. --depth=12 genera un modelo de ~50M parámetros que corre en una sola H100 en unos 40 minutos, o en una RTX 4090 (consumer) en unas 6-8 horas. No es GPT-2, es un GPT-2 bebé, pero el pipeline es literalmente el mismo. Y ese es el punto pedagógico real del repositorio: no que puedas hacer GPT-2 en tu laptop, sino que puedas entender cómo se hace GPT-2 leyendo 8,159 líneas y ajustando una perilla.

Este razonamiento — “un modelo más pequeño bien alimentado gana a uno grande mal alimentado” — lo profundicé en inglés en Cheap model won: context beats parameters, que sirve como marco mental para entender por qué depth=24 con --target-param-data-ratio=8 le gana a experimentos más ambiciosos.

El truco silencioso: FP8 con ~150 líneas

Un detalle que vale la pena aislar. nanochat/fp8.py implementa el reemplazo de capas lineales con FP8. La implementación equivalente en torchao son ~2,000 líneas; nanochat se queda en ~150 líneas al comprometerse con una sola estrategia de scaling (tensorwise) y renunciar a las variantes más sofisticadas.

# nanochat/fp8.py (esencia simplificada)
class Float8Linear(nn.Module):
    def forward(self, x):
        # Cuantiza x a float8_e4m3fn (precisión), y grad a e5m2 (rango)
        x_fp8, x_scale = quantize_e4m3(x)
        out = torch._scaled_mm(x_fp8, self.weight_fp8,
                                scale_a=x_scale, scale_b=self.weight_scale,
                                out_dtype=torch.bfloat16)
        return out

Es un ejemplo textbook de “escoger la peor estrategia que aún funciona bien”. torch._scaled_mm es 2x más rápido que la multiplicación bf16 equivalente en H100. El costo es que solo aplica a capas cuya dimensión es múltiplo de 16 y >= 128, así que un filtro en scripts/base_train.py elige qué reemplazar y qué dejar en bf16. Este tipo de “150 líneas suficientes” es lo que hace legible al repositorio entero.

Costo real desde LatAm en 2026

Si estás pensando “entonces mañana pruebo esto”, los números concretos de acceso desde LatAm:

  • RunPod H100 SXM 80GB: $3.99/hora on-demand, $1.99/hora spot. 8 GPUs = $32/hora on-demand.
  • Lambda Labs H100 SXM: $3.29/hora on-demand (nodo dedicado 8x).
  • Vast.ai H100: $2.10-2.80/hora spot (buscar host verificado con al menos 100 mbps).

Con RunPod on-demand: 8 GPUs × 2 horas × $4 = ~$64. Con spot: ~$32. El README dice $48; la diferencia es que Karpathy midió con precios de Lambda a inicios de 2026, cuando estaban un poco más bajos. Rangos actuales: $32-$70 por speedrun completo. Sin sorpresas.

Lo que puede salirte caro es no cortar la instancia. Un olvido de 24 horas en un nodo 8xH100 son ~$800. Configurar auto-shutdown por inactividad de CPU en el script de lanzamiento es la primera línea de defensa. Segunda línea: cargar el checkpoint final a S3 (o al bucket que uses) antes del bloque de evaluación, no después. Si el training termina y se cae la instancia sin subir el modelo, perdiste esas 2 horas.

Lo que este repositorio realmente entrega

Después de leer las 8,159 líneas y correr el speedrun tres veces, mi conclusión es que nanochat no es un tutorial. Es un artefacto de referencia. La distinción importa: un tutorial te explica; un artefacto de referencia te muestra cómo se ve un pipeline moderno de entrenamiento cuando alguien con 15 años de contexto en el área lo escribe de manera minimalista.

Los --depth, --target-param-data-ratio, --device-batch-size no son mágicos. Son la forma en que un investigador serio parametriza el mismo experimento a distintas escalas para poder iterar rápido. Ver eso en código legible de 8,000 líneas es lo que $43,000 en 2019 no compraba: no la potencia de cómputo, sino el conocimiento de qué perillas ajustar.

Ese conocimiento sigue siendo caro. Solo que ahora está en un repo público bajo licencia MIT en lugar de un paper de OpenAI. Es un cambio.

ken imoto · WebRTC & Voice AI engineer · kenimoto.dev