Como os Transformers prestam atenção?
Neste artigo, você vai entender o que é atenção escalada — o mecanismo central dos Transformers. Da álgebra ao código, explico como modelos modernos aprendem a focar nas palavras certas e gerar linguagem com contexto.

Transformers são a espinha dorsal da IA moderna. E no centro da arquitetura, está um mecanismo essencial: atenção escalada. Neste artigo, vamos explorar com profundidade esse componente — da teoria ao código, da álgebra linear ao aprendizado pronominal. Esta é a Parte 1 de uma série que vai dissecar os elementos fundamentais do Transformer. Aqui, o foco está na atenção. Nos próximos, vamos falar sobre embeddings, múltiplas cabeças, arquitetura do encoder, e muito mais.
🔍 Quer ver isso ganhando vida no código?
No meu canal do YouTube, eu mostro passo a passo como essa lógica se traduz em vetores reais, tensores PyTorch e visualizações interpretáveis.
🎥 Acesse: youtube.com/@francisquiniai
O passado: RNNs, LSTMs e seus limites
Antes de entender a revolução, precisamos lembrar do que veio antes. RNNs e LSTMs dominavam o processamento de sequências por anos. Tinham memória, passavam informações passo a passo e funcionavam bem para tarefas como modelagem de linguagem, tradução e reconhecimento de fala. Mas havia um limite prático difícil de superar: o custo do processamento sequencial. Cada palavra precisava esperar pela anterior para ser processada, o que dificultava paralelização e tornava o aprendizado de relações de longo prazo um desafio. Além disso, a memória das RNNs não era confiável para sequências longas — a informação se diluía.
A revolução: Attention is All You Need
Foi em 2017 que Vaswani e colegas publicaram o artigo Attention is All You Need. Nele, apresentaram o Transformer, uma arquitetura que eliminava completamente a recorrência. A proposta parecia ousada: em vez de processar as palavras em sequência, o modelo consideraria todas ao mesmo tempo. E faria isso aprendendo a "prestar atenção" nas partes mais relevantes do input. Com isso, destravou-se a paralelização total, aumentou-se a capacidade de capturar dependências de longo prazo e, de quebra, abriu-se caminho para arquiteturas maiores, com mais dados e mais contexto.
Mas o que é, de fato, essa atenção? Qual a matemática por trás dela?
A atenção é uma operação baseada em produto escalar entre vetores. Para cada palavra da sequência, geramos três vetores: %%\textbf{query} ((Q))%%, %%\textbf{key} ((K))%% e %%\textbf{value} ((V))%%. A ideia é que a query represente o que a palavra está buscando, a key represente o que as demais palavras oferecem e o value contenha a informação associada a cada uma.
Calculamos a similaridade entre a query de uma palavra e todas as keys das palavras da sequência usando produto escalar, resultando em uma matriz de escores de atenção:
%% \text{scores} = \frac{QK^\top}{\sqrt{d_k}} %%
Em seguida, aplicamos uma função %%\textit{softmax}%% para normalizar os escores e obter os pesos de atenção:
%%\text{weights} = \text{softmax}\left(\frac{QK^\top}{\sqrt{d_k}}\right)%%
Esses pesos são usados para calcular uma média ponderada dos valores (V), produzindo a saída da atenção:
%% \text{output} = \text{weights} \cdot V %%
Esse vetor resultante representa um novo estado da palavra, agora incorporando contexto de outras palavras relevantes da sequência.
Um exemplo interpretável
Imagine a frase:
Input: "A inteligência artificial está transformando o mundo"
O modelo vai gerar vetores Q, K e V para cada uma das palavras da frase. Por simplicidade, vamos assumir que temos vetores com 4 dimensões para cada token. A atenção para a palavra "transformando", por exemplo, será influenciada por tokens como "inteligência" e "mundo", se os vetores estiverem alinhados em suas direções semânticas. O produto escalar entre Q de "transformando" e os K das demais palavras vai definir o quanto cada uma pesa na combinação final.
Um exemplo mais real e interpretável:
Input: "Maria entregou o relatório para Ana porque ela estava no escritório."
Essa frase é ambígua: quem estava no escritório? Maria ou Ana?
Em modelos treinados como BERT, a matriz de atenção da palavra "ela" mostra forte peso em "Ana", indicando que o modelo aprendeu, com base em corpus e contexto, que é mais provável que a referência seja Ana. Isso acontece porque os vetores de Q, K e V foram ajustados durante o treinamento para capturar padrões estatísticos e relacionamentos linguísticos. Os pesos de atenção expressam essas relações.
Como os pesos são aprendidos?
A resposta está nas matrizes W_q, W_k e W_v. Elas são parâmetros treináveis que transformam os embeddings de entrada nos vetores Q, K e V. Durante o treinamento, o erro na tarefa (ex: predição da próxima palavra) é propagado de volta por meio da atenção, e essas matrizes são ajustadas com gradiente descendente.
Com o tempo, o modelo aprende que certas combinações de vetores ativam corretamente os relacionamentos mais úteis. Assim, ele passa a "olhar" para os tokens certos nas horas certas. Não porque alguém programou isso, mas porque aprender a fazer isso reduz o erro na tarefa final.
Código básico da atenção com NumPy
O código a seguir implementa a lógica da atenção de forma explícita usando NumPy. Começamos com uma sequência de três palavras representadas como vetores em um espaço de 4 dimensões. Multiplicamos esses vetores pelas matrizes aprendidas W_q, W_k, W_v, que simulam os parâmetros do modelo, para obter os vetores de consulta (Q), chave (K) e valor (V). O produto escalar entre Q e a transposta de K mede a similaridade entre palavras. Em seguida, aplicamos uma normalização via softmax para gerar os pesos de atenção. Finalmente, realizamos uma combinação ponderada dos vetores V com esses pesos para obter a saída final: um novo vetor contextualizado para cada palavra, considerando a relevância das outras. Este processo espelha exatamente o que acontece dentro de uma camada de atenção em Transformers.
import numpy as np
# Sequência de 3 palavras, cada uma com 4 dimensões
x = np.random.rand(3, 4)
# Matrizes W aprendidas (para Q, K e V)
W_q = np.random.rand(4, 4)
W_k = np.random.rand(4, 4)
W_v = np.random.rand(4, 4)
Q = x @ W_q
K = x @ W_k
V = x @ W_v
# Produto escalar entre Q e K transposta
scores = Q @ K.T / np.sqrt(4)
weights = np.exp(scores) / np.exp(scores).sum(axis=1, keepdims=True)
# Output final
attention = weights @ V
Código modular com PyTorch
Antes de vermos como tudo isso é utilizado na prática, vale encapsular a lógica da atenção escalada em um módulo reutilizável. A implementação abaixo define exatamente a equação:
%%\text{Attention}(Q, K, V) = \text{softmax}\left( \frac{Q K^T}{\sqrt{d_k}} \right) V%%
Essa operação é o núcleo da atenção em Transformers. Cada uma das várias cabeças de atenção do modelo executa esse mesmo cálculo, mas com parâmetros distintos, permitindo que o Transformer observe a sequência sob múltiplas perspectivas ao mesmo tempo.
import torch
import torch.nn.functional as F
class ScaledDotProductAttention(torch.nn.Module):
def forward(self, Q, K, V):
d_k = Q.size(-1)
scores = Q @ K.transpose(-2, -1) / d_k**0.5
weights = F.softmax(scores, dim=-1)
return weights @ V
Esse módulo é o núcleo da atenção escalada. Ele é utilizado em cada uma das múltiplas cabeças de atenção no Transformer, permitindo que o modelo enxergue o input de diferentes perspectivas simultaneamente.
A atenção permite que o modelo entenda relações de longo alcance de forma eficiente. Em tradução, isso significa que o modelo consegue conectar um sujeito no início da frase com um verbo no final. Em geração de texto, que ele pode lembrar do estilo e do tom de uma introdução enquanto escreve a conclusão. E tudo isso sem loops, sem recursão, com pura álgebra linear — totalmente paralelizável em GPUs modernas.
Um experimento real: atenção aprendendo referência pronominal
Vamos observar o processo de aprendizado na prática. Vamos treinar uma rede pequena para que ela aprenda que a palavra "ela" em uma frase deve focar sua atenção em "Maria".
Agora vamos observar, passo a passo, como a atenção aprende a capturar uma referência pronominal. Esse é um experimento didático com poucos tokens, vetores pequenos e total transparência nas operações.
Objetivo
Queremos que a atenção da palavra "ela" aprenda a focar na palavra "Maria". Nossa frase de treino será:
"João deu Maria ela"
O vocabulário é composto por apenas 4 palavras:
João, deu, Maria, ela
Atribuímos os índices:
João = 0, deu = 1, Maria = 2, ela = 3
Embeddings iniciais
Cada palavra é representada por um vetor de embedding de dimensão 3 (inicializados aleatoriamente):
[[ 0.1013, 1.3199, -0.8060], # João
[-0.3447, 2.1137, -1.3133], # deu
[ 0.7930, 0.3330, 0.9407], # Maria
[-0.8380, -2.0299, -1.1218]] # ela
Geração dos vetores Q, K e V
Cada embedding é projetado em três espaços diferentes por matrizes treináveis:
- Q (query): representa o que a palavra está buscando
- K (key): representa o que a palavra oferece
- V (value): representa a informação da palavra
As projeções seguem:
%% Q = X W_q \quad,\quad K = X W_k \quad,\quad V = X W_v %%
Com valores numéricos gerados na primeira época (exemplo real):
Q =
[[-0.2001, 0.3570, 0.5615],
[-0.0999, 0.5520, 0.8895],
[-1.0008, 0.2845, -0.2464],
[ 1.8992, -0.9212, -0.0309]]
K =
[[ 0.0473, -0.6073, 0.1295],
[ 0.0513, -1.2269, 0.4704],
[-0.2684, 0.3256, -0.6502],
[ 0.5608, 0.3946, 0.7527]]
V =
[[-0.7065, 0.7598, -0.2885],
[-1.2361, 1.4640, -0.6187],
[ 0.3903, -0.6640, 0.1630],
[ 0.0844, 0.1751, 0.2359]]
Produto escalar entre Q e K
O coração da atenção é o produto escalar entre queries e keys, seguido de uma normalização:
%% \text{scores} = \frac{Q K^\top}{\sqrt{d_k}} %%
Com isso, temos os scores de atenção entre cada par de palavras:
Scores =
[[-0.0886, -0.1063, -0.1127, 0.2606],
[-0.1297, -0.1524, -0.2146, 0.4799],
[-0.1455, -0.2980, 0.3011, -0.3663],
[ 0.3725, 0.7003, -0.4559, 0.3917]]
Aplicação do softmax
Transformamos os scores em probabilidades com softmax. Cada linha agora representa a distribuição de atenção de uma palavra sobre as demais:
Pesos (linha de "ela") =
[0.2601, 0.3611, 0.1136, 0.2652]
Nosso objetivo é que esse vetor evolua até:
Target: [0, 0, 1, 0]
Ou seja, a palavra "ela" deve olhar apenas para "Maria".
Função de perda
Utilizamos o erro quadrático médio (MSE):
%% \mathcal{L} = \frac{1}{n} \sum_{i=1}^n (a_i - t_i)^2 %%
Com valor inicial de:
Loss = 0.2635
Aprendizado ao longo do tempo
À medida que o modelo treina, ele ajusta suas matrizes de projeção. Isso faz com que os vetores Q, K e V mudem, e os pesos de atenção também.
Veja como o vetor de atenção de "ela" evolui ao longo das épocas:
Época 0: [0.2601, 0.3611, 0.1136, 0.2652]
Época 3: [0.2411, 0.1934, 0.3335, 0.2320]
Época 5: [0.2080, 0.1335, 0.5052, 0.1533]
Época 7: [0.1216, 0.0585, 0.7267, 0.0932]
Época 9: [0.0389, 0.0134, 0.9097, 0.0380]
Convergência
Depois de poucas épocas, a atenção da palavra "ela" se concentra quase totalmente em "Maria", como queríamos.
Atenção final: [0.0389, 0.0134, 0.9097, 0.0380
]Perda final: 0.0028
Esse experimento mostra o processo completo: inicialização aleatória, cálculos da atenção, retropropagação do erro e ajuste dos pesos. Em poucas épocas, mesmo uma rede pequena aprende dependências sutis — como que "ela" se refere a "Maria".
O gráfico a seguir ilustra a evolução dos pesos de atenção conforme a evolução das épocas de treino.

No fundo, tudo é álgebra linear. Mas com as parametrizações certas, os Transformers aprendem coisas poderosas. Transformers são o núcleo das LLMs modernas, mas não são tudo. Eles representam a base matemática e arquitetural que tornou possível lidar com sequências longas de forma eficiente. No entanto, os modelos de linguagem atuais combinam essa base com escala massiva, pré-treinamento em grandes volumes de dados e técnicas de refinamento posteriores. Entender transformers é fundamental — mas é apenas uma parte da engenharia que sustenta a inteligência artificial contemporânea.