21

Attention

How does a word decide which other words to look at?

12 min

Attention

With voice. You can pause and jump to any step.

Lesson complete

Subtitles

Read this sentence: “I sat on the bank of the river.” Here, a bank is the land along a river.

Voice

No voice: the lesson advances on its own with subtitles.

The voice for this lesson is being recorded: for now it advances on its own with subtitles.

0:00 / 0:00

What you will achieve

  • Explain why a token needs to look at the others to make sense, and how attention decides whom to look at and how much.
  • Compute by hand, with two-number vectors, a dot product, the attention weights with softmax, and the blend of values.
  • Recognize what the formula softmax(QKᵀ/√d)V sums up, what several heads are for, and what all tokens at once gains and costs.

Key ideas

  1. A word can mean different things (“bank”), yet it enters the model with a single vector. To understand it you have to look at the others: that is what attention does.
  2. Each token produces a query (what it is looking for), a key (how it gets found), and a value (what it hands over). The dot product says how well a query and a key match.
  3. Softmax turns the scores into attention weights that add up to one hundred percent, and each token’s new vector is the blend of the values with those weights.
  4. It all sums up as softmax(QKᵀ/√d)·V. We divide by √d so that, with long vectors, softmax does not give all the weight to a single token.
  5. Several heads look at different things at once, and all tokens are computed at the same time: fast on a GPU, but the cost grows with the square of the text length.

What many people believe

Common belief An attention map shows why the model answered what it answered: what it “looked at” is its reason.

What is true It shows how one head, in one layer, spreads its weights. It is a clue about where information flows, but the answer depends on many heads and layers, and very different weights can give the same answer. Whether that counts as an explanation is still debated.

Check yourself

Four questions. No grade, no account: they just show whether the idea stuck.

ConceptIn attention, what role does a token’s key play?
CalculationOne token’s query is (2, 1) and another token’s key is (1, 3). What is their dot product?
ConceptWhy do we divide by √d before the softmax?
ApplicationA team goes from sending 4,000-token texts to 8,000-token texts, changing nothing else. What should it expect for the number of attention comparisons?

Under the hoodFor those who want the math and the code. You do not need it to follow the course.

The formula

For nn tokens, their queries and keys (with dkd_k numbers) and their values (with dvd_v) are stacked into matrices Q,K∈Rn×dkQ,K\in\mathbb{R}^{n\times d_k} and V∈Rn×dvV\in\mathbb{R}^{n\times d_v}:

Attention(Q,K,V)=softmax ⁣(QK⊤dk)V\text{Attention}(Q,K,V)=\text{softmax}\!\left(\frac{QK^{\top}}{\sqrt{d_k}}\right)V

Row by row: the score of token ii on token jj is sij=qi⋅kj/dks_{ij}=q_i\cdot k_j/\sqrt{d_k}; the weight is αij=esij/∑lesil\alpha_{ij}=e^{s_{ij}}/\sum_l e^{s_{il}} (each row adds up to 1) and the output is oi=∑jαij vjo_i=\sum_j\alpha_{ij}\,v_j. This is the “scaled dot-product attention” of Vaswani et al. (2017), who describe attention as a function that maps a query and a set of key-value pairs to an output: a weighted sum of the values.

In the scene, the query of “bank” is q=(1,1)q=(1,1) and the key of “river” is k=(1,0.8)k=(1,0.8): q⋅k=1⋅1+1⋅0.8=1.8q\cdot k=1\cdot1+1\cdot0.8=1.8. The scores 1.8, 0.7, 0, −1.61.8,\,0.7,\,0,\,-1.6 give e1.8≈6.0e^{1.8}\approx6.0, e0.7≈2.0e^{0.7}\approx2.0, e0=1e^{0}=1, and e−1.6≈0.2e^{-1.6}\approx0.2; the sum is ≈10\approx10, and the weights are 60%60\%, 20%20\%, 10%10\%, and 2%2\%. The scene does not divide by 2\sqrt{2} so the arithmetic can be followed in your head; with the division, the weights would be 46%46\%, 21%21\%, 13%13\%, and 4%4\%, and the new vector (0.63, 0.06)(0.63,\,0.06) instead of (0.75, 0.05)(0.75,\,0.05): the same side, less pronounced. All the numbers in the example are written by hand (see data.ts).

