Deep Learning

Transformers

A transformer reads every word at once and lets each word decide which other words matter to it, which is the architecture behind almost every modern AI model.

On this page 7
  1. Why this had to be invented
  2. How it works
  3. The three roles every word plays
  4. Where you have already seen this
  5. What is honestly hard here
  6. Remember this
  7. What to learn next

One lesson, three depths. Pick the one that fits you today — you can switch any time.

Beginner — No maths. Plain English.

A transformer reads a whole sentence at once. It lets every word decide which other words matter to it.

Think about standing in a crowded wedding hall. Fifty conversations are going on around you, plus a band. Yet when your cousin says your name from across the room, you hear it.

Your ears received all of that sound. Your brain chose what to pay attention to and pushed the rest into the background.

That choosing is the entire idea behind a transformer. Every word looks at every other word and decides which ones it needs.

Why this had to be invented

Earlier language models read a sentence one word at a time, left to right. Picture reading with a finger under the line.

Two things went wrong with that.

The beginning faded. By the end of a long paragraph, the model had a blurry memory of how it started. Details from twenty words back were mostly gone.

It could not be sped up. Word ten could not be processed until word nine was done. Computers are good at doing thousands of things at once, and this design forbade it. Training on large amounts of text took far too long.

Transformers fixed both at once. Every word is processed at the same moment. Every word can also look directly at every other word, however far away.

How it works

Take the sentence "the mango is ripe". The word "ripe" needs to know what is ripe. It looks around and finds "mango".

   the      mango      is      ripe
    │         │        │        │
    └─────────┴────────┴────────┘
         every word looks at every word

   "ripe" pays attention like this:
      the    ▏░                      10%
      mango  ▏████████████████       71%
      is     ▏░                      10%
      ripe   ▏░                      10%

Now "ripe" carries some mango-meaning inside it. It is no longer a lonely word — it is a word that knows what it describes.

Stack this a few dozen times. Each round lets meaning spread a little further, and connections build up between distant parts of the text.

The three roles every word plays

This part has odd names, so here they are in plain terms. Each word produces three things:

  • A query — what am I looking for? ("ripe" is looking for the thing it describes)
  • A key — what do I offer? ("mango" offers: I am the fruit here)
  • A value — what do I hand over if you pick me? (the actual meaning of "mango")

Every query is compared against every key. Strong matches get a large share of attention, and the winners hand over their values.

The names come from databases and are honestly not the clearest choice. Think of a room full of people. Everyone announces what they need, and what they have. Useful pairs then find each other.

Where you have already seen this

  • ChatGPT, Claude and Gemini are transformers. Every one of them.
  • Google Translate switched to this design and got noticeably better.
  • Google Search uses one to understand what your question means.
  • GitHub Copilot and phone keyboard suggestions use them too.

The word "GPT" ends in T for Transformer. This is the architecture underneath the entire current wave of AI.

What is honestly hard here

Attention is quick to describe and slow to truly absorb. The query, key and value idea feels arbitrary at first, and that reaction is reasonable — it is somewhat arbitrary. It was engineered because it worked, not derived from a principle.

There is also a real cost. Because every word looks at every other word, doubling the text length makes the work roughly four times greater. That is why chatbots have a limit on how much you can paste in. It is a genuine constraint, not a business decision.

Remember this

  • A transformer reads all words at once and lets each word attend to the others.
  • Attention means deciding which other words matter, and by how much.
  • This design powers almost every well-known AI model in use today.

What to learn next

Developer — Code and libraries.

Setup

bash
pip install numpy

Attention is a handful of matrix operations. Written out with small numbers, the whole mechanism fits on one screen and needs no framework.

Self-attention, end to end

attention.py
import numpy as np

tokens = ["the", "mango", "is", "ripe"]

