Attention: the intuition
Each token decides which other tokens matter to it.
Create a free account Try part of it here
Why it matters
"Attention is all you need" is the title of the 2017 paper that introduced the transformer. Attention is the part that lets a language model link words across a text. It connects "it" to the right noun. It ties a pronoun to a name three paragraphs back. It carries an instruction at the top of a prompt down to the answer at the bottom. This lesson builds the idea with a tiny example you can check by hand. Then you'll watch it work on real words. The full formulas come over the next few lessons.
What you'll be able to do
- Explain why a word's meaning depends on other words, possibly far away
- Describe attention as a weighted average where each token chooses its own weights
- Compute content-based attention weights from similarity and a softmax
- Say what attention fixes compared with fixed windows and recurrent networks
What it covers
- Meaning comes from context
- Attention in three steps
- Worked example: "dry" next to "jacket"
- Same word, new sentence
- Seeing it
- What real attention adds
Meaning comes from context
Read these two sentences.
- "The river bank flooded."
- "The bank approved the loan."
The word "bank" is the same in both. Its meaning isn't. You can tell which bank is meant from the other words around it. Here the clue sits right next to it. In real text, the clue can be anywhere: one word away, or three paragraphs back.
A group chat works the same way. Someone writes "Sounds good, see you at 7." To know what they agreed to, you scroll up to the message they're answering. It might be the one just before. It might be from an hour ago.
A language model has the same job. To understand a token, it must pull in facts from whichever other tokens matter. Those can be near or far.
Two earlier tries
The last lesson's model looked through a fixed window. It saw the last few characters, with a separate set of weights for each slot. Anything outside the window was invisible.
Recurrent networks, from Phase 4, read one token at a time. They squeezed everything so far into one vector, a single list of numbers. Over a long text, early details faded.
Attention takes a different route. Each token looks at all the tokens directly. It decides how much each one matters to it, based on what the tokens contain. Then it blends their information in.
Blending advice from friends
Say you ask two friends whether a new restaurant is any good. One is a chef. The other mostly eats takeout. The chef gives it 9 out of 10. The other friend gives it 5. You trust the chef more on food. So you let the chef's view count for 75% of your opinion, and the other friend's for 25%.
0.75 × 9 + 0.25 × 5 = 6.75 + 1.25 = 8
That's a weighted average. Each value counts in proportion to its weight, and the weights add up to 1. Attention does this for every token. The "friends" are the tokens in the text. The weights say how relevant each one is, and each token works them out from what the tokens contain.
print(0.5 * 6 + 0.5 * 10)
Show the answer
8.0
0.5 × 6 + 0.5 × 10 is 3 + 5, which prints as 8.0. Equal weights of 0.5 give the plain middle. Attention does the same, with weights it works out for each token.
Attention in three steps
Pick one token. Call its position : it's the token doing the looking. Any token it looks at sits at a position called . Token looks at every position, itself included.
- Score. Give every token a score for how relevant it is to token . In this lesson the score is the dot product of their two vectors, from Phase 2. Vectors that point in similar directions score high.
- Normalize. Run the scores through a softmax, also from Phase 2. It turns them into weights that are all positive and add up to 1.
- Mix. Token 's new vector is the weighted average of all the tokens' vectors.
Here are steps 2 and 3 as formulas. First, the symbols, one at a time.
- is the vector of token : its embedding, a list of numbers.
- is the score token gives token in step 1.
- is the weight token gives token after the softmax.
- is token 's new vector, after attention.
- is the number 2.718..., the base of the exponential that softmax uses.
- (the Greek letter sigma) means "add these up over every position".
- The letter under the sigma names the position that changes as you add. Here it's or .
In plain words: on the left, take token 's score for token and raise to that power. That always gives a positive number. Then divide by the same thing added up over all tokens. That makes the weights add up to 1. On the right, multiply each token's vector by its weight, and add them all up. That's token 's new vector.
The weights aren't learned parameters fixed in advance. They're recomputed for every input. So the same token attends to different things in different sentences. The worked example below runs all three steps on three words, with numbers small enough to check by hand.
Pick "bank": it leans on "river", which is how it knows this isn't about money. These scores are illustrative, but the percentages are a real softmax.
Show the answer
A score computed from the content of both tokens, normalized with a softmax
Attention weights are recomputed for every input from the tokens themselves.
Worked example: "dry" next to "jacket"
Take the phrase "the dry jacket". Give each word a vector of two numbers. The first number means "rain gear". The second means "food".
- "the" is
[0, 0]: no topic at all. - "dry" is
[1, 1]: it could be rain gear (a dry jacket) or food (dry toast). - "jacket" is
[2, 0]: pure rain gear.
Follow "dry" through the three steps by hand.
- Score with the dot product. "the": 1 × 0 + 1 × 0 = 0. "dry": 1 × 1 + 1 × 1 = 2. "jacket": 1 × 2 + 1 × 0 = 2.
- Normalize with softmax. is 1 and is about 7.39. The total is about 15.78. So the weights are about 0.06, 0.47 and 0.47.
- Mix. 0.06 ×
[0, 0]+ 0.47 ×[1, 1]+ 0.47 ×[2, 0]comes to about[1.4, 0.47].
The code does the same thing with plain loops.
import math the, dry, jacket = [0, 0], [1, 1], [2, 0] words = [the, dry, jacket] # Step 1, score: how relevant is each word to "dry"? scores = [dry[0] * w[0] + dry[1] * w[1] for w in words] print("scores: ", scores) # Step 2, normalize: softmax turns the scores into weights exps = [math.exp(s) for s in scores] weights = [e / sum(exps) for e in exps] print("weights:", [round(w, 2) for w in weights]) # Step 3, mix: the weighted average of the word vectors new_dry = [sum(weights[j] * words[j][k] for j in range(3)) for k in range(2)] print("new dry:", [round(v, 2) for v in new_dry])
"dry" started at [1, 1], an even split between rain gear and food. After attention it's about [1.4, 0.47], so it now leans toward rain gear. It took most of its new meaning from "jacket". "the" got the lowest score and the smallest weight, and its vector is all zeros, so it added nothing.
Same word, new sentence
Now swap "jacket" for "toast", which is [0, 2]: pure food. This time the code uses NumPy and does every word at once.
X holds one row per word. X @ X.T is matrix multiplication, from Phase 2. It works out the dot product of every pair of words in one go. Row , column of the result is the score . The next line is the softmax for each row. W @ X then does the mixing step for every word.
import numpy as np X = np.array([[0.0, 0.0], # the [1.0, 1.0], # dry [0.0, 2.0]]) # toast scores = X @ X.T # every word scores every word W = np.exp(scores) / np.exp(scores).sum(axis=1, keepdims=True) new = W @ X # every word's mixed vector print("weights for dry:", W[1].round(2)) print("new dry:", new[1].round(2))
The weights for "dry" come out the same as before: 0.06, 0.47, 0.47. But the vectors it blends are different. So "dry" ends up at [0.47, 1.4], leaning toward food. Same word, same starting vector, a different meaning in a different sentence. That's attention's whole point.
import numpy as np scores = np.array([1.0, 1.0, 1.0]) w = np.exp(scores) / np.exp(scores).sum() print(np.round(w, 2))
Show the answer
[0.33 0.33 0.33]
Softmax only cares about the gaps between scores. Equal scores mean no gaps, so each token gets an equal share of 1 / 3, which rounds to 0.33, and the three shares add up to 1.
Seeing it
Now try real words. The code below embeds each word of a sentence with the practice embedding model. Its 24 dimensions are topics, much like "rain gear" and "food" above. Then every word attends to every word.
Two details in the code are new.
* 4sharpens the scores. Multiplying every score by 4 widens the gaps between them. So softmax hands more weight to the top few words and less to the rest.plt.imshowdraws a heatmap. It's a grid of colored squares. Each square shows one weight. Brighter means more attention.
import numpy as np import requests import matplotlib.pyplot as plt words = "the waterproof jacket kept me dry on a wet muddy trail run".split() data = requests.post("https://api.gradient.test/v1/embeddings", json={"input": words}).json()["data"] X = np.array([d["embedding"] for d in data]) # 12 words × 24 dimensions scores = X @ X.T * 4 # similarity of every pair (× 4 to sharpen) W = np.exp(scores - scores.max(axis=1, keepdims=True)) W /= W.sum(axis=1, keepdims=True) # softmax: each row sums to 1 for w in ["jacket", "trail"]: i = words.index(w) top = np.argsort(-W[i])[:4] print(f"{w!r} attends to: " + ", ".join(f"{words[j]} {W[i, j]:.2f}" for j in top)) new = W @ X # each word's context-mixed vector print("\nshape before and after attention:", X.shape, new.shape) plt.imshow(W, cmap="viridis") plt.xticks(range(len(words)), words, rotation=60, fontsize=8) plt.yticks(range(len(words)), words, fontsize=8) plt.xlabel("attends to") plt.ylabel("word")
Each row of the heatmap is one word's attention. The rain-gear words (waterproof, jacket, dry, wet) attend to each other. "Trail" and "run" attend to each other. The filler words, which this practice model can't tell apart, spread their attention among themselves.
The shape line shows that attention doesn't change the size of anything. There are 12 words with 24 numbers each, before and after. What changes is what's in those numbers. The vector for "dry" now carries information from "waterproof", "jacket" and "wet". It means "dry" in the context of rain gear.
Go deeper Why the code subtracts the biggest score
Softmax uses raised to each score. For a large score, that number can get too big for the computer to store, and the result breaks. So the code subtracts each row's biggest score from every score in that row first.
This doesn't change the weights, because softmax only cares about the gaps between scores. The scores [2, 0] and [0, -2] have the same gap. Both give the weights [0.88, 0.12].
Show the answer
Their embeddings point in similar directions, both about rain gear, so their dot product scores high
In this toy, the score is plain similarity. "jacket" and "wet" aren't neighbors, but they share a topic, so they score high and get large weights.
What real attention adds
This toy uses raw embeddings and plain similarity. So it can only link words about the same topic. Real transformer attention, coming up over the next lessons, adds four things.
- It learns what to look for. It uses three learned projections, called queries, keys and values. A projection is a learned matrix that turns each vector into a new one. With them, "it" can look for a noun, a verb can look for its subject, and a closing bracket can look for the opening one. Those links aren't simple similarity.
- It has several heads. Each head is a separate attention pattern, and they run side by side. Each one is free to learn a different kind of link.
- It can't look ahead in a language model. The model's job is to predict the next token, so it must not peek at it. A causal mask hides every later token.
- It knows word order. Attention on its own can't tell which token came first. Positional information, added to the embeddings, tells it.
Why it won
- Any distance, one step. Token 1 and token 1,000 are linked directly. In a recurrent network, information passes through a long chain of hidden states and fades along the way. So do the gradients that train the model.
- All at once. Every position is computed at the same time. That suits GPUs, which are built for parallel work. A recurrent network has to finish each step before it starts the next.
- Driven by content. What gets blended depends on the input. So one mechanism handles countless kinds of links.
The price
Every token scores every other token. With 10 tokens, that's 10 × 10 = 100 scores. With 1,000 tokens, it's 1,000 × 1,000, a million scores. Double the text and the work goes up four times. In other words, the work grows with the square of the sequence length. That's the problem the long-context lesson tackles.
For marketers: say a model follows an instruction from the top of a long brief while it writes the last paragraph. Attention is what carries that instruction across the gap.
Show the answer
About 4 times as much
Every token scores every token. 2,000 × 2,000 is 4 million scores, against 1 million before: four times the work.
Practice what you just read
- 3 code examples you can edit and run
- a practice exercise that checks your code
- a 5-question quiz
- 4 review cards for spaced repetition
Try a quiz question
After attention, what is token i's new vector?