Where Q, K, and V come from

If XX is the matrix of input vectors (embeddings; in a transformer, plus position), Q=XWQQ=XW^Q, K=XWKK=XW^K, and V=XWVV=XW^V. Those three matrices are the parameters that are learned; the weights αij\alpha_{ij} are not: they are computed anew for each sentence. In self-attention, QQ, KK, and VV all come from the same XX. In the attention of Bahdanau et al. (2014), built for translation, the queries came from the decoder and the keys and values from the encoder, and the score was a small network, eij=a(si−1,hj)e_{ij}=a(s_{i-1},h_j), rather than a dot product; the weights were the same softmax and the result the same weighted sum ci=∑jαijhjc_i=\sum_j\alpha_{ij}h_j. In the authors’ words, the decoder decides which parts of the source sentence to pay attention to.

Why dk\sqrt{d_k}

If the components of qq and kk are independent with mean 0 and variance 1, the product q⋅k=∑i=1dkqikiq\cdot k=\sum_{i=1}^{d_k}q_ik_i has mean 0 and variance dkd_k (footnote 4 of Vaswani et al., 2017): its typical size is dk\sqrt{d_k}, eight when dk=64d_k=64. Dividing by dk\sqrt{d_k} brings the variance back to 1. Without it, softmax saturates: if α=softmax(s)\alpha=\text{softmax}(s), then ∂αi/∂sj=αi(δij−αj)\partial\alpha_i/\partial s_j=\alpha_i(\delta_{ij}-\alpha_j), which goes to 0 as one weight approaches 1 and the rest approach 0. That is the “slope that nearly vanishes” in the narration. With eight random keys and dk=64d_k=64, the largest weight averages 0.890.89 unscaled and 0.360.36 scaled (100 draws; the scene’s draw gives 0.980.98 and 0.350.35).

Several heads

MultiHead(Q,K,V)=Concat(head1,…,headh) WO,headi=Attention(XWiQ, XWiK, XWiV)\text{MultiHead}(Q,K,V)=\text{Concat}(\text{head}_1,\dots,\text{head}_h)\,W^O,\qquad \text{head}_i=\text{Attention}(XW_i^Q,\,XW_i^K,\,XW_i^V)

According to Vaswani et al. (2017), several heads let the model attend at once to information from different representation “subspaces”; with a single head, averaging inhibits this. In their base model, h=8h=8 and dk=dv=512/8=64d_k=d_v=512/8=64, so eight heads cost about the same as one big one. BERT base has 12 layers of 12 heads (144) with 768 dimensions, that is, 768/12=64768/12=64 per head (Devlin et al., 2018).

The lesson’s real maps

They were computed with real-attention.py (in the lesson folder; it runs on a CPU): bert-base-uncased for English and bert-base-multilingual-cased for Spanish, both Apache-2.0 with a pinned revision. They are the weights of a single head after softmax, not averaged; the pieces of a word are added up. The “bank” head was chosen like this: among the heads in which the largest weight of “bank” falls on the word that decides its meaning in both sentences (river and money), the one with the largest sum of those two weights. Only 6 of 144 heads meet that condition in English (layer 3, head 11: 93% and 60%) and 4 of 144 in Spanish (layer 1, head 4: 44% and 65%). By contrast, the largest weight of “bank” falls on a mark ([CLS], [SEP], or the period) in 113 of 144 heads in English and 58 of 144 in Spanish. The other three are the heads with the largest average weight on the previous word, on the next word, and on the marks. Clark et al. (2019) describe these same kinds of heads in BERT: heads that attend to delimiter tokens, to a fixed positional offset, and broadly over the whole sentence.

Cost

