Chapter 5 · Section 5.6 · 55 min

One Transformer block, counted

You now own every part of a Transformer block. The residual stream carries each token up through the model (§5.1), norms keep what each sublayer reads at a fixed scale (§5.2), the feed-forward network transforms each token on its own (§5.3), and RoPE or a learned table tells positions apart (§5.4, §5.5). This section bolts them together and asks the questions an engineer asks of any block before building a model from it. How many parameters does it hold? How many floating-point operations does one token cost, forward and backward? Which of those numbers change when you swap GPT-2's recipe for LLaMA's?

Here is the kind of answer you should be able to give on a whiteboard. One block of GPT-2 small holds exactly 7,087,872 parameters. Pushing one token through it costs about 14.2 million FLOPs in its weight matrices plus 3.1 million in attention over a 1,024-token context (counting the full score matrix), and training on that token costs three times as much. A whole model is LL copies of the block plus a fixed overhead for the vocabulary, so these per-block numbers are how you budget a run, sanity-check a paper, and catch bugs. A block whose count is off by 1,536 has lost a LayerNorm. Interviewers ask these questions because they separate people who have built models from people who have read about them.

Assembling a decoder block

A GPT-2 block takes the stream X∈RT×d\mathbf{X} \in \R^{T\times d} for TT tokens of width dd and applies two pre-LN residual sublayers (§5.2),

H=X+MHA⁡(LN⁡1(X)),Y=H+FFN⁡(LN⁡2(H)),\mathbf{H} = \mathbf{X} + \operatorname{MHA}\big(\operatorname{LN}_1(\mathbf{X})\big), \qquad \mathbf{Y} = \mathbf{H} + \operatorname{FFN}\big(\operatorname{LN}_2(\mathbf{H})\big),

where MHA⁡\operatorname{MHA} is causal multi-head attention with hh heads of size dk=d/hd_k = d/h (§4.5, §4.4) and FFN⁡\operatorname{FFN} is the GELU MLP of §5.3 with dff=4dd_{\text{ff}} = 4d. Every shape along the way is worth writing out once.

stepoperationshape
inputstream X\mathbf{X}T×dT \times d
normU=LN⁡1(X)\mathbf{U} = \operatorname{LN}_1(\mathbf{X})T×dT\times d
projections[Q,K,V]=UWQKV+bQKV[\mathbf{Q}, \mathbf{K}, \mathbf{V}] = \mathbf{U}\mathbf{W}_{QKV} + \mathbf{b}_{QKV} with WQKV∈Rd×3d\mathbf{W}_{QKV} \in \R^{d\times 3d}T×3dT \times 3d
split headsreshape each to h×T×dkh \times T \times d_kh×T×dkh\times T\times d_k
scoresQK⊤/dk\mathbf{Q}\mathbf{K}^\top/\sqrt{d_k} plus causal mask, softmaxh×T×Th\times T\times T
mixweights times V\mathbf{V}, merge headsT×dT \times d
writeH=X+(⋅) WO+bO\mathbf{H} = \mathbf{X} + (\cdot)\,\mathbf{W}_O + \mathbf{b}_OT×dT\times d
FFNY=H+GELU⁡(LN⁡2(H)W1+b1)W2+b2\mathbf{Y} = \mathbf{H} + \operatorname{GELU}(\operatorname{LN}_2(\mathbf{H})\mathbf{W}_1 + \mathbf{b}_1)\mathbf{W}_2 + \mathbf{b}_2T×4d→T×dT\times 4d \to T\times d

A model stacks LL such blocks between an embedding and an unembedding. Token and position embeddings form the first stream X(0)\mathbf{X}^{(0)}, the blocks run in sequence, a final norm LN⁡f\operatorname{LN}_f normalizes the last stream, and the unembedding WU∈Rd×V\mathbf{W}_U \in \R^{d\times V} turns it into one row of next-token logits per position,

X(ℓ)=Block⁡ℓ(X(ℓ−1)),logits=LN⁡f(X(L)) WU∈RT×V.\mathbf{X}^{(\ell)} = \operatorname{Block}_\ell\big(\mathbf{X}^{(\ell-1)}\big), \qquad \text{logits} = \operatorname{LN}_f\big(\mathbf{X}^{(L)}\big)\,\mathbf{W}_U \in \R^{T\times V}.

