"Dog bites man" and "Man bites dog" are very different events.
To a bag-of-words model, they are identical.
To an attention layer with no positional information — they are also identical.
Attention is extraordinarily good at understanding relationships between words.
But it has no idea which word came first.
Positional encodings are the solution that made the transformer complete.
01 — The Problem: Attention Has No Memory of Order
In Day 24 we built the full attention mechanism. Recall the formula:
Notice what's missing: position. The dot product Q·KT measures semantic similarity between token representations — it has no term for "how far apart are these tokens?" or "which comes first?" If you give the transformer the sentence "The cat sat on the mat" in shuffled order — "mat the on sat cat the" — the attention scores between "cat" and "sat" are identical in both cases. The model sees the same bag of token embeddings either way.
This is called permutation equivariance: permute the input tokens, and the attention outputs permute in the same way. There is no mechanism inside attention itself that distinguishes position 1 from position 10. For some tasks this doesn't matter much. For language, it is catastrophic — word order is meaning.
Imagine a concert where every seat is identical — same colour, same shape. Without row and seat numbers, 5,000 people cannot find their places. But the moment you stamp a number on each seat, everyone knows exactly where they belong and how to navigate relative to each other ("three seats to the left of mine").
Positional encodings are the seat numbers. The words (token embeddings) are the seats — already meaningful on their own. Position encodings don't change what the word means, they tell the model where in the sequence each word sits. Without them, the transformer sees 10 words in no particular order. With them, it sees word 1, word 2, word 3 — a proper sequence.
All the notes are correct, but the bar numbers have been removed. A skilled musician can identify each note's pitch and duration — but they cannot tell you whether a note comes in the first bar or the last. Played in random order, it no longer resembles the original piece.
Positional encodings are the bar numbers. They don't change the notes (token identities), but they tell the model how to arrange them in time.
The solution is to add a position-dependent signal to each token's embedding before it enters the transformer. The challenge: the signal must be:
02 — Sinusoidal Encodings: The 2017 Original
The original "Attention Is All You Need" paper (Vaswani et al., 2017) introduced sinusoidal positional encodings — a fixed, non-learned formula that assigns every (position, dimension) pair a unique value using sine and cosine waves at different frequencies.
PE(pos, 2i+1) = cos( pos / 10000^(2i / d_model) )
pos = token position (0, 1, 2, …)
i = dimension index (0, 1, 2, … d_model/2 − 1)
Even dimensions → sine Odd dimensions → cosine
d_model = embedding size (e.g. 512 for the original transformer)
What Does This Actually Do?
Think of it like a clock — but one with many hands. A normal clock has three hands (seconds, minutes, hours), each rotating at a different speed. At any moment, the combination of all three hand positions uniquely identifies the time. No two moments in a 12-hour cycle produce exactly the same combination of three hand positions. Sinusoidal PE works exactly the same way, but with hundreds of "hands" instead of three:
- Low dimensions (small i) → fast waves: these dimensions change rapidly from position to position, like the seconds hand. They help distinguish nearby positions (position 1 vs position 2).
- High dimensions (large i) → slow waves: these dimensions change slowly across positions, like the hours hand. They help distinguish positions that are far apart (position 100 vs position 500).
- The combination of all d_model dimensions gives every position a unique "fingerprint" — like a unique combination of all clock hands. No two positions share the same encoding.
Imagine a row of lighthouses along a coastline. Each lighthouse flashes its light in a repeating pattern — but at different speeds. Lighthouse #1 flashes fast (every 2 seconds), lighthouse #2 flashes medium (every 10 seconds), lighthouse #3 flashes slow (every 60 seconds). At any moment in time, the combination of which lighthouses are on/off uniquely identifies the time of day.
Sinusoidal encodings are those lighthouse patterns. Each "lighthouse" (each dimension pair) pulses at a different frequency. A word at position 47 gets a unique combination of lighthouse states — different from position 46, different from position 48. The transformer uses this pattern to figure out where in the sequence each word sits.
The divisor 10000 is chosen so the lowest-frequency wave has a period of ~62,832 positions — far longer than any sequence seen in 2017. Each pair of dimensions (sin, cos at frequency f) forms a 2D rotation at that frequency, which is precisely why this method works well with dot products.
The Key Mathematical Property
The crucial insight the paper highlights: PE(pos + k) is a linear function of PE(pos). Specifically, there exists a rotation matrix M_k such that PE(pos + k) = M_k · PE(pos). This means relative positions can be expressed as linear transformations — and linear transformations play nicely with the linear projections inside attention. The model can learn to attend to "the token 3 positions back" by learning an appropriate weight matrix, because relative displacement has a consistent linear representation.
Visual Intuition: The Encoding Grid
Here's what sinusoidal encodings look like across 8 positions and 8 dimensions — each cell shows the PE value (blue = negative, orange = positive, intensity = magnitude):
Rows = positions 0–7 · Columns = dimensions 0–7 · High freq (left) → Low freq (right)
Strengths: No learned parameters — zero training cost. Deterministic and interpretable. Works reasonably well for sequences up to ~2× training length (the linear property helps). Still the default in many non-LLM transformer applications (ViT uses learned positions, but many academic models use sinusoidal).
Limitations: Absolute positions only — the model learns "position 5 has this encoding" but not directly "this token is 3 positions after that token." Degrades outside training length. Cannot be updated if you want to extend context later. Modern LLMs require much longer contexts (128k+) than sinusoidal handles gracefully.
03 — Learned Absolute Positions: Simpler and Often Better
BERT (2018) and GPT-2 replaced the fixed sinusoidal formula with a learnable lookup table: a matrix of shape (max_seq_len × d_model) that is trained alongside all other model parameters. Position 0 gets embedding P[0], position 1 gets P[1], and so on. These position embeddings are added to the token embeddings before the first transformer layer — exactly like sinusoidal encodings, but the values are learned from data rather than computed by formula.
Position_Embedding ∈ ℝ^(max_len × d_model) ← fully learnable
BERT-base: max_len = 512, d_model = 768 → 393,216 extra parameters
GPT-2: max_len = 1024, d_model = 768 → 786,432 extra parameters
In practice, learned positions often match or slightly outperform sinusoidal on standard benchmarks. The model can learn whatever positional signal is most useful for the task — it's not constrained to a fixed mathematical pattern. The cost is small (the positional embedding table is tiny compared to the full model) and the benefit is flexibility.
The critical limitation: hard cutoff at max_seq_len. There is no position embedding for position 513 in BERT — the model has never seen it and has nothing to look up. You cannot extrapolate beyond the training length without retraining or architectural changes. This was acceptable in 2018 when 512 tokens covered most NLP tasks. It becomes a severe constraint when building LLMs that need to handle documents, codebases, or conversations spanning thousands of tokens.
04 — RoPE: Rotary Position Embedding
RoPE (Su et al., 2021) is the dominant positional encoding in modern open-source LLMs — used by LLaMA (all versions), Mistral, Qwen, Falcon, Yi, and most models released since 2023. It elegantly solves the main weakness of both sinusoidal and learned absolute positions: neither directly encodes relative distances in the attention score itself.
The Core Idea
Instead of adding a positional encoding to the token embedding before the attention layer, RoPE modifies the query and key vectors directly — by rotating them by an angle proportional to their position. Think of it like setting the hands of a clock to a specific angle before you hand it to the attention mechanism. If word A is at position 3, you rotate its "meaning vector" by 3°. If word B is at position 7, you rotate it by 7°.
Imagine two people comparing directions. Person A says "the café is 30° north-east of me" and Person B says "the café is 30° north-east of me too." Even though they're standing in different places, the relative direction between them and the café is the same. What matters is not where they are in the city (absolute position) — it's the angle between them (relative position).
RoPE does this for words. It rotates each word's vector by an angle that depends on its position in the sentence. When attention computes the similarity between two words, the result automatically captures how far apart they are — regardless of where in the sentence they appear. The word "walked" 3 positions after "She" gives the same positional signal whether we're at the start of the sentence or the middle.
Specifically, for a token at position m, its query vector q is rotated by angle mθ, and for position n, its key vector k is rotated by angle nθ. Because both are rotated before the dot product:
This equals: qT · R((m−n)θ) · k
→ The dot product depends only on (m − n): the RELATIVE position
R(θ) = 2D rotation matrix at angle θ (applied independently per dimension pair)
This is the key insight: when you rotate both Q and K before computing their dot product, the result depends only on the difference in their rotation angles — which equals the difference in their positions. The model inherently learns relative positional relationships, not just absolute ones. "The token 3 positions back" has a consistent geometric meaning regardless of where in the sequence you are.
How the Rotation Works
RoPE treats the d-dimensional vector as d/2 pairs of 2D coordinates. Each pair is independently rotated by an angle that depends on both the position and the pair index (different frequencies for different pairs — just like sinusoidal PE):
Rotate pair (x_{2i}, x_{2i+1}) at position pos by angle pos·θ_i:
[x_{2i} · cos(pos·θ_i) − x_{2i+1} · sin(pos·θ_i)]
[x_{2i} · sin(pos·θ_i) + x_{2i+1} · cos(pos·θ_i)]
Applied to Q and K before the dot product — not added to the token embedding
Why RoPE Handles Long Contexts Better
Learned absolute positions have a hard wall at max_seq_len. Sinusoidal positions degrade because they were designed for fixed sequence lengths. RoPE's rotation angle grows linearly with position — and because the dot product depends only on relative rotation, the model learns to interpret relative distances, which generalise better to longer sequences it hasn't seen. With techniques like YaRN (Yet Another RoPE extensioN) and LongRoPE, models trained with RoPE at 4k context can be extended to 128k+ context by adjusting the rotation frequencies — without full retraining. This is why LLaMA-3.1 supports 128k context without architectural changes.
05 — ALiBi: Attention with Linear Biases
ALiBi (Press et al., 2022) takes a completely different approach. Instead of adding anything to the token embeddings or rotating the Q/K vectors, it adds a penalty directly to the attention scores based on the distance between tokens — before the softmax.
When you're on a phone call, you can hear nearby people clearly but distant sounds are muffled — not because they're less important, but because distance reduces signal strength. ALiBi works the same way: it doesn't change what words mean, it just makes attention naturally weaker between tokens that are far apart.
Each attention head has a different "signal decay rate" (slope). Some heads decay quickly and only pay attention to nearby words — great for local grammar. Others decay slowly and can attend across long distances — great for long-range references. And crucially: this penalty is always calculable, even for positions the model has never seen, because it only depends on distance.
|i − j| = absolute distance between positions i and j
m = head-specific slope (fixed, not learned): m = 2^(−8/H) for head h
H = number of attention heads
Example slopes for H=8: [1/2, 1/4, 1/8, 1/16, 1/32, 1/64, 1/128, 1/256]
The effect: tokens that are far apart receive a penalty proportional to their distance, making them attend to each other less. The slope m controls how quickly this penalty grows — heads with larger m focus on local context, heads with smaller m can attend globally. No learned parameters, no changes to embedding dimensions, no rotation — just a simple subtraction before softmax.
ALiBi's linear penalty is not position-specific — it only cares about distance. A token at position 1000 attending to a token at position 997 gets the same penalty (3 · m) as position 10 attending to position 7. This means the model's behaviour at positions it has never seen during training is well-defined and smooth — it just applies the same distance penalty it always has. ALiBi models trained at 1024 context often maintain 90%+ of their performance at 2048 context without any modification. BLOOM (176B) and MPT use ALiBi for this reason.
06 — Head-to-Head Comparison
| Method | Added to | Learned? | Absolute position | Relative position | Extrapolation | Used by |
|---|---|---|---|---|---|---|
| Sinusoidal | Token embedding | ✗ Fixed | ✓ | ~ Via linear property | ~ Degrades slowly | Original Transformer (2017) |
| Learned Absolute | Token embedding | ✓ Full | ✓ | ✗ | ✗ Hard cutoff | BERT, GPT-2, original GPT |
| RoPE | Q and K vectors | ✗ Fixed frequencies | ✓ | ✓ Native | ✓ Via YaRN/LongRoPE | LLaMA (all), Mistral, Qwen, Falcon, Yi |
| ALiBi | Attention scores | ✗ Fixed slopes | ✗ | ✓ Only relative | ✓ Excellent | BLOOM (176B), MPT |
| NoPE (no PE) | Nothing | N/A | ✗ | ✗ | ~ Depends on context | Some state-space models, research |
The practical answer for 2026: if you're training or fine-tuning an LLM from scratch, use RoPE. If you're working with a BERT-style encoder for classification or retrieval, learned absolute positions are fine (context rarely exceeds 512 tokens for these tasks). If you need to extend an existing model's context window cheaply, RoPE + YaRN is the standard approach.
07 — Context Length Extension: YaRN and LongRoPE
One of the most practically important applications of understanding positional encodings is context window extension — taking a model trained at 4k context and extending it to 32k, 128k, or beyond. RoPE makes this possible via frequency adjustment.
The Problem: Frequency Mismatch
A LLaMA model trained at 4096 tokens has seen rotation angles from 0 to 4096·θ_i for each dimension pair. At position 8000 (beyond training), the rotation angle 8000·θ_i for high-frequency dimensions wraps around multiple times — the model has never seen these angle values and the attention pattern breaks down. Low-frequency dimensions (large i, slow rotation) are fine because they've barely rotated even at 8000 tokens. The problem is specifically with high-frequency dimensions getting "confused" at positions beyond training length.
YaRN: Scaling Frequencies
YaRN (Peng et al., 2023) extends RoPE context by scaling down the rotation frequencies — stretching the "clock" so that angles that previously completed one cycle per 4096 tokens now complete one cycle per 32768 tokens. Only a short fine-tuning run (~0.1% of original training compute) on long documents is needed to adapt to the new frequencies. LLaMA-3.1's 128k context uses exactly this approach. The key insight: you don't need to retrain from scratch — you just need the model to see enough long-context examples to adjust to the new frequency scale.
08 — Code: All Four Methods Side by Side
Working implementations of sinusoidal PE, learned absolute PE, RoPE, and ALiBi — all in PyTorch, all producing outputs compatible with the transformer block from Day 24.
import torch, math
import torch.nn as nn
# ─────────────────────────────────────────────────────────────────────────
# 1. Sinusoidal Positional Encoding (Vaswani et al. 2017)
# ─────────────────────────────────────────────────────────────────────────
class SinusoidalPE(nn.Module):
def __init__(self, d_model, max_len=4096):
super().__init__()
pe = torch.zeros(max_len, d_model)
pos = torch.arange(0, max_len).unsqueeze(1).float()
div = torch.exp(torch.arange(0, d_model, 2).float()
* -(math.log(10000.0) / d_model))
pe[:, 0::2] = torch.sin(pos * div) # even dims → sin
pe[:, 1::2] = torch.cos(pos * div) # odd dims → cos
self.register_buffer('pe', pe.unsqueeze(0)) # (1, max_len, d_model)
def forward(self, x):
return x + self.pe[:, :x.size(1)] # add PE to token embeddings
# ─────────────────────────────────────────────────────────────────────────
# 2. Learned Absolute Positional Encoding (BERT / GPT-2 style)
# ─────────────────────────────────────────────────────────────────────────
class LearnedPE(nn.Module):
def __init__(self, d_model, max_len=512):
super().__init__()
self.pe = nn.Embedding(max_len, d_model) # fully learnable lookup table
def forward(self, x):
B, T, _ = x.shape
positions = torch.arange(T, device=x.device) # [0, 1, 2, ..., T-1]
return x + self.pe(positions) # add learned PE to token embeddings
# ─────────────────────────────────────────────────────────────────────────
# 3. RoPE — Rotary Position Embedding (Su et al. 2021)
# Applied to Q and K vectors INSIDE attention — not to the token embedding
# ─────────────────────────────────────────────────────────────────────────
def precompute_rope_freqs(d_k, max_len=4096, base=10000):
"""Precompute cosine and sine tables for RoPE rotation."""
theta = 1.0 / (base ** (torch.arange(0, d_k, 2).float() / d_k))
positions = torch.arange(max_len).float()
angles = torch.outer(positions, theta) # (max_len, d_k/2)
freqs_cos = torch.cos(angles) # (max_len, d_k/2)
freqs_sin = torch.sin(angles) # (max_len, d_k/2)
return freqs_cos, freqs_sin
def apply_rope(x, freqs_cos, freqs_sin):
"""
Apply RoPE rotation to Q or K vectors.
x: (batch, heads, seq_len, d_k)
"""
B, H, T, d_k = x.shape
cos = freqs_cos[:T].unsqueeze(0).unsqueeze(0) # (1, 1, T, d_k/2)
sin = freqs_sin[:T].unsqueeze(0).unsqueeze(0)
x1 = x[..., :0::2] # Wait — cleaner to reshape into pairs
x_r = x.reshape(B, H, T, d_k // 2, 2) # (B, H, T, d_k/2, 2)
x_even = x_r[..., 0] # (B, H, T, d_k/2)
x_odd = x_r[..., 1]
# Apply 2D rotation: [x_even, x_odd] → [x_even·cos − x_odd·sin, x_even·sin + x_odd·cos]
rotated_even = x_even * cos - x_odd * sin
rotated_odd = x_even * sin + x_odd * cos
# Interleave back: (B, H, T, d_k/2, 2) → (B, H, T, d_k)
out = torch.stack([rotated_even, rotated_odd], dim=-1).reshape(B, H, T, d_k)
return out
# ─────────────────────────────────────────────────────────────────────────
# 4. ALiBi — Attention with Linear Biases (Press et al. 2022)
# Returns an additive bias matrix to subtract from attention scores
# ─────────────────────────────────────────────────────────────────────────
def build_alibi_bias(n_heads, seq_len):
"""
Returns (n_heads, seq_len, seq_len) bias matrix.
Subtract from attention scores before softmax.
"""
# Head-specific slopes: m_h = 2^(-8h/H) for h = 1 … H
slopes = torch.pow(2, -torch.arange(1, n_heads + 1).float() * (8 / n_heads))
# Distance matrix: entry [i,j] = |i − j|
positions = torch.arange(seq_len)
distances = (positions.unsqueeze(0) - positions.unsqueeze(1)).abs().float() # (T, T)
# Bias = −slope × distance, one per head
bias = -slopes.view(n_heads, 1, 1) * distances.unsqueeze(0) # (H, T, T)
return bias
# ─────────────────────────────────────────────────────────────────────────
# Quick sanity checks
# ─────────────────────────────────────────────────────────────────────────
d_model, seq_len = 64, 8
x = torch.randn(2, seq_len, d_model) # batch=2
# Sinusoidal
sin_pe = SinusoidalPE(d_model)
out_sin = sin_pe(x)
print(f"Sinusoidal PE output: {out_sin.shape}")
print(f" PE values differ by position: {not torch.allclose(sin_pe.pe[0,0], sin_pe.pe[0,3])}")
# Learned
learned_pe = LearnedPE(d_model)
out_learned = learned_pe(x)
print(f"Learned PE output: {out_learned.shape} ({sum(p.numel() for p in learned_pe.parameters()):,} params)")
# RoPE
cos_f, sin_f = precompute_rope_freqs(d_k=d_model, max_len=128)
q = torch.randn(2, 4, seq_len, d_model) # (batch, heads, seq, d_k)
q_rotated = apply_rope(q, cos_f, sin_f)
print(f"RoPE Q rotated: {q_rotated.shape} (same shape, values rotated)")
print(f" Rotation changes values: {not torch.allclose(q, q_rotated)}")
# ALiBi
alibi = build_alibi_bias(n_heads=4, seq_len=seq_len)
print(f"ALiBi bias shape: {alibi.shape} ← add to attn scores before softmax")
print(f" Head-0 bias [0→0]: {alibi[0,0,0]:.3f} (same pos, no penalty)")
print(f" Head-0 bias [0→3]: {alibi[0,0,3]:.3f} (3 apart, penalty applied)")
print(f" Head-3 bias [0→3]: {alibi[3,0,3]:.3f} (same distance, smaller slope = less penalty)")
Sinusoidal PE output: torch.Size([2, 8, 64])
PE values differ by position: True
Learned PE output: torch.Size([2, 8, 64]) (32,768 params)
RoPE Q rotated: torch.Size([2, 4, 8, 64]) (same shape, values rotated)
Rotation changes values: True
ALiBi bias shape: torch.Size([4, 8, 8]) ← add to attn scores before softmax
Head-0 bias [0→0]: 0.000 (same pos, no penalty)
Head-0 bias [0→3]: -1.500 (3 apart, penalty applied)
Head-3 bias [0→3]: -0.188 (same distance, smaller slope = less penalty)
09 — Why This Matters in Practice
Your choice of PE determines your context ceiling. BERT's learned positions hard-cap at 512 tokens — trying to run BERT on a 600-token input requires truncation or a different model. GPT-2 caps at 1024. Models using RoPE can be extended post-training via frequency scaling; models using learned absolute positions cannot. When you see a model advertise "128k context" — that capability is almost always a consequence of RoPE + YaRN fine-tuning.
PE affects what the model knows about structure. ALiBi's local-focus bias means models using it naturally pay more attention to nearby context, which can help with tasks where local coherence matters but may limit very long-range reasoning. RoPE's relative position awareness means the model has a consistent notion of "3 tokens back" regardless of absolute position — useful for code (function calls, variable scoping) and long documents where patterns repeat at varying positions.
PE matters for fine-tuning and extending base models. If you fine-tune a LLaMA-3 model (RoPE, 8k base) on longer documents, the model degrades gracefully beyond 8k tokens because RoPE's relative representation is smooth. If you try to fine-tune BERT on longer inputs, you hit a hard wall — you'd need to add new position embeddings and retrain. Understanding PE is essential for any practitioner working on RAG (Day 29) or long-document applications.
10 — A Thought to Sit With
Humans process language sequentially but also holistically — we don't just track "word 1, word 2, word 3." We understand that the beginning of a sentence sets up expectations the end fulfils, that a pronoun refers back to a noun established paragraphs ago, that a poem's structure is inseparable from its meaning.
Positional encodings give transformers a notion of "where" — but do they give a notion of "structure"? Is knowing that token 400 comes after token 399 the same as understanding that a conclusion follows from premises, or that a punchline comes at the end of a setup? What would a truly structure-aware positional encoding look like?
11 — Coming Up Next
Tomorrow — Day 26: BERT vs GPT. We now have the complete transformer with positional encodings. Day 26 splits the same architecture in two directions: the encoder (BERT) that reads the whole sentence at once with bidirectional attention, and the decoder (GPT) that generates one token at a time with causal masking. We cover what each is actually good at, how pre-training objectives differ (masked vs causal language modelling), and why the industry converged on decoder-only models for modern LLMs.
12 — Go Deeper
📄 Attention Is All You Need — Vaswani et al. (2017)
Section 3.5 covers sinusoidal encodings. The choice of 10000 as base and the discussion of linear offset properties is in the appendix.
📄 RoFormer: Enhanced Transformer with Rotary Position Embedding — Su et al. (2021)
The original RoPE paper. Section 3 derives the rotation formulation from scratch. Required reading if you plan to implement or modify RoPE.
📄 Train Short, Test Long: ALiBi — Press et al. (2022)
The ALiBi paper. Table 1 shows the perplexity comparison at lengths beyond training — the extrapolation advantage is striking.
📄 YaRN: Efficient Context Window Extension of Large Language Models
How to extend LLaMA from 4k to 128k context using RoPE frequency scaling. Sections 2–3 are the practical how-to.
▶ Positional Encoding in Transformer — Aladdin Persson
YouTube · 18 min · Clear visual walkthrough of the sinusoidal formula with code. Good complement to this post.
- The problem: attention is permutation equivariant — it has no built-in notion of word order. "Dog bites man" and "Man bites dog" look identical to an attention layer. Positional encodings inject position information before attention runs.
- Requirements: a good PE must be unique per position, consistent across sequences, generalisable to new lengths, and compatible with the attention dot product mechanism.
- Sinusoidal PE (2017): fixed formula using sin/cos at multiple frequencies. PE(pos, 2i) = sin(pos / 10000^(2i/d)). Key property: PE(pos+k) is a linear function of PE(pos) — relative positions expressible as rotation matrices. No learned parameters. Degrades for sequences much longer than training length.
- Learned absolute PE (BERT, GPT-2): a trainable embedding lookup table — one vector per position. Simple, often slightly better than sinusoidal in practice. Hard cutoff at max_seq_len — cannot extrapolate beyond training length without retraining.
- RoPE (LLaMA, Mistral, Qwen): rotates Q and K vectors by angles proportional to position before the dot product. Because <R(mθ)q, R(nθ)k> = q·R((m−n)θ)·k, the attention score depends only on relative position (m−n). Native relative position encoding. Extendable via YaRN/LongRoPE for long-context models.
- ALiBi (BLOOM, MPT): subtracts a distance penalty directly from attention scores: score −= slope × |i−j|. No parameters, no changes to embeddings. Each head has a different slope, creating a spectrum from local to global attention. Extrapolates gracefully because the distance metric is always defined.
- Practical choice (2026): RoPE for training LLMs from scratch. Learned absolute for short-context encoders (BERT-style). ALiBi if you need simple, parameter-free extrapolation. Sinusoidal for academic / non-LLM transformers.
- Context extension: RoPE models (LLaMA, Mistral) can be extended from 4k to 128k+ context via YaRN — scaling down rotation frequencies and fine-tuning briefly on long documents. This is impossible for models using learned absolute positions without adding new embeddings and retraining.
- Connection to Day 24: the transformer block from Day 24 applies PE before the first attention layer (sinusoidal/learned) or inside the Q/K projection (RoPE). Everything else in the transformer is identical — PE is a plug-in component, not a structural change.
- Why do transformers need positional encodings at all?
Attention is permutation equivariant — it computes token relationships from semantic content alone, with no positional signal. Without PE, "cat sat on mat" and any scrambled version are processed identically: the model cannot learn subject-verb-object structure, resolve pronouns, or model any grammatical pattern that depends on order. PE adds a position-dependent signal to each token's representation before attention, breaking the permutation symmetry. The choice of how to add that signal — fixed formula, lookup table, or rotation — is what distinguishes the four major approaches. - What is the difference between absolute and relative positional encodings? Give an example of each.
Absolute encodings (sinusoidal, learned) assign every position a fixed or learned vector — position 5 gets encoding E[5], regardless of where it appears relative to other tokens. The model must infer relative distances by comparing two absolute positions. Examples: sinusoidal PE (original transformer), learned PE (BERT, GPT-2). Relative encodings (RoPE, ALiBi) directly embed the distance between token pairs into the attention score itself — the score between positions 3 and 7 encodes "4 apart," not "position 3" and "position 7" separately. This is why RoPE's attention score depends on (m−n) not on m and n independently. Relative encodings generalise better to new lengths because the model learns "3 apart means this" rather than "position 1003 means this." - What is RoPE and why has it become the dominant PE in open-source LLMs?
RoPE (Rotary Position Embedding) rotates Q and K vectors by angles proportional to their position before the dot product. Because the inner product between two rotated vectors equals the inner product between the originals multiplied by the rotation for their difference: ⟨R(mθ)q, R(nθ)k⟩ = q·R((m−n)θ)·k — the attention score depends only on the relative position (m−n). Three reasons drive its adoption: (1) Native relative position — the model directly learns positional relationships rather than inferring them. (2) Context extensibility — via YaRN frequency scaling, RoPE models can extend from 4k to 128k+ context with a short fine-tuning run (~0.1% of original compute). (3) No extra parameters — the rotation frequencies are fixed, adding no parameter overhead. LLaMA (all versions), Mistral, Qwen, Gemma, and Falcon all use RoPE. - A model trained with BERT-style learned PE at 512 tokens needs to handle 1,024 tokens. What are your options? What if it used RoPE instead?
For learned absolute PE: (1) Truncate to 512 — simplest, loses the extra tokens. (2) Add new embeddings for positions 512–1,023 and fine-tune on long examples — the existing 0–511 weights are preserved but you need sufficient long-context training data. (3) Switch to sinusoidal PE — drop-in replacement, no parameters, degrades gradually beyond training length. There is no free option; the hard cutoff is a fundamental limitation of the lookup-table design. For RoPE: extend via YaRN frequency scaling — divide rotation frequencies by scaling factor 2 (since 1024/512=2), then fine-tune for a few thousand steps on long examples. The attention mechanism works identically; only the precomputed cos/sin tables change. Total cost is roughly 1,000 GPU-hours for a BERT-scale model versus retraining from scratch. This is why practitioners now prefer RoPE for any model that may need context extension later. - Why does ALiBi extrapolate to long sequences while learned absolute PE cannot? What is the architectural reason?
Learned absolute PE is a lookup table: E[0], E[1], …, E[max_len−1]. Position 513 in a 512-trained BERT model has no entry — the model has never seen it and produces undefined behaviour (usually garbage or an index error). The hard wall is architectural: there is literally nothing stored for positions beyond training length. ALiBi applies a penalty based only on distance: score −= slope × |i−j|. The distance |i−j| is a well-defined number for any i and j, including positions far beyond training length. A token at position 2,000 attending to position 1,997 gets exactly the same penalty (3 × slope) as position 10 attending to position 7. There is nothing to "look up" and nothing that runs out of range — the model's behaviour at long positions is completely determined by the fixed slope values and the always-computable distance metric. The only degradation is that the model may not have learned to use very long-range context effectively during training. But it will not break — which is why BLOOM (176B) chose ALiBi: at that scale, unexpected failures at long context would be catastrophic.
📄 RoFormer: Enhanced Transformer with Rotary Position Embedding — Su et al. (2021)
The original RoPE paper. Section 3 is the essential derivation — 15 minutes of reading that explains why every major open-source LLM uses this method.
📄 Train Short, Test Long: Attention with Linear Biases (ALiBi) — Press et al. (2022)
The ALiBi paper. Table 1's perplexity comparison at 2× training length is the most convincing demonstration of extrapolation advantage anywhere in the literature.
📄 YaRN: Efficient Context Window Extension of Large Language Models — Peng et al. (2023)
The practical guide to extending LLaMA context. Used by LLaMA-3.1 128k. Section 2 explains the frequency scaling derivation clearly.
▶ Positional Encoding in Transformer — Aladdin Persson
YouTube · 18 min · Best visual walkthrough of the sinusoidal formula with live NumPy code. Complements the derivation in this post.
🔗 Rotary Embeddings: A Relative Revolution — EleutherAI Blog
Excellent intuitive explanation of RoPE with visualisations. Explains why rotation is the right mathematical operation and compares alternatives.