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

Interno dos blocos de encoder e decoder, conectados

As seis partes

  1. Embedding + sinal posicional. Tokens → vetores. Posição injetada via RoPE (moderno) ou senoidal (clássico).
  2. Self-attention. Cada posição atende a todas as outras. Mascarada em decoders.
  3. 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.
  4. Conexão residual. x + sublayer(x). Sem isso, os gradientes desaparecem após cerca de 6 camadas.
  5. Layer normalization. LayerNorm ou RMSNorm (moderno). Estabiliza o fluxo residual.
  6. 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 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) e silu(x) * W3 x (SwiGLU).
  • ffn_swiglu(x, W1, W2, W3).
  • encoder_block(x, params) e decoder_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

  1. Fácil. Conte os parâmetros no seu encoder_block com d_model=512, n_heads=8, ffn_expansion=4, swiglu=True. Valide implementando o bloco e usando sum(p.numel() for p in block.parameters()).
  2. 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.
  3. Difícil. Implemente um encoder-decoder de 4 camadas em uma tarefa simples de cópia (copiar x invertido). 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

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