Self-Attention in Transformers: Explained Visually

Updated 02 Nov 202611 min readLevel: Intermediate2 interactive visuals

Self-attention is the idea that made Transformers, ChatGPT and every modern LLM possible. It lets each word in a sentence look at every other word and decide which ones matter for understanding it. In "The animal didn't cross the street because it was too tired", self-attention helps the model work out that it means the animal, not the street.

What you will learn
  • What problem attention solves that RNNs could not
  • Query, Key and Value, explained with a simple analogy
  • The attention formula, computed number by number
  • Why we divide by √dₖ
  • Multi-head attention, causal masking and positional encoding
  • Self-attention in NumPy and PyTorch
Contents
  1. Why do we need attention?
  2. Self-attention in action
  3. Query, Key and Value: the core idea
  4. The attention formula
  5. Why divide by √dₖ?
  6. Self-attention in NumPy
  7. Multi-head attention
  8. Masked (causal) self-attention
  9. Positional encoding: where is each word?
  10. Self-attention vs RNN

Why do we need attention?

Before 2017, language models used RNNs and LSTMs. They read one word at a time and squeezed everything into a single memory vector. That caused two problems:

  1. Long sentences were forgotten. Information from the beginning faded by the end.
  2. Training was slow. Word 50 could not be processed until words 1 to 49 were done, so GPUs could not work in parallel.

The paper "Attention Is All You Need" (Vaswani et al., 2017) removed recurrence completely. In a Transformer, every word connects directly to every other word in one step, and all words are processed at the same time.

Self-attention in action

Click any word below to see how much attention it pays to each other word. Then switch the last word between tired and wide and click it again.

When the sentence ends in tired, it attends to animal (animals get tired). When it ends in wide, it attends to street (streets are wide). The word "it" has no fixed meaning. Attention builds its meaning from context, and this is why Transformers understand language so well.

Query, Key and Value: the core idea

For every word, self-attention creates three vectors by multiplying the word's embedding with three learned weight matrices:

Vector Made by Question it answers
Query (Q) x · WQ "What am I looking for?"
Key (K) x · WK "What do I contain? How should others find me?"
Value (V) x · WV "If you pick me, what information do I pass on?"
A library search

You go to a library with a query: "books about cricket". Each book has a key on its spine: its title and topic. You compare your query with every key and find the best matches. Then you read the value, the content inside those books. Self-attention does exactly this, for every word at the same time, with numbers instead of titles.

The attention formula

All of self-attention fits in one line:

Attention(Q, K, V) = softmax( Q · KT / √dk ) · V

Read it from the inside out:

  1. Q · KT: compare every query with every key using a dot product. A large dot product means a good match. For n words, this gives an n × n table of scores.
  2. ÷ √dk: scale the scores down (dk is the size of each key vector).
  3. softmax: turn each row of scores into percentages that add up to 1. These are the attention weights.
  4. · V: each word's output is the weighted average of all value vectors.

Step through the exact calculation for a three-word sentence. Every number below is computed live:

Why divide by √dₖ?

A dot product adds up dk multiplications. If each number in Q and K has a variance of about 1, the dot product has a variance of about dk. With dk = 64, scores can easily be ±20.

Softmax of large numbers is extreme: one word gets almost 100% and the rest get almost 0%. In that region the softmax curve is flat, so its gradient is nearly zero and the model stops learning. Dividing by √dk (8 for dk = 64) brings the variance back to about 1 and keeps the softmax soft.

See it in the visual

In the first visual, move the softmax sharpness slider to 3×. Attention snaps onto one word. That is what happens without scaling. At 1× the weights stay spread out and learnable.

Self-attention in NumPy

This is the same three-word example as the step-through visual above.

PYTHON
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)

words = ["I", "love", "AI"]
X = np.array([[1, 0, 1, 0],      # "I"
              [0, 2, 0, 1],      # "love"
              [1, 1, 1, 1]])     # "AI"

W_q = np.array([[1, 0], [0, 1], [1, 0], [0, 1]])
W_k = np.array([[0, 1], [1, 0], [0, 1], [1, 0]])
W_v = np.array([[1, 0], [0, 1], [1, 1], [0, 0]])

Q, K, V = X @ W_q, X @ W_k, X @ W_v
d_k = K.shape[-1]
scores = Q @ K.T / np.sqrt(d_k)          # how well each query matches each key
weights = softmax(scores)                # each row sums to 1
output = weights @ V                     # weighted mix of values

print("Attention weights:\n", weights.round(2))
print("Output:\n", output.round(2))
OUTPUT
Attention weights:
 [[0.01 0.8  0.19]
 [0.5  0.01 0.5 ]
 [0.05 0.19 0.77]]
Output:
 [[0.41 1.99]
 [1.99 1.5 ]
 [1.63 1.95]]

Read the first row: the word I puts 80% of its attention on love and 19% on AI. Its new vector [0.41, 1.99] is mostly made of the value of "love". In a real model, the weight matrices are learned during training, so these patterns become meaningful.

Multi-head attention

