Redes Neurais Artificiais e Deep Learning · Insper

Mecanismos de Atenção

Uma camada densa cujos pesos vêm da entrada

Duas escolhas opostas

Convolução

  • decide de antemão quem conversa com quem: os vizinhos
  • os mesmos pesos em todas as posições
  • fixos depois do treino

Atenção

  • calcula as forças de conexão a partir dos dados, em tempo de execução
  • para cada par de posições
  • diferentes para cada entrada

Atenção é uma camada densa cujos pesos são uma função da entrada. O , as máscaras, as cabeças e o cache que domina a conta de inferência saem todos dessa frase — e do custo de levá-la a sério.

Antes: a recorrência

  • O estado é uma folha de anotações de tamanho fixo, reescrita a cada palavra .
  • O gradiente do último token até o primeiro é um produto de fatores, um por token: se cada um vale 0,9, .
  • O LSTM multiplica a célula por uma porta em vez de uma matriz — melhor, mas ainda é um produto.
  • E os passos são sequenciais: a GPU espera um estado de cada vez.

O LSTM resolve o problema ou só o empurra para mais longe?

Simulador · até onde o gradiente chega

O que aposentou a recorrência

caminho do último token até o primeiro 64 tokens 256 tokens
RNN simples, escala dos pesos 1 (medido no simulador)
caminho da célula do LSTM, por passo
atenção um passo um passo
  • A recorrência decai exponencialmente com a distância; o LSTM só melhora a constante.
  • A atenção lê todas as posições de uma vez: o gradiente é multiplicado por um peso de atenção, não por um produto com um fator por passo.
  • Alcance e paralelismo — não um ganho isolado de acurácia — é o que fez o "Attention Is All You Need" (2017) largar a recorrência.

O que é um token

  • O modelo lê inteiros, nunca letras. Um token é um pedaço de texto de uma lista fixa, o vocabulário; o seu número é o id.
  • Palavras inteiras? A lista nunca acaba e uma palavra fora dela não tem id.
  • Caracteres? Nada fica desconhecido, mas "o gato subiu no" vira 15 símbolos e o custo da atenção cresce com .
  • Subwords: palavras frequentes inteiras, as mais raras em pedaços, bytes soltos como último recurso.
texto tokens do GPT-2
the cat climbed the 4
o gato subiu no 6
uma frase em inglês de 16 palavras 17
a versão em português, 13 palavras 25

GPT-2: 256 bytes + 50.000 fusões + 1 token especial = 50.257. "o gato subiu no" = o ␣g ato ␣sub iu ␣no. O espaço pertence à palavra seguinte: "the" é o id 1169, "␣the" é o 262.

BPE na frase do exemplo

Conte os pares vizinhos de um corpus, funda o mais frequente, anote, repita. Para tokenizar um texto novo, repita as fusões na ordem.

depois da fusão pedaços quantos
início: caracteres o ␣ g a t o ␣ s u b i u ␣ n o 15
1 ␣+n · 2 ␣n+o · 3 t+o · 4 ␣+s o ␣ g a to ␣s u b i u ␣no 11
8 ␣+g · 9 ␣g+a · 10 ␣ga+to o ␣gato ␣s u b i u ␣no 8
13 i+u o ␣gato ␣s u b iu ␣no 7
40 ␣s+u · 41 ␣su+b o ␣gato ␣sub iu ␣no 5

Corpus de brinquedo: 8 frases ("o gato sentou no telhado.", "o cão quer subir no sofá." …), 23 caracteres, 41 fusões; "subiu" não está nele. O modelo de brinquedo fica com 12 tokens: o = 0, ␣no = 1, ␣gato = 2, ␣sub = 4, iu = 5 → [0, 2, 4, 5, 1].

Simulador · BPE, fusão a fusão

Do id ao vetor

Uma consulta à tabela de embeddings () mais a linha da tabela de posições : nenhuma conta com o id em si.