Here the superscript counts blocks (unlike §5.1, where it counted sublayers), and VV is the vocabulary size. GPT-2 reuses its token table as the unembedding, WU=E⊤\mathbf{W}_U = \mathbf{E}^\top, which is called weight tying (Press & Wolf, 2017). Building that whole model end to end, embeddings, head, and every last parameter, is the job of §6.2. This section stays with the block, because everything else is a fixed overhead on top of LL copies of it.

The LLaMA recipe changes five things and keeps the skeleton. LayerNorm becomes RMSNorm (§5.2), learned positions are replaced by RoPE inside attention (§5.5), the GELU MLP becomes SwiGLU with dff≈8d/3d_{\text{ff}} \approx 8d/3 (§5.3), every bias is removed, and the output matrix is a separate parameter rather than tied. Larger LLaMA models also share keys and values across groups of query heads, which is called grouped-query attention (Ainslie et al., 2023, and §12.5), and which matters for counting.

Three families of Transformer

The block is the same in every family. What differs is the attention mask and how the stacks are wired.

familyattentiontrained tostrengthsexamples
encoder-onlybidirectional, every token sees every tokenfill in masked tokensrepresentations for classification, retrievalBERT, RoBERTa (§7.1)
decoder-onlycausal, each token sees its prefixpredict the next tokengeneration, in-context learning, one stack for everythingGPT, LLaMA, Mistral
encoder-decoderbidirectional encoder, causal decoder with cross-attention to itmap an input sequence to an output sequencetranslation, summarization, explicit input/output splitoriginal Transformer, T5, Whisper

Decoder-only models now dominate because one causal stack covers every task once tasks are phrased as text, because every token of every document provides a training signal, and because generation with a KV cache (§6.5) is simple and efficient. Section 8.1 compares the training objectives behind the three families. An encoder-decoder's decoder blocks contain a third sublayer, cross-attention (§4.6), with its own four projections and its own norm, which shows up in the count below.

Counting one block exactly

Count one GPT-2 block, piece by piece.

  • Attention. WQKV\mathbf{W}_{QKV} is d×3dd \times 3d with a 3d3d bias, and WO\mathbf{W}_O is d×dd\times d with a dd bias, so 4d2+4d4d^2 + 4d.
  • FFN. W1\mathbf{W}_1 is d×4dd\times 4d with a 4d4d bias, and W2\mathbf{W}_2 is 4d×d4d \times d with a dd bias, so 8d2+5d8d^2 + 5d.
  • Two LayerNorms. A gain and a shift each, so 4d4d.

Writing NblockN_{\text{block}} for the parameters of one block, the total is

Nblock=12d2+13d.N_{\text{block}} = 12d^2 + 13d .

In plain words, each block costs twelve d×dd\times d matrices' worth of weights, four in attention and eight in the FFN, plus a few vectors of size dd that hardly matter.

GPT-2 small, by hand. With d=768d = 768, one block has 12⋅7682+13⋅768=7,077,888+9,984=7,087,87212 \cdot 768^2 + 13 \cdot 768 = 7{,}077{,}888 + 9{,}984 = 7{,}087{,}872 parameters, and the 12 blocks hold 85,054,46485{,}054{,}464. The rule of thumb 12Ld2=84,934,65612Ld^2 = 84{,}934{,}656 is within 0.15% of that. Yet the released checkpoint has about 124 million parameters. The other 39 million live outside the blocks, and §6.2 finds every one of them.

LLaMA-2 7B, by hand. Now there are no biases, the norms are RMSNorm with a gain only (dd each), and the FFN is SwiGLU with three matrices of d×dffd\times d_{\text{ff}}. With d=4096d = 4096 and dff=11,008d_{\text{ff}} = 11{,}008, one block has 4⋅40962=67,108,8644 \cdot 4096^2 = 67{,}108{,}864 attention weights, 3⋅4096⋅11,008=135,266,3043\cdot 4096\cdot 11{,}008 = 135{,}266{,}304 FFN weights, and 2⋅4096=8,1922\cdot4096 = 8{,}192 norm weights, a total of 202,383,360202{,}383{,}360. The 32 blocks hold 6,476,267,5206{,}476{,}267{,}520, almost all of the model's 6.7 billion. Because dffd_{\text{ff}} is 8d/38d/3 rounded up, the block is 4d2+3d⋅8d3≈12d24d^2 + 3d\cdot\tfrac{8d}{3} \approx 12d^2 again (12.06 d2d^2 after the rounding). The 12d212d^2 rule survives the change of recipe.

