Skip to content

English · Español

Break 00 — Sustituye GELU por la identidad en TenseMLP

🇪🇸 Rompemos GELU para que sea la función identidad. Tu MLP de 2 capas debería colapsar a una sola transformación lineal — predice qué métrica te lo dirá antes de mirar la pérdida.

Anchors: LYNX_CORTEX.md §4 / PHASE 9; .claude/commands/break.md.


La rotura

En src/minimodel/nn/activations.py:

class GELU(Module):
    def forward(self, x: Tensor) -> Tensor:
        return x                       # was: return x.gelu()

Edición de una sola línea. La clase sigue existiendo, sigue teniendo el nombre correcto, sigue componiendo dentro de Sequential. El forward pass se convierte en:

H1 = X @ W1.T + b1                    # (4, 16) — same as before
H1 = H1                               # identity, no nonlinearity
logits = H1 @ W2.T + b2               # (4, 5)

Predice y luego ejecuta

Dos capas de Linear sin no-linealidad entre ellas es, por composición de mapeos afines, equivalente a una sola capa lineal:

\[ \text{logits} = (X W_1^{\top} + b_1) W_2^{\top} + b_2 = X (W_1^{\top} W_2^{\top}) + (b_1 W_2^{\top} + b_2) = X W_{\text{eff}}^{\top} + b_{\text{eff}} \]

donde \(W_{\text{eff}} = W_2 W_1\) tiene rango como mucho min(23, 16, 5) = 5. La tarea de clasificación de tiempos verbales de §A13 tiene un ground-truth exactamente lineal (one-hot verb ⊕ person → 5 tiempos), así que podrías pensar que no cambia nada. Pero:

  1. La familia de funciones expresables es idéntica — una sola capa lineal puede representar cualquier función de conjugación que el MLP roto pueda.
  2. La trayectoria de optimización difiere: el MLP roto sobreparametriza un objetivo de rango 5 con un compuesto de (16, 23) + (5, 16) = 448 parámetros, frente a los (5, 23) = 115 parámetros de la única capa. El step por parámetro de Adam se diluye a través de direcciones redundantes.

Predicciones

  • Accuracy final de validación: aproximadamente la misma (>85 %), quizá ligeramente más baja o más ruidosa.
  • Pasos hasta convergencia: más (~1.5–2×) por la dilución del optimizador descrita arriba.
  • Rango efectivo de W2 @ W1: como mucho 5 (el cuello de botella).
  • El MLP entrenado con GELU debería alcanzar menor pérdida en un set adversarial held-out (puede ajustar la pequeña no-linealidad en el acuerdo persona/tiempo, como la -s de la 3ª persona).

Escribe tus predicciones en learners/borja/phase-09/notes/breaks.md antes de ejecutar. El objetivo es fijar una hipótesis.

Observa

Ejecuta experiments/09-tense-mlp/train.py con la activación rota. Compara contra la ejecución anterior:

just exp 09-tense-mlp --tag broken-gelu-identity

Diagnósticos para graficar:

  1. Solapamiento de curvas de pérdida — gelu vs identity.
  2. np.linalg.matrix_rank(W2.data @ W1.data) al final del entrenamiento — debería ser ≤ 5.
  3. Accuracy held-out sobre los 10 ejemplos más difíciles (p. ej., past-participle de 3ª persona de irregulares).

Síntoma que verá Borja

  • La convergencia funciona pero es más lenta. La pérdida no explota; este es el caso sutil.
  • Accuracy adversarial ligeramente peor.
  • matrix_rank(W2 @ W1) == 5.

Causa oculta (una frase)

GELU se sustituyó por la identidad, colapsando el MLP a un mapeo lineal efectivo de rango 5.

Cascada de pistas

  1. El forward pass muestra (B, 16) → (B, 5) — pero ¿es la representación intermedia alguna vez no lineal? Imprime la distribución de H1.
  2. ¿Cuál es np.linalg.matrix_rank(W2.data @ W1.data)? ¿Está acotada por algo arquitectónico?
  3. La clase GELU en nn/activations.py ha cambiado. Compara el método forward con la teoría de la Phase 9 §02.

Diff del arreglo

class GELU(Module):
    def forward(self, x: Tensor) -> Tensor:
        return x.gelu()                # restored

Por qué esto enseña el concepto

Los teoremas de aproximación universal (Cybenko 1989, Hornik et al. 1989) requieren una no-linealidad entre capas. Sin ella, la profundidad no añade expresividad. La tarea de §A13 resulta ser linealmente separable, así que el síntoma es optimización degradada, no representación degradada. Este es el tipo de bug que se envía a producción — el modelo "funciona", pero peor de lo que debería — y la única forma de cazarlo es entender por qué GELU está ahí. La FFN del transformer en la Phase 17 usa GELU por la misma razón; la derivación residual de la Phase 10 necesita curvatura no nula en f(x) para tener sentido.


Siguiente: el /break de la Phase 10 sobre la residual highway.