Skip to the lesson
FixTheRobot Explore Robotics / LLM / LM3 Embeddings and the Mathematics of Attention / LM3-T2

LLM · Theory · LM3

LM3-T2 — Positional Information

Attention treats its input as a bag of vectors. Position has to be put back in on purpose.

  • Lesson 2 of 6
  • Module 3: Embeddings and the Mathematics of Attention
  • Theory
  • Difficulty: Beginner
  • About 8 minutes to read
  • Prerequisite: LM3-T1

Embeddings tell the model which token it is looking at, not where in the sequence it sits. Self-attention, the operation that lets tokens exchange information, is blind to order: shuffle the inputs and the outputs are the same vectors in shuffled order. A language model that cannot tell “the robot picks the box” from “the box picks the robot” is useless, so every Transformer adds position information. This lesson shows why order is lost, then covers the schemes in use: learned absolute embeddings (GPT-2 and this track), fixed sinusoids (the original Transformer), and relative schemes (modern models, treated again in Module 10). Lab LM3-L1 and the notebook LM3-L3 build on these inputs.

Learning objectives

After this lesson, you should be able to:

  • Explain why self-attention without position information is insensitive to token order.
  • Add a learned absolute position embedding to token embeddings and state the shapes.
  • Compute sinusoidal position encodings by hand from the formula.
  • Compare absolute, sinusoidal and relative schemes by parameters, length limits and extrapolation.

The idea: attention is a set operation

Self-attention (Lesson LM3-T3) computes each output as a weighted average over all tokens, with weights that depend only on the content of the vectors. Nothing in the formula refers to a position. If you swap two inputs, their outputs swap too and nothing else changes. Formally the layer is permutation equivariant: permuting the input permutes the output the same way.

Attention without position cannot tell word orderTwo sequences with the same three tokens in different order: the robot picks box and box picks the robot. Without positional information both produce the same set of output vectors, only listed in a different order, so the model cannot tell who picked what. Adding a position vector to each embedding makes the two inputs different.Same tokens, different order: the unordered view is identical robotpicksboxboxpicksrobot = same set → same outputs (reordered) Add a position vector p₁, p₂, p₃ to each embedding: e(robot)+p₁e(picks)+p₂e(box)+p₃
Without position information the two orders of the same words look identical to attention. A position vector added to each embedding breaks the tie.

Recurrent networks received order for free, because they read one token at a time. A Transformer reads all tokens at once, which is what makes it fast to train, and pays for it by having to supply order explicitly.

Absolute position embeddings

The simplest fix: keep a second learned table with one row per position, 0 to L − 1 where L is the context length, and add the row for position i to the token embedding at position i. The model learns whatever a position vector must contain. This is what GPT-2 does, and what the book builds. Its limits: the table has only L rows, so the model cannot take longer inputs, and it learns no explicit relation between neighbouring positions; position 7 and 8 are just two different rows that happen to be used together often.

Sinusoidal encodings

The original Transformer used fixed (not learned) vectors built from sines and cosines of different frequencies. Position i gets a vector whose entries are sin(i/100002k/d) and cos(i/100002k/d) for k = 0, 1, … Fast-moving pairs distinguish adjacent positions; slow-moving pairs distinguish far-apart ones, like the digits of a clock. No parameters are needed, any length can be generated, and the dot product of two encodings depends only on their distance, which is convenient.

Relative schemes

What often matters is not “this is token 513” but “this token is three positions before that one”. Relative schemes put the offset directly into the attention scores. Rotary position embeddings (RoPE), used in many recent open models, rotate query and key vectors by an angle proportional to position, so that their dot product depends on the offset. These schemes extend more gracefully to long inputs. They are covered in Module 10; here it suffices to know that absolute learned embeddings are one design among several.

The mathematics: adding position vectors

Let the token embeddings of a sequence be the matrix X of shape (n, d) (Lesson LM3-T1). With a learned position table P of shape (L, d), the input to the first layer is

