Transformers e Mecanismo de Atenção
- ⬜💬 O que é um LLM?(Fundamentos da IA)
- ⬜🔤 Tokens e Tokenização(Fundamentos da IA)
Recomendamos completar os pré-requisitos antes de seguir, mas nada te impede de continuar.
Em junho de 2017, oito pesquisadores do Google publicaram “Attention is All You Need” — um paper de 15 páginas que mudou a IA para sempre. A arquitetura que eles propuseram, o Transformer, é a base de GPT, Claude, Gemini, LLaMA e praticamente todo modelo de linguagem moderno. Neste artigo, você vai entender como ele funciona por dentro: da fórmula de atenção à máscara causal que torna a geração de texto possível.
O problema que o Transformer resolveu
Antes de 2017, modelos de linguagem usavam RNNs (Recurrent Neural Networks) e LSTMs (Long Short-Term Memory). Essas arquiteturas processavam texto token por token, em sequência. Dois problemas fatais:
| Problema | RNN/LSTM | Transformer |
|---|---|---|
| Paralelização | Impossível — token N depende do output de N-1 | Total — todos os tokens processados de uma vez |
| Dependências longas | Gradiente desaparece após ~100 tokens | Atenção alcança qualquer posição na sequência |
| Velocidade de treino | Lento — operações sequenciais | Rápido — GPUs adoram operações matriciais paralelas |
| Escalabilidade | Modelo com 1B params já era impraticável | GPT-4: ~1.7T params (estimado) |
O Transformer resolveu tudo de uma vez: substituiu a recorrência por um mecanismo de atenção pura que processa toda a sequência em paralelo. Resultado: modelos 10-100× maiores treinados em fração do tempo.
Self-Attention: a ideia central
A pergunta que self-attention responde é simples: “para entender este token, quais outros tokens da sequência são relevantes?”
Para cada token, o modelo cria três vetores multiplicando o embedding por três matrizes de pesos aprendidas:
Analogia da biblioteca: Query = sua pergunta; Key = título de cada livro; Value = conteúdo do livro. Atenção = match(Query, Key) → pega Value.
Cada token gera seus próprios Q, K e V. A mágica acontece quando comparamos o Query de um token com as Keys de todos os outros tokens.
A fórmula: Attention(Q, K, V)
A fórmula completa de scaled dot-product attention é:
Attention(Q, K, V) = softmax( Q · Kᵀ / √dₖ ) · V
Passo a passo para “sentou” na frase “O gato sentou no tapete”:
| O | gato | sentou | no | tapete | |
|---|---|---|---|---|---|
| sentou | 0.06 | 0.31 | 0.33 | 0.07 | 0.23 |
Forma matricial: processando tudo de uma vez
Na prática, Q, K e V não são vetores individuais — são matrizes onde cada linha é o vetor de um token. Para uma sequência de n tokens com dimensão dₖ:
Custo: O(n²·dₖ) — quadrático no comprimento da sequência. Para n=8192, ~67 milhões de scores por camada.
Multi-Head Attention: olhar de múltiplos ângulos
Uma única cabeça de atenção aprende um tipo de relação. Mas linguagem tem muitas dimensões: sintaxe, semântica, correferência, posição relativa. A solução: múltiplas cabeças em paralelo, cada uma operando em um subespaço diferente.
O custo computacional é o mesmo que single-head: h cabeças de dimensão d/h = uma multiplicação matricial de dimensão d. Mas o modelo ganha capacidade expressiva massivamente superior.
Positional Encoding: ensinando ordem ao Transformer
Self-attention é invariante à ordem: “gato mordeu cachorro” e “cachorro mordeu gato” produzem os mesmos scores de atenção (os mesmos tokens, os mesmos dot products). Isso é um problema sério — ordem muda significado.
A solução: somar ao embedding de cada token um vetor que codifica sua posição na sequência. O paper original usou funções seno/cosseno com frequências diferentes:
| dim0 | dim1 | dim2 | dim3 | dim4 | dim5 | dim6 | dim7 | |
|---|---|---|---|---|---|---|---|---|
| pos 0 | 0.00 | 1.00 | 0.00 | 1.00 | 0.00 | 1.00 | 0.00 | 1.00 |
| pos 1 | 0.84 | 0.54 | 0.10 | 0.99 | 0.01 | 1.00 | 0.00 | 1.00 |
| pos 2 | 0.91 | -0.42 | 0.20 | 0.98 | 0.02 | 1.00 | 0.00 | 1.00 |
| pos 3 | 0.14 | -0.99 | 0.30 | 0.96 | 0.03 | 1.00 | 0.00 | 1.00 |
Frequências altas (dim0–1) mudam rápido entre posições; frequências baixas (dim6–7) mudam devagar — o modelo aprende a ler posição a partir do padrão combinado. Embedding final = token_embedding + positional_encoding.
| Tipo de PE | Como funciona | Usado em |
|---|---|---|
| Sinusoidal (fixa) | Funções sin/cos com frequências diferentes; não treinável | Transformer original (2017) |
| Learned (aprendida) | Vetor treinável por posição; mais flexível | GPT-2, BERT |
| RoPE (Rotary) | Rotação no espaço complexo; extrapola para sequências maiores | LLaMA, GPT-NeoX, Claude |
| ALiBi | Bias linear na atenção proporcional à distância; sem PE no embedding | BLOOM, MPT |
Modelos modernos preferem RoPE porque ela permite extrapolar para sequências mais longas do que as vistas no treino — essencial para context windows de 100k+ tokens.
Bloco Transformer completo
Cada bloco Transformer (também chamado de “layer”) combina atenção com uma rede feed-forward e usa duas técnicas cruciais: conexões residuais e layer normalization.
Encoder vs Decoder: as três arquiteturas
O paper original propôs encoder + decoder juntos (para tradução). Mas a comunidade descobriu que cada metade funciona sozinha para tarefas diferentes.
Máscara causal: como o decoder gera texto
O decoder precisa gerar tokens um de cada vez, da esquerda para a direita. Mas self-attention, por padrão, permite que cada token veja a sequência inteira — incluindo tokens futuros. Isso seria “trapacear”.
A solução é a máscara causal: antes do softmax, setamos -∞ em todas as posições futuras. Após o softmax, elas viram 0 — atenção zero.
| O | gato | sentou | |
|---|---|---|---|
| O | 1.00 | 0.00 | 0.00 |
| gato | 0.29 | 0.71 | 0.00 |
| sentou | 0.05 | 0.60 | 0.35 |
Posições bloqueadas (futuro) recebem score -∞ antes do softmax → atenção 0 após softmax. “O” só vê a si mesmo; “sentou” vê os três tokens.
Durante o treinamento, a máscara permite processar toda a sequência em paralelo (teacher forcing). Durante a inferência, o modelo gera token por token, e a máscara garante que cada novo token é condicionado apenas nos anteriores.
Cross-Attention: encoder fala com decoder
No Transformer original (encoder-decoder), o decoder tem uma camada extra entre self-attention e FFN: cross-attention. Nela, o Query vem do decoder mas Key e Value vêm do output do encoder.
Modelos decoder-only como GPT e Claude não precisamde cross-attention porque não têm encoder. Toda a informação (input + output) vive na mesma sequência — o prompt é o “encoder”.
Por que GPT é decoder-only?
Uma das decisões mais impactantes da história recente da IA: por que não usar encoder?
| Fator | Encoder-Decoder | Decoder-Only |
|---|---|---|
| Complexidade | 2 stacks separados, cross-attention | 1 stack, mais simples de escalar |
| Escala | Parâmetros divididos entre encoder e decoder | Todos os parâmetros focados em uma direção |
| Treinamento | Precisa de pares (input, output) | Treina em texto contínuo — dados infinitos na web |
| Versatilidade | Especializado em seq2seq (tradução, resumo) | Qualquer tarefa formulada como "completar texto" |
| Few-shot/ICL | Difícil — encoder e decoder têm papéis fixos | Natural — exemplos vão no prompt como contexto |
| Emergência | Escala até ~10B params com retornos decrescentes | Capacidades emergentes em escala (>100B params) |
📋 Qual arquitetura para um LLM de uso geral?
Simplicidade de escalar + treino em texto da web sem supervisão + versatilidade de prompt = dominância total desde GPT-3 (2020). O mercado validou: GPT, Claude, LLaMA, Gemini — todos decoder-only.
Alt: Encoder-only (BERT) — Ainda usado para embeddings, classificação e busca semântica onde bidirecionalidade importa.
Alt: Encoder-decoder (T5) — Usado em tradução, speech-to-text (Whisper) e tarefas com input/output claramente separados.
Escala: dos 65M aos 1.7T parâmetros
O padrão é claro: a mesma arquitetura (Transformer decoder-only) escala de 117M a 1.7T parâmetros com capacidades emergentes previsíveis. A inovação não está mais na arquitetura — está nos dados, no treinamento (RLHF, DPO) e na infraestrutura (MoE, Flash Attention, KV Cache).
Código real: self-attention em PyTorch
import torch
import torch.nn.functional as F
def scaled_dot_product_attention(Q, K, V, mask=None):
"""Attention(Q, K, V) = softmax(QKᵀ / √dₖ) V"""
d_k = Q.size(-1)
scores = torch.matmul(Q, K.transpose(-2, -1)) / (d_k ** 0.5)
if mask is not None:
scores = scores.masked_fill(mask == 0, float('-inf'))
weights = F.softmax(scores, dim=-1)
return torch.matmul(weights, V), weights
# Exemplo: sequência de 5 tokens, dimensão 64
Q = torch.randn(1, 5, 64) # (batch, seq_len, d_k)
K = torch.randn(1, 5, 64)
V = torch.randn(1, 5, 64)
# Máscara causal: triângulo inferior
mask = torch.tril(torch.ones(5, 5)) # [[1,0,0,0,0],[1,1,0,0,0],...]
output, attn_weights = scaled_dot_product_attention(Q, K, V, mask)
print(f"Output shape: {output.shape}") # (1, 5, 64)
print(f"Attn weights shape: {attn_weights.shape}") # (1, 5, 5)
print(f"Attn weights[0,0]: {attn_weights[0,0]}")
# → tensor([1.0, 0.0, 0.0, 0.0, 0.0]) ← token 0 só atende a siPerguntas e respostas
❓ Se atenção é O(n²), como modelos processam 128k tokens?
❓ O que são as 'camadas' de um Transformer? Quando alguém diz 'GPT-3 tem 96 layers', o que significa?
❓ Feed-forward tem 4× a dimensão do modelo. Por quê?
Quiz rápido
4 perguntas · Acerte tudo e ganhe o badge 🎯 Gabarito