Level 10 · AdvancedLesson 10.1 40 min read 10 question quiz 10 marks of written practice

Attention: Learning Where to Look

Why context changes meaning

Read these two sentences:

  • The animal didn't cross the street because it was too tired.
  • The animal didn't cross the street because it was too wide.

In the first, "it" is the animal. In the second, it is the street. One word changed, five words after "it", and the meaning of "it" flipped.

An embedding from Level 9 gives "it" the same vector in both sentences. To understand language, each token's vector has to be updated with information from the other tokens that matter to it. Attention does exactly that: every token looks at every other token, decides how relevant each one is, and takes in a weighted mix of their information.

Attention depends on contextTwo copies of the sentence: The animal didn't cross the street because it was too tired, and the same sentence ending too wide. From the word it, curved lines run to every other word, thicker for higher attention weights. In the first sentence the thickest line goes to animal, weight 0.46, then street 0.12 and tired 0.10. In the second the thickest goes to street, weight 0.48, then animal 0.10. The weights are illustrative values for one attention head, and in each sentence they add up to 1.Attention: “it” looks at every word and weighs each one0.100.120.46Theanimaldidn'tcrossthestreetbecauseitwastootired0.100.48Theanimaldidn'tcrossthestreetbecauseitwastoowide“tired” fits an animal, so “it” attends most to “animal” (0.46)“wide” fits a street, so “it” attends most to “street” (0.48)
Illustrative weights for one head: one changed word moves the attention.

An RNN passes information along one step at a time, so distant words fade. Attention connects any two tokens directly, however far apart they are.

Queries, keys and values

Attention borrows its vocabulary from searching a library:

PartIn a libraryIn attention
QueryWhat you type into the search boxWhat this token is looking for
KeyThe label on each book's spineWhat each token offers, to be matched against queries
ValueWhat is inside the bookThe information each token passes on

Each token's embedding x is multiplied by three learned weight matrices to make three new vectors:

Three projections

q = x × WQ, k = x × WK, v = x × WV

Queries, keys and valuesAn embedding vector x for the word it, six numbers, has arrows to three 3D blocks: multiply by W Q in violet, by W K in blue and by W V in teal. Each block produces a new vector of four numbers: q, the query, what it is looking for; k, the key, what it offers to other tokens; and v, the value, what it will pass on. The same three matrices are used for every token.Every token makes a query, a key and a value from its embeddingx0.8−0.30.50.1−0.60.9embeddingof “it”× WQ−1.32−0.400.87−0.21qquery: what “it” is looking for× WK−0.68−0.22−0.61−0.76kkey: what “it” offers to others× WV−0.47−1.050.94−0.24vvalue: what “it” will pass onThe same three matrices are used for every token; training tunes their numbers.
Three learned matrices turn one embedding into three vectors with three jobs.

The same three matrices are used for every token. Training adjusts them so that a token's query lines up with the keys of the tokens it needs: a pronoun's query might come to match the keys of nouns, and a verb's query might match its subject.

Scaled dot-product attention, step by step

For one token, attention takes four steps:

  1. Score: take the dot product of its query with every key. Vectors pointing the same way give large scores.
  2. Scale: divide every score by √dk, the square root of the length of the keys.
  3. Softmax: turn the scores into weights between 0 and 1 that add up to 1.
  4. Blend: add up the value vectors, each multiplied by its weight.