input embeddings with position
Z[i, : ]=X[i, : ]+P[i, : ]Z[i,\,:\,]=X[i,\,:\,]+P[i,\,:\,]
i=0, …, n−1,i=0,\,\ldots ,\,n-1,
n≤Ln\le L
X
token embeddings, shape (n, d); the row i depends only on the token at position i.
P
position embeddings, shape (L, d); the row i depends only on the position i.
L
the context length: the longest sequence the model supports; n ≤ L.
Z
the sum, shape (n, d); the same shape as X, so no later layer changes.

For a batch of shape (B, n, d) the same (n, d) position matrix is added to every sequence by broadcasting. The position table adds L · d parameters: for GPT-2 small, 1,024 × 768 = 786,432.

Sinusoidal formula. For position i and pair index k = 0, …, d/2 − 1:

sinusoidal encoding
PE⁡[i, 2k]=sin⁡(i10000(2kd))\operatorname{PE}[i,\,2k]=\sin (\frac{i}{10000^{(\frac{2k}{d})}})
PE⁡[i, 2k+1]=cos⁡(i10000(2kd))\operatorname{PE}[i,\,2k+1]=\cos (\frac{i}{10000^{(\frac{2k}{d})}})
i
position in the sequence, 0, 1, 2, …
k
index of a sine/cosine pair; each pair has its own frequency ωk = 1 / 10000^(2k/d).
d
the embedding width; even, so there are d/2 pairs.
PE[i, ·]
the d-dimensional position vector of position i; entries lie in [−1, 1].

With d = 4 there are two pairs, with ω0 = 1 and ω1 = 1/100. Then PE[0] = [0, 1, 0, 1]; PE[1] = [sin 1, cos 1, sin 0.01, cos 0.01] ≈ [0.841, 0.540, 0.010, 1.000]; PE[2] = [sin 2, cos 2, sin 0.02, cos 0.02] ≈ [0.909, −0.416, 0.020, 1.000]. The first pair turns quickly with position; the second barely moves in this short range. In a large model the frequencies span many orders of magnitude.

Distance property. By the identity cos(a − b) = cos a cos b + sin a sin b,

dot product depends only on the offset m
PE⁡[i]⋅PE⁡[i+m]=∑k cos⁡(m⋅ωk)\operatorname{PE}[i]\cdot \operatorname{PE}[i+m]=\sum _{k}\,\cos (m\cdot \omega _{k})

so two positions the same distance apart have the same dot product wherever they are. For d = 4 and m = 2: cos(2) + cos(0.02) ≈ −0.416 + 0.9998 = 0.584 for every i.

A worked example: order matters, and a learned table

Permutation test. Take two tokens with embeddings a and b. Self-attention on (a, b) gives outputs (oa, ob). On (b, a) it gives (ob, oa): the same two vectors in swapped order, so nothing in the outputs records which came first. The code below verifies this numerically, then breaks the symmetry by adding position vectors.

import torch

torch.manual_seed(0)
n, d = 3, 4
x = torch.randn(n, d)                       # three token embeddings
Wq, Wk, Wv = (torch.randn(d, d) for _ in range(3))

def self_attention(z):
    q, k, v = z @ Wq, z @ Wk, z @ Wv
    w = torch.softmax(q @ k.T / d ** 0.5, dim=-1)
    return w @ v

perm = torch.tensor([2, 0, 1])              # reorder the tokens
print(torch.allclose(self_attention(x)[perm], self_attention(x[perm]), atol=1e-5))   # True: outputs are just reordered

pos = torch.nn.Embedding(n, d)              # a learned position table with n rows (the book uses context_length rows)
p = pos(torch.arange(n))                    # shape (n, d): row i is the vector of position i
a = self_attention(x + p)
b = self_attention(x[perm] + p)
print(torch.allclose(a[perm], b, atol=1e-5))   # False: with positions the order changes the result

The first comparison is True: reordering inputs only reorders outputs. In the second, position vectors go with the slot, not the token, so putting the same tokens into different slots produces different sums, and the outputs genuinely differ.

Sinusoids in code

import math
import torch

def sinusoidal(n, d):
    pe = torch.zeros(n, d)
    pos = torch.arange(n).unsqueeze(1).float()                   # (n, 1)
    freq = 1.0 / 10000 ** (torch.arange(0, d, 2).float() / d)    # (d/2,)
    pe[:, 0::2] = torch.sin(pos * freq)
    pe[:, 1::2] = torch.cos(pos * freq)
    return pe