token (id)
1 o (0) [−0,5; −0,5; 0; 1] [0; 0; 0; 0] [−0,5; −0,5; 0; 1]
2 ␣gato (2) [1; −0,5; 0; −0,5] [0,5; 0; −0,5; 0] [1,5; −0,5; −0,5; −0,5]
3 ␣sub (4) [−0,5; 1; 0; −0,5] [0; 0,5; 0; −0,5] [−0,5; 1,5; 0; −1]
4 iu (5) [0; 0,5; −0,5; 0] [−0,5; 0; 0,5; 0] [−0,5; 0,5; 0; 0]
5 ␣no (1) [−0,5; 0; −0,5; 1] [0; −0,5; 0; 0,5] [−0,5; −0,5; −0,5; 1,5]

Brinquedo: V = 12, d = 4, 8 posições, pesos escolhidos à mão. GPT-2 small: E é 50.257 × 768 (38,6M de parâmetros, 31% do modelo), P é 1024 × 768, as duas aprendidas. O Llama não tem P: a RoPE gira q e k.

A ideia: cada palavra relê as outras

  • "Sentei no banco da praça."
  • "Fui ao banco sacar dinheiro."

A camada de embedding dá um vetor a "banco". É o mesmo vetor nas duas frases?

  • É. O embedding olha só para o token, não para os vizinhos.
  • A atenção desfaz a ambiguidade: "banco" monta um vetor novo misturando os outros — de "sentei" e "praça" na primeira frase, de "sacar" e "dinheiro" na segunda.
  • A saída: um vetor por token, agora com contexto.

Um dicionário suave

estoque = {"maçã": 3, "pera": 7}
estoque["pera"]   # casamento exato → 7

Um dicionário exige casamento exato e devolve um valor.

A atenção compara a pergunta com todas as chaves, dá a cada uma uma nota de semelhança e devolve uma média ponderada de todos os valores.

A média é uma função suave das notas, então dá para derivar — e a rede aprende, por gradiente, o que perguntar e o que anunciar.

Query, Key, Value

  • Query (consulta) — o que esta posição procura. A pergunta.
  • Key (chave) — o que cada posição anuncia sobre si. A etiqueta.
  • Value (valor) — o que cada posição entrega se for escolhida. O conteúdo.

X: uma linha por token, n tokens de d números · dk: o comprimento de cada query e chave · softmax: escore dividido pela soma da linha.

Três projeções da mesma entrada; WQ, WK e WV são os únicos parâmetros aprendidos. É por isso que um token pode pedir uma coisa, anunciar outra e entregar uma terceira — um pronome pode consultar por "o substantivo a que me refiro" sem ser um substantivo.

Um exemplo feito à mão

"sentei · banco · praça", duas dimensões escolhidas à mão. Os valores se leem como .

token chave valor
sentei
banco — ambíguo
praça

A query de "banco" é e . Qual palavra recebe o maior peso? O que sai?

Os quatro passos

, :

token ① score ② ③ ③ peso ④
sentei 0,71 2,03 2,03 / 27,29 = 0,074
banco 2,12 8,34 0,306
praça 2,83 16,92 0,620
soma 27,29 1

"banco" entrou em cima do muro () e sai como assento. Nenhum peso foi fixado por posição: a escolha veio de quanto a query se parece com cada chave.

Simulador · uma query, três chaves

q — a query de "banco": [q1, q2]
d_k (só muda o divisor √d_k)
chave k — o que ele anuncia
valor v — [assento, dinheiro]
sentei
banco
praça

A forma de cada matriz

tensor forma o que é
um vetor por token
aprendidas
aprendida
uma query e uma chave por token
um valor por token
um score para cada par — o
linha : para onde o token olha; soma 1
um vetor novo, com contexto, por token

Laboratório, seção 2: n = 6, d = 16, dk = 8, dv = 4 → A é 6 × 6, O é 6 × 4, 16·8 + 16·8 + 16·4 = 320 parâmetros — nenhum depende de n. As nossas três linhas batem com F.scaled_dot_product_attention a menos de 1,2 × 10−7.

Simulador · a matriz inteira

Por que dividir por

Se e têm componentes independentes com média 0 e variância 1:

  • Com os scores têm desvio padrão ; diferenças de 20 são rotina.
  • : um peso vale 1, o resto vale — um argmax duro e sem gradiente.
  • Dividir por devolve a variância a 1.