Scaled dot-product attention, step by stepA stepped table for the query of it, q = 2, 1, against four tokens. Animal has key 2, 1; cross 0, 1; street 1, minus 1; it 1, 1. Step 1, the dot products are 5, 1, 1 and 3. Step 2, divided by the square root of 2 they are 3.54, 0.71, 0.71 and 2.12. Step 3, softmax gives weights 0.735, 0.043, 0.043 and 0.179, drawn as amber bars. Step 4, the values 4, 0; 2, 2; 0, 4; and 1, 1, shown as colours from pink to blue, are blended by those weights into the new vector 3.20, 0.44, which is mostly the value of animal.Scaled dot-product attention for the token “it”query of “it”: q = [2, 1]tokenkeyq · k÷ √2softmax weightvalueanimal[2, 1]cross[0, 1]street[1, −1]it[1, 1]511351133.540.710.712.1251133.540.710.712.120.7350.0430.0430.17951133.540.710.712.120.7350.0430.0430.179[4, 0][2, 2][0, 4][1, 1]0.735 × [4, 0] + 0.043 × [2, 2] + 0.043 × [0, 4] + 0.179 × [1, 1]= [3.20, 0.44]: the new vector for “it” is mostly “animal”share of each value
Score, scale, softmax, blend: the whole of attention for one token.

Here is the query of "it", q = [2, 1], against four keys. The keys have 2 numbers, so the scores are divided by √2 ≈ 1.41:

TokenKeyq · k÷ √2exWeight
animal[2, 1]53.5434.310.735
cross[0, 1]10.712.030.043
street[1, −1]10.712.030.043
it[1, 1]32.128.340.179

Each weight is its ex divided by the column total, 46.71, so the weights add up to 1. If the values are animal [4, 0], cross [2, 2], street [0, 4] and it [1, 1], the output is

0.735 × [4, 0] + 0.043 × [2, 2] + 0.043 × [0, 4] + 0.179 × [1, 1] = [3.20, 0.44]

The new vector for "it" is now mostly made of "animal".

Why divide by √dk?

Dot products grow with the length of the vectors. For two random vectors of 64 numbers, the dot product is typically about 8 times (√64) larger than for vectors of one number. Big scores push softmax to give almost all the weight to one token, where its gradient is almost zero and learning stalls. Dividing by √dk keeps the scores in a range where softmax still learns.

Every token at once: the attention matrix

In self-attention every token does this at the same time. Stack all the queries as the rows of a matrix Q, the keys as K and the values as V, and the whole calculation is two matrix multiplications and a softmax:

Scaled dot-product attention

Attention(Q, K, V) = softmax(Q × KT ÷ √dk) × V

Here KT is K transposed, its rows turned into columns, so that Q × KT holds the dot product of every query with every key. After the softmax this is the attention matrix: one row per token, showing how much it attends to every token. Every row adds up to 1.

An attention matrixAn 11 by 11 grid of coloured cells, rows and columns labelled with the words of the sentence The animal didn't cross the street because it was too tired. Each row is one query token, each column a key, and colour runs from dark blue for 0 to yellow for high weights. Every row adds up to 1. The diagonal and the cells beside it are moderately bright, because tokens attend to themselves and their neighbours. The row for it, outlined in amber, is brightest under animal, 0.60, then tired, 0.11. The row for tired is brightest under animal. The weights are softmaxes of scores set by hand to illustrate patterns that trained attention heads learn.The attention matrix: every token's weights over every tokenTheanimaldidn'tcrossthestreetbecauseitwastootiredquery ↓ key →The.29animal.23didn't.31cross.22.27the.36street.24becauseit.60was.26too.40tired.37.2200.6 or moreattention weightEach row is one token's query.Each row adds up to 1.Most tokens look at themselvesand their neighbours.“it” (outlined) looks mostly at“animal”, 0.60, and “tired”, 0.11.“tired” looks back at “animal”.Weights here come from scores set byhand, to show patterns trained heads learn.
One head's attention matrix: a row per query, a column per key, each row summing to 1.

Because it is all matrix multiplication, a GPU computes attention for every token in parallel. That is the speed advantage over an RNN, which must read one token after another.

Try it yourself: drag the query and the keys, and watch the scores, the softmax and the blended value change.

Attention lab

Drag a query and some keys, and watch the dot products, the softmax and the blended output change. This tool needs JavaScript, so turn it on to use it. Everything you need to answer the exam question is explained in the text above.

Masking: no peeking at the future

