Skip to content

English · Español

Fase 18 — Árbol de decisión de stability check

🇪🇸 Cuando un run se porta raro, no improvises. Camina este árbol de arriba a abajo. Cada nodo es una pregunta con un umbral numérico explícito; cada hoja te lleva a un fix concreto o a la Fase 19.


Una checklist ejecutable que un learner recorre cuando un run de entrenamiento de la Fase 18 se comporta mal. Úsala antes de abrir la Fase 19 — muchos síntomas de "el entrenamiento está roto" son problemas de configuración, no de dinámica.

Cómo usarla

  1. Ten dashboard.html (o tus métricas en vivo) abierto.
  2. Ten experiments/<run>/manifest.json abierto (seed, versions, config).
  3. Recorre de §1 a §6 en orden. No te saltes pasos. El orden es por probabilidad × baratura del check.
  4. La primera hoja que coincida es tu fix. Aplica, re-ejecuta y vuelve a caminar desde §1.

§1 — ¿Arrancó el run?

Q1.1 ¿Está la loss en el step 0 dentro de \(\ln V \pm 0.5\) donde \(V\) es el tamaño de vocab? - Para el vocab §A13 \(\approx 512\): espera \(\ln 512 \approx 6.24\). - Si loss(step 0) > 8.0: el init está roto o el bias de la cabeza LM es no-cero. Ve a §5. - Si loss(step 0) < 4.0: el modelo no está sin entrenar. O la seed está mal, o cargaste un checkpoint por error. Comprueba manifest.json. - Si no: pasa, ve a §2.

Q1.2 ¿Está la norma del gradient del primer batch en \([0.1, 10]\)? - Si grad-norm(step 1) > 100: init mal (ver §5) o tu loss tiene la reducción mal (mean vs sum cruzados). - Si grad-norm(step 1) < 0.001: los datos son degenerados — todo target token es el mismo, o la loss mask es todo cero. - Si no: pasa, ve a §2.


§2 — ¿Se está aplicando el schedule de LR?

Q2.1 Grafica LR vs step. ¿La curva coincide con el warmup + cosine configurado? - Si el LR es constante ≠ 0: el scheduler nunca hizo step. Bug habitual: el optimizer se reconstruye en cada step, perdiendo el state del schedule. Fix: instancia el optimizer + scheduler una sola vez fuera del bucle de entrenamiento. - Si el LR es constante = 0: el warmup está configurado pero el multiplicador se aplica a un param_group["lr"] de 0. Fix: inicializa param_group["lr"] = lr_max y deja que el scheduler lo escale. - Si el LR salta de forma discontinua: estás usando step decay, no cosine. Iguala a la config. - Si no: pasa, ve a §3.

Q2.2 En el step warmup_steps, ¿está el LR dentro del 5% de lr_max? - Si LR en step W es < 0.5 × lr_max: off-by-one en la fórmula de warmup. t / W vs (t+1) / W importa con W pequeño. - Si no: pasa, ve a §3.


§3 — ¿Son los gradients razonables?

Q3.1 Grafica la norma del gradient pre-clip. Media móvil sobre una ventana de 20 steps. - Si rolling-mean(grad-norm) > 5.0: el entrenamiento está en el régimen "siempre clipping". O el LR es demasiado alto, o el batch size demasiado pequeño, o un batch tiene un ejemplo patológico. Reduce lr_max 2× y vuelve a caminar. - Si rolling-mean(grad-norm) < 0.01: entrenamiento muerto. O la loss mask anula la mayoría de tokens, o todos los parámetros están congelados, o el LR es tan pequeño que no aterriza ninguna actualización. - Si no: pasa.

Q3.2 ¿Algún step individual con grad-norm > 100? - Si sí una vez en 200 steps: aceptable, el clipping lo manejó. Investiga ese batch — anota su índice. - Si sí más de 3 veces en 200 steps: inestabilidad. Ve a la Fase 19 stability-check.md §2 (picos de loss). - Si no: pasa, ve a §4.


§4 — ¿Se mueven las losses?