The weight matrix is n×nn\times n: the compute per layer grows as O(n2⋅d)O(n^2\cdot d). A recurrent layer costs O(n⋅d2)O(n\cdot d^2), but it needs O(n)O(n) steps one after another, and information between two positions travels a path of length O(n)O(n); in self-attention both are O(1)O(1) (Table 1 of Vaswani et al., 2017). That is why attention makes good use of a GPU, and why long contexts are expensive.

In code

import numpy as np

def softmax(x):
    e = np.exp(x - x.max(axis=-1, keepdims=True))
    return e / e.sum(axis=-1, keepdims=True)

def attention(Q, K, V, scale=True):
    d = K.shape[-1]
    scores = Q @ K.T                     # dot products: how well each query matches each key
    if scale:
        scores = scores / np.sqrt(d)     # the piece the scene leaves for the end
    weights = softmax(scores)            # each row adds up to 1
    return weights @ V, weights          # weighted blend of the values

# "I sat on the bank of the river": the scene's numbers (d = 2)
K = np.array([[-1.6, 0], [-0.3, 1], [-0.8, -0.8], [-0.8, -0.8],
              [0.5, -0.5], [-0.8, -0.8], [-0.8, -0.8], [1, 0.8]])
V = np.array([[0, 0], [0.5, 0], [0, 0], [0, 0],
              [0.5, 0.5], [0, 0], [0, 0], [1, 0]])     # (riverbank, money)
q = np.array([[1.0, 1.0]])                          # the query of "bank"

out, w = attention(q, K, V, scale=False)
print(np.round(w, 2), np.round(out, 2))
out, w = attention(q, K, V)                          # with the division by √2
print(np.round(w, 2), np.round(out, 2))
# [[0.02 0.2  0.02 0.02 0.1  0.02 0.02 0.6 ]] [[0.75 0.05]]
# [[0.04 0.21 0.04 0.04 0.13 0.04 0.04 0.46]] [[0.63 0.06]]

And the check of d\sqrt{d}: the standard deviation of q⋅kq\cdot k grows as d\sqrt{d}; after dividing, it stays at 1.

rng = np.random.default_rng(0)
for d in (1, 2, 64, 512):
    q = rng.standard_normal((20000, d))
    k = rng.standard_normal((20000, d))
    s = (q * k).sum(axis=1)                         # 20,000 dot products q·k
    print(d, round(s.std(), 1), round((s / np.sqrt(d)).std(), 2))
# 1 1.0 1.0
# 2 1.4 1.0
# 64 8.0 1.0
# 512 22.6 1.0

What comes next

What attention alone does not do: it does not know word order (shuffle the words and each token spreads the same weights over the same tokens), and when writing text each token may only look back (−∞-\infty is placed on the scores that look at the future, before the softmax). With a position encoding, that mask, a network per token, residual connections, and normalization, blocks are stacked: the transformer, in lesson 22.

Decision lens

What this idea means if your job is to decide, not to code.

  • Every token is compared with all the others: the cost of attention grows with the square of the context. Doubling the text costs about four times as many comparisons; it is one of the reasons long contexts are expensive (lessons 26 and 30).
  • An attention map is not an explanation. If a vendor shows “what the model looked at” as the justification for an answer, also ask for tests with your own cases and quality measures (lessons 33 and 34).
  • Everything in the context can influence everything else: the architecture does not tell your instruction apart from a stranger’s document on its own. Hence the risk of prompt injection: separate and validate what comes in (lesson 35).
  • More context does not guarantee more understanding: with more tokens, attention spreads across more candidates. Test with your own long documents and do not assume the model uses everything equally (lesson 30).

Terms in this lesson

Full transcript
CommentsThis lesson’s public conversation: questions, facts to check, ideas.

Comments live in GitHub Discussions, in the course’s public repository; writing one requires a GitHub account. They load from giscus.app when you open this section. The course reviewers read them, and if a change is accepted, it is applied in both languages. Open the thread on GitHub

Sources