Concept units

Cross-attention: reading from another set

Advanced

Read these first if they are new to you

The idea in one paragraph

Cross-attention is attention between two sets of tokens. The tokens being updated, set A, make the queries. The tokens being read, set B, make the keys and values. Each A token gets a weighted sum of B's values, weighted by how well B's keys match its own query. Set B is often a condition: the words of a caption, say, or the points of the part of a shape you can see. The output has one vector per A token, however many tokens B has.

Queries come from one set; keys and values come from the other

In self-attention, one set of tokens makes all three vectors. In cross-attention, the roles split. For each token a of set A and each token b of set B, learned matrices make

q = WQa,   k = WKb,   v = WVb

Stack A's n queries as the rows of Q, and B's m keys and values as the rows of K and V. The formula is the same as before:

CrossAttention = softmax( QKT / √dk ) V

Only the shapes change, because n and m can differ:

MatrixMade fromRowsColumns
Q, the queriesset Andk
K, the keysset Bmdk
V, the valuesset Bmdv
the weights, softmax(QKT / √dk)bothnm
the outputbothndv

Each row of the weights belongs to one A token and adds up to 1. The output has n rows: one new vector per A token. Set B is read, not changed. Self-attention is the special case where A and B are the same set.

Worked by hand: two tokens read from three

Set A has two tokens with queries q1 = [1, 0] and q2 = [0, 2]. Set B has three tokens with keys [2, 0], [0, 1] and [1, 1], and values [6, 0], [0, 6] and [3, 3]. The key size is dk = 2.

Predict first. Which B token will A's first token read most? Which will the second read most?

Worked example: the scores, the weights and the outputs
  1. Scores q · k: for q1, [2, 0, 1]; for q2, [0, 2, 2].
  2. Divide by √2: [1.414, 0, 0.707] and [0, 1.414, 1.414].
  3. Softmax of each row: [0.576, 0.140, 0.284] and [0.108, 0.446, 0.446].
  4. Weighted sums of the values. First token: 0.576 × [6, 0] + 0.140 × [0, 6] + 0.284 × [3, 3] = [4.31, 1.69]. Second token: 0.108 × [6, 0] + 0.446 × [0, 6] + 0.446 × [3, 3] = [1.99, 4.01].

The first A token reads mostly B's first token. The second splits its attention evenly between B's second and third tokens, whose keys match its query equally well.

Each A token spreads one unit of weight over the B tokens
set A: queries set B: keys, values 0.58 0.14 0.28 0.11 0.45 0.45 a1 a2 q [1, 0] q [0, 2] b1k [2, 0] b2k [0, 1] b3k [1, 1]
What to notice. Line width shows the weight. Each A token's three weights add up to 1, but a B token's incoming weights need not: B's first token gets 0.58 + 0.11 = 0.69 in total. A tokens are not joined to each other; cross-attention gives them no way to read one another.

The order of set B does not matter, and its size can change

Each A token takes a weighted sum over all of B, and a sum does not depend on the order of its terms. So reordering B's tokens leaves every output the same: cross-attention is permutation invariant in B. Reordering A's tokens reorders the outputs in the same way. Set B can also hold 3 tokens or 3,000; the output still has one vector per A token.

cross_attention.py

import numpy as np

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

def cross_attention(Qa, Kb, Vb):
    w = softmax(Qa @ Kb.T / np.sqrt(Qa.shape[-1]))   # n rows, m columns
    return w @ Vb, w

Qa = np.array([[1.0, 0.0], [0.0, 2.0]])               # queries from set A
Kb = np.array([[2.0, 0.0], [0.0, 1.0], [1.0, 1.0]])   # keys from set B
Vb = np.array([[6.0, 0.0], [0.0, 6.0], [3.0, 3.0]])   # values from set B
out, w = cross_attention(Qa, Kb, Vb)
print(np.round(w, 3))
print(np.round(out, 2))

perm = [2, 0, 1]                                      # reorder set B
# np.allclose is True when two arrays match, up to tiny rounding errors
print(np.allclose(cross_attention(Qa, Kb[perm], Vb[perm])[0], out))
[[0.576 0.14  0.284]
 [0.108 0.446 0.446]]
[[4.31 1.69]
 [1.99 4.01]]
True

A model uses cross-attention to let each token choose what to read

A common block for set A runs three steps, each with a layer normalization and a residual connection: self-attention, so A's tokens can coordinate with each other; cross-attention, so each A token can read the condition B; and an MLP. The original transformer for translation worked this way: the words of the sentence being written read the words of the sentence being translated. The transformer block unit shows this pattern without the cross-attention step.

There are two other common ways to bring a condition in. A model can join A and B into one set and run self-attention over all n + m tokens. Or it can squeeze B into one summary vector and add it to every A token, which is cheap but gives every token the same information. Cross-attention lets each A token pick the parts of B it needs, at a cost of n × m scores.

Cross-attention can also shrink a large set. A model can learn a handful of query tokens that read a set of thousands of tokens, and so get a fixed-size summary of it. The Perceiver model works this way; PointNet pooling is a simpler order-free summary.

What this page leaves out

Like self-attention, cross-attention usually has several heads, and each projection usually has a bias. When the B sets in a batch have different sizes, libraries pad the short ones with dummy tokens and use a mask that sets the scores of the dummies to −∞, so they get zero weight.

Check yourself

  1. Set A has 1,000 tokens and set B has 16. What shape is the table of weights, and how many output vectors are there?

    Answer

    The weights form a table of 1,000 rows and 16 columns: one row per A token, one column per B token. There are 1,000 output vectors, one per A token.

  2. Work a similar problem. One A token has query [2, 0]. Set B has keys [1, 0] and [−1, 0], with the one-number values 5 and −5. What is the output?

    Answer

    Scores 2 and −2; divided by √2: 1.414 and −1.414. Softmax: e1.414 = 4.113 and e−1.414 = 0.243, so the weights are 0.944 and 0.056. Output: 0.944 × 5 + 0.056 × (−5) = 4.44.

  3. You shuffle set B, then separately shuffle set A. What happens to the outputs each time?

    Answer

    Shuffling B changes nothing: each output is a sum over all of B. Shuffling A shuffles the outputs in the same way, since each A token still gets its own output.

  4. Can one A token learn anything about another A token through a cross-attention layer?

    Answer

    No. Its keys and values come only from B. Information passes between A tokens in self-attention layers, which models place before or after the cross-attention.

Go further

Optional background, for readers who want more than this page.

Where this shows up

No unit or explanation builds on this one yet.