# In a real model these three come from multiplying the word embeddings by learned
# weight matrices. They are written out here so the numbers stay readable.
# Q = "what am I looking for", K = "what do I offer", V = "what do I pass on".
Q = np.array([
    [0., 0., 2., 0.],    # the   - looking for nothing in particular
    [0., 2., 0., 0.],    # mango - looking for a ripeness word
    [0., 0., 2., 0.],    # is    - looking for nothing in particular
    [2., 0., 0., 0.],    # ripe  - looking for the thing being described
])
K = np.array([
    [0., 0., 0., 2.],    # the   - offers "function word"
    [2., 0., 0., 0.],    # mango - offers "I am the fruit"
    [0., 0., 0., 2.],    # is    - offers "function word"
    [0., 2., 0., 0.],    # ripe  - offers "I am a ripeness word"
])
V = np.array([
    [0., 0.],            # the   - carries nothing useful
    [1., 0.],            # mango - carries "fruit-ness"
    [0., 0.],            # is    - carries nothing useful
    [0., 1.],            # ripe  - carries "ripe-ness"
])


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


d_k = Q.shape[1]
scores = Q @ K.T / np.sqrt(d_k)     # dividing by sqrt(d_k) keeps the numbers in a sane range
weights = softmax(scores)
output = weights @ V

print("attention weights (each row sums to 1):")
print(f"{'':8s}" + "".join(f"{t:>9s}" for t in tokens))
for i, t in enumerate(tokens):
    print(f"{t:8s}" + "".join(f"{w:9.4f}" for w in weights[i]))

print("\nwhat each word ends up carrying (fruit-ness, ripe-ness):")
for i, t in enumerate(tokens):
    print(f"  {t:8s} {output[i][0]:7.4f} {output[i][1]:7.4f}")

print("\n--- causal mask: a word may not look at the future ---")
mask = np.triu(np.ones((4, 4)), k=1).astype(bool)   # True above the diagonal
masked = np.where(mask, -np.inf, scores)
causal = softmax(masked)
print(f"{'':8s}" + "".join(f"{t:>9s}" for t in tokens))
for i, t in enumerate(tokens):
    print(f"{t:8s}" + "".join(f"{w:9.4f}" for w in causal[i]))
Output
attention weights (each row sums to 1):
              the    mango       is     ripe
the        0.2500   0.2500   0.2500   0.2500
mango      0.0963   0.0963   0.0963   0.7112
is         0.2500   0.2500   0.2500   0.2500
ripe       0.0963   0.7112   0.0963   0.0963

what each word ends up carrying (fruit-ness, ripe-ness):
  the       0.2500  0.2500
  mango     0.0963  0.7112
  is        0.2500  0.2500
  ripe      0.7112  0.0963

--- causal mask: a word may not look at the future ---
              the    mango       is     ripe
the        1.0000   0.0000   0.0000   0.0000
mango      0.5000   0.5000   0.0000   0.0000
is         0.3333   0.3333   0.3333   0.0000
ripe       0.0963   0.7112   0.0963   0.0963

Reading the attention matrix

Row "ripe" puts 71% of its attention on "mango". Its query was "the thing being described"; mango's key was "I am the fruit". They matched, and mango won most of the weight.

Row "mango" puts 71% on "ripe" — the same relationship seen from the other side. These two words found each other with nothing in the code saying they should.

Rows "the" and "is" spread attention evenly at 25% each. Their queries matched nothing, all four scores tied at zero, and softmax over equal scores gives a uniform distribution. This is the honest behaviour of a function word with nothing to look for, and it happens in real models too.

The output rows show what changed. Before attention, "ripe" carried no information about fruit. Afterwards it carries 0.7112 of fruit-ness. That mixing is the entire contribution of an attention layer.

The causal mask produces a triangle. Row "the" attends only to itself. Row "mango" splits evenly between "the" and itself. Setting future positions to -inf makes exp(-inf) = 0, so those weights vanish and the rows still sum to one. This is what makes GPT-style generation possible: during training, every position predicts the next token without ever seeing it.

Note that the final row is unchanged by the mask, since the last token has no future to hide.

Why divide by the square root

Dot products of two vectors with dimension d_k grow in size roughly like the square root of d_k. For d_k = 64, raw scores would be about eight times larger than here.

Feed large scores into softmax and it saturates: one weight approaches 1.0 and the rest approach 0.0. A saturated softmax has almost no gradient, so the layer stops learning. The sqrt(d_k) division holds scores in a range where softmax stays responsive. Remove it from a real model and training fails.