A model that generates text predicts each token from the tokens before it. In training it is shown whole sentences at once, so something must stop each token attending to the tokens after it, or it could simply copy the answer it is meant to predict.

The fix is a causal mask. Before the softmax, every score where the key comes after the query is set to minus infinity. Since e to the power of minus infinity is 0, those weights become exactly 0, and the remaining weights in each row still add up to 1.

A causal maskTwo 6 by 6 grids for the tokens the cat sat on the mat. The left grid holds scores; every cell above the diagonal, where the key comes after the query, is replaced by minus infinity and drawn grey. An arrow labelled softmax, row by row, leads to the right grid of attention weights, where those cells are exactly 0. The weights on and below the diagonal, row by row, are the: 1.00; cat: 0.13, 0.87; sat: 0.21, 0.56, 0.23; on: 0.07, 0.54, 0.16, 0.22; the: 0.02, 0.37, 0.09, 0.02, 0.50; mat: 0.07, 0.20, 0.08, 0.15, 0.40, 0.09.A causal mask: no token may attend to a later tokenscores, with the mask addedthecatsatonthematthe0.4−∞−∞−∞−∞−∞cat−0.31.6−∞−∞−∞−∞sat−0.70.3−0.6−∞−∞−∞on−0.91.1−0.10.2−∞−∞the−1.01.90.5−0.92.2−∞mat−0.80.2−0.7−0.10.9−0.6attention weightsthecatsatonthematthe1.0000000cat0.130.870000sat0.210.560.23000on0.070.540.160.2200the0.020.370.090.020.500mat0.070.200.080.150.400.09softmax, row by rowe to the power −∞ is 0Row 1 (“the”) can only see itself, so its weight is 1. The last row sees all six tokens. Every row still adds up to 1.
Masking happens before the softmax, so each row's weights still add up to 1.

Transformers use attention in three ways:

KindQueries fromKeys and values fromUsed for
Self-attentionThe sequenceThe same sequenceUnderstanding a whole text at once
Masked self-attentionThe sequenceOnly the same and earlier positionsGenerating text one token at a time
Cross-attentionThe text being writtenThe input textTranslation: looking back at the source sentence

Cross-attention is the 2014 idea from Level 9: while writing each word, the decoder looks back at the whole source sentence.

Multi-head attention

One attention calculation gives each token one weighted mix. But a word has many relationships at once: "it" needs the noun it refers to, a verb needs its subject, and every word benefits from its neighbours. Multi-head attention runs several attention calculations, called heads, side by side, each with its own WQ, WK and WV.

Multi-head attentionFour input tokens, she, saw, the, bird, each a vector of 6 numbers, fan out to three attention heads, each with its own W Q, W K and W V. Head 1 attends to the previous word, so its 4 by 4 pattern is bright just below the diagonal. Head 2 attends to the subject, so its first column is bright. Head 3 attends to each word itself, so its diagonal is bright. Each head outputs 2 numbers per token; the three outputs are concatenated into 6 numbers, coloured violet, pink and teal, then multiplied by W O to give the output, 4 tokens by 6 numbers. The patterns are illustrations of the kinds of pattern found in trained models.Multi-head attention: several heads look for different things at onceinput4 tokens × 6shesawthebirdhead 1: the previous wordWQWKWVits own weightshead 2: the subjectWQWKWVits own weightshead 3: the word itselfWQWKWVits own weightsconcatenate2 + 2 + 2 = 6× WOoutput4 tokens × 6Each head works on 6 ÷ 3 = 2 numbers, sothree heads cost about the same as one.
Illustrative patterns: trained models contain heads much like these.
  1. Each of the h heads projects the tokens down to dmodel ÷ h numbers and computes attention on its own.
  2. The h outputs are joined end to end, concatenated, back to dmodel numbers per token.
  3. A final matrix, WO, mixes what the heads found.

The original transformer used 8 heads of 64 numbers, so dmodel = 512. Because each head works on fewer numbers, 8 heads cost about the same as one big one.

