// M2 · lección 04
Más datos, no más red: de small a medium-v4
La parte del módulo que no estaba en el plan: por qué small se quedaba corto, qué se probó primero y qué se probó después, la escalera de Elo que estaba mal calibrada y el modelo de 115 millones de parámetros sobre 1 681 millones de tokens del que parten los dos módulos siguientes.
Parte 4 de 4 del módulo «El decoder». Viene de «El decoder: exportarlo y mirar dentro» y cierra el módulo con el modelo que M4 y M5 van a afinar.
Qué vas a construir
Al terminar esta parte tendrás medium-v4: el decoder de 115 120 128 parámetros entrenado
sobre 1 681 millones de tokens, con 1504 de Elo estimado (IC 95 % 1446-1558) y un 99,8 % de
jugadas legales sin máscara. Es el modelo del que parten el afinado por Elo y los adaptadores de
M4 y los tres modelos alineados de M5, y es el que se publica como chorcat/rukh-medium.
Pero el objeto de esta parte no es el modelo: es cómo se llegó a él, porque el camino
contradice lo que las tres partes anteriores daban por sentado. M2 cerró diciendo que a small
le faltaba entrenamiento y que el siguiente paso era una red más grande. Las dos cosas estaban
mal, y lo que las desmintió fue una división y una escalera.
El diagnóstico cabe en una división
La tercera parte terminó con el hueco entre la pérdida de entrenamiento y la de validación de
small creciendo —0,021, 0,046, 0,060— mientras el top-1 se aplanaba. Antes de tocar nada, la
cuenta que había que hacer:
data/tokens/uci/train/meta.json: 2 949 514 partidas, 240 068 954 tokensparámetros de small: 38 971 392tokens únicos por parámetro: 240 M / 39 M = 6,2pasadas sobre el corpus: 20 000 pasos × 256 secuencias × 200 tokens = 1 024 M / 240 M = 4,3Seis tokens por parámetro, cuando la relación que la literatura de escalado da como razonable
ronda los veinte. Y cuatro pasadas y pico sobre el mismo material. El hueco que crecía no era
«algo de sobreajuste, normal»: era la firma de repetir material, y explica también por qué
medium, con el triple de parámetros y los mismos datos, solo había comprado 84 puntos de Elo
—1091 (IC 990-1194) frente a los 1007 (920-1101) de small, medidos los dos en la escalera de
entonces (D-054)— con los intervalos solapados. Más red sobre el mismo libro recita mejor el libro.
Y había 238 millones de tokens parados: el mes de febrero entero era validación, para un bucle que evalúa leyendo 640 000. Esa es la primera palanca, y cuesta cero descargas.
Tres corridas, una palanca cada una
// Antes de empezarQué cuesta cada lab, y cuál puedes saltarte
| Lab | Ruta | Reloj | Deja | Atajo |
|---|---|---|---|---|
| Corrida v2Más datos con la misma red pequeña | cuesta máquina | ~45 min | checkpoints/small-v2/best.pt | no hay |
| Corrida v3La Elite de 44 meses, tokenizada y entrenada | cuesta máquina | ~1,5 h de descarga + 5 min de tokenizar + ~45 de entrenar | data/elite/games.parquet, checkpoints/small-v3/best.pt | rukh pull rukh-games-elite, y rukh pull small para el modelo |
| Corrida v4medium-v4: la capacidad, cuando por fin está justificada | cuesta máquina | 4 h 3 min + ~30 de evaluación | checkpoints/medium-v4/best.pt, la base de M4 y M5 | rukh pull medium-v4 |
- cuesta máquina
- Tiempo real de GPU, red o motor. El reloj es el de la RTX 5090 de referencia.
Las tres corridas comparten planificador, semilla y la misma validación congelada: las 100 000 primeras partidas de 2025-02, que desde aquí es lo único que no entrena (D-062). Cada una cambia una sola cosa respecto a la anterior, para que la diferencia sea atribuible.
v2: devolver el mes de validación al entrenamiento. configs/data/pipeline-v2.yaml deja
val_games: 100000 y manda el resto de febrero al corpus: 240 M → 471 M tokens únicos, 6,2 →
12,1 por parámetro. La misma receta de small, 28 000 pasos en vez de 20 000.
uv run rukh data tokenize --config configs/data/pipeline-v2.yaml --scheme uci --packuv run rukh train --config configs/train/small-v2.yamlv3: cambiar de qué está hecho el corpus. La Lichess Elite Database publica en
database.nikonoel.fr las partidas de 2500+ contra 2300+, sin bullet, por meses. Veinticuatro meses
son unos 6,5 millones de partidas, y son a la vez más datos y otra distribución: el corpus de 1800+
tiene un 3,4 % de partidas con las blancas en 2400 o más, la Elite un 93 %.
configs/data/pipeline-v3.yaml los añade con extra_train_parquets y deja la validación donde
estaba, para que la pérdida siga siendo comparable.
uv run rukh data elite --config configs/data/pipeline-elite44.yaml # o: uv run rukh pull rukh-games-eliteuv run rukh data tokenize --config configs/data/pipeline-v3.yaml --scheme uci --packuv run rukh train --config configs/train/small-v3.yaml(pipeline-elite44.yaml descarga los 44 meses que usa la corrida final; v3 se hizo con los
primeros 24 y la config tokeniza lo que haya en data/elite/games.parquet. Si sigues el orden de
esta página tu v3 llevará 44, que es más de lo que tuvo la de referencia.)
Salida real de las dos, con v1 de M2 al lado. «General» es la validación congelada; «fuerte» es la misma pérdida sobre partidas de 2200+:
| Modelo | Par. | Tokens únicos | general | top-1 | fuerte 2200+ | top-1 | hueco train/val |
|---|---|---|---|---|---|---|---|
small v1 |
39 M | 240 M | 1,5197 | 51,20 % | 1,5391 | 50,84 % | +0,0603 |
medium v1 |
115 M | 240 M | 1,4782 | 52,31 % | 1,4880 | 52,06 % | — |
small v2 |
39 M | 471 M | 1,4703 | 52,49 % | 1,4652 | 52,69 % | +0,0242 |
small v3 |
39 M | 1 095 M | 1,4770 | 52,19 % | 1,4537 | 53,02 % | — |
Dos lecturas, en el orden en que se hicieron:
- Recuperar el mes vale −0,049 nats y el hueco cae a la mitad.
smallv2, con 39 millones de parámetros, bate almediumde 115 millones entrenado con los datos viejos. El cuello eran los datos; ahora está medido y no inferido. El tiempo de entrenamiento fue 58 minutos. - La composición mueve otra cosa que la cantidad. v3 cede 0,007 en juego promedio —su corpus
se parece menos a la validación— y gana 0,012 sobre juego fuerte. Y mueve el eje de Elo: la
divergencia entre las distribuciones condicionadas a
<2800>y a<1800>pasa de 0,047 nats en v1 y 0,049 en v2 a 0,115 en v3. Duplicar los datos (v2) no había movido ese eje; cambiar de qué están hechos (v3) sí. Sin élite, el «juega como 1500 / 2000 / 2400» de M4 no tenía de dónde salir.
La escalera estaba torcida
Mientras se medían esas corridas apareció una contradicción que ningún modelo puede resolver:
small v2 puntuaba 0,725 contra un Stockfish con UCI_Elo 1320 y perdía veinte a cero contra
skill-3, un rival etiquetado con 1250. Un modelo no puede ganar a uno de 1320 y perder siempre
contra uno de 1250. Lo que se puede medir es el rival, y el descubrimiento entero —motor contra
motor, la tabla de los cuatro escalones medidos entre 1381 y 1678 en vez de entre 800 y 1250, y el
control de uci-1500 a +179 sobre uci-1320 que valida el método— está contado en el lab 4 de la
segunda parte, al lado del informe que lo destapó. Aquí solo lo que esa parte no podía saber
todavía: qué le hizo la corrección a todo lo publicado hasta ese día.
| Etapa | Publicado | Corregido | IC 95 % |
|---|---|---|---|
small v1 |
1007 | 1359 | 1293-1429 |
small v2 |
1070 | 1407 | 1344-1462 |
small v3 |
1095 | 1425 | 1367-1485 |
El listón de 1200 nunca se falló. small v1 estaba publicado en Hugging Face con «Elo bar not
met» y estaba en 1359. Hubo que corregir once model cards, dos planes y las lecciones, explicando
el porqué y no solo la cifra. Lo relativo no cambia: la corrección sube a todos por igual, y el
trabajo de datos vale +66 Elo en la escala corregida igual que valía +88 en la torcida.
medium-v4: capacidad, cuando por fin está justificada
Con 1 681 millones de tokens únicos —enero, el resto de febrero y 44 meses de Elite, 18 942 740
partidas— la división sale a 14,6 tokens únicos por parámetro para 115 millones (1 681 M / 115
M), siete veces más que los 2,1 de medium v1 y a la altura de los 12,1 de small v2, así que
medium deja de ser una red grande sobre un libro pequeño. configs/train/medium-v4.yaml: 48 000
pasos, 2 457 millones de tokens vistos —21 por parámetro, la cifra que engaña si se confunde con
la de únicos—, 1,46 pasadas.
uv run rukh data tokenize --config configs/data/pipeline-v4.yaml --scheme uci --packuv run rukh train --config configs/train/medium-v4.yamluv run rukh eval --model checkpoints/medium-v4/best.pt --config configs/eval/greedy.yaml --stage medium-v4-greedyTokenizar tarda cinco minutos y deja 3,3 GB en data/tokens-v4/uci/. El entrenamiento duró
4 h 3 min y es el trabajo más largo de la primera fase; se lanza al final de una sesión y se
recoge en la siguiente. La evaluación canónica —greedy.yaml, temperatura 0,05, la cabecera fijada
en 1800, la escalera corregida— tarda media hora.
| Modelo | Par. | Tokens únicos | val/loss | Elo | IC 95 % | legal argmax | top-1 | puzles |
|---|---|---|---|---|---|---|---|---|
small v1 |
39 M | 240 M | 1,5197 | 1359 | 1293-1429 | 99,40 % | 51,10 % | 22,07 % |
medium v1 |
115 M | 240 M | 1,4782 | — | — | 99,40 % | 52,90 % | 23,93 % |
small v2 |
39 M | 471 M | 1,4703 | 1407 | 1344-1462 | 99,30 % | 51,80 % | 26,93 % |
small v3 |
39 M | 1 095 M | 1,4770 | 1425 | 1367-1485 | 99,10 % | 52,40 % | 26,73 % |
medium-v4 |
115 M | 1 681 M | 1,3733 | 1504 | 1446-1558 | 99,80 % | 54,40 % | 37,50 % |
Las columnas de Elo, legalidad, top-1 y puzles son las de la evaluación canónica (greedy.yaml),
que no es la misma medida que el top-1 de validación de la tabla anterior: por eso medium v1
lee 52,90 % aquí y 52,31 % allí. Su Elo va en blanco porque solo se midió en la escalera torcida
—1091 (990-1194)— y nunca se reevaluó en la corregida; ponerle un número a esa celda sería
inventarlo.
Tres cosas de esa fila:
- No era la arquitectura. El mismo
medium, con la misma receta, pasa de 1,4782 con 240 M de tokens repetidos 4,3 veces a 1,3733 con siete veces más material único y 1,46 pasadas. - La legalidad no era una tendencia. La serie 99,40 → 99,30 → 99,10 % de las tres
smallse había leído como algo a vigilar. Era un modelo de 39 M estirándose sobre un corpus creciente; con capacidad suficiente, fuerza y legalidad suben juntas, y 99,80 % es la más alta del proyecto. - Los puzles son donde más se ve. De 22,07 % a 37,50 %, un 70 % relativo. Un puzle es la jugada única que gana, no la que jugó un humano, y es la métrica que menos premia imitar.
El reparto de los +145 Elo entre small v1 y medium-v4, palanca a palanca y en la escala
corregida: recuperar el mes de validación +48, la Elite +18, la capacidad ya justificada
+79. El condicionamiento por Elo, cero: esa historia es la de M4.
Lo que se publica y lo que se mide después
chorcat/rukh-small es small v3 —la mejor small de la serie—, y chorcat/rukh-medium es
medium-v4. La demo sirve small porque 79 MB en fp16 caben en un móvil y 231 no; medium-v4 es
para correrlo en local y para lo que viene.
Aquí es donde esos dos modelos publicados empiezan a ganarse el sueldo, y no es para que juegues
contra ellos: es para comparar. Exporta tu medium-v4 y cárgalo en la demo con «Tu propio
modelo»; después elige Rukh medium (fp16) en el desplegable de etapa y juega la misma apertura.
Si tu corrida salió bien, las dos partidas deberían parecerse; si la tuya juega notablemente peor,
tienes una pista de que algo del entrenamiento no fue como el de referencia, y la tienes antes
de gastar media hora de evaluación en averiguarlo.
Sus cards llevan los números de la evaluación canónica del día en que se publicaron, y ahí
aparece un detalle que conviene tener presente al comparar: small v3 midió 1425 (1367-1485) el 19
de septiembre y 1365 (1293-1423) el 20, con la misma config, la misma semilla y los mismos
pesos. El rival juega por tiempo y con UCI_LimitStrength, que aleatoriza a propósito, así que la
escalera se repite a sí misma con un suelo de unos 40 puntos (D-107). Es una báscula de baño que
te da 82 y 84 al subirte dos veces seguidas: no está rota, pero con ella no se puede afirmar que
has adelgazado un kilo, y una diferencia entre dos pesadas solo empieza a decir algo cuando supera
con holgura esos dos kilos de oscilación. M5 va a construir un instrumento distinto por esto. De
momento, la regla: dos Elo de la escalera se comparan dentro de una misma tirada, no entre
tiradas. Las cifras de esta parte son las del 19 y 20 de septiembre; la tabla única las volvió a
medir el 21 en una sola tirada (small v1 1355 (1297-1420) y medium-v4 1535 (1476-1599)) y M6
explica la diferencia.
// Ejercicio 01Haz la división antes de tu siguiente corrida
Toma meta.json del corpus con el que entrenaste small en la parte 2 y calcula tokens únicos
por parámetro y pasadas para esa corrida. Después haz lo mismo para tiny (6 000 pasos de 128 × 2
× 200 tokens sobre el mismo corpus). ¿Cuál de las dos estaba más lejos de los veinte? ¿Cuál
repitió más veces el material?
// SoluciónVer la solución
small: 240 M / 39 M ≈ 6,2 tokens por parámetro y 1 024 M / 240 M ≈ 4,3 pasadas. tiny: 240 M
/ 5,3 M ≈ 45 por parámetro, y 6 000 × 256 × 200 = 307 M tokens vistos, 1,3 pasadas. tiny
estaba sobrado de datos y corto de red; small, al revés. Por eso la palanca correcta para
small era material y para tiny habría sido capacidad —que es, dicho de otro modo, lo que
small es respecto a tiny.
// Ejercicio 02Predice el Elo desde la pérdida, y comprueba cuánto te fías
Con small v1 (1,5197 → 1359) y medium-v4 (1,3733 → 1504) traza una recta pérdida-Elo y predice
el Elo de small v2 (1,4703). Compáralo con el medido, 1407 (1344-1462). ¿Sirve la recta como
criterio de aceptación?
// SoluciónVer la solución
La pendiente sale de (1504 − 1359) / (1,5197 − 1,3733) ≈ 990 Elo por nat, y la predicción para v2 es 1359 + 990 × 0,0494 ≈ 1408: clavado. Y aun así no sirve como criterio, por dos razones que el módulo ya ha enseñado. La recta se ajustó sobre modelos entrenados con la distribución de la validación, y v3 —con élite— cae fuera de ella (1,4770 predice 1401 y midió 1425). Y la escalera tiene un suelo de reproducibilidad de unos 40 puntos, así que una predicción a diez puntos es más precisa que el instrumento que la contrastaría. Sirve para descartar un experimento en 40 minutos; el Elo se mide siempre.
Qué has aprendido, cómo se mide
Que la primera pregunta ante un modelo que se queda corto no es cuántos parámetros tiene sino cuántos tokens distintos ha visto por cada uno, y que el hueco entre entrenamiento y validación es el instrumento que responde antes de gastar una GPU. Que cantidad y composición del corpus son dos palancas distintas, y que solo la segunda mueve un eje que no estaba en los datos. Que una escala contra la que mides tiene que haberse medido a sí misma, y que un control con respuesta conocida es lo que te dice si puedes fiarte de ella. Y que escalar la red es la decisión correcta exactamente cuando la división lo permite, y la equivocada un momento antes.
Lo medido: medium-v4, 1504 de Elo (1446-1558) y 99,80 % de legalidad sin máscara, los dos
criterios del hito con el intervalo entero por encima; 37,50 % de puzles; y la historia
completa en D-062 a D-073 del registro de decisiones.
Lo siguiente es M3, el encoder, que va a pasar por la misma corrección: el que se publicó también se reentrenó a 39 M sobre este corpus, y la última parte de M3 cuenta qué compró eso y qué no. La cheatsheet del módulo está justo debajo.
// cheatsheet M2
Once preguntas para llevarte
- 01Explica la atención en tres frases.
- Cada posición proyecta su vector en una consulta (Q), una clave (K) y un valor (V). El producto escalar entre la consulta de una posición y las claves de todas las demás, dividido por la raíz de la dimensión de la cabeza y pasado por softmax, da un reparto de pesos que suma 1. La salida es la media de los valores ponderada por esos pesos: softmax(QKᵀ/√d)·V. La escala 1/√d existe porque el producto escalar de d dimensiones tiene varianza d y sin ella el softmax saturaría y el gradiente se anularía.
- 02¿Qué es la máscara causal y qué pasa si falta?
- Poner a menos infinito, antes del softmax, el peso de toda clave posterior a la consulta, de modo que la matriz de atención queda triangular inferior. Sin ella la posición t puede leer el token t+1, que es justo el que tiene que predecir: la tarea pasa de predecir a copiar, la pérdida se desploma y el top-1 se dispara, pero al generar (donde no hay futuro) el modelo juega al azar. Se detecta porque la legalidad sin máscara se hunde, y se verifica con un test: cambiar un token futuro no debe mover los logits pasados ni un decimal.
- 03¿Por qué pre-norm y no post-norm?
- Pre-norm pone el LayerNorm dentro de la rama, antes de la subcapa (x = x + attn(ln(x))), así que la conexión residual queda libre: la derivada de cada bloque es la identidad más la de la subcapa y el gradiente llega íntegro desde la pérdida hasta el embedding. Post-norm normaliza después de la suma, mete la normalización en el camino residual y hace que una pila profunda solo entrene con warmup largo e inicialización muy cuidada. Es un cambio de dos caracteres que convierte doce capas entrenables con suerte en doce capas entrenables.
- 04¿Qué hace el warmup y qué pasa sin él?
- Sube la tasa de aprendizaje linealmente desde cero durante los primeros pasos (1 000 en rukh-small) antes de empezar el coseno. En los primeros pasos, Adam estima el segundo momento con un puñado de gradientes y su paso normalizado puede mover un parámetro tanto como la tasa entera; sobre una red recién inicializada eso satura la atención y el modelo cae en predecir siempre las jugadas más frecuentes. No suele explotar con un NaN: se queda en una pérdida mediocre que ya no baja, que es peor porque no se nota.
- 05Temperatura y top-k: ¿en qué se diferencian?
- La temperatura divide los logits antes del softmax y reescala toda la distribución: por debajo de 1 la concentra (con 0 es el argmax), por encima la aplana. El top-k no reescala, recorta: deja solo los k tokens de mayor probabilidad y pone el resto a cero. Son complementarios, no alternativas. Con 2 030 jugadas, la cola de mil opciones improbables suma un porcentaje apreciable de disparates; el top-k la elimina y la temperatura decide cuánto arriesga el modelo dentro de lo que queda. Rukh usa temperatura 0,6 y k = 20.
- 06¿Qué mide la legalidad sin máscara, por qué hay dos cifras y por qué no vale medirla con máscara?
- Mide el porcentaje de jugadas legales que propone el modelo cuando se le deja emitir el token que quiera, sobre 1 000 posiciones de validación. Se publica dos veces: legality_argmax toma el token más probable (sin temperatura ni top-k) y legality_sampled saca uno como lo saca la demo (temperatura 0,6 y top-k 20), que es siempre la menor de las dos porque muestrear mete cola. El listón de ≥ 99 % para rukh-small es el argmax, porque habla de lo que saben los pesos y no de un ajuste del muestreador; la muestreada es lo que experimenta un jugador y va al lado con su definición. Con la máscara de legalidad puesta el 100 % está garantizado por construcción, así que estarías midiendo a python-chess y no al modelo.
- 07¿Por qué bf16 y no fp16?
- Los dos ocupan 16 bits, pero reparten distinto. fp16 tiene 5 bits de exponente y 10 de mantisa: mucha precisión y poco rango, con el mínimo normal en torno a 6e-5, justo donde viven los gradientes de una red profunda, así que se van a cero y hace falta un GradScaler con escalado dinámico de la pérdida. bf16 tiene 8 bits de exponente (el mismo rango que fp32) y 7 de mantisa: menos precisión por número y nada que se desborde por abajo, así que entrena sin escalar nada. En una GPU moderna es nativo, así que la simplicidad sale gratis.
- 08¿Qué es un checkpoint y qué tiene que llevar dentro?
- Una fotografía del entrenamiento en disco. No basta con los pesos: lleva el paso alcanzado, el estado del optimizador (los momentos de Adam), el estado de los generadores aleatorios, la configuración del modelo y de la tirada y la procedencia (hash del vocabulario, hash del manifiesto de datos y SHA de git). Con eso se puede reanudar exactamente donde se cortó, evaluar el modelo meses después y decir con qué datos exactos se entrenó. Rukh escribe uno cada 1 000 pasos más best.pt por pérdida de validación.
- 09¿Qué es la perplejidad?
- La exponencial de la entropía cruzada media. Se lee como entre cuántas opciones equiprobables duda el modelo: con un vocabulario de 2 030 jugadas, un modelo sin entrenar tiene perplejidad 2 030 y pérdida ln(2030) ≈ 7,6; una pérdida de 3,0 es perplejidad 20, es decir, acierta como si eligiera al azar entre veinte jugadas. Como en una posición típica hay unas treinta legales, esa cifra ya implica bastante aprendizaje. No es una métrica aparte: es la misma pérdida en otra escala.
- 10¿Qué significa un intervalo de confianza del Elo y por qué importa?
- El Elo estimado es el valor que mejor explica los resultados contra rivales de fuerza conocida según la fórmula logística P = 1/(1+10^((rival−modelo)/400)); el intervalo del 95 % dice cuánto podría moverse ese valor si se repitieran las partidas, y se calcula por bootstrap remuestreando los resultados y reajustando mil veces. La suite juega 20 partidas contra cada uno de ocho escalones, 160 en total: a un 50 % de rendimiento la desviación típica sería de unos 27 puntos (±54), y el bootstrap sobre las partidas reales de small da ±68 (1359, IC 1293-1429), porque contra la mayoría de escalones el modelo está lejos del 50 % y cada partida informa menos. Es decir: una mejora de 10, 30 o 50 puntos de Elo medida con una escalera de 160 partidas no es una mejora, es ruido, y presumir de ella es el error clásico de los benchmarks.
- 11¿Cuándo se escala la red? Tokens únicos por parámetro.
- Antes de subir de talla, divide los tokens únicos del corpus (los de meta.json, no los vistos por el bucle: repetir material no lo convierte en material nuevo) entre los parámetros. small v1 iba a 240 M / 39 M = 6,2, con 4,3 pasadas sobre el mismo corpus, y su hueco train/val crecía (+0,060) mientras el top-1 se aplanaba: la firma de material agotado, y triplicar la red (medium v1, mismos datos) solo compró 84 Elo con los intervalos solapados. Devolver el mes de validación al corpus (471 M, 12,1 por parámetro) bajó la pérdida del mismo small de 1,5197 a 1,4703 y batió a medium. medium-v4, con 1 681 M de tokens únicos (14,6 por parámetro, 1,46 pasadas), da 1504 de Elo y 99,80 % de legalidad. Hueco creciendo y métrica plana: faltan datos; hueco estable y métrica subiendo: hay margen en pasos o en capacidad.