You’ll learn why self-attention is position-blind and needs an added signal to tell “dog bites man” from “man bites dog.” You’ll see how sinusoidal encodings use waves, why nearby positions stay similar, and how RoPE puts relative position directly into attention.
The Problem: Attention Doesn’t Know Order
Consider “Dog bites man” (routine news) versus “Man bites dog” (breaking news!). These sentences have completely different meanings, but raw self-attention computes the same attention weights for both. Play the 3 rounds below to see why:
Round 1 · Test “Dog → bites”Click the cell where Query “Dog” attends to Key “bites” in either matrix.
Sentence A: “Dog bites man”
Key →
Query ↓
Dog
bites
man
Dog
bites
man
Sentence B: “Man bites dog”
Key →
Query ↓
Man
bites
dog
Man
bites
dog
RNNs don’t have this problem because they process tokens one at a time, left to right. Position is baked into the computation order. Transformers process all positions in parallel, which makes them fast but position-blind until we stamp position vectors onto each token.
✍️Fill in the Blanks
Self-attention is -invariant: shuffling the input tokens produces the same attention weights.
Fill in every blank, then check your answer.
A blank is still empty.
Fill in every blank before checking your answer.
Not quite.
Try another answer, or reveal the solution.
Correct.
One accepted answer is shown in each blank.
Answer revealed.
The blanks now show the answers.
The Fix: Add Position Vectors
The idea: create a positional encoding vector for each position and add it to the token embedding before attention. Now “cat” at position 1 has a different representation than “cat” at position 0, even though they are the same word. Step through the animation to see how:
Token Embeddings
d0
d1
d2
d3
d4
d5
d6
d7
"The"
0.20
-0.50
0.80
0.10
-0.30
0.70
-0.10
0.40
"cat"
-0.30
0.60
0.20
-0.70
0.40
0.10
0.50
-0.20
"sat"
0.10
-0.20
0.50
0.30
-0.60
0.40
0.20
0.80
Each token has an embedding vector that encodes WHAT the token is. But notice: if "cat" appeared at position 0 instead of position 1, its embedding would be identical. The embedding carries no information about WHERE the token sits in the sentence.
What to look for: in Step 3, the final vectors (green) differ between tokens that would otherwise be identical. The position “nudge” breaks the symmetry that self-attention can’t see on its own.
xinput=token_embedding+positional_encoding
The positional encoding vector has the same dimension as the token embedding (dmodel), so the two can simply be added element-wise.
How Sinusoidal Encoding Works
The original Transformer (2017) builds position vectors from sine and cosine waves at different frequencies. One wave isn’t enough to distinguish every position (it repeats). But layering many waves, fast and slow, creates a unique fingerprint for each one. Step through this buildup:
Building a Positional Fingerprint
dim 0 (fast)
Position fingerprints (values from active waves):
pos 0
0.00
pos 1
0.84
pos 2
0.91
pos 3
0.14
pos 4
-0.76
pos 5
-0.96
pos 6
-0.28
pos 7
0.66
A single sine wave gives each position a value. But notice: positions 0 and 6 have similar values (the wave repeats). One wave alone can't uniquely identify every position.
What to look for: with one wave, positions 0 and 6 look similar. Adding a second wave separates them. By the final step, every position has a completely unique combination of values across all dimensions.
Think of it like the hands on a clock. The second hand spins fast (low dimensions), the minute hand spins at medium speed (middle dimensions), and the hour hand barely moves (high dimensions). Together they uniquely identify any moment in time. Where this analogy breaks: unlike a clock, PE uses sine and cosine at each frequency, giving it two values per frequency rather than one.
Even dimensions use sine, odd dimensions use cosine
The denominator 100002i/d creates exponentially decreasing frequencies as the dimension i increases
def sinusoidal_positional_encoding(max_len, d_model): PE = np.zeros((max_len, d_model)) position = np.arange(max_len)[:, np.newaxis] # Different frequency for each dimension pair div_term = np.exp(np.arange(0, d_model, 2) * (-np.log(10000.0) / d_model)) PE[:, 0::2] = np.sin(position * div_term) # Even dims: sin PE[:, 1::2] = np.cos(position * div_term) # Odd dims: cos return PE
Explore the Full Encoding
Switch between heatmap view (all dimensions at once) and wave view (individual dimensions across positions):
High dimensions (right columns) change slowly, low-frequency “hour hand”
Each position has a unique combination of values across all dimensions
Nearby Positions Have Similar Encodings
A useful property of sinusoidal encoding: similarity between positions depends on distance, not absolute values. Hover over the matrix below to see cosine similarity between position vectors:
PE Cosine Similarity Matrix
Hover cells to see similarity values
pos
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
0.55 (least similar)1.00 (most similar)
Key observation: The diagonal stripe pattern shows that similarity depends mainly on relative distance between positions, not absolute position. Nearby positions are similar; distant ones are less so.
What to look for: the bright diagonal band shows that nearby positions have high similarity, while distant positions are dissimilar. Notice the pattern is symmetric and repeats: the similarity between positions 2 and 5 is the same as between positions 10 and 13.
This means the model can learn patterns like “the word 2 positions to my left is usually a verb,” and that pattern works at position 5 the same as at position 500. No relearning needed for each absolute position.
🤔Quick Check
Can sinusoidal positional encoding handle sequences longer than those seen during training?
Your turn
Choose an answer to check your understanding.
Try another option
That option does not fit. Try another answer, or reveal the solution.
Correct!
Since sine and cosine are continuous functions defined for all real numbers, sinusoidal PE can generate encodings for any position, even positions never seen during training. In practice, the model may not have learned to use very different positions well, but the encoding itself extrapolates.
Answer: B
Since sine and cosine are continuous functions defined for all real numbers, sinusoidal PE can generate encodings for any position, even positions never seen during training. In practice, the model may not have learned to use very different positions well, but the encoding itself extrapolates.
Attempts: 0
Other Position Encoding Methods
Method
Used By
Key Idea
Extrapolates?
Sinusoidal
Original Transformer
Fixed sin/cos functions
Yes (in theory)
Learned
BERT, GPT-2
Trainable position embeddings
No (max_len is fixed)
RoPE
LLaMA, Mistral
Rotate Q and K by position
Yes (better than sinusoidal)
ALiBi
BLOOM
Subtract linear bias from attention scores
Yes (strong extrapolation)
Learned embeddings: BERT and GPT-2 learn a (max_len,dmodel) parameter matrix, giving each position a trainable vector. Slightly more flexible, but can’t handle sequences longer than max_len.
RoPE: Rotary Position Embedding
Sinusoidal encoding adds position information before attention. What if position information lived inside the attention score instead? RoPE rotates query and key vectors by an angle proportional to their position. The dot product of two rotated vectors naturally depends on their relative distance.
q at position m=2k at position n=5
dot product = q·k = 0.8×0.6 + 0.3×0.7 = 0.69
Two vectors on a 2D plane. Their dot product measures similarity. But there's no position information yet.
What to look for: in step 3, two pairs of tokens at the same distance (positions 2,5 and positions 10,13) produce the same dot product. RoPE makes attention scores depend on how far apart tokens are, not where they sit in absolute terms.
Used by LLaMA, LLaMA 2, Mistral, Gemma, and most modern open-source LLMs.
Why RoPE won:
Encodes relative position directly in the Q·K dot product, not as an additive offset
With NTK-aware scaling or YaRN, handles sequences 4-8x longer than training
No extra parameters (fixed mathematical functions, like sinusoidal)
Sinusoidal positional encoding. Vaswani et al., “Attention Is All You Need” (2017), Section 3.5. The authors chose fixed sinusoidal functions over learned embeddings and found similar performance, but sinusoidal PE can extrapolate to longer sequences.
RoPE. Su et al., “RoFormer: Enhanced Transformer with Rotary Position Embedding” (2021) introduced rotary position embedding, encoding relative position directly in the Q·K dot product. Now the default in LLaMA, Mistral, Gemma, and most modern open-source LLMs.
Self-attention produces identical outputs regardless of word order
It sees a set of tokens, not a sequence: “dog bites man” = “man bites dog”
Positional encoding
A position vector is added to each token embedding before attention
Like stamping a seat number on each concert ticket: same person, different seat
Sinusoidal encoding
Each dimension uses a sine/cosine wave at a different frequency
Like clock hands: seconds change fast (low dims), hours change slowly (high dims)
Nearby similarity
Positions close together have similar encoding vectors
The model can learn relative patterns like “the word 2 positions left is usually a verb”
✍️Fill in the Blanks
Sinusoidal positional encoding uses different waves to create unique position signatures, where low dimensions change and high dimensions change slowly.
Fill in every blank, then check your answer.
A blank is still empty.
Fill in every blank before checking your answer.
Not quite.
Try another answer, or reveal the solution.
Correct.
One accepted answer is shown in each blank.
Answer revealed.
The blanks now show the answers.
Learning Checklist
I can explain why self-attention is position-blind and give a concrete example where word order changes meaning
I understand that positional encoding is added to the token embedding, and can explain why addition works
I can describe the clock analogy: low dimensions = fast-changing second hand, high dimensions = slow-changing hour hand
I understand why nearby positions have similar encodings and why that helps the model learn
I know that sinusoidal PE can extrapolate to unseen sequence lengths because sine/cosine are defined for all positions
Next: Where Attention Came From
You now understand how modern transformers use attention: self-attention, multiple heads, and positional encoding. But where did the idea of “attention” originate? Before transformers, it was invented to help translation models focus on relevant parts of the input sentence. Seq2Seq Attention →