Researchers who look inside trained models find heads with recognisable jobs: heads that attend to the previous token, heads that link a pronoun to its noun, and induction heads, which spot that a pattern appeared earlier in the text and predict that it will repeat.

Hands-on: attention from scratch

One query, four keys

This is the worked example from the table above, in code. Check every number against the table.

Python
import math

def softmax(xs):
    m = max(xs)                      # subtract the largest for numerical safety
    exps = [math.exp(x - m) for x in xs]
    total = sum(exps)
    return [e / total for e in exps]

words  = ["animal", "cross", "street", "it"]
keys   = [[2, 1], [0, 1], [1, -1], [1, 1]]
values = [[4, 0], [2, 2], [0, 4], [1, 1]]
query  = [2, 1]                      # the query of "it"

d_k = len(query)
scores = [sum(q * k for q, k in zip(query, key)) / math.sqrt(d_k) for key in keys]
weights = softmax(scores)
output = [sum(w * v[i] for w, v in zip(weights, values)) for i in range(2)]

for word, s, w in zip(words, scores, weights):
    print(f"{word:7} score {s:5.2f}  weight {w:.3f}  " + "#" * round(w * 40))
print("weights add up to", round(sum(weights), 6))
print("new vector for 'it':", [round(x, 2) for x in output])

Self-attention with matrices, with and without a mask

Every token gets a query, a key and a value from the same three matrices, and the whole attention matrix comes out at once. The weights here are random, so the pattern means nothing yet: training is what gives it meaning.

Python
import math, random

def matmul(A, B):
    return [[sum(a * b for a, b in zip(row, col)) for col in zip(*B)] for row in A]

def transpose(A):
    return [list(col) for col in zip(*A)]

def softmax(xs):
    m = max(xs)
    exps = [math.exp(x - m) for x in xs]
    return [e / sum(exps) for e in exps]

def self_attention(X, W_Q, W_K, W_V, causal=False):
    Q, K, V = matmul(X, W_Q), matmul(X, W_K), matmul(X, W_V)
    d_k = len(Q[0])
    scores = matmul(Q, transpose(K))
    for i in range(len(X)):
        for j in range(len(X)):
            scores[i][j] /= math.sqrt(d_k)
            if causal and j > i:
                scores[i][j] = float("-inf")    # e to the power of -inf is 0
    A = [softmax(row) for row in scores]
    return A, matmul(A, V)

random.seed(4)
tokens = ["the", "cat", "sat", "down"]
d_model, d_k = 4, 3
X = [[random.uniform(-1, 1) for _ in range(d_model)] for _ in tokens]
W_Q, W_K, W_V = ([[random.uniform(-1, 1) for _ in range(d_k)] for _ in range(d_model)]
                 for _ in range(3))

for causal in (False, True):
    A, out = self_attention(X, W_Q, W_K, W_V, causal)
    print("causal mask:", causal)
    print("        " + "".join(f"{t:>7}" for t in tokens))
    for t, row in zip(tokens, A):
        print(f"{t:>7} " + "".join(f"{w:7.2f}" for w in row) + f"   sum {sum(row):.2f}")
    print("output:", len(out), "tokens x", len(out[0]), "numbers")
    print()

With the mask, everything above the diagonal is 0, and "the" can only attend to itself. The last row is the same in both runs, because the last token may see everything either way.

Several heads

Python
import math, random

def matmul(A, B):
    return [[sum(a * b for a, b in zip(row, col)) for col in zip(*B)] for row in A]

def softmax(xs):
    m = max(xs)
    exps = [math.exp(x - m) for x in xs]
    return [e / sum(exps) for e in exps]

def head(X, d_head, seed):
    rnd = random.Random(seed)          # every head has its own weights
    W = [[[rnd.uniform(-1.5, 1.5) for _ in range(d_head)] for _ in X[0]] for _ in range(3)]
    Q, K, V = (matmul(X, w) for w in W)
    A = [softmax([sum(q * k for q, k in zip(Q[i], K[j])) / math.sqrt(d_head)
                  for j in range(len(X))]) for i in range(len(X))]
    return A, matmul(A, V)

