Phase 07 - Lesson 05

El Transformer Completo — Encoder + Decoder

This lesson includes a graded coding exercise that runs in your browser, unlocked with lifetime access.

La atención es la estrella. Todo lo demás — conexiones residuales, normalización, feed-forward, atención cruzada — es el andamiaje que permite apilarlo profundamente.

Tipo: Construcción Lenguajes: Python Prerrequisitos: Fase 7 · 02 (Self-Attention), Fase 7 · 03 (Multi-Head Attention), Fase 7 · 04 (Positional Encoding) Tiempo: ~75 minutos

El Problema

Una sola capa de atención es un extractor de características, no un modelo. Una multiplicación de matrices (matmul) por capa no es capacidad suficiente para el lenguaje. Necesitas profundidad — y la profundidad falla sin las conexiones correctas.

El artículo de Vaswani de 2017 empaquetó seis decisiones de diseño que convirtieron una capa de atención en un bloque apilable. Cada transformer desde entonces — solo encoder (BERT), solo decoder (GPT), encoder-decoder (T5) — herda el mismo esqueleto. En 2026, los bloques se han refinado (RMSNorm, SwiGLU, pre-norm, RoPE), pero el esqueleto es idéntico.

Esta lección presenta el esqueleto. Las siguientes lecciones lo especializan — 06 para encoders, 07 para decoders, 08 para encoder-decoder.

El Concepto

Interior de los bloques de encoder y decoder, conectados

Las seis piezas

  1. Embedding + señal posicional. Tokens → vectores. Posición inyectada mediante RoPE (moderno) o sinusoidal (clásico).
  2. Self-attention. Cada posición atiende a todas las demás. Enmascarada en decoders.
  3. Red de feed-forward (FFN). MLP de dos capas por posición: W_2 · activation(W_1 · x). Relación de expansión de 4× por defecto.
  4. Conexión residual. x + sublayer(x). Sin esto, los gradientes se desvanecen después de unas 6 capas.
  5. Layer normalization. LayerNorm or RMSNorm (moderno). Estabiliza el flujo residual.
  6. Atención cruzada (solo decoder). Las consultas (queries) provienen del decoder, las claves (keys) y los valores (values) provienen de la salida del encoder.

Bloque de encoder (usado por BERT, encoder de T5)

x → LN → MHA(self) → + → LN → FFN → + → out
                     ^              ^
                     |              |
                     └── residual ──┘

El encoder es bidirecional. Sin enmascaramiento. Todas las posiciones ven todas las posiciones.

Bloque de decoder (usado por GPT, decoder de T5)

x → LN → MHA(masked self) → + → LN → MHA(cross to encoder) → + → LN → FFN → + → out

El decoder tiene tres subcapas por bloque. La del medio — atención cruzada — es el único lugar donde la información flui del encoder al decoder. En una arquitectura pura de solo decoder (GPT), la atención cruzada se omite y solo se tiene self-attention enmascarada + FFN.

Pre-norm vs post-norm

Artículo original: x + sublayer(LN(x)) frente a LN(x + sublayer(x)). Post-norm perdió favor alrededor de 2019 — es más difícil de entrenar a gran profundidad sin un precalentamiento (warmup) cuidadoso. Pre-norm (LN antes de la subcapa) es el valor predeterminado de 2026: Llama, Qwen, GPT-3+, Mistral lo usan.

El bloque modernizado de 2026

Vaswani 2017 incluía LayerNorm + ReLU. Las arquitecturas modernas reemplazaron ambos. Cómo se ven realmente los bloques de producción:

Componente 2017 2026
Normalización LayerNorm RMSNorm
Activación FFN ReLU SwiGLU
Expansión FFN 2.6× (SwiGLU usa tres matrices, el total de parámetros coincide)
Posición Sinusoidal absoluta RoPE
Atención MHA completa GQA (o MLA)
Términos de sesgo (bias) No

RMSNorm elimina el centrado de media de LayerNorm (una resta menos), lo que ahorra cómputo y empíricamente es al menos tan estable. SwiGLU (Swish(W1 x) ⊙ W3 x) supera consistentemente a FFN con ReLU/GELU por ~0.5 puntos de perplejidad (ppl) en los artículos de Llama, PaLM y Qwen.

Conteo de parámetros

Para un bloque con d_model = d y expansión FFN r:

  • MHA: 4 · d² (projecciones Q, K, V, O)
  • FFN (SwiGLU): 3 · d · (r · d)3rd²
  • Normas: insignificante

Con d = 4096, r = 2.6, layers = 32 (aproximadamente Llama 3 8B), el total es: 32 · (4·4096² + 3·2.6·4096²) ≈ 32 · (16 + 32) M = ~1.5B parameters per layer × 32 ≈ 7B (más embeddings y cabezal final). Coincide con los conteos publicados.

Constrúyelo

Paso 1: los bloques de construcción

Utilizando la pequeña clase Matrix de la Lección 03 (copiada a este archivo para independencia):

  • layer_norm(x, eps=1e-5) — resta la media, divide por la desviación estándar.
  • rms_norm(x, eps=1e-6) — divide por RMS. Sin resta de media.
  • gelu(x) y silu(x) * W3 x (SwiGLU).
  • ffn_swiglu(x, W1, W2, W3).
  • encoder_block(x, params) y decoder_block(x, enc_out, params).

