LLMs from Scratch
Advanced 35 min Module 1 of 6
Module 1 of 6

How Transformers Work

Every text autocomplete, every coding assistant, every chatbot you have used in the last three years runs on the same core mechanism: the transformer. Not as a metaphor. As a literal computation. In this module you will see exactly what that computation is: how words become vectors, how vectors vote on each other through attention, and how that produces the next token in a sequence. By the end you will be able to explain Q/K/V matrices, dot-product attention, and softmax scaling to a colleague without hand-waving.

By the end of this module you will be able to

The Problem Transformers Solve

Before 2017, the dominant approach to sequence modeling was the recurrent neural network. To predict the next word in a sentence, an RNN read the sentence left to right, updating a hidden state at each step. The problem: by the time it reached word 50, its memory of word 1 had been diluted through 49 successive updates. Long-range dependencies were hard to capture.

The transformer, introduced in Vaswani et al. "Attention Is All You Need" (arXiv:1706.03762), discarded the recurrence entirely. Instead it processes the entire sequence at once, letting every position attend directly to every other position. Long-range dependency becomes trivial: position 1 and position 50 can interact directly with equal computational cost as adjacent positions.

That architectural decision is what made modern large language models possible. Scale a transformer wide enough, train it on enough data, and it learns to model language with a fidelity that surprised most researchers. The rest of this module explains the exact mechanism behind that.

Key concept The transformer processes all tokens simultaneously rather than left to right. This makes it parallelizable on GPUs and able to capture long-range relationships directly. Both properties matter enormously at scale.

Step 1: Tokens to Vectors

A language model cannot operate on words directly. It needs numbers. The first step is tokenization (covered fully in Module 2) and embedding. Each token in the vocabulary is assigned a vector of fixed dimension, called the embedding dimension or model dimension d_model. For reference, GPT-2 small uses d_model = 768; larger models use 4096 or more.

The embedding is a learned lookup table. At the start of training it is random. After training, similar tokens end up with similar vectors. "King" and "queen" will sit close together in the embedding space. "King" and "carburetor" will sit far apart. The geometry of the embedding space encodes semantic relationships the model has learned from data.

Positional information is added at this stage too, because unlike an RNN, the transformer has no built-in notion of order. A positional encoding vector is added to each token embedding before any attention is computed. The combined vector now carries both "what this token means" and "where it sits in the sequence." Module 2 covers both embedding and positional encoding in detail.

Step 2: Self-Attention and the Q/K/V Mechanism

Self-attention is the heart of the transformer. Here is the intuition: every token in the sequence produces three different representations of itself, called the Query, the Key, and the Value. These come from multiplying the token's embedding by three separate learned weight matrices: W_Q, W_K, and W_V.

Think of it like a search engine. The Query is the question a token is asking: "what context do I need to understand myself?" The Keys are what every other token is broadcasting: "here is what I can tell you about." The Values are the actual information each token contributes if selected. Attention is the process of scoring each key against each query, then taking a weighted sum of the corresponding values.

The scoring step uses a dot product. For token i querying token j, the score is:

score(i, j) = Q_i · K_j

Dot products measure alignment. If Q_i and K_j point in similar directions, their dot product is large and positive. If they are orthogonal, the dot product is near zero. This is how relevance is computed: two vectors that "agree" produce a high score, meaning i will attend strongly to j.

The Scaling Factor

There is a problem with raw dot products. As d_model grows, the magnitude of dot products also grows, because you are summing more terms. Large dot products drive softmax into a regime where the gradients are vanishingly small, making training unstable. The fix, from Vaswani et al., is to divide each dot product by the square root of the key dimension d_k before applying softmax:

Attention(Q, K, V) = softmax(QK^T / sqrt(d_k)) V

Dividing by sqrt(d_k) keeps the variance of the dot products roughly constant regardless of dimensionality. This is not an arbitrary design choice: it directly addresses a numerical stability problem that would otherwise prevent training from converging.

After scaling, softmax converts the raw scores into a probability distribution: positive numbers that sum to 1. The token with the highest score gets the highest weight. The attention output is then the weighted sum of all Value vectors, with higher-scored tokens contributing more.

