Positional Encoding

Demonstrações interativas da aula de positional encodings

Quatro visualizações que acompanham os slides: por que a self-attention é invariante a permutação (a frase “O gato sentou no tapete” embaralhada produz as mesmas saídas sem PE), a codificação sinusoidal PE(i, 2k) = sin(i·ωk), PE(i, 2k+1) = cos(i·ωk) com ωk = 10000−2k/d, o produto interno PEi·PEj que depende apenas do offset i − j, e o RoPE, que codifica posição rotacionando pares de dimensões de q e k por um ângulo i·θ.

1 · Atenção é invariante a permutação

O score de atenção visto em aula, score(i, j) = qikj / √d, não depende dos índices i e j, apenas dos valores dos vetores. Aqui usamos a frase dos slides, “O gato sentou no tapete”, com os mesmos embeddings xi de dimensão d = 4 do cálculo manual. Embaralhe a frase e compare a saída da atenção de cada palavra com a saída na ordem original: sem PE elas são idênticas (a palavra só muda de lugar), com o PE sinusoidal somado (xi ← xi + PE(i)) cada posição vira uma representação única e a saída muda.

Matriz de atenção softmax(qikj/√d) na ordem atual da frase

Saída da atenção por palavra (barras: ordem atual; traço: a mesma palavra na ordem original)

💡 Na aba Sem PE, permute a frase várias vezes: o readout mostra Δ ≈ 0, a saída de “gato” é a mesma em qualquer posição, ou seja, “O tapete sentou no gato” é indistinguível para o modelo. Troque para Com PE sinusoidal e permute de novo: as barras se afastam dos traços e Δ cresce, porque cada posição agora contribui com um vetor PE(i) diferente. Note também como a matriz de atenção muda com a permutação apenas no caso com PE.

2 · A codificação sinusoidal

Cada posição i recebe um vetor de d valores: a dimensão par 2k usa sin(i·ωk) e a ímpar 2k+1 usa cos(i·ωk), mesma frequência com defasagem de 90°. A base 10000 em ωk = 10000−2k/d cria um espectro de frequências: low-k (índice pequeno) oscila rápido e codifica posição fina, high-k (índice grande) quase não se move e codifica posição grosseira, o análogo contínuo da contagem binária vista em aula. Escolha k para destacar um par de dimensões no heatmap e ver suas curvas.

Matriz PE(i, dim): posição no eixo x, dimensão no eixo y (retângulo: par 2k, 2k+1 selecionado)

Curvas sin(i·ωk) e cos(i·ωk) do par selecionado

💡 Com d = 32, compare k = 0 (ω = 1, período ≈ 6 posições, listras finas na base do heatmap) com k = 15 (ω ≈ 0.0002, a curva parece constante: precisaria de milhares de posições para completar um ciclo). Reduza para d = 4 e L = 16 para reproduzir a tabela do cálculo manual dos slides: as dimensões 2 e 3 quase não mudam ao longo da frase. Aumente d e veja mais “relógios” intermediários surgirem.

3 · Produto interno PEi·PEj

Para cada par de frequência vale sin(i·ωk)sin(j·ωk) + cos(i·ωk)cos(j·ωk) = cos((i−j)·ωk), então o produto interno completo é PEi·PEj = Σk cos((i−j)·ωk): ele depende apenas do offset i − j, nunca das posições absolutas. É essa estrutura que permite ao modelo aprender offsets relativos a partir de um PE absoluto (PE(i+δ) é função linear de PE(i)). O heatmap fica constante ao longo das diagonais e a curva de similaridade tem a mesma forma para qualquer posição de referência, apenas transladada.

Heatmap PEi·PEj normalizado (linhas destacadas: i e i′)

PEi·PEj em função de j, para as referências i (ciano) e i′ (laranja)

💡 Arraste i de 0 até 60 e observe que a curva ciano apenas desliza, sem mudar de forma: a similaridade decai com a distância relativa do mesmo jeito em qualquer ponto da sequência, e o readout confirma numericamente que PEi·PEi+8 dá o mesmo valor para i e i′. Com d = 4 o decaimento oscila bastante (poucas frequências); com d = 64 a soma de muitos cossenos produz um pico limpo em j = i que decai suavemente.

4 · RoPE: posição como rotação

Em vez de somar PE ao embedding, o RoPE rotaciona cada par de dimensões de q e k dentro do attention head: i = Rθ(i)·q e j = Rθ(j)·k, com ângulos i·θ e j·θ. Como Rθ(i)Rθ(j) = Rθ(j−i), o score expande para i·k̃j = (q0k0+q1k1)·cos((j−i)θ) + (q1k0−q0k1)·sin((j−i)θ), que depende apenas do offset j − i. O slider δ desloca a dupla inteira pela sequência: os vetores giram juntos, mas o score não se mexe.

Bloco 2D: q e k originais (tracejados, arraste-os) e rotacionados q̃i, k̃j (sólidos)

Score q̃i·k̃j em função do offset j − i (ponto: offset atual)

💡 Reproduza o cálculo manual dos slides: com q = (1, 0), k = (0, 1), θ = 1.0, i = 1 e j = 3, o bloco contribui −sin(2) ≈ −0.909. Agora aumente δ até 16: os dois vetores giram no plano, mas o readout mostra o mesmo score, pois o offset continua 2. Compare θ = 1.0 (low-k, curva de score oscila rápido, posição relativa fina) com θ = 0.05 (high-k, curva quase plana, posição grosseira). Arraste os vetores tracejados para mudar q e k.