PASO 6 / 10

Feed-Forward Network

La "otra mitad" de cada bloque Transformer. Después de que self-attention mezcla la información entre tokens, cada token pasa por una pequeña red neuronal que procesa esa información — independientemente y con no-linealidad.

🎯 De un vistazo

El FFN procesa la información de cada token individualmente, agregando la no-linealidad que la atención no puede dar.

PROPÓSITO

¿Para qué sirve?

Procesar la información de cada token por separado, después de que la atención la mezcló entre tokens. Es la fase de "pensar individualmente".

APORTE

¿Qué aporta al modelo?

No-linealidad (vía ReLU) + capacidad de cómputo (vía la expansión). De hecho, la mayoría de los parámetros del modelo (60-70%) viven en los FFN.

NECESIDAD

¿Por qué es indispensable?

Sin FFN, todo el modelo sería lineal (solo aprendería líneas rectas) y apilar muchas capas no agregaría nada — se colapsarían matemáticamente a una sola. Sin FFN, no hay LLM posible.

↓ A continuación, el detalle con ejemplos y visualizaciones · ¿solo querés la intuición? Andá al paso 6b

1. La idea en una sola frase

Feed-forward = cada token pasa por una pequeña red neuronal de 2 capas, independientemente de los demás tokens, para procesar su información.

Comparado con self-attention y multi-head, este paso es mucho más simple. Si entendiste lo de multiplicar por matrices, ya entendiste el 80% de FFN. Lo único realmente nuevo es la función no-lineal entre las dos capas, que vamos a explicar desde cero.

2. ¿Por qué necesitamos FFN?

Para entender por qué FFN existe, tenemos que notar algo que se nos puede haber escapado: todas las operaciones del paso 4 y 5 son lineales.

¿Qué significa "operación lineal"?

Una operación es lineal si cumple dos propiedades:

  • Si multiplicás el input por 2, el output también se multiplica por 2.
  • Si sumás dos inputs, el output es la suma de los outputs individuales.

Multiplicar por una matriz es lineal. Sumar vectores es lineal. Multiplicar por un número es lineal.

Y aquí está el problema: si encadenás solo operaciones lineales, todo lo que podés hacer sigue siendo... una sola operación lineal. Por más capas que apiles, equivalen a una sola matriz grande.

Sin FFN, todo el Transformer sería equivalente a una única gran multiplicación de matrices. No importaría tener 12 capas o 96 — el modelo solo podría aprender funciones lineales del input. Y lo que queremos aprender (lenguaje, razonamiento, patrones complejos) NO es lineal.
FFN aporta la no-linealidad. Es lo que le permite al modelo aprender funciones complejas como "si la palabra anterior es un verbo Y la actual es un sustantivo, entonces la siguiente probablemente sea un adjetivo". Sin no-linealidad, ese tipo de reglas no se pueden aprender.

3. 📐 Repaso: la función de activación (ReLU)

La "no-linealidad" se introduce con una función llamada función de activación. La más simple y popular es ReLU (Rectified Linear Unit). Suena complicado pero es ridículamente simple:

ReLU(x) = max(0, x)

"Si x es positivo, devolvé x. Si es negativo o cero, devolvé 0."

Ejemplos triviales

ReLU(3)    = max(0, 3)    = 3
ReLU(-2)   = max(0, -2)   = 0
ReLU(0)    = max(0, 0)    = 0
ReLU(5.7)  = max(0, 5.7)  = 5.7
ReLU(-0.1) = max(0, -0.1) = 0

Aplicada a un vector: se aplica elemento por elemento.
ReLU([3, -1, 5, -2, 0, 7]) = [3, 0, 5, 0, 0, 7]

Visualmente

La izquierda (x negativo) se "aplasta" a cero. La derecha (x positivo) queda igual. El "doblez" en x=0 es exactamente lo que hace a ReLU una función no-lineal.

🎮 Calculadora interactiva de ReLU

Ingresá 6 números y observá cómo ReLU los transforma:

¿Por qué este "doblez" en x=0 es tan poderoso?

Sin ReLU (u otra función no-lineal), la red solo puede aprender líneas rectas en el espacio. Con ReLU, puede aprender funciones quebradas, y combinando muchas ReLU encadenadas puede aproximar cualquier función que quieras (esto se llama universal approximation theorem).

Variantes modernas: los Transformers actuales (GPT, LLaMA, Claude) usan variantes más suaves de ReLU:
  • GELU: versión "redondeada" de ReLU. Usada en BERT, GPT-2, GPT-3.
  • SwiGLU: una combinación más sofisticada. Usada en LLaMA y modelos recientes.
Pero todas cumplen la misma función conceptual: introducir no-linealidad.

4. La arquitectura del FFN

El FFN tiene 3 operaciones encadenadas, aplicadas a cada token por separado:

Vector input
dim = d_model (= 4)
↓ multiplicar por W₁ (matriz 4×8)
Vector expandido
dim = d_ff (= 8) — más grande
↓ aplicar ReLU (elemento por elemento)
Vector "activado"
dim = d_ff (= 8) — sin negativos
↓ multiplicar por W₂ (matriz 8×4)
Vector output
dim = d_model (= 4) — vuelta al tamaño original
3 operaciones, 2 matrices aprendidas:
  1. Expandir: multiplicamos por W₁ para llevar el vector a un espacio más grande.
  2. Activar: aplicamos ReLU para introducir no-linealidad.
  3. Contraer: multiplicamos por W₂ para volver al tamaño original.

