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 . Problemet är att den inte vet var i sekvensen tokenen står.
RoPE:s idé: rotera och 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 radianer om den står på position . När du sedan tar skalärprodukten mellan en vektor roterad och en roterad beror resultatet bara på — rotationerna delvis tar ut varandra.
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 (av ) används vinkelfrekvensen
På position roteras par med vinkeln :
Varför det blir relativt. En rotationsmatris uppfyller . Alltså:
Resultatet beror bara på . Detta gäller exakt, inte approximativt.
Frekvensspektrumet. Låga ger nära 1 — snabb rotation, som skiljer grannar åt. Höga ger nära — 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:
| Metod | Idé | Kostnad |
|---|---|---|
| Positionsinterpolation (PI) | dividera positionen med en faktor så att den nya längden mappas in i det tränade intervallet | kort finjustering; tappar lite upplösning på korta avstånd |
| NTK-aware skalning | ändra basen i stället för positionen — skalar högfrekventa dimensioner mindre | fungerar ofta utan finjustering |
| YaRN | kombinerar: interpolerar långsamma frekvenser, extrapolerar snabba, plus temperaturjustering | bä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å och men inte på . 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
- arXiv — RoFormer: Enhanced Transformer with Rotary Position Embedding — arXiv (öppen åtkomst; licens per artikel)
- arXiv — YaRN: Efficient Context Window Extension of Large Language Models — arXiv (öppen åtkomst; licens per artikel)
- arXiv — Extending Context Window of Large Language Models via Positional Interpolation — arXiv (öppen åtkomst; licens per artikel)