Grouped-query attention breaks it. With hh query heads but only nkvn_{kv} key-value heads, WK\mathbf{W}_K and WV\mathbf{W}_V shrink to d×nkvdkd \times n_{kv}d_k, so attention costs 2d2+2d nkvdk2d^2 + 2d\,n_{kv}d_k. LLaMA-2 70B (d=8192d = 8192, h=64h = 64, nkv=8n_{kv} = 8, dk=128d_k = 128) has nkvdk=d/8n_{kv}d_k = d/8, so its attention is 2.25d22.25d^2 instead of 4d24d^2. At the same time its FFN is wider than the 8/3 rule (dff=28,672=3.5dd_{\text{ff}} = 28{,}672 = 3.5d), so the FFN is 3⋅3.5=10.5 d23 \cdot 3.5 = 10.5\,d^2. The block comes to 12.75 d212.75\,d^2 plus 2d2d of norm gains, 855,654,400855{,}654{,}400 parameters, and more than four fifths of it is FFN. LLaMA 3 8B makes the same trade at d=4096d = 4096 (nkv=8n_{kv} = 8 of 32 heads, so attention is 2.5d22.5d^2, and dff=3.5dd_{\text{ff}} = 3.5d) and lands at 13d213d^2 per block, plus its two norm gains.

The encoder-decoder block. A decoder block with cross-attention has a second set of four attention projections and a third norm. With dff=4dd_{\text{ff}} = 4d and biases it holds 16d2+19d16d^2 + 19d, so the original Transformer's base model (d=512d = 512) has 3,152,384 parameters per encoder block and 4,204,032 per decoder block.

The cell below prints the split for real configurations.

Python · cell 1
Interview

Count one block exactly, for any recipe

Write block_params(cfg) returning the exact number of parameters in one Transformer block, for a config dictionary with these keys.

  • d_model, n_head, d_ff
  • n_kv_head (default n_head), the number of key-value heads nkvn_{kv} for grouped-query attention, each of width dkd_k = d_model // n_head
  • bias (bool), biases in every linear layer and, for LayerNorm, the shift
  • norm, "layernorm" (gain, plus shift when bias is true) or "rmsnorm" (gain only)
  • ffn, "mlp" (two matrices) or "swiglu" (three matrices)
  • cross_attn (default False), whether the block also has a cross-attention sublayer with its own projections and its own norm, as in an encoder-decoder's decoder

A block has one norm per sublayer. The tests check GPT-2 small and XL, LLaMA-2 7B and 70B, LLaMA 3 8B, and a decoder block of the original Transformer, all exactly.

Hidden tests, hints, and AI help, from $1.99 a month.

FLOPs per token and the 6N rule

The arithmetic cost of a Transformer is dominated by matrix multiplications, and those are easy to count. Multiplying A∈Rm×k\mathbf{A} \in \R^{m\times k} by B∈Rk×n\mathbf{B} \in \R^{k\times n} takes mnkmnk multiply-adds, or 2mnk2mnk floating-point operations (FLOPs), the convention of §4.7.

Forward, one block. A linear layer Y=XW\mathbf{Y} = \mathbf{X}\mathbf{W} with X∈RT×a\mathbf{X} \in \R^{T\times a} and W∈Ra×b\mathbf{W}\in\R^{a\times b} costs 2Tab2Tab FLOPs, which is 2ab2ab per token, two FLOPs per weight per token. A GPT-2 block's matrices hold 12d212d^2 weights, so they cost 24d224d^2 FLOPs per token, a third in attention's projections and two thirds in the FFN. The two attention products with no weights, the scores qK⊤\mathbf{q}\mathbf{K}^\top and the weighted sum of values, cost 2Tdk2Td_k each per head for a token that attends to TT positions, so 4Td4Td per token across all heads (§4.7 counted them product by product). Biases, norms, activations, and the softmax add only O(d)O(d) or O(T)O(T) per token, which we ignore. For GPT-2 small at T=1024T = 1024 that is 24⋅7682≈14.224 \cdot 768^2 \approx 14.2 million FLOPs in the weights and 4⋅1024⋅768≈3.14 \cdot 1024 \cdot 768 \approx 3.1 million in attention, per token and per block.

