Codificação Posicional Rotatória (RoPE)¶
1. Por que a posição precisa ser adicionada¶
A self-attention, descrita honestamente, não tem ideia de onde, em uma sequência, qualquer um de seus tokens reside. Os produtos escalares \(Q K^{\top}\) dependem apenas dos conteúdos dos vetores de query e key, não dos índices que os produziram, de modo que um Transformer que recebe uma sequência como entrada e produz uma sequência como saída é equivariante por permutação na ausência de qualquer sinal auxiliar: embaralhe os tokens, e as saídas serão embaralhadas da mesma forma [src_017, src_002]. Para modelagem de linguagem, essa é uma propriedade fatal. A frase "o gato sentou no tapete" e a frase "o tapete sentou no gato" são compostas pelos mesmos tokens; a diferença entre elas vive inteiramente em sua ordem. Algum mecanismo precisa injetar informação posicional na computação, e a questão é qual mecanismo.
Duas estratégias amplas foram tentadas [src_017]. A primeira é aditiva: computar um vetor posicional \(p_m\) para cada índice absoluto \(m\) e adicioná-lo ao embedding do token antes do primeiro bloco. O esquema senoidal de Vaswani et al. e a tabela de embedding aprendida do BERT pertencem ambos a essa família [src_017]. A segunda é multiplicativa: deixar o caminho de value intacto e modificar os vetores de query e key de modo que o produto escalar \(\langle q_m, k_n \rangle\) codifique o deslocamento relativo \(n - m\) diretamente. RoPE pertence a essa segunda família, e é a ideia multiplicativa — composta via rotação no espaço de features — que deslocou ambas as variantes aditivas da fronteira dos modelos de linguagem grandes decoder-only entre 2024 e 2026 [src_002, src_010, src_047].
Este capítulo segue a mesma tabela de notação introduzida no Capítulo 1: \(B\) é o tamanho do batch, \(T\) o comprimento da sequência, \(D\) a dimensão do residual stream, \(h\) o número de cabeças de attention, e \(d_h = D / h\) a dimensão por cabeça. RoPE é aplicado por cabeça, e dentro de cada cabeça atua sobre os vetores de query e key de dimensão \(d_h\). Usaremos \(d\) para a dimensão sendo rotacionada quando quisermos ser neutros sobre se a discussão se refere a dimensões por cabeça ou de modelo completo; na prática, o objeto rotacionado é a query ou key por cabeça, então \(d = d_h\) ao longo da discussão de implementação da Seção 8.
2. O panorama pré-RoPE e por que ambos os antecessores desapareceram¶
O Transformer de 2017 usava codificação posicional absoluta senoidal: cada índice absoluto \(m\) é mapeado a um vetor fixo cujas componentes \(2t\)-ésima e \((2t{+}1)\)-ésima são \(\sin(m / 10000^{2t/d})\) e \(\cos(m / 10000^{2t/d})\) respectivamente, e esse vetor é adicionado ao embedding do token antes do primeiro bloco [src_017]. A progressão geométrica de frequências, de um comprimento de onda de \(2\pi\) na coordenada mais rápida a aproximadamente \(10000 \cdot 2\pi\) na mais lenta, foi escolhida porque os autores hipotetizaram que o modelo poderia aprender a atender a deslocamentos relativos por meio da propriedade de deslocamento linear das senoides [src_017]. A esperança era que codificações senoidais também extrapolassem de forma limpa para comprimentos de sequência além da distribuição de treinamento.
Na prática, a esperança de extrapolação não se sustentou. Codificações senoidais degradam fora da janela de comprimento de treinamento de maneiras que não podem ser corrigidas sem retreinamento, e a família encoder-only que se seguiu ao Transformer original adotou um esquema diferente: um embedding posicional absoluto aprendido, no qual \(p_m\) é uma linha de uma tabela de lookup treinável de formato \(T_{\max} \times D\) [src_017, src_002]. BERT e seus sucessores usaram essa convenção, e ela tem a limitação óbvia de que a tabela só contém linhas para posições vistas no pretraining; qualquer posição além de \(T_{\max}\) não tem representação alguma [src_002].
Uma terceira família, vieses posicionais relativos, tentou corrigir a assimetria injetando informação posicional diretamente nos logits de attention em vez de no embedding do token. T5 agrupa o deslocamento \(i - j\) em um pequeno conjunto de deslocamentos escalares aprendidos que são adicionados ao logit pré-softmax; ALiBi adiciona uma penalidade linear \(-\beta \cdot (i - j)\) com inclinações por cabeça \(\beta_k = 1 / 2^{8k / n_{\text{head}}}\) [src_002]. Ambos os esquemas produzem comportamento de posição relativa genuíno e ambos extrapolam além da janela de comprimento de treinamento de uma maneira que esquemas absolutos não fazem. Nenhum, no entanto, alcança a propriedade que RoPE disponibiliza quase de graça: uma codificação multiplicativa que preserva normas vetoriais, compõe-se de forma limpa com linear attention e induz uma estrutura de decaimento de longo prazo que coincide com a intuição de que tokens distantes deveriam interagir menos [src_017].
Em 2024 a fronteira dos modelos de linguagem decoder-only de pesos abertos havia convergido para RoPE. Llama, Qwen, Gemma e DeepSeek todos o utilizam; ModernBERT, o renascimento encoder-only, também o utiliza [src_002, src_010, src_047]. Codificações absolutas senoidais e absolutas aprendidas sobrevivem no registro histórico, mas efetivamente desapareceram das novas arquiteturas decoder-only.
3. RoPE em duas dimensões: a derivação no plano complexo¶
🎯 Intuição
RoPE rotaciona os vetores de query e key como uma função da posição — literalmente girando cada vetor por um ângulo proporcional à sua posição, como os ponteiros de um relógio que avançam um tique por token. Quando dois vetores rotacionados se encontram dentro de um produto escalar, os ângulos absolutos se cancelam e apenas o deslocamento relativo sobrevive. O restante desta seção transforma essa imagem em álgebra; a álgebra é a consequência, não o objetivo.
A maneira mais limpa de ver por que RoPE funciona é começar em duas dimensões e usar o plano complexo. Suponha que a dimensão por cabeça seja \(d = 2\), e sejam \(W_q, W_k \in \mathbb{R}^{2 \times 2}\) as matrizes de projeção de query e key. O objetivo que Su et al. estabelecem é encontrar funções \(f_q\) e \(f_k\) tais que o produto interno da query codificada posicionalmente no índice \(m\) e a key codificada posicionalmente no índice \(n\) dependa apenas dos conteúdos dos embeddings e do deslocamento relativo \(n - m\) [src_017]:
Identifique o vetor 2D \(W_q x_m \in \mathbb{R}^2\) com o número complexo \(z_m^{(q)} = (W_q x_m)_1 + i \cdot (W_q x_m)_2\), e analogamente para \(W_k x_n\). A multiplicação por \(e^{i m \theta}\) no plano complexo é exatamente uma rotação anti-horária pelo ângulo \(m \theta\). A unicidade aqui não é mágica. As formas codificadas posicionalmente precisam satisfazer a restrição de distância relativa e reduzir-se a \(W_q x_m\), \(W_k x_n\) em \(m = n = 0\) (a condição inicial de ausência de codificação posicional). Su et al. mostram que qualquer par de funções satisfazendo ambas é forçado à forma \(z \, e^{i m \theta}\) a menos de uma fase que as matrizes de projeção podem absorver. O argumento completo — uma equação funcional na variável de posição — reside em RoFormer §3.4.1; registramos apenas que a restrição mais a condição inicial fixam a forma. O resultado é que a query e a key codificadas posicionalmente são [src_017, src_002]:
O produto interno, computado como a parte real de \(f_q(x_m, m) \cdot \overline{f_k(x_n, n)}\), então depende das posições apenas pela sua diferença [src_017]:
O mecanismo é portanto uma rotação: a query na posição \(m\) é rotacionada por \(m \theta\), a key na posição \(n\) é rotacionada por \(n \theta\), e quando os dois vetores rotacionados são multiplicados via produto escalar, as rotações se cancelam parcialmente e deixam apenas a rotação do deslocamento relativo \((m - n) \theta\) [src_017].
Engenheiros preferem escrever a mesma operação como uma multiplicação matricial. A multiplicação no plano complexo \(z \mapsto z \cdot e^{i m \theta}\) é idêntica ao produto matriz-vetor \(2 \times 2\) de valores reais
Ambas as formas computam a mesma coisa; a forma complexa torna a prova de distância relativa transparente em duas linhas, enquanto a forma real é o que uma implementação realmente produz em um kernel vetorizado. Usaremos a forma complexa sempre que uma derivação estiver em jogo e a forma real sempre que uma implementação estiver em jogo.
4. A propriedade de posição relativa como desfecho¶
O ponto da construção não é a rotação em si; o ponto é o que a rotação faz ao produto interno. Insira a \(q_m\) e a \(k_n\) codificadas posicionalmente na fórmula de attention e leia do que o produto escalar depende [src_017]:
Três passos estão dobrados na igualdade encadeada. (i) Distribua a transposta: \((R_{m\theta} W_q x_m)^{\top} = x_m^{\top} W_q^{\top} R_{m\theta}^{\top}\). (ii) Use \(R_{m\theta}^{\top} = R_{-m\theta}\) (rotação ortogonal, então a transposta é igual à inversa). (iii) Componha \(R_{-m\theta} R_{n\theta} = R_{(n - m)\theta}\), a identidade de fechamento que usa \(R_{m\theta}^{\top} R_{n\theta} = R_{(n - m)\theta}\) porque as matrizes de rotação formam um subgrupo abeliano de um parâmetro de \(\mathrm{SO}(2)\) [src_017].
⚠️ Armadilha
O resultado de posição relativa vale para o produto interno, não para os vetores rotacionados individuais. Duas queries nas posições \(m_1, m_2\) com o mesmo conteúdo mas posições diferentes ainda produzem representações rotacionadas distintas \(R_{m_1\theta}(W_q x), R_{m_2\theta}(W_q x)\); somente quando essas representações se encontram com uma key dentro de um produto escalar é que as rotações absolutas se cancelam.
🤔 Pause e pense
Antes de seguir — você consegue dizer, em uma frase e sem olhar a equação, o que acontece com o produto interno \(q_m^{\top} k_n\) quando substituímos \(m, n\) por \(m+\Delta, n+\Delta\)? Por que o caminho do value não aparece na resposta? (Não olhe adiante — escreva a resposta ou diga em voz alta.)
Duas consequências seguem imediatamente. Primeiro, o logit de attention entre a query na posição \(m\) e a key na posição \(n\) é uma função dos embeddings juntamente com a distância relativa \(n - m\), exatamente como a formulação original na Equação \(g(x_m, x_n, n - m)\) exigia [src_017]. Segundo, esse comportamento de posição relativa é alcançado sem modificar o caminho de value de forma alguma: \(V\) é deixado intacto, e a codificação vive inteiramente no produto interno query-key [src_017, src_002].
A Figura 1 visualiza a geometria. A query na posição \(m\) e a key na posição \(n\) residem cada uma em um círculo no plano complexo; suas respectivas rotações por \(m \theta\) e \(n \theta\) deixam os raios (as normas vetoriais) invariantes, e o ângulo entre eles após a rotação difere do ângulo antes da rotação por exatamente \((n - m) \theta\). O logit de attention depende desse ângulo rotacionado, não dos ângulos absolutos, então qualquer par de posições \((m, n)\) com o mesmo deslocamento produz a mesma contribuição posicional ao logit.
Esta é a declaração da propriedade de posição relativa que Su et al. provaram na Seção 3.4 do artigo do RoFormer, e é a propriedade na qual o restante do capítulo se apoia [src_017]. Tudo o que segue — a generalização a dimensões mais altas, a escolha de frequências, o decaimento de longo prazo, os truques de extensão de contexto, o uso em modelos modernos de pesos abertos — é uma consequência de preservar essa propriedade e escolher bem os ângulos de rotação.
💡 Resultado-chave
Rotacionar \(q\) na posição \(m\) e \(k\) na posição \(n\) deixa o produto interno deles dependendo apenas do deslocamento relativo \(n - m\) — e o caminho do value permanece intocado.
🔄 Recapitulação
- Complete: em \(q_m^{\top} k_n = x_m^{\top} W_q^{\top} R_{?} W_k x_n\), o que preenche o subscrito?
- Explique: por que o caminho do value não aparece no resultado de posição relativa, mesmo que attention multiplique queries, keys e values?
- Preveja: se \(\theta = 0\) (sem rotação alguma), a que se reduz o produto interno, e o que isso recupera?
🔗 Conexão
O Capítulo 4 trata a propriedade de posição relativa em nível de implementação: como RoPE compõe-se com o KV-cache, o que FlashAttention faz ao kernel de rotação, e por que GQA/MQA preservam a propriedade inalterada.
5. Generalização para \(d\) dimensões: uma pilha bloco-diagonal de rotações 2D¶
🎯 Intuição
Pareamos as dimensões de feature porque \(\mathrm{SO}(2)\) — o grupo de rotações 2D — é o grupo de rotação abeliano mais simples, e essa abeliano-cidade é o que fez o cancelamento da posição relativa funcionar em duas dimensões. Rotações tridimensionais (\(\mathrm{SO}(3)\)) não comutam, e o cancelamento se quebra. Pareamento é, portanto, a menor construção que permite ao argumento 2D se elevar de forma limpa a dimensões mais altas.
Uma cabeça de attention real tem \(d_h\) dimensões, não duas, e precisamos elevar a construção. O truque que Su et al. usam é direto: para um \(d\) par, particione as \(d\) dimensões de feature em \(d/2\) pares consecutivos, trate cada par como seu próprio plano complexo e aplique uma rotação 2D em cada par com sua própria frequência angular \(\theta_j\) [src_017]. Por par \(j \in \{1, \ldots, d/2\}\), defina o número complexo
e rotacione-o pelo ângulo dependente da posição \(m \cdot \theta_j\):
A query codificada posicionalmente completa é a concatenação dos \(d/2\) pares rotacionados, lida de volta em coordenadas reais [src_017]. Equivalentemente, a matriz de rotação \(d\)-dimensional \(R^{d}_{\Theta, m}\) é bloco-diagonal, com \(d/2\) blocos da forma de rotação \(2 \times 2\) \(R_{m \theta_j}\) empilhados ao longo da diagonal [src_017]:
Cada bloco rotaciona apenas as duas coordenadas que possui; os blocos não interagem. Como \(R^{d}_{\Theta, m}\) é bloco-diagonal e ortogonal, preserva normas vetoriais, e como cada bloco é em si uma rotação planar, o argumento de distância relativa por par da Seção 4 se eleva ao produto interno \(d\)-dimensional completo [src_017]:
O produto escalar \(d\)-dimensional completo codifica apenas o deslocamento relativo \(n - m\), exatamente como no caso 2D [src_017]. A propriedade é herdada da construção por par; não há teorema adicional a provar.
6. Escolha de frequência: por que \(\theta_j = 10000^{-2(j-1)/d}\)¶
A decisão de design restante é o que definir como as frequências angulares \(\theta_j\). Su et al. escolhem [src_017]:
🎯 Intuição
Para \(d_h = 64\), quatro bandas representativas dão a alça concreta: \(\theta_1 \approx 1\) rad/token (comprimento de onda \(\approx 6\) tokens); \(\theta_8 \approx 0,18\) rad/token (comprimento de onda \(\approx 35\) tokens); \(\theta_{16} \approx 0,032\) rad/token (comprimento de onda \(\approx 200\) tokens); \(\theta_{32} \approx 10^{-4}\) rad/token (comprimento de onda \(\approx 6 \times 10^4\) tokens). Bandas rápidas resolvem tokens próximos com granularidade fina; bandas lentas distinguem posições a milhares de tokens de distância.
Leia isto como uma progressão geométrica. Em \(j = 1\), a frequência é \(\theta_1 = 1\) radiano por posição; em \(j = d/2\), é \(\theta_{d/2} = 10000^{-(d - 2)/d} \approx 10000^{-1} = 10^{-4}\) radianos por posição. O primeiro par, portanto, completa uma rotação completa aproximadamente a cada \(2\pi\) posições, enquanto o último par leva da ordem de \(2\pi \cdot 10^4\) posições para completar uma única rotação. A progressão de frequência é a mesma que Vaswani et al. usaram para codificações absolutas senoidais, com a mesma base de 10000 e o mesmo espaçamento geométrico [src_017]; o que difere é para que as frequências são usadas. Em RoPE elas parametrizam ângulos de rotação, não componentes senoidais aditivas.
O espaçamento geométrico tem duas virtudes que justificam a escolha empiricamente e parcialmente teoricamente. Primeiro, dá à rede acesso a informação posicional em múltiplas escalas simultaneamente: pares que rotacionam rapidamente resolvem tokens próximos com granularidade fina, enquanto pares que rotacionam lentamente distinguem posições que estão a milhares de tokens de distância. Segundo, com este cronograma Su et al. provam uma propriedade de decaimento de longo prazo: uma quantidade que limita a magnitude da contribuição do produto interno de um dado deslocamento relativo \(|m - n|\) cai conforme \(|m - n|\) cresce [src_017]. O decaimento corresponde à intuição de que dois tokens distantes em uma sequência deveriam, em média, contribuir menos um para a representação do outro do que dois tokens adjacentes, e a correspondência não é imposta manualmente — ela emerge do cronograma de frequência geométrica [src_017]. A Figura 1 ilustra o caráter multi-escala da construção: a figura mostra a rotação ao longo de uma banda, mas a codificação rotatória completa empilha \(d/2\) tais rotações, com as bandas \(\theta_1, \theta_8, \theta_{16}, \theta_{d/2}\) dando uma varredura representativa do rápido ao lento.
🤔 Pause e pense
Olhe novamente as quatro bandas no callout de Intuição acima (\(\theta_1, \theta_8, \theta_{16}, \theta_{32}\) para \(d_h = 64\)). Para dois tokens a 100 posições de distância, qual banda tem a diferença de rotação \(|m-n| \theta_j\) mais próxima de um ângulo significativo (digamos, \(\pi/2\)), e qual banda tem a diferença de rotação pequena demais para registrar? O que isso lhe diz sobre quais bandas carregam informação posicional de curto vs. longo alcance? (Não olhe adiante — resolva mentalmente antes de continuar.)
Um fato sutil mas útil é que rotações preservam normas exatamente, então a codificação rotatória não altera a magnitude de qualquer vetor de query ou key — apenas os ângulos em cada subespaço bidimensional [src_017]. Essa preservação de norma é o que torna RoPE compatível com linear attention (variantes de attention cujo custo cresce linearmente no comprimento da sequência, tratadas no Capítulo 4): os mapas de feature preservadores de positividade que linear attention requer podem ser aplicados antes da rotação sem conflito, já que a rotação não alterará sua não-negatividade [src_017]. Em softmax attention padrão isso é uma nota de rodapé; em projetos de linear-attention é uma propriedade essencial.
💡 Resultado-chave
O cronograma de frequência geométrica produz uma propriedade de decaimento de longo prazo: a contribuição do produto interno de um deslocamento relativo \(|m - n|\) cai conforme \(|m - n|\) cresce, então tokens distantes interagem menos — sem que esse decaimento seja imposto à mão.
🔗 Conexão
O Capítulo 4 desenvolve a linear attention propriamente dita: quais mapas de feature preservadores de positividade funcionam, o que a visão por kernel-trick oferece, e por que a preservação de norma de RoPE é a propriedade que torna a composição limpa.
7. Variantes para extensão de contexto: interpolação posicional, NTK-aware, YaRN¶
Um modelo treinado com RoPE até o comprimento de contexto \(T_{\text{train}}\) pode ser avaliado, em princípio, em qualquer comprimento de contexto maior, porque a matriz de rotação \(R^{d}_{\Theta, m}\) é definida para todo inteiro \(m\). Na prática, a qualidade do modelo degrada acentuadamente uma vez que o comprimento de avaliação excede o comprimento de treinamento: posições \(m > T_{\text{train}}\) produzem ângulos de rotação que a rede nunca encontrou durante o treinamento, e os logits de attention se tornam mal calibrados nesse regime [src_002]. Várias técnicas foram propostas para estender a janela de contexto utilizável sem retreinamento do zero.
Interpolação posicional linear (Kong & Chen 2023; o predecessor imediato do NTK-aware) é a mais simples. Reescale cada posição de entrada pelo fator \(T_{\text{train}} / T_{\text{eval}}\) antes de computar a rotação, de modo que posições na sequência de avaliação mais longa sejam mapeadas de volta ao intervalo treinado [src_002]. O truque é barato e funciona em fatores de extensão modestos, mas comprime os pares de rotação de alta frequência mais severamente, embaçando a capacidade do modelo de resolver tokens próximos com precisão [src_002].
🎯 Intuição
Imagine novamente as quatro bandas. A banda mais rápida já rotaciona por \(\approx 1\) radiano por token; reescalar posições por \(T_{\text{train}}/T_{\text{eval}} = 1/4\) encolhe isso para \(\approx 0{,}25\) radianos por token, comprimindo quatro posições originalmente distintas em um único passo de rotação. A banda mais lenta começa em \(10^{-4}\) rad/token; o mesmo reescalonamento mal altera sua rotação já glacial. NTK-aware existe exatamente para deixar as bandas rápidas em paz.
Escalonamento NTK-aware aborda essa assimetria ajustando a base de rotação \(b\) (o 10000 em \(\theta_j = b^{-2(j-1)/d}\)) de forma não uniforme entre as bandas de frequência [src_002]. As bandas de alta frequência são deixadas efetivamente intocadas, então a resolução posicional de granularidade fina é preservada, enquanto as bandas de baixa frequência são esticadas para absorver o fator de extensão [src_002, src_017]. Relatos de profissionais sugerem que o escalonamento NTK-aware estende o contexto utilizável a talvez 2 a 4 vezes o comprimento de treinamento sem perda séria de qualidade [src_002].
YaRN (Yet another RoPE extensioN) refina o escalonamento NTK-aware combinando o reescalonamento não uniforme de frequência com um ajuste de temperatura de attention e um cronograma de extensão por partes em chunks [src_002]. A derivação completa requer argumentos sobre a taxa de mudança da rotação entre bandas de frequência e uma combinação cuidadosa do perfil de loss após a extensão; esboçamos a ideia aqui e nos abstemos de derivá-la linha por linha. Leitores que queiram o tratamento completo devem consultar diretamente o artigo do YaRN e a Seção 2.3.5.4 da monografia de Xiao e Zhu para uma exposição trabalhada [src_002].
ℹ️ Necessita revisão
Uma citação primária para o YaRN em si (Peng et al. 2024, arXiv:2309.00071) ainda não está no pool de fontes deste livro; a descrição acima depende do tratamento secundário em [src_002]. Um futuro contribuinte deve anexar o artigo do YaRN e refinar este parágrafo. O mesmo se aplica à proposta de escalonamento NTK-aware, que se originou em uma discussão comunitária que não é diretamente citável no pool atual.
🔗 Conexão
Os métodos de extensão de contexto (interpolação linear, NTK-aware, YaRN) são decisões de pretraining-time e inference-time; o Capítulo 7 os trata sob a lente encoder-decoder, e o Capítulo 8 cobre seu uso em LLMs decoder-only modernos (Llama-3 long-context, Qwen-3 needle-in-haystack, alegações de 1M tokens do DeepSeek-V3).
8. RoPE em 2026: onde ele é realmente usado¶
Entre 2024 e 2026, RoPE tornou-se o esquema padrão de codificação posicional de essencialmente todo modelo de linguagem grande decoder-only de pesos abertos produzido na fronteira. A família Llama (Llama, Llama-2, Llama-3) o usa; Qwen-2 e Qwen-3 o usam; Gemma-2 e Gemma-3 o usam; DeepSeek-V2 e DeepSeek-V3 o usam; o renascimento encoder-only ModernBERT o usa [src_002, src_010, src_047]. Implementações diferem em algumas convenções de baixo nível — o mais importante a escolha entre o layout original de Su et al., que pareia coordenadas de feature adjacentes \((x_1, x_2), (x_3, x_4), \ldots\), e o layout half-strided do GPT-NeoX / Llama, que pareia \((x_1, x_{d/2 + 1}), (x_2, x_{d/2 + 2}), \ldots\) [src_017, src_010]. As duas convenções são equivalentes a menos de uma permutação de dimensões de feature e produzem numéricos idênticos uma vez que as matrizes de projeção tenham absorvido a permutação, mas não são intercambiáveis em um único checkpoint de pesos, então profissionais devem ser cuidadosos para combinar o layout de quaisquer pesos pré-treinados que carreguem.
⚠️ Armadilha
Layouts equivalentes por permutação produzem numéricos idênticos somente após as matrizes de projeção \(W_q, W_k\) terem absorvido a permutação. Um checkpoint de pesos treinado contra o layout de Su não é intercambiável com um treinado contra o layout GPT-NeoX, porque \(W_q\) foi ajustado a uma ordenação específica de features — carregar o layout errado silenciosamente rotaciona os pares de feature errados.
🔗 Conexão
O Capítulo 8 cobre a stack moderna de LLMs decoder-only (Llama, Qwen, Gemma, DeepSeek), na qual RoPE é a escolha padrão de codificação posicional; a convenção de layout que cada família usa (Su-original vs GPT-NeoX) é documentada lá.
8.1 Além de decoder-only: ModernBERT e RoPE 2D¶
O ressurgimento encoder-only merece uma nota separada. ModernBERT explicitamente substitui o embedding posicional absoluto aprendido do BERT original por RoPE, que é a principal mudança arquitetônica que permite ao ModernBERT estender sua janela de contexto a 8192 tokens ou mais sem retreinar a tabela posicional [src_002, src_010]. O mesmo ingrediente arquitetônico que foi desenvolvido para modelos de linguagem autorregressivos, portanto, foi transferido de forma limpa para um encoder bidirecional, ilustrando que a propriedade de posição relativa de RoPE é independente do padrão de masking usado no momento de attention.
Uma segunda extensão, útil para Vision Transformers, aplica RoPE independentemente ao longo dos dois eixos espaciais de uma imagem: o índice de linha e o índice de coluna recebem suas próprias bandas de frequência, e as rotações ao longo dos dois eixos são compostas em uma única rotação por token [src_002]. SigLIP-ViT e codificadores de visão similares adotam este RoPE 2D para herdar as mesmas propriedades de flexibilidade de comprimento e decaimento de longo prazo que a versão 1D. O Capítulo 5 trata os Vision Transformers em detalhe; o presente capítulo apenas registra que a mesma construção generaliza.
🔗 Conexão
O ModernBERT (Capítulo 7) leva RoPE para a família encoder-only; o SigLIP-ViT e codificadores de visão similares (Capítulo 5) usam RoPE 2D conforme a decomposição por eixo espacial esboçada aqui.
9. Esboço de implementação em PyTorch¶
A implementação real de RoPE é curta, e as operações que parecem matriciais no tratamento formal se tornam um par de produtos elemento a elemento contra tabelas de cosseno e seno pré-computadas [src_017, src_010]. A multiplicação ingênua densa \(R^{d}_{\Theta, m} x\) é \(O(d^2)\), mas a esparsidade bloco-diagonal a reduz a \(O(d)\), e um kernel vetorizado computa a rotação em duas multiplicações elemento a elemento e uma soma elemento a elemento por feature de token [src_017].
A listagem abaixo usa a convenção half-strided (GPT-NeoX) adotada por Llama e pelos notebooks de referência de Raschka [src_010]. Dado um tensor de query (ou key) \(q\) de formato \((B, h, T, d_h)\), a função rotaciona cada par por cabeça \((q_{\cdot, \cdot, m, j}, q_{\cdot, \cdot, m, j + d_h/2})\) pelo ângulo \(m \theta_j\). A mesma rotina aplica-se simetricamente ao tensor de key.
import torch
def rope_freqs(d_h: int, max_seq_len: int, base: float = 10000.0,
device: torch.device | str = "cpu") -> tuple[torch.Tensor, torch.Tensor]:
"""Precompute the cos and sin tables for RoPE.
Returns tensors of shape (max_seq_len, d_h // 2), holding cos(m * theta_j)
and sin(m * theta_j) for every position m in [0, max_seq_len) and every
frequency-band index j in [0, d_h // 2).
"""
# theta_j = base ** (-2 j / d_h), with j = 0, 1, ..., d_h/2 - 1.
j = torch.arange(0, d_h, 2, device=device, dtype=torch.float32)
theta = base ** (-j / d_h) # shape (d_h // 2,)
m = torch.arange(max_seq_len, device=device, dtype=torch.float32)
angles = torch.outer(m, theta) # shape (T, d_h // 2)
return angles.cos(), angles.sin()
def apply_rope(x: torch.Tensor, cos: torch.Tensor, sin: torch.Tensor) -> torch.Tensor:
"""Apply RoPE to a query or key tensor of shape (B, h, T, d_h).
The half-strided pairing rotates feature j with feature j + d_h/2.
"""
d_h = x.shape[-1]
half = d_h // 2
x1, x2 = x[..., :half], x[..., half:] # halves
cos_t = cos[: x.shape[-2]].unsqueeze(0).unsqueeze(0) # (1, 1, T, d_h/2)
sin_t = sin[: x.shape[-2]].unsqueeze(0).unsqueeze(0)
rotated_1 = x1 * cos_t - x2 * sin_t
rotated_2 = x1 * sin_t + x2 * cos_t
return torch.cat([rotated_1, rotated_2], dim=-1)
Alguns detalhes valem ser rastreados. As tabelas cos e sin são pré-computadas uma vez por modelo e indexadas pela posição absoluta \(m\) a cada passagem direta; elas não dependem dos tokens de entrada e não são aprendidas. A rotina apply_rope realiza uma rotação \(2 \times 2\) por par \((x_1, x_2)\), na convenção half-strided que Llama adota [src_010]. A mesma rotina é chamada com entradas separadas e as mesmas tabelas para os tensores de query e key; o tensor de value é deixado intocado, consistente com a propriedade de posição relativa derivada na Seção 4 [src_017]. Em uma camada de attention completa, apply_rope seria invocada entre as projeções de query/key e o cálculo de softmax do produto escalar; a scaled dot-product attention padrão do Capítulo 1 então prossegue inalterada.
Para leitores que queiram uma implementação de attention equipada com RoPE de ponta a ponta mais elaborada e totalmente executável, os notebooks abertos de Raschka que acompanham Build a Large Language Model (From Scratch) fornecem uma listagem em estilo Llama que integra a rotação com multi-head attention e o residual stream [src_010]. O conjunto de aulas do CS336 cobre o mesmo material do lado curricular e inclui uma versão que exercita a rotação como parte de um loop completo de treinamento de modelo de linguagem do zero [src_004]. O texto de engenharia de Grigorov cobre preocupações de kernel ao nível de PyTorch e os detalhes de implementação no lado CUDA que importam em escala de treinamento [src_047].
10. Resumo e ponte para o Capítulo 3¶
RoPE é, quando reduzido a seu essencial, uma ideia de duas linhas: rotacione a query na posição \(m\) por um ângulo \(m \theta_j\) em cada par de dimensões de feature, rotacione a key na posição \(n\) por \(n \theta_j\), e o produto interno capta apenas a rotação do deslocamento relativo \((n - m) \theta_j\) [src_017]. Tudo neste capítulo é uma consequência dessa única observação: a generalização bloco-diagonal a \(d\) dimensões, o cronograma de frequência geométrica, a propriedade de decaimento de longo prazo, as variantes para extensão de contexto, a implementação como produtos elemento a elemento contra tabelas pré-computadas, e o fato empírico de que essencialmente todo Transformer de fronteira de pesos abertos entre 2024 e 2026 convergiu para RoPE em vez de qualquer um de seus antecessores aditivos [src_002, src_010, src_017, src_047].
O que este capítulo mudou no bloco padrão do Transformer é exatamente uma coisa: o mecanismo de codificação posicional. O residual stream, a sub-camada feed-forward e a layer normalization ainda estão fazendo o que o Capítulo 1 disse que estavam fazendo. O Capítulo 3 trata das próximas duas substituições: a layer normalization é trocada por RMSNorm, e o bloco feed-forward baseado em ReLU é trocado por uma variante SwiGLU com gating. Ao final do Capítulo 3 teremos um bloco decoder moderno — pre-RMSNorm envolvendo uma sub-camada de attention aumentada com RoPE, depois pre-RMSNorm envolvendo uma sub-camada FFN SwiGLU, com conexões residuais em torno de cada — que coincide com a arquitetura usada por Llama-3, Qwen-3, Gemma-3 e DeepSeek-V3 [src_002, src_010, src_047].
🔄 Recapitulação
- Complete: qual é o ângulo de rotação atribuído à posição \(m\) no par \(j\), em termos de \(\theta_j\)?
- Explique: por que o caminho do value não é tocado por RoPE, e o que mudaria se o value também fosse rotacionado?
- Preveja: para \(d_h = 64\), qual banda de frequência carrega a informação posicional de maior alcance — e aproximadamente quantos tokens seu comprimento de onda abrange?
- Compare: em uma frase cada, o que distingue o layout original de Su do layout half-strided do GPT-NeoX?
Referências¶
- src_002 — Tong Xiao and Jingbo Zhu. Foundations of Large Language Models. arXiv:2501.09223v2, 2025. https://arxiv.org/pdf/2501.09223
- src_004 — Tatsunori Hashimoto and Percy Liang. Stanford CS336: Language Modeling from Scratch (Spring 2025). Stanford University, 2025. https://stanford-cs336.github.io/spring2025/
- src_010 — Sebastian Raschka. Build a Large Language Model (From Scratch). Manning, 2024. https://github.com/rasbt/LLMs-from-scratch
- src_017 — Jianlin Su, Yu Lu, Shengfeng Pan, Ahmed Murtadha, Bo Wen, and Yunfeng Liu. RoFormer: Enhanced Transformer with Rotary Position Embedding. arXiv:2104.09864v5, 2021 (revised 2023). https://arxiv.org/pdf/2104.09864
- src_047 — Dilyan Grigorov. Building Large Language Models from Scratch. Apress, 2026. https://doi.org/10.1007/979-8-8688-2297-1