One attention calculation can only focus on one kind of relationship at a time. Language has many: grammar (subject and verb), meaning (it and animal), position (neighbouring words) and more. So Transformers run several attention heads in parallel, each with its own WQ, WK and WV:

MultiHead(Q, K, V) = Concat( head1, …, headh ) · WO

In the original Transformer, dmodel = 512 and there are h = 8 heads, so each head works with dk = 512 / 8 = 64 numbers. The total cost is about the same as one big head, but the model gets 8 different points of view.

PYTHON
import torch
import torch.nn as nn

mha = nn.MultiheadAttention(embed_dim=512, num_heads=8, batch_first=True)
x = torch.randn(2, 10, 512)                  # 2 sentences, 10 tokens each
out, attn = mha(x, x, x)                     # self-attention: Q, K, V all come from x

print(out.shape)
print(attn.shape)                            # averaged over heads by default
print(sum(p.numel() for p in mha.parameters()))
OUTPUT
torch.Size([2, 10, 512])
torch.Size([2, 10, 10])
1050624

The parameter count is 4 matrices of 512 × 512 (Q, K, V and output) plus their biases: 4 × 262,144 + 4 × 512 = 1,050,624. The attention map is 10 × 10 because every token attends to every token.

Masked (causal) self-attention

A model like GPT writes text one token at a time. When it predicts word 5, it must not peek at words 6, 7 and 8. A causal mask sets the scores for all future positions to −∞ before the softmax, so their weights become exactly 0:

Query ↓ / Key → The cat sat down
The sees masked masked masked
cat sees sees masked masked
sat sees sees sees masked
down sees sees sees sees

Encoder models such as BERT use no mask (every word sees both sides). Decoder models such as GPT, Llama and Claude use the causal mask.

Positional encoding: where is each word?

Self-attention treats a sentence like a bag of words. Without extra help, "dog bites man" and "man bites dog" produce the same set of attention scores. Transformers fix this by adding position information to each word's embedding before attention:

  • Sinusoidal encoding (original Transformer): fixed sine and cosine waves of different frequencies.
  • Learned position embeddings (BERT, GPT-2): one learned vector per position.
  • Rotary position embedding, RoPE (Llama and most modern LLMs): rotates Q and K by an angle that depends on position, so attention scores depend on the distance between words.

Self-attention vs RNN

Feature RNN / LSTM Self-attention
Path between word 1 and word 100 99 steps 1 step
Parallel training No Yes
Long-range dependencies Fade over time Direct connection
Cost per layer O(n · d²) O(n² · d)
Memory for long inputs Small Grows with n²

That last row is the main weakness of attention: doubling the context length makes the score table four times bigger. Techniques such as FlashAttention, sliding-window attention and KV caching are used to make long-context LLMs practical.

Key takeaways
  • Self-attention lets each token look at every token and decide what matters, in one step.
  • Each token gets a Query, a Key and a Value. Scores = Q·KT, scaled by √dk, softmaxed, then used to mix the Values.
  • Scaling keeps softmax from saturating, so gradients keep flowing.
  • Multi-head attention runs several attention patterns in parallel; causal masking stops decoders from seeing the future.
  • Positional encoding gives the order information that attention alone does not have.
  • The cost grows with n², which is why long context windows are expensive.

Test yourself

Score: 0 / 5

1. In self-attention, what does the Query vector represent?

The Query is compared against every Key. The Value (not the Query) is the content that gets passed on.

2. Why are attention scores divided by √dₖ?

Dot products grow with dₖ. Scaling keeps the variance near 1, so softmax stays in a range where it can still learn.

3. A sentence has 1,000 tokens. How many attention scores does one head compute?

Every token attends to every token: 1,000 × 1,000 = 1,000,000 scores. This n² cost is attention's main weakness.

4. Which models use a causal mask?

Decoders must not see future tokens when predicting the next one. Encoders like BERT see the whole sentence.

5. What does multi-head attention add?

Each head has its own Q, K and V matrices. Their outputs are joined and projected with W_O.

Frequently asked questions

What is the difference between attention and self-attention?

In general attention, the queries come from one sequence and the keys and values from another (for example, a translation decoder attending to the encoded English sentence). In self-attention, queries, keys and values all come from the same sequence, so the sentence attends to itself.

Is self-attention the same as a Transformer?

No. Self-attention is one part of a Transformer block. A full block also has a feed-forward network, residual connections and layer normalisation, and many blocks are stacked on top of each other.

Why do we need separate Key and Value vectors?

The Key decides whether a token is relevant; the Value decides what it contributes. Separating them lets a token be easy to find for one reason but share different information, which makes attention much more flexible.

What is cross-attention?

Cross-attention is attention where the Queries come from one sequence and the Keys and Values come from another. It connects the decoder to the encoder in translation models, and connects text to images in models like Stable Diffusion.

What is the KV cache in LLMs?

When an LLM generates text, the Keys and Values of earlier tokens do not change. The KV cache stores them so each new token only needs to compute its own Query, Key and Value. This makes generation much faster, but uses a lot of GPU memory for long contexts.

Prefer watching?

Real interview walkthroughs and AI explained simply on our YouTube channel.

Subscribe on YouTube