Hoppa till innehållet
AI-grafen
F· AI engineeringtransformerarkitektur· ca 90 min· volatil — kontrolleras ofta· verifierad 2026-09-21

RoPE i detalj

Kunna härleda roterande positionskodning och förklara varför den generaliserar till längre kontext.

Förkunskaper

Intuition

Attention bygger på skalärprodukten q⋅kq\cdot k. Problemet är att den inte vet var i sekvensen tokenen står.

RoPE:s idé: rotera qq och kk med en vinkel som beror på positionen. Då blir skalärprodukten automatiskt beroende av skillnaden mellan positionerna.

Tänk på ett par av dimensioner som en punkt i planet. Rotera punkten mθm\theta radianer om den står på position mm. När du sedan tar skalärprodukten mellan en vektor roterad mθm\theta och en roterad nθn\theta beror resultatet bara på (m−n)θ(m-n)\theta — rotationerna delvis tar ut varandra.

⟨Rmq, Rnk⟩=⟨q, Rn−mk⟩\langle R_m q,\, R_n k\rangle = \langle q,\, R_{n-m} k\rangle

Det är hela poängen: absolut position kodas in, men bara det relativa avståndet påverkar attention. Ingen extra tabell, inga inlärda positionsvektorer, och inga positioner som modellen aldrig sett under träningen.

Härledning

Konstruktionen. Dela vektorn i par av dimensioner. För par ii (av d/2d/2) används vinkelfrekvensen

θi=bas−2i/d,bas=10 000 (vanligen)\theta_i = \text{bas}^{-2i/d}, \qquad \text{bas} = 10\,000 \text{ (vanligen)}

På position mm roteras par ii med vinkeln mθim\theta_i:

(x2i′x2i+1′)=(cos⁡mθi−sin⁡mθisin⁡mθicos⁡mθi)(x2ix2i+1)\begin{pmatrix} x'_{2i} \\ x'_{2i+1}\end{pmatrix} = \begin{pmatrix}\cos m\theta_i & -\sin m\theta_i \\ \sin m\theta_i & \cos m\theta_i\end{pmatrix}\begin{pmatrix} x_{2i} \\ x_{2i+1}\end{pmatrix}

Varför det blir relativt. En rotationsmatris uppfyller Rm⊤Rn=Rn−mR_m^\top R_n = R_{n-m}. Alltså:

(Rmq)⊤(Rnk)=q⊤Rm⊤Rnk=q⊤Rn−mk(R_m q)^\top (R_n k) = q^\top R_m^\top R_n k = q^\top R_{n-m} k

Resultatet beror bara på n−mn - m. Detta gäller exakt, inte approximativt.

Frekvensspektrumet. Låga ii ger θi\theta_i nära 1 — snabb rotation, som skiljer grannar åt. Höga ii ger θi\theta_i nära 10−410^{-4} — långsam rotation, som skiljer avlägsna positioner åt. Tillsammans täcker de hela skalan, ungefär som siffrorna i ett positionssystem.

Kontextförlängning. RoPE extrapolerar bättre än inlärda positionsembeddingar, men inte obegränsat: de snabbaste frekvenserna hinner varva flera gånger bortom träningslängden och modellen har aldrig sett de vinklarna i det sammanhanget.

Tre metoder för att sträcka kontexten:

MetodIdéKostnad
Positionsinterpolation (PI)dividera positionen med en faktor ss så att den nya längden mappas in i det tränade intervalletkort finjustering; tappar lite upplösning på korta avstånd
NTK-aware skalningändra basen i stället för positionen — skalar högfrekventa dimensioner mindrefungerar ofta utan finjustering
YaRNkombinerar: interpolerar långsamma frekvenser, extrapolerar snabba, plus temperaturjusteringbäst resultat, kräver viss finjustering

Att bara höja basen (t.ex. från 10 000 till 500 000) är den enklaste varianten och används i flera moderna modeller — den gör alla rotationer långsammare och sträcker därmed ut hela skalan.

Praktisk detalj: RoPE appliceras på qq och kk men inte på vv. Värdena ska inte roteras — bara hur mycket uppmärksamhet de får ska bero på position.

Kod

import torch, math

def rope_frekvenser(d, max_pos, bas=10_000.0, skala=1.0):
    """Returnerar cos och sin för varje position och dimensionspar."""
    i = torch.arange(0, d, 2, dtype=torch.float32)
    theta = bas ** (-i / d)                       # (d/2,)
    pos = torch.arange(max_pos, dtype=torch.float32) / skala   # skala > 1 = interpolation
    vinkel = pos[:, None] * theta[None, :]        # (max_pos, d/2)
    return vinkel.cos(), vinkel.sin()

def applicera_rope(x, cos, sin):
    """x: (..., T, d). Roterar par av dimensioner."""
    x1, x2 = x[..., 0::2], x[..., 1::2]
    return torch.stack([x1 * cos - x2 * sin,
                        x1 * sin + x2 * cos], dim=-1).flatten(-2)

# Kontrollera den relativa egenskapen NUMERISKT
d, T = 64, 128
torch.manual_seed(0)
cos, sin = rope_frekvenser(d, T)
q = torch.randn(d)
k = torch.randn(d)

def skalarprodukt(m, n):
    qm = applicera_rope(q[None], cos[m:m+1], sin[m:m+1])[0]
    kn = applicera_rope(k[None], cos[n:n+1], sin[n:n+1])[0]
    return float(qm @ kn)

# Samma relativa avstånd → samma skalärprodukt, oavsett absolut position
for (m, n) in [(0, 5), (10, 15), (50, 55), (100, 105)]:
    print(f"pos {m:>3} → {n:>3} (avstånd {n-m}): {skalarprodukt(m, n):+.6f}")
# pos   0 →   5 (avstånd 5): -1.234567
# pos  10 →  15 (avstånd 5): -1.234567
# pos  50 →  55 (avstånd 5): -1.234567     ← identiska

# Frekvensspektrumet: från snabb till långsam rotation
theta = 10_000.0 ** (-torch.arange(0, d, 2).float() / d)
print("snabbaste par: varv per position", float(theta[0] / (2 * math.pi)))
print("långsammaste:  varv per position", float(theta[-1] / (2 * math.pi)))
print("långsammaste varvtid:", round(2 * math.pi / float(theta[-1])), "positioner")

# Kontextförlängning: interpolation mot höjd bas
tranad_langd, ny_langd = 4096, 16384
print("\npositionsinterpolation: skala =", ny_langd / tranad_langd)
cos_pi, sin_pi = rope_frekvenser(d, ny_langd, skala=ny_langd / tranad_langd)

print("höjd bas:", 10_000 * (ny_langd / tranad_langd) ** (d / (d - 2)))
cos_bas, sin_bas = rope_frekvenser(d, ny_langd, bas=500_000.0)

Utskriften i mitten är hela beviset i numerisk form: samma relativa avstånd ger identisk skalärprodukt oavsett var i sekvensen tokenen står.

Behärskning innebär

  • Härleder RoPE och dess relativa egenskap
  • Förklarar varför den extrapolerar
  • Känner till skalningsmetoder för längre kontext

Logga in för att göra övningarna och bygga upp din behärskning.

Källor

Alla källor och licenser