English · Español
02 — La sublayer FFN y por qué GELU¶
🇪🇸 Si attention es comunicación entre tokens, FFN es computación dentro de un token. Dos capas lineales, una nolinealidad, factor de expansión \(4\times\). Esa proporción no es arbitraria — es donde la mayoría de los parámetros viven.
El FFN — forma exacta¶
La red feed-forward por posición es un MLP de dos capas aplicado de forma independiente a cada posición de token (sin mezcla entre posiciones):
Formas:
- \(x \in \mathbb{R}^{d_\text{model}}\) (el residual stream en una posición)
- \(W_1 \in \mathbb{R}^{d_\text{ff} \times d_\text{model}}\), \(b_1 \in \mathbb{R}^{d_\text{ff}}\) — la "up-projection"
- \(W_2 \in \mathbb{R}^{d_\text{model} \times d_\text{ff}}\), \(b_2 \in \mathbb{R}^{d_\text{model}}\) — la "down-projection"
- La salida vuelve a \(\mathbb{R}^{d_\text{model}}\), lista para sumarse al residual stream
Tres operaciones: up-project, nolinealidad, down-project. Eso es todo.
La proporción de expansión \(4\times\)¶
Elección canónica: \(d_\text{ff} = 4 \cdot d_\text{model}\). Para nuestro Mini-GPT: \(d_\text{model} = 64, d_\text{ff} = 256\).
¿De dónde sale este número? Vaswani et al. 2017 lo usaron sin gran justificación; las ablaciones posteriores (p. ej., GPT-3, PaLM) confirmaron \(4\times\) como un buen default. La intuición: el FFN es la parte "pesada en cómputo" del bloque, así que darle más capacidad que el ancho residual compensa. Ir a \(8\times\) o \(16\times\) ayuda marginalmente pero a un coste cuadrático en parámetros; \(2\times\) subajusta (infraajuste). La comunidad se quedó con \(4\times\).
Para modelos muy grandes (p. ej., LLaMA), la variante SwiGLU usa \(\sim 2{,}67 \times d_\text{model}\) para \(W_1\) y una proyección de gating, de modo que el conteo efectivo de parámetros coincide con un FFN GELU vanilla a \(4\times d_\text{model}\). La Fase 17 usa la forma GELU vanilla; SwiGLU es una nota a pie de una página que podemos añadir más tarde si hace falta.
Conteo de parámetros del FFN¶
Para una capa FFN:
Para nuestra configuración:
Compara con los parámetros de attention de un bloque (Q, K, V, proyecciones de salida, cada una \(d_\text{model} \times d_\text{model}\), sin sesgo por convención):
Así que el FFN es aproximadamente 2× el conteo de parámetros de attention. Esta proporción 2:1 es consistente en los transformers modernos (GPT-2, GPT-3, LLaMA, etc.). Cuando oigas "los parámetros del transformer son mayoritariamente FFN", ya sabes qué significa.
GELU — la Gaussian Error Linear Unit¶
Introducida por Hendrycks & Gimpel (2016). Definida como:
donde \(\Phi\) es la CDF gaussiana estándar. Intuitivamente: "multiplica \(x\) por la probabilidad de que una muestra \(\mathcal{N}(0, 1)\) sea menor que \(x\)". Así que GELU es un gate suave, motivado probabilísticamente — una versión soft de ReLU.
La forma aproximada, usada en producción:
Diferencia con la forma exacta: < 0,01% en valores de salida, pero la forma aproximada es mucho más rápida (sin erf, sólo tanh). Usa la forma aproximada. F.gelu(approximate='tanh') de PyTorch activa esto; nuestro default en NumPy es la forma aproximada.
¿Por qué GELU y no ReLU?¶
ReLU es dura en 0: gradiente cero para cualquier \(x < 0\), lo que puede causar "neuronas muertas" que dejan de actualizarse. GELU es suave en todas partes: tiene gradiente no nulo para todo \(x\), incluyendo entradas negativas (pequeño pero no nulo). Empíricamente, GELU le gana consistentemente a ReLU en modelado de lenguaje por un margen pequeño pero real (~1-2% de perplejidad).
Un breve pedigrí:
- ReLU (Glorot et al. 2011) — cero para \(x < 0\), identidad para \(x > 0\). Barata y simple.
- GELU (Hendrycks & Gimpel 2016) — suave, probabilística. Default en BERT, GPT-2, GPT-3.
- Swish / SiLU (Ramachandran et al. 2017) — \(x \cdot \sigma(x)\). Muy similar a GELU en la práctica.
- SwiGLU (Shazeer 2020) — variante con gate: \(\text{SwiGLU}(x) = (x W_1) \odot \text{Swish}(x W_3) \cdot W_2\). Default en LLaMA, PaLM.
La Fase 17 usa GELU porque es lo que usó GPT-2 y ese es el modelo mental más limpio. SwiGLU es una mejora equivalente en coste de parámetros que harías en un proyecto real; aquí sería sólo un cambio directo.
Por qué existe el FFN — la razón profunda¶
Una pregunta habitual: "Attention puede mezclar información arbitraria entre tokens. ¿Por qué necesitamos también FFN?"
Dos ángulos:
- Linealidad. El softmax de attention produce mezclas convexas de vectores de entrada — una combinación lineal. La composición de operaciones lineales (capas de attention sin FFN) colapsa a un único operador lineal (salvo por la nolinealidad del softmax, que es leve). Para obtener una aproximación de funciones realmente no lineal, necesitas el GELU del FFN.
- Puntual vs entre posiciones. El poder expresivo de attention está en mezclar posiciones. El poder expresivo del FFN está en transformar posiciones individuales. Son ortogonales. Un bloque transformer necesita ambas porque la comprensión del lenguaje necesita ambas: "¿cuál es el papel del token 5 en contexto?" (attention) y "dado que el token 5 está en este papel, ¿qué debería computar sobre él?" (FFN).
El trabajo de interpretabilidad mecanística (Anthropic 2022, "Toy Models of Superposition") enmarca el FFN como la memoria clave-valor: \(W_1\) son las claves (qué patrón de entrada dispara esta neurona), \(W_2\) son los valores (qué escribir en el residual stream cuando esta neurona se activa). Esta es una perspectiva bonita y útil para fases posteriores.
Qué NO cubre este archivo¶
- El paso backward del FFN. Implícito en el autograd construido en la Fase 8; la Fase 18 lo ejercitará.
- Implementación de SwiGLU. Ruta de mejora a nivel de nota a pie; fuera del alcance de la Fase 17.
- FFN a escalas muy anchas (clase LLaMA). Misma matemática; números más grandes. Fuera de alcance.
Siguiente: 03-tied-embeddings-and-lm-head.md