Por que não dividir por ?

Medido no laboratório

Uma query contra 16 chaves, 200 linhas; mediana sobre as linhas.

maior peso, com , com maior peso, sem , sem linhas mortas, sem
4 0,21 0,26 0,37 0,27 0%
64 0,22 0,27 0,94 0,054 17%
1024 0,22 0,28 0,99999 0,000011 64%
  • Com a divisão, nada se mexe quando cresce.
  • Sem ela, com a linha mediana é one-hot e o gradiente dela é vezes menor.

Maior peso: o máximo de uma linha de A (uniforme seria 1/16 ≈ 0,06). ‖∂L/∂S‖: o tamanho do gradiente que chega aos scores da linha. "Morta" = ‖∂L/∂S‖ abaixo de 10−3. Notebook, seção 3.

A volta pelo softmax

  • é o produto elemento a elemento. Se uma linha de é quase one-hot, quase todo o parêntese é multiplicado por …
  • …e onde não é minúsculo, o parêntese em si é quase zero: ele subtrai a média ponderada da linha (o a copia em cada coluna), que é o próprio elemento.
  • Uma linha saturada devolve gradiente numericamente nulo.
  • Conferência de graça: cada linha de soma zero — somar uma constante a uma linha de scores não muda o softmax dela.

Laboratório, seção 4: queries × 100 → quatro de cinco linhas imprimem maior peso 1,0 e os gradientes delas caem de 0,15–0,51 para algo entre 10−34 e 7 × 10−4.

Máscaras: o que um token não pode ver

Somadas aos scores antes do softmax, como : .

posição enxerga deve prever
1 o ␣gato
2 o␣gato ␣sub
3 o␣gato␣sub iu
4 o␣gato␣subiu ␣no
5 o␣gato␣subiu␣no ␣telhado
  • A máscara causal é toda a diferença entre um encoder e um decoder.
  • Uma passagem direta dá n exemplos de treino em vez de um. Sem a máscara, a posição 1 veria ␣gato e aprenderia a copiar.

Quatro máscaras, uma linha cada

máscara proíbe onde
causal olhar o futuro todo decoder autorregressivo
de padding atender ao enchimento <pad> todo batch de sequências de tamanhos diferentes
janela deslizante olhar mais de para trás Mistral, Gemma, pilhas híbridas
de documento cruzar para outro documento empacotado todo pré-treino moderno

Um vetor zero não é "nada": o score dele é 0, não . No laboratório, padding sem máscara mexeu a saída dos tokens de verdade em 0,67; com a máscara, em . Esquecer a máscara de documento é a versão silenciosa do mesmo bug.

De onde vem a posição

A atenção é equivariante a permutações: embaralhe os tokens e as saídas embaralham junto. "cachorro morde homem" = "homem morde cachorro" (no laboratório: diferença de ).

esquema mecanismo veredito
senoidal sen/cos fixos somados à entrada histórico
absoluta aprendida um vetor treinável por posição não passa do comprimento treinado
ALiBi penalidade linear na distância, nos scores largamente superada
RoPE girar Q e K por um ângulo ∝ posição universal
NoPE / híbrida nenhum sinal em algumas camadas crescendo em contexto longo

RoPE: um relógio por par de dimensões

  • Query na posição , chave na posição . Cada par de dimensões é um ponteiro, girado (: a velocidade do par). Rotações somam ângulos, então o score só depende de .
  • Ponteiros rápidos distinguem vizinhos; os lentos, distâncias longas.
  • Reescale as frequências e os mesmos pesos endereçam um contexto mais longo (interpolação de posição, NTK, YaRN).

01234567891011121314152θkqq em 3, k em 101234567891011121314152θkqq em 10, k em 8mesmo ângulo entre os ponteiros → mesmo score

Laboratório, seção 6: a mesma q e a mesma k nas posições (3, 1), (10, 8) e (1000, 998) dão o mesmo score, +0,033671; os deslocamentos 1 e 3 dão −0,81 e +1,18.

Multi-head é um reshape

passo forma
, um só produto para todas as cabeças
separar as cabeças
scores, uma por cabeça
concatenar, depois