Ver code/main.py para la conexión completa.

Paso 2: conectar un encoder de 2 capas y un decoder de 2 capas

Apílalos. Pasa la salida del encoder a cada atención cruzada del decoder. Agrega una LN final antes de la proyección de salida.

def encode(tokens, params):
    x = embed(tokens, params.emb) + sinusoidal(len(tokens), params.d)
    for block in params.encoder_blocks:
        x = encoder_block(x, block)
    return x

def decode(target_tokens, encoder_out, params):
    x = embed(target_tokens, params.emb) + sinusoidal(len(target_tokens), params.d)
    for block in params.decoder_blocks:
        x = decoder_block(x, encoder_out, block)
    return x

Paso 3: ejecutar forward en un ejemplo simple

Pasa una secuencia de origen de 6 tokens y un objetivo de 5 tokens. Verifica que la forma de salida sea (5, vocab). Sin entrenamiento — esta lección se centra en la arquitectura, no en la pérdida (loss).

Paso 4: intercambiar por RMSNorm + SwiGLU

Reemplaza LayerNorm y ReLU-FFN con RMSNorm y SwiGLU. Confirma que las formas sigan coincidiendo. Esta es la modernización de 2026 con una sustitución de función.

Úsalo

Las implementaciones de referencia de PyTorch/TF: nn.TransformerEncoderLayer, nn.TransformerDecoderLayer. Pero la mayoría del código de producción de 2026 desarrolla su propio bloque porque:

  • Flash Attention se llama dentro de la atención, no a través de nn.MultiheadAttention.
  • GQA / MLA no están en la referencia de la biblioteca estándar (stdlib).
  • RoPE, RMSNorm, SwiGLU no son los valores predeterminados de PyTorch.

HF transformers tiene bloques de referencia limpios que deberías leer: modeling_llama.py es el bloque canónico de solo decoder de 2026. Tiene ~500 líneas y vale la pena analizarlo una vez.

Encoder frente a decoder frente a encoder-decoder — cuándo elegir:

Necesidad Elección Ejemplo
Clasificación, embeddings, QA sobre texto Solo encoder BERT, DeBERTa, ModernBERT
Generación de texto, chat, código, razonamiento Solo decoder GPT, Llama, Claude, Qwen
Entrada estructurada → salida estructurada (traducción, resumen) Encoder-decoder T5, BART, Whisper

El modelo de solo decoder ganó en lenguaje porque escala de la forma más limpia y maneja tanto la comprensión como la generación. El modelo encoder-decoder sigue siendo el mejor cuando la entrada tiene una identidad clara de "secuencia de origen" (traducción, reconocimiento de voz, tareas estructuradas).

Publícalo

Ver outputs/skill-transformer-block-reviewer.md. La skill revisa una nueva implementación de bloque de transformer frente a los valores predeterminados de 2026 y señala las piezas faltantes (pre-norm, RoPE, RMSNorm, GQA, relación de expansión FFN).

Ejercicios

  1. Fácil. Cuenta los parámetros en tu encoder_block con d_model=512, n_heads=8, ffn_expansion=4, swiglu=True. Valida implementando el bloque y usando sum(p.numel() for p in block.parameters()).
  2. Medio. Cambia de post-norm a pre-norm. Inicializa ambos y mide la norma de activación después de 12 capas apiladas con entrada aleatoria. Las activaciones de post-norm deberían explotar; las de pre-norm deberían permanecer acotadas.
  3. Difícil. Implementa un encoder-decoder de 4 capas en una tarea simple de copia (copiar x invertido). Entrena 100 pasos. Reporta la pérdida. Intercambia por RMSNorm + SwiGLU + RoPE —¿disminuye la pérdida?

Términos Clave

Término Lo que la gente dice Lo que realmente significa
Bloque "Una capa de transformer" Pila de normalización + atención + normalización + FFN, envuelta en conexiones residuales.
Residual "Conexión de salto (skip connection)" Salida x + f(x); permite el flujo de gradientes a través de pilas profundas.
Pre-norm "Normalizar antes, no después" Moderno: x + sublayer(LN(x)). Entrena a mayor profundidad sin gimnasia de warmup.
RMSNorm "LayerNorm sin la media" Divide por RMS; una operación menos, misma estabilidad empírica.
SwiGLU "La FFN a la que todos se cambiaron" Swish(W1 x) ⊙ W3 x → W2. Supera a ReLU/GELU en ppl de modelos de lenguaje.
Atención cruzada "Cómo el decoder ve al encoder" MHA con Q del decoder, K/V de las salidas del encoder.
Expansión FFN "Qué tan amplia es la MLP intermedia" Relación entre la dimensión oculta (hidden-size) y d_model, generalmente 4 (LayerNorm) o 2.6 (SwiGLU).
Libre de sesgo (bias-free) "Elimina los términos +b" Las arquitecturas modernas omiten sesgos en las capas lineales; ligera mejora en ppl, modelo más pequeño.

Lecturas Adicionales

0 lifetime access. Curriculum based on AI Engineering from Scratch by Rohit Ghumare (MIT, used under attribution).