5. ¿Por qué expandir y luego contraer?

Pregunta natural: ¿no sería más simple multiplicar por una matriz 4×4 y aplicar ReLU? ¿Para qué pasar por dim 8 en el medio?

La razón es capacidad expresiva: con más dimensiones intermedias, ReLU tiene más "lugar" para "doblar" la función y aprender patrones complejos. Es como darle al modelo un espacio temporal más grande para hacer sus cálculos.

Analogía: imaginá que tenés que resolver un problema matemático complicado y solo te dan media hoja para hacer las cuentas. Si te dan 4 hojas, podés desarrollar el problema con más pasos intermedios y llegar a una solución mejor. Después escribís solo la respuesta final en la hoja original.

Dimensiones típicas en modelos reales

Modelo d_model d_ff Factor expansión
GPT-2 small76830724x
GPT-312288491524x
LLaMA-2 7B409611008~2.7x
Nuestro ejemplo482x

El factor más común es 4x. Acá usamos 2x para que los números sean más manejables.

Dato curioso: en un Transformer típico, el FFN tiene más parámetros que el bloque de atención. La mayor parte del modelo (en cantidad de pesos) está en los FFN, no en attention.

6. La fórmula completa

FFN(x) = ReLU(x · W₁ + b₁) · W₂ + b₂

📖 Diccionario de elementos

  • x: el vector input (un token), de dimensión d_model = 4.
  • W₁: matriz de pesos aprendida, shape [d_model, d_ff] = [4, 8]. La "expandidora".
  • b₁: vector de bias aprendido, dim 8. Se suma al resultado.
  • ReLU(...): aplica la función ReLU elemento por elemento.
  • W₂: segunda matriz de pesos, shape [d_ff, d_model] = [8, 4]. La "contraedora".
  • b₂: segundo bias, dim 4.

Mini-repaso: ¿qué es el "bias"?

El bias (en español "sesgo") es simplemente un vector que se suma al resultado de una multiplicación de matrices. Su propósito: dar más flexibilidad al modelo.

Sin bias:    output = x · W
Con bias:    output = x · W + b

Ejemplo:
  x · W = [2.5, -1.3, 0.8]
  b     = [0.1, 0.5, -0.2]
  output = [2.5+0.1, -1.3+0.5, 0.8-0.2] = [2.6, -0.8, 0.6]

Sin bias, la transformación lineal tiene que pasar siempre por el origen (cuando x=0, output=0). Con bias, podés "desplazar" la salida. Es como la "b" en y = m·x + b de la línea recta.

7. Crucial: cada token se procesa INDEPENDIENTEMENTE

A diferencia de self-attention (donde los tokens "se mezclan"), en FFN cada token se procesa por separado:

División del trabajo entre atención y FFN:
  • Atención (paso 4-5): comunica información ENTRE tokens.
  • FFN (paso 6): procesa la información DENTRO de cada token.
Es como una conversación grupal: primero todos se hablan entre sí (atención), después cada uno se queda pensando individualmente sobre lo que escuchó (FFN).

8. Ejemplo trabajado con "el perro come"

Los inputs son los 3 vectores que salieron del paso 5 (multi-head attention). Vamos a calcular el FFN para uno de ellos, "perro", paso a paso.

📥 Input (vector de "perro" desde el paso 5)

x_perro = [+0.71, +0.55, +0.22, +0.78]   (dim 4)

Paso A: expandir (x · W₁ + b₁)

Multiplicamos el vector de dim 4 por W₁ (matriz 4×8) y sumamos el bias b₁ (dim 8). Resultado: vector de dim 8.

Paso B: aplicar ReLU

Tomamos el vector del paso A y aplicamos ReLU elemento por elemento. Los valores negativos se vuelven 0; los positivos quedan igual.

Paso C: contraer (... · W₂ + b₂)

Multiplicamos el vector "activado" (dim 8) por W₂ (matriz 8×4) y sumamos b₂ (dim 4). Resultado: vector de dim 4 — igual tamaño que el input original.

Observación final: el vector de salida tiene la misma forma que el input (dim 4). Eso es importante porque el FFN se inserta dentro de un bloque donde se necesita preservar las dimensiones (lo verás en el paso 7 con las conexiones residuales).

🎮 Demo interactivo: el flujo completo paso a paso

Click "Siguiente paso" para ver cada fase del FFN aplicada a las 3 palabras. Cada paso muestra qué entra, qué hacemos y qué sale.

0. Input
1. Expandir (·W₁ + b₁)
2. ReLU
3. Contraer (·W₂ + b₂)
Listo para comenzar
Apretá "Siguiente paso" para arrancar.

✅ Resumen: lo que entra al siguiente paso

  1. Empezamos con los 3 vectores de salida del paso 5 (multi-head attention).
  2. Cada token (independientemente) pasa por una pequeña red de 2 capas: expandir (4→8) + ReLU + contraer (8→4).
  3. El output tiene la misma forma que el input (3 vectores de dim 4).
  4. La gran novedad de este paso es la no-linealidad introducida por ReLU — sin ella, el Transformer no podría aprender patrones complejos.

En el Paso 7: Residual Connections + LayerNorm vamos a ver cómo se "conectan" attention y FFN entre sí dentro de un bloque, y cómo se evita que las señales se distorsionen al apilar muchos bloques.