Fig 1 · Scaled Dot-Product Attention Flow
Q K V MatMul QK^T Scale / sqrt(d_k) Softmax MatMul x V Output INPUTS SCORING RESULT

Multi-Head Attention: Running Many Attention Patterns in Parallel

A single attention head can learn one type of relationship between tokens. But language has many types of relationships simultaneously: grammatical agreement, coreference (which "it" refers to), semantic similarity, positional proximity. A single attention head must pick one focus.

Multi-head attention runs h separate attention operations in parallel, each with its own W_Q, W_K, W_V matrices projecting into a smaller dimension d_k = d_model / h. Each head specializes in a different type of relationship. The outputs of all h heads are concatenated and projected back to d_model through a final W_O matrix.

GPT-2 small uses 12 attention heads. Larger models use 32, 64, or more. Different heads in trained models have been shown empirically to specialize: some heads track grammatical structure, others track semantic content, others handle positional relationships. This specialization emerges from training, not from design.

Why multiple heads? Each head sees the same sequence but through a different linear projection. The model can learn to ask different questions simultaneously: "who is the subject of this verb?" and "what pronoun does this refer to?" and "what words are semantically similar to this one?" are all different attention patterns.

The Transformer Block: Feed-Forward, Residuals, and LayerNorm

Attention is not the only component in a transformer block. After multi-head attention, the output passes through a position-wise feed-forward network (FFN): two linear transformations with a ReLU or GELU activation in between. The FFN is applied independently to each token position. Its purpose is to transform the attended representation nonlinearly, giving the model more expressive power than attention alone provides.

Two more elements complete the block: residual connections and layer normalization. Residual connections add the input of each sublayer (attention or FFN) directly to its output before normalization. This solves the vanishing gradient problem for deep networks: gradients can flow backward through the addition operation without diminishing. A transformer with 96 layers (like GPT-3) would not be trainable without residual connections.

Layer normalization stabilizes the activations at each layer, normalizing each token's representation to have zero mean and unit variance. The combined recipe: attention then add-and-norm, FFN then add-and-norm. This block is repeated N times. GPT-2 small repeats it 12 times. GPT-3 repeats it 96 times.

QK
Query/Key Dot Product
Measures alignment between what a token is looking for and what every other token offers. High dot product means high relevance.
SM
Softmax Normalization
Converts raw scores into a probability distribution. Ensures attention weights are positive and sum to 1. Scaling by 1/sqrt(d_k) prevents saturation.
RS
Residual Stream
Each block's output is added back to its input. This lets the model route information through many layers without gradients vanishing in backpropagation.
Interactive 1: Attention Score Visualizer Try it

Adjust the Query/Key alignment angle and the embedding dimension to see how the raw dot product, the scaled score, and the softmax weight change. Notice how larger embedding dimensions produce larger raw scores that need the scaling factor to remain stable.

30 deg
64
Raw dot product: --    Scaled: --    Softmax weight (vs uniform token): --
Python · Scaled Dot-Product Attention
import numpy as np

def scaled_dot_product_attention(Q, K, V):
    """
    Q: (seq_len, d_k) — queries
    K: (seq_len, d_k) — keys
    V: (seq_len, d_v) — values
    Returns: (seq_len, d_v) weighted sum of values
    """
    d_k = Q.shape[-1]

    # Step 1: Compute dot products between every Q and every K
    scores = Q @ K.T          # shape: (seq_len, seq_len)

    # Step 2: Scale to prevent large dot products from saturating softmax
    scores = scores / np.sqrt(d_k)

    # Step 3: Softmax to get attention weights (each row sums to 1)
    exp_scores = np.exp(scores - scores.max(axis=-1, keepdims=True))
    weights = exp_scores / exp_scores.sum(axis=-1, keepdims=True)

    # Step 4: Weighted sum of values
    output = weights @ V
    return output, weights

# Example: 4 tokens, d_k = 8, d_v = 8
np.random.seed(42)
seq_len, d_k, d_v = 4, 8, 8
Q = np.random.randn(seq_len, d_k)
K = np.random.randn(seq_len, d_k)
V = np.random.randn(seq_len, d_v)