Q4.1 Grafica la loss con una EMA de 50 steps. ¿Decrece monótonamente sobre los primeros 200 steps? - Si train-loss(step 200) > train-loss(step 50): el entrenamiento está divergiendo o estático. Comprueba LR (§2), comprueba el optimizer state (§6). - Si train-loss decrece pero la pendiente es <0.001 por 100 steps: el LR puede ser demasiado bajo. Prueba lr_max × 3 y vuelve a caminar. - Si no: pasa.

Q4.2 ¿La val loss sigue a la train loss dentro de 0.5? - Si val − train > 1.0 en el step 500: sobreajuste rápido. Sube weight_decay (prueba 0.2), o reduce capacidad, o aumenta datos (dentro del scope §A13 significa más variantes de conjugación). - Si val < train por > 0.3: fuga de datos. El set de val contiene items del de train. - Si no: pasa, ve a §5.


§5 — ¿Es el modelo arquitectónicamente sólido?

Q5.1 Imprime las estadísticas iniciales de pesos: mean, std, min, max por cada grupo de parámetro con nombre. - Esperado (con Kaiming para ReLU/GELU o Xavier escalado): - Std de peso Linear: \(\sqrt{2 / \text{fan\_in}}\) (Kaiming) o \(\sqrt{2 / (\text{fan\_in} + \text{fan\_out})}\) (Xavier). - Para nuestro mini-GPT: la std de peso Linear debería estar en \([0.02, 0.1]\). - Std del embedding: 0.02 (la convención GPT). - Bias y escala de LayerNorm: 0 y 1 respectivamente. - Si la std es 100× lo esperado: el fallo de mal init de la Fase 19. Ve a la Fase 19. - Si la mean ≠ 0 por más de 0.01 en pesos Linear: re-inicializa.

Q5.2 Forward de un batch, registra magnitudes de activación en cada capa. - Esperado: la norma de Frobenius de las activaciones crece como mucho ~1.5× por capa. - Si la salida de una capa es > 10× su entrada: normalización ausente o rota. Comprueba la colocación de LN/RMSNorm.


§6 — ¿Es coherente el optimizer state?

Q6.1 ¿Están los param_groups correctamente divididos para weight decay? - Tensores 2-D+ (weights, embeddings): deberían estar en el grupo decay con weight_decay = 0.1. - Tensores 1-D (biases, escala/bias de LN): deberían estar en el grupo no_decay con weight_decay = 0.0. - Si todo está en un grupo: los biases derivan a cero a lo largo del entrenamiento. Arregla la división.

Q6.2 ¿Están m y v (los momentos) acumulando? - Tras 100 steps, registra ||m||_F y ||v||_F para un tensor de parámetros. - Si \(\|v\|_F\) es próximo a cero en el step 100: el optimizer state no persiste entre steps. El optimizer se está reconstruyendo. Arregla. - Si \(\|m\|_F = \|g\|_F\) exactamente: falta la corrección de sesgo. Aplica \(\hat m_t = m_t / (1 - \beta_1^t)\).

Q6.3 ¿Está la acumulación de gradient sumando correctamente? - Si acumulas \(k\) micro-batches, el gradient efectivo es \(\sum / k\), no \(\sum\). - Si la grad-norm es exactamente \(k\)× lo esperado: olvidaste el /k. Arregla.


Cuando llegas al fondo

Si todos los checks pasan y el run sigue portándose mal, el problema es dinámica, no configuración. Pasa a docs/phase-19-training-dynamics/stability-check.md.

Si un check te apuntó a un fix, aplícalo, re-ejecuta y vuelve a caminar desde §1. La mayoría de los runs estables atraviesan el árbol entero en una lectura.

Umbrales numéricos — tarjeta de referencia rápida

Síntoma Umbral Primera acción
loss(step 0) lejos de \(\ln V\) \(\|\Delta\| > 0.5\) comprueba init
grad-norm(step 1) enorme \(> 100\) comprueba init / reducción de loss
grad-norm rolling \(> 5\) reduce LR 2×
grad-norm rolling \(< 0.01\) comprueba la loss mask
gap val − train \(> 1.0\) en el step 500 sube weight_decay
crecimiento de activación por capa \(> 10\times\) entrada comprueba la normalización
\(\|v\|_F\) en el step 100 \(\approx 0\) optimizer state perdido

Documento hermano: docs/phase-19-training-dynamics/stability-check.md — para picos, NaN, overflow fp16, divergencia.