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
Las seis piezas
- Embedding + señal posicional. Tokens → vectores. Posición inyectada mediante RoPE (moderno) o sinusoidal (clásico).
- Self-attention. Cada posición atiende a todas las demás. Enmascarada en decoders.
- 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. - Conexión residual.
x + sublayer(x). Sin esto, los gradientes se desvanecen después de unas 6 capas. - Layer normalization.
LayerNormorRMSNorm(moderno). Estabiliza el flujo residual. - 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 | 4× | 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) | Sí | 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)ysilu(x) * W3 x(SwiGLU).ffn_swiglu(x, W1, W2, W3).encoder_block(x, params)ydecoder_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
- Fácil. Cuenta los parámetros en tu
encoder_blockcond_model=512, n_heads=8, ffn_expansion=4, swiglu=True. Valida implementando el bloque y usandosum(p.numel() for p in block.parameters()). - 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.
- Difícil. Implementa un encoder-decoder de 4 capas en una tarea simple de copia (copiar
xinvertido). 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
- Vaswani et al. (2017). Attention Is All You Need — especificación original del bloque.
- Xiong et al. (2020). On Layer Normalization in the Transformer Architecture — por qué pre-norm supera ampliamente a post-norm en profundidad.
- Zhang, Sennrich (2019). Root Mean Square Layer Normalization — RMSNorm.
- Shazeer (2020). GLU Variants Improve Transformer — el artículo de SwiGLU.
- HuggingFace
modeling_llama.py— bloque canónico de solo decoder de 2026.