output, attn_weights = scaled_dot_product_attention(Q, K, V)
print("Attention weights (each row sums to 1):")
print(np.round(attn_weights, 3))
print("\nOutput shape:", output.shape)
Try this

Open the code above and change d_k from 8 to 64 without the scaling step (remove the / np.sqrt(d_k) line). Run both versions and compare the attention weight distributions. The unscaled version will show much more extreme weights: one token gets almost all the attention and the rest get near-zero. This is the gradient saturation problem the scaling factor solves.

Putting It All Together: One Forward Pass

Now you can trace a token through one complete transformer block. Start with a sequence of N tokens. Each becomes a d_model-dimensional vector through the embedding lookup plus positional encoding. These N vectors enter the multi-head attention sublayer.

In multi-head attention, each vector is projected h times into d_k dimensions (once per head) to produce queries, keys, and values. Each head computes scaled dot-product attention over all N positions, producing N output vectors of dimension d_k. The h heads' outputs are concatenated (giving N vectors of dimension h * d_k = d_model) and projected through W_O.

The result is added back to the original input via the residual connection, then layer-normalized. This goes to the FFN: a linear layer expanding to 4 * d_model dimensions, a GELU activation, and a linear layer contracting back to d_model. Another residual connection and layer normalization complete the block.

Repeat this N times. After the final block, a linear projection maps d_model dimensions to vocabulary size (say, 50,000 tokens). Softmax over this vocabulary distribution gives the probability of each possible next token. The token with the highest probability (or a sampled token, depending on the decoding strategy) becomes the next output. Module 5 covers decoding strategies in detail.

Why does the transformer need both attention and a feed-forward network? Isn't attention enough?
Attention handles routing: which tokens should influence which other tokens, and by how much. But attention itself is a linear operation (it computes a weighted sum of values). The feed-forward network adds nonlinearity, which is where the model can learn arbitrary transformations of the attended representations. Research into transformer internals (e.g., Geva et al. "Transformer Feed-Forward Layers Are Key-Value Memories," 2021) has found that FFN layers store factual associations learned during training, while attention handles context-sensitive routing. Both components are necessary.
Knowledge check
Why is the dot product in self-attention divided by the square root of d_k before applying softmax?
Correct. As d_k grows, dot products grow in magnitude, which pushes softmax into regions where the gradient is near zero. Dividing by sqrt(d_k) keeps the variance stable regardless of dimensionality, which is critical for training deep transformers.
Not quite. Softmax normalizes the weights to sum to 1 on its own. The scaling factor's purpose is numerical stability: large d_k produces large dot products, which push softmax into a flat region with near-zero gradients, preventing effective training.
In multi-head attention with h heads and model dimension d_model, what is the dimension each head projects queries and keys into?
Correct. Each head projects into d_k = d_model / h. This keeps the total computation comparable to single-head attention at the full dimension, while allowing the model to attend to different aspects of the sequence simultaneously.
Not quite. If each head used d_model, the total computation would multiply by h. Instead, each head uses d_k = d_model / h, so the total parameter count and computation stays comparable to single-head attention while gaining the benefit of multiple specialized heads.
Before you go
Reflection: A transformer attends to all tokens simultaneously. What might be the disadvantage of this when sequences get very long?
You might also like
Was this module helpful?

Work directly with Arjun on LLM architecture decisions for your organization.

Schedule a call

References

  1. Vaswani, A. et al. "Attention Is All You Need." arXiv:1706.03762. arxiv.org/abs/1706.03762
  2. Radford, A. et al. "Language Models are Unsupervised Multitask Learners." OpenAI, 2019. GPT-2 architecture reference.
  3. Brown, T. et al. "Language Models are Few-Shot Learners." arXiv:2005.14165. arxiv.org/abs/2005.14165
  4. Geva, M. et al. "Transformer Feed-Forward Layers Are Key-Value Memories." arXiv:2012.14913. arxiv.org/abs/2012.14913
  5. He, K. et al. "Deep Residual Learning for Image Recognition." arXiv:1512.03385. arxiv.org/abs/1512.03385
  6. Ba, J. et al. "Layer Normalization." arXiv:1607.06450. arxiv.org/abs/1607.06450
← Previous Module 2: Tokenization and Embeddings →