random.seed(0)
tokens = ["she", "saw", "the", "bird"]
d_model, h = 6, 3
X = [[random.uniform(-1, 1) for _ in range(d_model)] for _ in tokens]
outputs = []
for n in range(h):
    A, out = head(X, d_model // h, seed=10 + n)
    outputs.append(out)
    focus = [tokens[max(range(len(tokens)), key=lambda j: A[i][j])] for i in range(len(tokens))]
    print(f"head {n + 1}: each token attends most to", dict(zip(tokens, focus)))
concat = [sum((o[i] for o in outputs), []) for i in range(len(tokens))]
print("each head gives", d_model // h, "numbers; joined:", len(concat[0]), "numbers per token")

Try this

  1. In the first program, change the query to [6, 3]. The scores triple. What happens to the weight on "animal", and why does that show the reason for scaling?
  2. Change the key of "street" to [2, 1] so it matches "animal". How do the weights and the output change?
  3. In the second program, count the zeros in the masked matrix. How many would there be for 10 tokens?

Level check

  • Explain why a word's meaning depends on its context, and how attention uses context
  • Describe the roles of queries, keys and values, and how each is made
  • Calculate attention weights from scores using softmax, and the blended output
  • Explain why scores are divided by √dk
  • Explain what a causal mask does and why generation needs it
  • Distinguish self-attention, masked self-attention and cross-attention
  • Describe how multi-head attention splits, computes and recombines

Key terms you must be able to define

Attention
A way for each token to build its new vector from a weighted mix of the information in other tokens.
Query
The vector a token uses to search the other tokens: what it is looking for.
Key
The vector each token is matched against queries with: what it offers.
Value
The information a token passes on when other tokens attend to it.
Scaled dot-product attention
softmax(Q × KT ÷ √dk) × V: dot-product scores, scaled, turned into weights, used to blend the values.
Attention weights
The softmax outputs: for each token, numbers between 0 and 1 that add up to 1.
Self-attention
Attention in which the queries, keys and values all come from the same sequence.
Cross-attention
Attention in which the queries come from one sequence and the keys and values from another.
Causal mask
Setting the scores for later positions to minus infinity, so no token can attend to the future.
Multi-head attention
Several attention heads with their own weights, run side by side and combined.
Head
One attention calculation inside multi-head attention, with its own WQ, WK and WV.

Flashcards

DefineAttention
AnswerA way for each token to build its new vector from a weighted mix of the information in other tokens.
DefineQuery
AnswerThe vector a token uses to search the other tokens: what it is looking for.
DefineKey
AnswerThe vector each token is matched against queries with: what it offers.
DefineValue
AnswerThe information a token passes on when other tokens attend to it.
DefineScaled dot-product attention
Answersoftmax(Q × KT ÷ √dk) × V: dot-product scores, scaled, turned into weights, used to blend the values.
DefineAttention weights
AnswerThe softmax outputs: for each token, numbers between 0 and 1 that add up to 1.
DefineSelf-attention
AnswerAttention in which the queries, keys and values all come from the same sequence.
DefineCross-attention
AnswerAttention in which the queries come from one sequence and the keys and values from another.

Go further: notes for future AI engineers

Attention as a soft dictionary

A Python dictionary does hard lookup: a key matches exactly or not at all, and you get one value back. Attention is a soft lookup: every key matches a little, and you get back a blend of all the values, weighted by how well each key matched. Because the blend is smooth, gradient descent can adjust the queries and keys, so the network learns what to look up.

The cost of attention

Every token attends to every token, so n tokens need an n × n attention matrix: double the context and the work quadruples. That quadratic cost long limited context windows to a few thousand tokens. FlashAttention (2022) computes exactly the same result in tiles that fit the GPU's fastest memory, and sliding-window and sparse attention let each token attend to only some of the others. Together with similar engineering, they are how modern models handle contexts of hundreds of thousands of tokens.

Misconceptions to avoid

Avoid these
  • Thinking someone sets the attention weights by hand. WQ, WK and WV are learned, so the model learns what to attend to.
  • Forgetting the softmax. Raw dot products are scores, not weights, until softmax makes them positive and add up to 1.
  • Mixing up keys and values. Keys decide how much attention a token gets; values are what it passes on.
  • Thinking a causal mask deletes tokens from the input. They are still there; only attention to later positions is blocked.
  • Assuming more heads means more parameters. Each head works on dmodel ÷ h numbers, so the total stays about the same.
Fun fact

In a recurrent network, information from the first word of a 1,000-word text has to survive 999 steps to reach the last word. With attention it takes one step, however far apart the two words are.

Knowledge check

0 of 10 answered
1In attention, what does a token's query represent?
2How is the score between a query and a key calculated?
3Why are the scores divided by √dk?
4After the softmax, what is true of one token's attention weights?
5Three scaled scores are 2, 0 and 0. Roughly what weight does softmax give the first token?
6What is the output of attention for one token?
7What does a causal mask do?
8In cross-attention in a translation model, where do the keys and values come from?
9Why does multi-head attention use several heads?
10A transformer has dmodel = 512 and 8 heads. How many numbers does each head's query have?
Score 0 / 10

Check you could explain this

10 marks
1Explain how scaled dot-product attention calculates the new vector for one token.4 marks
Explain

Mark scheme0 / 4
  • LThe token's query is compared with every key by a dot product, giving a score
  • LThe scores are divided by the square root of d_k
  • LSoftmax turns the scores into weights between 0 and 1 that add up to 1
  • LThe output is the weighted sum of the value vectors
Model answer

First the token's query vector is compared with the key vector of every token by taking dot products, which gives a score for each: the more the vectors point the same way, the higher the score. The scores are divided by the square root of the key length, d_k, so they do not grow too large. Softmax then turns the scores into weights between 0 and 1 that add up to 1. Finally each token's value vector is multiplied by its weight and the results are added, so the new vector is a weighted blend of the values, dominated by the most relevant tokens.

The marker looks for key phrases. If you said it another way and it is still right, give yourself the mark.

Think this answer or mark scheme is wrong? Report it

2Explain why a transformer that generates text needs a causal mask during training.3 marks
Explain

Mark scheme0 / 3
  • LIn training, the whole sequence is processed at once / in parallel
  • LWithout a mask a token could attend to later tokens, including the one it must predict, and copy it
  • LThe mask sets later scores to minus infinity so their weights are 0, matching generation where later tokens do not exist yet
Model answer

During training, a whole sentence is fed in at once so that every position can be trained in parallel. Without a mask, the token at each position could attend to the tokens after it, including the very token it is supposed to predict, so it would learn to copy rather than to predict. The causal mask sets every score for a later position to minus infinity before the softmax, which makes those weights exactly 0, so each token sees only earlier tokens, just as it will when generating, when later tokens do not exist yet.

The marker looks for key phrases. If you said it another way and it is still right, give yourself the mark.

Think this answer or mark scheme is wrong? Report it

3Describe the roles of the query, key and value vectors in attention.3 marks
Describe

Mark scheme0 / 3
  • LThe query represents what a token is looking for
  • LKeys are matched against the query to decide how relevant each token is / how much attention it gets
  • LValues carry the information that is passed on / blended into the output
  • LAll three are made by multiplying the token's embedding by learned weight matrices
Model answer

Each token's embedding is multiplied by three learned weight matrices to make a query, a key and a value. The query represents what the token is looking for. Every token's key is compared with the query, and how well they match decides how much attention that token receives. The values carry the actual information: the output is a blend of the values, weighted by those attention scores.

The marker looks for key phrases. If you said it another way and it is still right, give yourself the mark.

Think this answer or mark scheme is wrong? Report it

Machine mark 0 / 10