pe = sinusoidal(3, 4)
print(pe[1].tolist())                         # [0.841..., 0.540..., 0.0099998..., 0.99995...]
d1 = (pe[0] @ pe[2]).item()
d2 = (sinusoidal(12, 4)[5] @ sinusoidal(12, 4)[7]).item()
print(round(d1, 4), round(d2, 4))             # 0.5837 0.5837  (same offset, same dot product)

Engineering implications and limitations

Position tables set the context length. A learned table with L rows cannot represent position L. Feeding a longer sequence is an index error, so the tokenizer or the application must truncate or window (Lesson LM2-T5).

Extending the context is not free. You cannot just append rows to a trained table: the new rows are untrained. Relative schemes and some rescaling tricks allow longer inputs, but quality at lengths never seen in training needs checking (Module 10).

Add, do not concatenate. The usual choice is to add position to token embeddings so the width stays d. Concatenation would enlarge every later matrix.

Position and tokens share the vector. Because they are summed, the first layers must disentangle “what” from “where”. Models learn to do so, which is part of why a small d hurts.

Test with a shuffle. A cheap sanity test of a new implementation: shuffle the tokens of an example. If the model output on the last position is unchanged, position information is not reaching the layers.

Left versus right padding. With absolute positions the same text gets different position vectors if you pad on the left. Be consistent between training and inference, and pass correct position indices when batching (Module 9).

For robots. Logged sensor streams have their own notion of time: timestamps. Whether to use token position or real elapsed time as the “position” is a design decision for sequence models in robotics; the VLA track discusses how action sequences are positioned.

Common misconceptions

“Attention knows the order because the tokens are processed in order.”

They are processed in parallel and the attention formula has no position in it. Order must be injected.

“Position embeddings are fixed by the architecture.”

In GPT-2 they are learned parameters, as free to change as any other weight. Sinusoids are fixed, but they are one option.

“A model can handle any length if it uses sinusoids.”

It can compute the vectors, but the model has only learned to use positions seen in training; quality can degrade beyond that.

“Position is added to the attention scores.”

In this design it is added to the input embeddings. Relative schemes such as RoPE act inside the scores.

“Position embeddings only matter for long texts.”

They matter for every sequence: without them “dog bites man” and “man bites dog” give the same representation.

Engineering takeaways

  1. Self-attention is permutation equivariant, so order must be supplied separately.
  2. Absolute learned position embeddings: a table of shape (L, d) added to the token embeddings; GPT-2 small uses 1,024 × 768 = 786,432 parameters for them.
  3. Sinusoidal encodings need no parameters and PE[i] · PE[i + m] depends only on the offset m.
  4. Relative schemes such as RoPE (Module 10) put the offset in the attention scores and extend more gracefully to long inputs.
  5. The position table sets the maximum context length of a learned-position model.

References and further reading

See Chapter 2 of the book (encoding word positions) for the absolute embedding used in this track; rotary embeddings are treated in Module 10.

Knowledge check

Three conceptual questions. Write an answer, then reveal the explanation. Your answers stay in your browser.

Question 1

A model has context length 2,048 and d = 512 with learned absolute positions. How many parameters does the position table have?

Explanation

2,048 × 512 = 1,048,576 parameters.

Question 2

Compute PE[0] for d = 4 with the sinusoidal formula.

Explanation

Position 0 gives sin(0) = 0 for the even entries and cos(0) = 1 for the odd entries, so PE[0] = [0, 1, 0, 1].

Question 3

Why does shuffling the tokens of an input leave the set of output vectors unchanged in an attention layer with no positional information?

Explanation

Each output is a weighted average over all inputs with weights computed from content only. Reordering the inputs reorders the rows of scores and weights consistently, so each token gets the same output vector as before, only in a different slot.

What’s next

Next: queries, keys and values, the three roles that turn embeddings into attention.

Practice this lesson

Compute Attention Step by Step

Compute scores, softmax weights and context vectors for a short sentence by hand-written code.

Next lesson

LM3-T3 — Queries, Keys, Values and Scaled Dot-Product Attention

Compute attention scores, weights and context vectors from queries, keys and values.

Nothing below is required to finish this lesson.