The whole forward pass. Summing over the blocks and adding the unembedding, a T×dT\times d by d×Vd\times V product that costs 2Vd2Vd per token whether or not it is tied, the forward pass costs about 2Nmm2N_{\text{mm}} FLOPs per token plus attention. Here NmmN_{\text{mm}} counts the weights that take part in matrix multiplications, every block matrix plus the unembedding. Embedding lookups cost no multiplications, so the input tables are not in NmmN_{\text{mm}}.

Backward. Given ∂L/∂Y\partial\mathcal{L}/\partial\mathbf{Y}, a linear layer needs two products (§0.2), one to pass the gradient down and one for its own weights,

∂L∂X=∂L∂Y W⊤(2Tab FLOPs),∂L∂W=X⊤∂L∂Y(2Tab FLOPs).\frac{\partial\mathcal{L}}{\partial\mathbf{X}} = \frac{\partial\mathcal{L}}{\partial\mathbf{Y}}\,\mathbf{W}^\top \quad (2Tab \text{ FLOPs}), \qquad \frac{\partial\mathcal{L}}{\partial\mathbf{W}} = \mathbf{X}^\top\frac{\partial\mathcal{L}}{\partial\mathbf{Y}} \quad (2Tab \text{ FLOPs}).

So the backward pass costs twice the forward, 4Nmm4N_{\text{mm}} per token, and training costs

Ctoken≈6N,Ctrain≈6 N DC_{\text{token}} \approx 6N, \qquad C_{\text{train}} \approx 6\,N\,D

for DD training tokens, where NN means NmmN_{\text{mm}}, which for large models is close to the total parameter count.

In plain words, every weight is used once going forward (a multiply and an add) and twice going backward (once to pass the gradient on, once to compute its own gradient), so training costs six operations per weight per token.

The attention term. The weightless products follow the same rule. Their backward pass is again two products of the same size (§4.3), so training triples the forward 4Td4Td per layer, and the full per-token cost is

Ctoken≈6Nmm+12 L T d.C_{\text{token}} \approx 6N_{\text{mm}} + 12\,L\,T\,d .

This counts the whole T×TT\times T score matrix. A causal kernel that skips the masked half does about half of that work, since on average each token sees about T/2T/2 keys. Papers do not agree on this term, so check which convention a number uses. Kaplan et al. (2020) list a forward term of 2LTd2LTd in their Table 1, half of our full 4LTd4LTd, which §4.7 reads as the causal count, while PaLM's utilization accounting (Chowdhery et al., 2022) uses the full 12LTd12LTd for training.

How big is the attention term? With Nmm≈12Ld2N_{\text{mm}} \approx 12Ld^2, its ratio to the weight term is 12LTd/(6⋅12Ld2)=T/(6d)12LTd/(6\cdot12Ld^2) = T/(6d). For GPT-2 small at T=1024T = 1024 that ratio is 0.22, and for LLaMA-2 7B at T=4096T = 4096 it is 0.17. Counted against everything, unembedding included, attention is 13% and 14% of the training FLOPs of these two models, as the cell below prints. The term grows with context length, which is why long-context training (§12.7) costs more per token than short-context training.

Python · cell 2

A worked example worth memorizing. GPT-3 has 175 billion parameters and was trained on 300 billion tokens, so 6ND≈3.15×10236ND \approx 3.15\times10^{23} FLOPs, and the GPT-3 paper's own table reports 3.14×10233.14\times10^{23} (its count uses 174.6 billion). On hardware that sustains 4×10144\times10^{14} FLOP/s per GPU, that is 7.9×1087.9\times10^{8} GPU-seconds, about 9,100 GPU-days. How close a real run comes to its hardware's peak, its model FLOPs utilization, is defined and measured in §6.7. The 6N rule is also the bridge to scaling laws, which ask how to split a compute budget C=6NDC = 6ND between parameters and data (§9.3).

Two refinements come up in practice. If a run recomputes activations during the backward pass to save memory (§10.1), the hardware executes a second forward pass, about 8N8N FLOPs per token, but the model's own cost is still 6N6N, and utilization is quoted against the 6N6N (§11.2). And for a mixture-of-experts model, NN in the rule is the number of parameters active per token, not the total (Chapter 13).