Quatro matrizes , qualquer que seja . Laboratório: → parâmetros (com os vieses) para = 1, 2, 8 ou 16. Cabeças são paralelismo de graça, não capacidade extra.

B: sequências no lote · n: tokens · h: cabeças, cada uma com d/h dimensões.

Simulador · multi-head, linha a linha

O cache de KV

Gerar o token exige atenção sobre todos os anteriores. O que você precisa guardar de cada um deles?

  • A query não: ela só serviu ao próprio token, lá atrás. A chave e o valor são lidos por todo token que vier depois.
  • Calculados uma vez, guardados: sem o cache o passo roda a rede de novo sobre os tokens — gerar tokens custa passagens de token em vez de .
  • O preço: a memória de K e V de todos os tokens, em todas as camadas.

Quanto ocupa o cache

ú

termo o que é Llama 3 8B
K e V 2
camadas 32
cabeças de chave/valor 8
dimensões por cabeça 128
bytes por número 16 bits 2
por token bytes 128 KiB
tokens × sequências: 128k = 131 072 tokens, um usuário 16 GiB

Simulador · o cache num acelerador

é o termo que dá para encolher

esquema cabeças de KV cache qualidade 2026
MHA 1× referência histórico em contexto longo
MQA 1 mensuravelmente pior onde a memória domina
GQA (≈ 8) ≈ MHA o padrão
MLA — ~1/57 reportada ≥ MHA DeepSeek-V2/V3

Decodificar lê o cache inteiro para produzir um token: o limite é a banda de memória, não os FLOPs. GQA, cache quantizado, PagedAttention, batches maiores — todos atacam bytes movidos.

Tornando o pagável

  • Mesma matemática, execução melhor — o FlashAttention nunca monta ; exato a menos do arredondamento. Use F.scaled_dot_product_attention.
  • Atender a menos — janelas deslizantes, sumidouros de atenção, camadas globais intercaladas.
  • Mudar a matemática — atenção linear, SSMs (Mamba): , mas recuperação exata mais fraca. Híbridos vencem.
  • Aprender o que pular — atenção esparsa treinável (NSA, MoBA).

Laboratório, seção 8, uma camada com 8 cabeças: S ocupa 8 · 4096² · 4 bytes = 512 MiB com n = 4096 e o trabalho cresce 4× a cada dobra; na CPU do laboratório a versão ingênua levou 216 ms e o kernel fundido, 77 ms.

Medido num modelo de verdade

SmolLM2-135M (arquitetura do Llama: RoPE, GQA): , , , .

  • Cache por token em float32 (4 bytes): 46 080 bytes — medido e previsto. Com MHA, × mais.
  • 80 tokens, de forma gulosa: os mesmos tokens com e sem o cache; sem ele, o passo foi de 56 a 90 ms (o prefixo cresce); com ele, ficou perto de 24 ms.
  • Uma cabeça de token anterior treinada (camada 16, cabeça 0): 0,78 do peso no token .
  • O primeiro token recebe, em média, 0,45 de cada linha — dividindo por igual entre os tokens visíveis, seriam 0,10: um sumidouro de atenção.

Laboratório · o mecanismo, calculado e medido

Notebook no Colab, ~2 h, CPU. Cada seção termina num checkpoint com valor de verificação.

  1. o dicionário suave, à mão
  2. todas as queries de uma vez; contra o PyTorch
  3. por que , medido
  4. a volta, à mão
  5. máscaras: causal e de padding
  1. sem ordem — e a RoPE
  2. multi-head e GQA
  3. a conta do
  4. o cache de KV no SmolLM2-135M
  5. ensine a atenção para onde olhar

abrir no Colab →

Para levar

  1. Atenção é uma camada densa com pesos calculados a partir da entrada — um dicionário suave. Ela venceu a recorrência por alcance e paralelismo.
  2. Q, K e V são três projeções da mesma entrada: o que perguntar, o que anunciar, o que entregar.
  3. O mantém a variância dos scores em 1. Sem ele o softmax satura e o gradiente some.
  4. A máscara é uma matriz somada: a distinção encoder/decoder, o padding, os documentos.
  5. A atenção não enxerga ordem. RoPE: rotações absolutas, deslocamentos relativos.
  6. Cabeças são um reshape. O cache de KV domina a memória de inferência; é o termo a encolher — GQA.
  7. O FlashAttention é exato e de graça; as outras respostas ao trocam alguma coisa.

