Phase 07 - Lesson 05
O Transformer Completo — Encoder + Decoder
This lesson includes a graded coding exercise that runs in your browser, unlocked with lifetime access.
A atenção é a estrela. Todo o resto — conexões residuais, normalização, feed-forward, atenção cruzada — é o andaime que permite empilhá-la profundamente.
Tipo: Construção Linguagens: Python Pré-requisitos: Fase 7 · 02 (Self-Attention), Fase 7 · 03 (Multi-Head Attention), Fase 7 · 04 (Positional Encoding) Tempo: ~75 minutos
O Problema
Uma única camada de atenção é um extrator de características, não um modelo. Uma multiplicação de matrizes (matmul) por camada não é capacidade suficiente para a linguagem. Você precisa de profundidade — e a profundidade falha sem a estrutura adequada.
O artigo de Vaswani de 2017 reuniu seis decisões de design que transformaram uma camada de atenção em um bloco empilhável. Cada transformer desde então — apenas encoder (BERT), apenas decoder (GPT), encoder-decoder (T5) — herda o mesmo esqueleto. Em 2026, os blocos foram refinados (RMSNorm, SwiGLU, pré-normalização, RoPE), mas o esqueleto é idêntico.
Esta lição apresenta o esqueleto. As próximas lições o especializam — 06 para encoders, 07 para decoders, 08 para encoder-decoder.
O Conceito
As seis partes
- Embedding + sinal posicional. Tokens → vetores. Posição injetada via RoPE (moderno) ou senoidal (clássico).
- Self-attention. Cada posição atende a todas as outras. Mascarada em decoders.
- Rede feed-forward (FFN). MLP de duas camadas por posição:
W_2 · activation(W_1 · x). Razão de expansão de 4× por padrão. - Conexão residual.
x + sublayer(x). Sem isso, os gradientes desaparecem após cerca de 6 camadas. - Layer normalization.
LayerNormouRMSNorm(moderno). Estabiliza o fluxo residual. - Atenção cruzada (apenas decoder). As consultas (queries) vêm do decoder, e as chaves (keys) e valores (values) vêm da saída do encoder.
Bloco do encoder (usado pelo BERT, encoder do T5)
x → LN → MHA(self) → + → LN → FFN → + → out
^ ^
| |
└── residual ──┘
O encoder é bidirecional. Sem mascaramento. Todas as posições veem todas as posições.
Bloco do decoder (usado pelo GPT, decoder do T5)
x → LN → MHA(masked self) → + → LN → MHA(cross to encoder) → + → LN → FFN → + → out
O decoder possui três subcamadas por bloco. A do meio — atenção cruzada — é o único lugar por onde a informação flui do encoder para o decoder. Em uma arquitetura puramente baseada apenas em decoder (GPT), a atenção cruzada é omitida e você tem apenas a self-attention mascarada + FFN.
Pré-norm vs pós-norm
Artigo original: x + sublayer(LN(x)) vs LN(x + sublayer(x)). A pós-norm perdeu preferência por volta de 2019 — é mais difícil de treinar profundamente sem um warmup cuidadoso. A pré-norm (LN antes da subcamada) é o padrão em 2026: Llama, Qwen, GPT-3+, Mistral, todos a utilizam.
O bloco modernizado de 2026
O modelo de Vaswani 2017 utilizava LayerNorm + ReLU. As arquiteturas modernas substituíram ambos. Como os blocos de produção realmente se parecem:
| Componente | 2017 | 2026 |
|---|---|---|
| Normalização | LayerNorm | RMSNorm |
| Ativação da FFN | ReLU | SwiGLU |
| Expansão da FFN | 4× | 2,6× (o SwiGLU usa três matrizes, o total de parâmetros é equivalente) |
| Posição | Senoidal absoluta | RoPE |
| Atenção | MHA completa | GQA (ou MLA) |
| Termos de viés (bias) | Sim | Não |
O RMSNorm remove a centralização pela média do LayerNorm (uma subtração a menos), o que economiza computação e é empiricamente pelo menos tão estável quanto. O SwiGLU (Swish(W1 x) ⊙ W3 x) supera consistentemente a FFN com ReLU/GELU em cerca de 0,5 ponto de perplexidade (ppl) nos artigos do Llama, PaLM e Qwen.
Contagem de parâmetros
Para um bloco com d_model = d e expansão de FFN r:
- MHA:
4 · d²(projeções Q, K, V, O) - FFN (SwiGLU):
3 · d · (r · d)≈3rd² - Norms (normalizações): desprezível
Com d = 4096, r = 2.6, layers = 32 (aproximadamente Llama 3 8B), o total é: 32 · (4·4096² + 3·2.6·4096²) ≈ 32 · (16 + 32) M = ~1.5B parameters per layer × 32 ≈ 7B (mais embeddings e a cabeça de projeção final/head). Corresponde às contagens publicadas.
Construa
Passo 1: os blocos de construção
Usando a pequena classe Matrix da Lição 03 (copiada para este arquivo para independência):
layer_norm(x, eps=1e-5)— subtrai a média, divide pelo desvio padrão.rms_norm(x, eps=1e-6)— divide pelo RMS. Sem subtração da média.gelu(x)esilu(x) * W3 x(SwiGLU).ffn_swiglu(x, W1, W2, W3).encoder_block(x, params)edecoder_block(x, enc_out, params).
Veja code/main.py para a fiação completa.
Passo 2: conecte um encoder de 2 camadas e um decoder de 2 camadas
Empilhe-os. Passe a saída do encoder para cada atenção cruzada do decoder. Adicione uma LN final antes da projeção de saída.
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
Passo 3: execute o passo forward em um exemplo simples
Passe uma sequência de origem de 6 tokens e um alvo de 5 tokens. Verifique se o formato da saída é (5, vocab). Sem treinamento — esta lição é sobre a arquitetura, não sobre a perda (loss).
Passo 4: substitua por RMSNorm + SwiGLU
Substitua o LayerNorm e a FFN com ReLU por RMSNorm e SwiGLU. Confirme que os formatos ainda coincidem. Esta é a modernização de 2026 com a substituição de apenas uma função.
Utilize
As implementações de referência do PyTorch/TF: nn.TransformerEncoderLayer, nn.TransformerDecoderLayer. Mas a maior parte do código de produção de 2026 implementa seu próprio bloco porque:
- A Flash Attention é chamada dentro da atenção, e não via
nn.MultiheadAttention. - GQA / MLA não estão na referência da biblioteca padrão (stdlib).
- RoPE, RMSNorm, SwiGLU não são os padrões do PyTorch.
A biblioteca transformers da HF possui blocos de referência limpos que você deve ler: modeling_llama.py é o bloco clássico apenas decoder de 2026. Tem cerca de 500 linhas e vale a pena analisar uma vez.
Encoder vs decoder vs encoder-decoder — quando escolher:
| Necessidade | Escolha | Exemplo |
|---|---|---|
| Classificação, embeddings, QA sobre texto | Apenas encoder | BERT, DeBERTa, ModernBERT |
| Geração de texto, chat, código, raciocínio | Apenas decoder | GPT, Llama, Claude, Qwen |
| Entrada estruturada → saída estruturada (tradução, sumarização) | Encoder-decoder | T5, BART, Whisper |
Os modelos apenas decoder venceram na área de linguagem porque escalam de forma mais limpa e lidam tanto com compreensão quanto com geração. O modelo encoder-decoder ainda é o melhor quando a entrada tem uma identidade clara de "sequência de origem" (tradução, reconhecimento de fala, tarefas estruturadas).
Publique
Veja outputs/skill-transformer-block-reviewer.md. A skill revisa uma nova implementação de bloco de transformer comparando-a com os padrões de 2026 e sinaliza partes ausentes (pré-norm, RoPE, RMSNorm, GQA, razão de expansão de FFN).
Exercícios
- Fácil. Conte os parâmetros no seu
encoder_blockcomd_model=512, n_heads=8, ffn_expansion=4, swiglu=True. Valide implementando o bloco e usandosum(p.numel() for p in block.parameters()). - Médio. Mude de pós-norm para pré-norm. Inicialize ambos e meça a norma das ativações após 12 camadas empilhadas com uma entrada aleatória. As ativações do pós-norm devem explodir; as do pré-norm devem permanecer limitadas.
- Difícil. Implemente um encoder-decoder de 4 camadas em uma tarefa simples de cópia (copiar
xinvertido). Treine por 100 passos. Relate a perda (loss). Substitua por RMSNorm + SwiGLU + RoPE — a perda diminui?
Termos-Chave
| Termo | O que dizem | O que realmente significa |
|---|---|---|
| Bloco | "Uma camada de transformer" | Pilha de norm + attention + norm + FFN, envolta em conexões residuais. |
| Residual | "Conexão de salto (skip connection)" | Saída de x + f(x); permite o fluxo de gradiente através de pilhas profundas. |
| Pré-norm | "Normaliza antes, não depois" | Moderno: x + sublayer(LN(x)). Treina de forma mais profunda sem a necessidade de warmup complexo. |
| RMSNorm | "LayerNorm sem a média" | Divide pelo RMS; uma operação a menos, mesma estabilidade empírica. |
| SwiGLU | "A FFN para a qual todos migraram" | Swish(W1 x) ⊙ W3 x → W2. Supera ReLU/GELU em ppl (perplexidade) do modelo de linguagem. |
| Atenção cruzada | "Como o decoder vê o encoder" | MHA com Q do decoder, K/V das saídas do encoder. |
| Expansão da FFN | "Quão larga é a MLP intermediária" | Razão da dimensão oculta (hidden-size) para o d_model, geralmente 4 (LayerNorm) ou 2,6 (SwiGLU). |
| Sem viés (bias-free) | "Remover os termos +b" | Arquiteturas modernas omitem vieses nas camadas lineares; leve melhora na ppl, modelo menor. |
Leituras Adicionais
- Vaswani et al. (2017). Attention Is All You Need — especificação original do bloco.
- Xiong et al. (2020). On Layer Normalization in the Transformer Architecture — por que a pré-norm supera amplamente a pós-norm em profundidade.
- Zhang, Sennrich (2019). Root Mean Square Layer Normalization — RMSNorm.
- Shazeer (2020). GLU Variants Improve Transformer — o artigo do SwiGLU.
- HuggingFace
modeling_llama.py— bloco clássico apenas decoder de 2026.