English · Español
Break 00 — MoE con un router roto (todos los tokens a un expert); el caso degenerado¶
🇪🇸 Si quitas la auxiliary loss del MoE, el router rápidamente colapsa a un experto único. La pérdida de entrenamiento se ve "bien" — todavía baja — pero el modelo se comporta como una FFN normal con N-1 experts sin entrenar. Este
/breaklo demuestra desactivando la aux loss.
Lo que vas a hacer¶
Desactivar la pérdida auxiliar de balanceo de carga en el MoE (Mixture of Experts) de juguete construido en docs/phase-36-frontier-architectures/lab/00-moe-on-grammar-tutor.md. Observar cómo el router colapsa a un único expert en menos de 200 pasos; observar que la pérdida principal no señala el fallo.
Paso 1 — Localizar el bloque MoE¶
src/minimoe/moe_block.py # the toy MoE layer (Phase 36 lab 00)
src/minimoe/loss.py # the combined train loss = main + aux
Paso 2 — Introducir el bug¶
En src/minimoe/loss.py, la pérdida combinada actualmente pondera el término auxiliar de balanceo de carga por alpha=0.01. Ponlo a cero:
# OLD
total_loss = main_loss + 0.01 * aux_load_balancing_loss(gates, mask)
# NEW (the broken version)
total_loss = main_loss + 0.0 * aux_load_balancing_loss(gates, mask)
Una constante numérica. La aux loss se sigue computando y logueando (así tenemos una métrica para detectar el colapso), simplemente no propaga gradiente.
Paso 3 — Registrar el break¶
learners/borja/phase-36/notes/breaks.md:
- bug-id: 36-01
concept: MoE load-balancing aux loss
symptom: main train loss falls normally; but the per-expert token count
collapses — after ~200 steps, expert 0 sees 95%+ of tokens and the
other 3 experts see ~0. Held-out val loss is worse than the dense
baseline despite 4× the params.
hidden_cause: the aux loss coefficient is zero; nothing pushes the router
to spread tokens across experts.
hint_1: "Log per-expert token counts. Plot them across steps. What's the trend?"
hint_2: "What does the aux loss penalize, and what does its coefficient control?"
hint_3: "diff src/minimoe/loss.py. Has anything multiplying the aux loss changed?"
fix_diff: restore alpha=0.01 in the loss combination.
Paso 4 — Verificar que es observable¶
Ejecuta just moe-train --steps 500. Salida esperada con el bug:
step= 10 main=2.401 aux=0.124 expert_counts=[ 240 238 244 254 ]
step= 100 main=2.018 aux=0.310 expert_counts=[ 540 191 119 126 ]
step= 200 main=1.872 aux=0.892 expert_counts=[ 893 62 18 3 ]
step= 300 main=1.748 aux=0.988 expert_counts=[ 945 20 8 3 ]
step= 400 main=1.665 aux=0.996 expert_counts=[ 962 10 3 1 ]
step= 500 main=1.601 aux=0.998 expert_counts=[ 970 4 2 0 ] <-- collapse
val_loss=2.143 (dense_baseline=1.890) <-- 4× params, *worse* val loss
La métrica aux_load_balancing_loss sube hacia 1.0 (su máximo bajo la formulación Switch: \(f_e P_e\) sumado con toda la masa en un expert tiende a 1). La pérdida principal pinta bien. La val loss held-out es la prueba del crimen.
El test tests/phase36/test_moe_balance.py::test_no_expert_above_50_percent se pone en rojo.
Paso 5 — El momento docente¶
Dos lecciones, un bug:
- La pérdida principal por sí sola no detecta la patología del MoE. Va a seguir bajando porque el único expert activo sigue aprendiendo una FFN decente. La arquitectura está degenerando silenciosamente a un modelo de 1 expert con 3 experts muertos.
- La aux loss es estructural, no cosmética. Sin ella, el sistema no es un MoE en ningún sentido significativo. El coeficiente
0.01parece pequeño; borrarlo borra la arquitectura.
El fix es una constante; la lección es que la función de pérdida es parte de la definición arquitectónica, no un detalle ajustable del entrenamiento.
Hard rules respetadas¶
- Un único bug (una constante numérica).
- Reversible en 1 línea.
- Observable (los conteos de tokens por expert divergen; la val loss regresiona).
- Sin impacto de seguridad.
- Tests no modificados.
Siguiente: cuando esté en verde, relee ../theory/05-moe-routing-math-and-mamba-intuition.md para la derivación formal de \(f_e P_e\).