The rest of a transformer block

Attention is the interesting part but not the whole block. A single layer is:

  x ──┬──> LayerNorm ──> Multi-head attention ──> (+) ──┬──> LayerNorm ──> Feedforward ──> (+) ──> out
      └──────────────────────────────────────────┘      └──────────────────────────────────┘
                    residual connection                          residual connection
  • Multi-head means running several attention operations in parallel with separate weights, then concatenating. One head can track grammatical subjects while another tracks nearby words.
  • The feedforward block is a two-layer MLP applied to each position independently. It holds roughly two-thirds of the model's parameters.
  • Residual connections (the +) give gradients a clean path through depth. Without them, models this deep do not train.
  • LayerNorm keeps activation scales stable. Modern models place it before each sublayer (pre-norm), which trains far more reliably than the original post-norm design.

Common mistakes

Leaving out positional information. Attention is permutation-equivariant: shuffle the input words and the outputs shuffle identically. "Mango eats the man" and "the man eats mango" would be indistinguishable. Position must be injected, through sinusoidal encodings, learned embeddings, or rotary embeddings (RoPE).

Masking with 0 instead of -inf. A score of 0 is not a low score — exp(0) = 1, so the position keeps meaningful weight. Mask before the softmax, with -inf or a large negative number.

Padding without a padding mask. Batch sentences of different lengths and the short ones get padding tokens. Without a mask, real words attend to padding and absorb noise. This trains without any error message and quietly hurts quality.

Writing attention by hand in production. Use torch.nn.functional.scaled_dot_product_attention. It dispatches to FlashAttention where available, which is faster and uses far less memory than a naive implementation, because it never materialises the full attention matrix.

Forgetting the quadratic cost. Doubling sequence length quadruples attention compute and memory. This is the reason context limits exist.

Try it yourself

Change the "is" row of Q to [0., 2., 0., 0.] so it hunts for a ripeness word, and rerun. Predict which column will dominate its row first. Then set d_k scaling to 1.0 instead of np.sqrt(d_k) and watch the weights sharpen toward a hard choice.

What to learn next

Researcher — Mathematics and papers.

Scaled dot-product attention

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

Where:

  • $Q \in \mathbb{R}^{n \times d_k}$ — queries, one row per position.
  • $K \in \mathbb{R}^{m \times d_k}$ — keys.
  • $V \in \mathbb{R}^{m \times d_v}$ — values.
  • $n$ — number of query positions; $m$ — number of key/value positions. In self-attention $n = m$.
  • $d_k$ — key/query dimension; $d_v$ — value dimension.
  • The softmax is applied row-wise, so each row of the $n \times m$ attention matrix sums to $1$.

Why $\sqrt{d_k}$: if the components of $q$ and $k$ are independent with zero mean and unit variance, then $q \cdot k = \sum_{i=1}^{d_k} q_i k_i$ has mean $0$ and variance $d_k$. Dividing by $\sqrt{d_k}$ restores unit variance, keeping softmax away from its saturated regime where gradients vanish. This argument is given in footnote 4 of Vaswani et al. (2017).

Multi-head attention

$$ \text{MHA}(X) = \text{Concat}(\text{head}_1, \dots, \text{head}_h)\, W^{O}, \qquad \text{head}_i = \text{Attention}!\left(X W_i^{Q},\, X W_i^{K},\, X W_i^{V}\right) $$

Where $h$ is the number of heads, $W_i^{Q}, W_i^{K} \in \mathbb{R}^{d_{\text{model}} \times d_k}$, $W_i^{V} \in \mathbb{R}^{d_{\text{model}} \times d_v}$, and $W^{O} \in \mathbb{R}^{h d_v \times d_{\text{model}}}$. Standard practice sets $d_k = d_v = d_{\text{model}} / h$, so multi-head attention costs the same as single-head attention at full width.

Complexity

OperationTimeMemorySequential steps
Self-attention$O(n^2 d)$$O(n^2)$ naive, $O(n)$ with FlashAttention$O(1)$
Recurrent$O(n d^2)$$O(nd)$$O(n)$
Convolutional (kernel $k$)$O(k n d^2)$$O(nd)$$O(1)$