Inference runs only the forward pass, about 2N2N FLOPs per generated token plus 4LTd4LTd for attention over TT earlier tokens. But during one-token-at-a-time decoding, each weight is read from memory to do just two FLOPs, so decoding is limited by memory bandwidth, not arithmetic. Caching past keys and values avoids recomputing the past (§6.5), and serving many requests at once lets each weight read do work for many tokens, the central idea of batching and memory management in serving.

Core

Training FLOPs per token, with the attention term

Write train_flops_per_token(cfg, T) returning 6Nmm+12LTd6N_{\text{mm}} + 12LTd, where NmmN_{\text{mm}} counts the weights in matrix multiplications. The config uses the block keys of Exercise 5.6f plus vocab_size and n_layer (any other keys are ignored). NmmN_{\text{mm}} is the block matrices of all n_layer blocks (respecting n_kv_head and ffn) plus one V×dV\times d unembedding, whether or not it is tied, and no biases, norms, or position tables.

Hidden tests, hints, and AI help, from $1.99 a month.

Memory per token, in brief

Two memory costs grow with tokens, and each has its own section later. At inference, every layer keeps the keys and values of every earlier token, 2Lnkvdk2Ln_{kv}d_k numbers per token. Section 4.7 already found 128 KiB per token for Llama 3 8B in 16-bit precision, and §6.5 builds the cache and sizes it. In training, each block stores the activations its backward pass needs, about 34d+5hT34d + 5hT bytes per token in GPT-3-style accounting (Korthikanti et al., 2022), where the 5hT5hT is the attention probabilities of the hh heads and their dropout mask and output. For a GPT-2 small block at T=1024T = 1024 that is 87,552 bytes per token, 70% of it attention matrices, which is why FlashAttention never stores them. Section 10.1 derives the 34 tensor by tensor and adds the 16 bytes per parameter that weights, gradients, and Adam's moments cost.

Where do one block's parameters and FLOPs go?
Pick a real model, then change its width, its key-value heads, and the context length. Width changes keep the head size and the FFN ratio of the preset.
Loading interactive figure…

A block, from scratch in NumPy

Here is a GPT-2 block in about a dozen lines, with random weights at a toy size, stacked four deep. The prints check the two properties that matter most, that the parameter count matches 12d2+13d12d^2 + 13d and that the stack is causal (changing a later token never changes an earlier position's output).

Python · cell 3
Interview

A GPT-2 decoder block, from scratch

Write gpt2_block(X, p, H) for one token sequence X of shape (T, d), a dictionary of weights p with the keys used in the cell above (ln1_g, ln1_b, W_qkv, b_qkv, W_o, b_o, ln2_g, ln2_b, W_fc, b_fc, W_proj, b_proj), and H heads (the hh of the text). It is a pre-LN block with causal multi-head attention and a tanh-GELU MLP. W_qkv is laid out as in GPT-2, with the query, key, and value projections side by side in that order, and head i owns columns i*d_k to (i+1)*d_k of each. No loops over heads or positions.

This is the canonical interview implementation. The tests check it against a reference, check causality, check that a block with zeroed output projections is the identity, and catch the usual slips (the scale, the split order, the norm placement).

Hidden tests, hints, and AI help, from $1.99 a month.

A stack of these blocks is the body of GPT-2. Section 6.2 adds the token and position embeddings in front, the final norm and the tied head behind, initializes the whole model, and checks it against a reference implementation.

In PyTorch

The same block in PyTorch. Built on the meta device, a GPT-2 small block is counted without allocating any memory. Then a toy-size block runs under PyTorch's FLOP counter, which confirms the forward cost 24d2T+4T2d24d^2T + 4T^2d exactly for one sequence of TT tokens.

🔥 In PyTorchfor a machine with PyTorch

Read more

Watch next

🎬 Let's reproduce GPT-2 (124M) · Andrej Karpathy · starts at 13:47Karpathy writes the GPT-2 module and checks it against the released 124M weights.
🎬 Transformers, the tech behind LLMs | Deep Learning Chapter 5 · 3Blue1BrownThe whole data flow of a Transformer, with the weight count of GPT-3 tallied as it goes.