Roteiro: (1) o que a recorrência não conseguia fazer; (2) a ideia: um dicionário suave, Q/K/V, uma conta à mão; (3) os detalhes que fazem funcionar: √d_k, máscaras, posição, cabeças; (4) o custo: o cache de KV e o O(n²). Fecha com o laboratório no Colab.

Padrão: 64 tokens, escala 1. RNN 5e-4, célula do LSTM 4e-2. Arraste para 256: 1e-14 e 2e-6. As duas são retas no gráfico logarítmico — decaimento exponencial. O LSTM comprou uma constante melhor. A atenção é plana em 1/n: pequena, mas igual para todas as posições.

Pergunte antes: "quantos tokens tem 'o gato subiu no'?" A maioria diz 4 palavras = 4 tokens; o GPT-2 usa 6. Na frase longa, 25 tokens para 13 palavras (1,92 por palavra contra 1,06 do inglês): mais conta, menos contexto e uma conta maior para o mesmo conteúdo.

A palavra nova é o ponto: "subiu" nunca esteve no corpus e o BPE a escreve com dois pedaços conhecidos, ␣sub (de subir) e iu (de dormiu, fugiu). Só 10 das 41 fusões tocam a frase. O GPT-2 corta "subiu" do mesmo jeito, mas corta "gato" em ␣g + ato: o vocabulário dele foi ajustado em inglês.

Avance pelas fusões: 15 caracteres, 5 pedaços, ids [0, 2, 4, 5, 1]. Depois os casos: "… telhado." acrescenta ␣telhado e "."; "o pássaro subiu no telhado." escreve "pássaro" letra a letra (␣ p á s s a r o, sem id entre os 12); a frase em inglês passando pelo tokenizador português: 22 pedaços.

X (5 x 4) é o que a atenção recebe. O mesmo id recebe sempre a mesma linha: "banco" ganha um vetor só nas duas frases do próximo slide. Fazer o vetor depender dos vizinhos é o trabalho da atenção.

Começa nos números do slide anterior. Depois: (a) valor de praça → [0, 1]: o veredito vira e nenhum peso se mexe; (b) query → [1, 0]: o peso sai de praça, 0,40/0,40/0,20; (c) query → 0: uniforme, uma média simples.

Leia por linha: para onde cada token olha; cada linha soma 1. As três primeiras cabeças são padrões que modelos treinados têm de verdade (conteúdo, token anterior, distância). Depois: desmarque "dividir por √d_k" e arraste d_k para cima — a matriz vira binária. Depois marque a máscara causal: o triângulo de cima escurece. A máscara volta daqui a quatro slides.

Variância 1/d_k: todo score perto de zero, um softmax quase uniforme que não escolhe ninguém.

Laboratório, seção 5: troque o último token; sem a máscara as cinco linhas mudam, com ela só a última.

O código do laboratório (seção 7, com a função atencao da seção 2), três tokens, d = 4, h = 2. A cabeça 1 é o banco dos slides 13 a 15: a linha termina em [0,85; 0,15]; a cabeça 2 faz banco olhar para sentei (0,77). Avançar linha a linha. Depois: "com a máscara causal" - banco perde praça e volta a ficar em cima do muro, [0,60; 0,40]; sentei só vê a si mesmo. "uma cabeça só (h = 1)": os mesmos números, √4 = 2, banco divide 0,38/0,38/0,23 - uma linha não aponta para as duas coisas. "chaves todas iguais": todo peso 1/3, uma média simples.

Comece em 8B, GQA, 32k, 8 requisições: 34,4 GB de cache + 16 GB de pesos, cabe. Troque para MHA: o cache cresce 4× (137 GB) e não cabe. Volte a GQA e suba para 128k: 137 GB de novo. 70B ou 405B: só os pesos (140 GB, 810 GB em 16 bits) já não cabem numa placa de 80 GB.