The decisive column is the last one. Attention has constant sequential depth, so an entire sequence is processed in parallel during training. That, more than any accuracy gain, is why transformers displaced RNNs — they use modern hardware fully.

Per transformer layer with $d = d_{\text{model}}$ and feedforward width $4d$:

  • Attention projections: $4 d^2$ parameters.
  • Feedforward: $8 d^2$ parameters.
  • Total: $\approx 12 d^2$ per layer, so the MLP holds two-thirds.

Positional information

Self-attention is permutation-equivariant. Position must be supplied explicitly.

Sinusoidal (Vaswani et al., 2017):

$$ PE_{(p,\, 2i)} = \sin!\left( \frac{p}{10000^{2i/d}} \right), \qquad PE_{(p,\, 2i+1)} = \cos!\left( \frac{p}{10000^{2i/d}} \right) $$

Where $p$ is the position index and $i$ the dimension index. Fixed, not learned; relative offsets are expressible as linear transforms of the encoding.

RoPE (Su et al., 2021, RoFormer) rotates query and key vectors by an angle proportional to position, making the dot product depend only on relative distance $p - q$. It is the standard in Llama, Qwen, Mistral and most current open models, largely because it extrapolates beyond the trained context length more gracefully.

ALiBi (Press et al., 2022) adds a linear distance penalty directly to attention scores, with no positional embedding at all.

Efficiency work that matters in practice

  • FlashAttention (Dao et al., 2022; Dao, 2023 for v2) computes exact attention with $O(n)$ memory by tiling the computation in SRAM and never writing the $n \times n$ matrix to HBM. It is exact, not an approximation — a pure systems optimisation, and it is why long contexts became affordable.
  • Multi-query attention (Shazeer, 2019) shares one key/value head across all query heads, shrinking the KV cache by a factor of $h$. Grouped-query attention (Ainslie et al., 2023) interpolates between MQA and MHA and is used in Llama 2 70B onward.
  • KV caching is essential at inference. Without it, generating token $t$ recomputes attention over all $t-1$ previous tokens, making generation $O(n^2)$ per token. Cache size is $2 \cdot L \cdot n \cdot h_{kv} \cdot d_{\text{head}} \cdot \text{bytes}$, and it, not the weights, dominates memory during long-context serving.
  • Sparse and linear attention — Longformer, BigBird, Performer, Linformer — reduce the asymptotic cost but have largely lost to FlashAttention plus dense attention in practice, since exact attention on modern hardware is fast enough.

Architecture families

FamilyAttentionTrained onExamples
Encoder-onlyBidirectionalMasked token predictionBERT, RoBERTa, DeBERTa
Decoder-onlyCausalNext-token predictionGPT, Llama, Claude, Qwen
Encoder-decoderBoth, plus cross-attentionSequence-to-sequenceT5, BART, original Transformer

Decoder-only won for general-purpose generation, largely because next-token prediction on raw text scales without labelled data.

Key papers

  • Vaswani et al. (2017), Attention is all you need — the architecture.
  • Bahdanau et al. (2015), Neural machine translation by jointly learning to align and translate — attention itself, predating transformers by two years and rarely credited.
  • Devlin et al. (2019), BERT — bidirectional pretraining.
  • Kaplan et al. (2020) and Hoffmann et al. (2022), Training compute-optimal large language models (Chinchilla) — the scaling laws. Chinchilla corrected Kaplan and showed most large models of that era were badly undertrained for their size.
  • Xiong et al. (2020), On layer normalization in the transformer architecture — the pre-norm versus post-norm analysis that changed standard practice.

Honest open problems

Quadratic scaling is a real architectural limit, softened but not removed by FlashAttention. Interpretability remains weak: attention weights are not reliable explanations of model behaviour (Jain and Wallace, 2019, Attention is not explanation), a point frequently ignored in applied write-ups. State-space models such as Mamba (Gu and Dao, 2023) offer linear scaling and are competitive at moderate scale, though they have not displaced transformers at the frontier.

What to learn next