8 Generative Pre-trained Transformers (GPT)
This chapter explains a minimal Generative Pre-trained Transformer (GPT). The aim is to understand the basic architecture of the neural networks behind systems such as ChatGPT, Claude, and Codex. From the outside these systems look like machines that read text and write useful text back. Here we look at the simpler mathematical question underneath: how can a neural network take a text context and predict a reasonable next token?
A large language model (LLM) is the broad name for such a model when trained on very large amounts of text. A GPT is just a particular kind of LLM.
The preceding chapters already introduced the basic neural-network ingredients: vectors, matrix layers, nonlinearities, residual connections, losses, and gradient descent. In this chapter we arrange those ingredients into a minimal GPT. We do not touch on the engineering details that become important to improve speed, computational cost, memory usage, and quality. For this, a useful concrete starting point is Karpathy’s Python implementation
8.1 Interacting with a GPT
From the user’s point of view, an LLM is a machine that we interact with by text, for instance by typing in a webbrowser. Once done typing, we press enter, wait a little, and the machine writes its response on the screen, often word by word. After a while it stops and waits for new input. How does this work?
At a high level, this is what happens; below we discuss the details. The machine has a vocabulary of tokens.1This is much like a dictionary, except that the entries are text pieces rather than only whole words. Most tokens stand for ordinary pieces of text, such as words or parts of words, but some tokens are special such as end of sequence \(\cn{Eos}\) to mark that the generation of text should stop.
After the user typed text and pressed enter, the machine follows these steps:
- Collect the current context, which consists of the new user text and (when present) preceding text from the conversation, and turn that with the vocabulary into a sequence of token ids.
- Embed each token as a vector in \(\R^{d}\) and add a vector in \(\R^{d}\) that encodes the token’s position in the sequence. Stacking \(N\) such vectors gives a matrix \(X \in \R^{N\times d}\) with one row per token.
- Pass \(X\) through \(L\) identically structured transformer blocks.2In essence this is just a neural network: a pipeline of matrix multiplications with simple nonlinear steps in between. Each block first lets the tokens exchange information through self-attention, then transforms each token on its own through a small feed-forward network.
- Multiply the output of step 3 by an output matrix to obtain, for each position, one score (logit) per vocabulary token, and apply softmax to turn the scores at the last position into a probability mass function (pmf) on the token vocabulary.
- Sample from this pmf to select a next token.
- Use the vocabulary in the reverse direction to detokenize this token into a text piece, and show that piece to the user.3Implementations separate the two steps: the sampled tokens are only appended, and the detokenization is applied once, after \(\cn{Eos}\), to all tokens added to \(X\) since the last input of the user. The text is the same, except that a token can end in the middle of a character, so a piece-by-piece detokenizer has to wait for the remaining bytes.
- Append the new token to the token sequence.4Such a model is called autoregressive as it writes one token after the other based on a window of previous tokens.
- Repeat steps 2–7,5So \(X\) is rebuilt from the extended sequence, generating one more token each round. until the selected token happens to be \(\cn{Eos}\), which stops the token generation.
- Wait for new input. Return to step 1 with the text generated so far and the new input of the user.
The magic of a GPT is that, once trained, it produces from the context \(X\) high-quality pmfs over the token vocabulary, so that the resulting text makes sense to us. That is all: nothing more, but certainly nothing less.
Training adjusts all the matrices involved in this process so that the predicted next-token pmfs match the tokens in a corpus, that is, a large collection of training text.6Training text can include books, articles, web pages, documentation, source code, forum posts, and other digitized text.
We now explain the concepts one by one in detail.
8.2 Representing text
8.2.1 Tokenizers
A language model does not read whole sentences at once. It reads a sequence of
small pieces called tokens. The vocabulary with \(v\) entries contains all
allowed tokens. For a character-level model of English the vocabulary is small:
the letters a–z, a space, and a few punctuation marks. The sentence the dog
then becomes the token sequence t, h, e, space, d, o, g. In modern
GPTs, a token is usually a larger text fragment, such as a common word, a word
part, or a space attached to words; such vocabularies contain on the order of
\(v \approx 10^5\) tokens.
A tokenizer is the fixed procedure that performs the mapping from real text to
tokens. For example, a longest-match tokenizer starts at the first character
of the text, looks for all vocabulary tokens that match the text starting at
that position, chooses the longest matching token, moves forward by the length
of that token, and repeats until the whole text has been tokenized. Thus, for
the word adds, if the vocabulary contains both add and adds, the tokenizer chooses
adds. If the vocabulary contains add but not adds, it chooses add and
then tokenizes the remaining s.
For a GPT-style byte-pair encoding (BPE) tokenizer, the vocabulary is built together with a fixed merge table. The merge table says which adjacent text pieces should be joined first, second, third, and so on.7Both the vocabulary and the merge table are learned from training text. BPE tokenizes a text as follows.
- Start by splitting the text into very small units, usually bytes or characters.
- Among all adjacent pairs, merge the pair that comes first in the fixed merge table.
- Repeat step 2 until no listed merge applies.
- Replace the final pieces by their token ids.
A tokenizer does not correct typos. It maps the text it receives to tokens. If a
misspelled word is not in the vocabulary as one token, the tokenizer splits it
into smaller known pieces, possibly down to bytes or characters. Thus, as the
typed word addds is not in the vocabulary, it can be split into pieces such as add,
d, and s. The neural network downstream has to handle typos.
The same vocabulary is also used in the reverse direction. Each time the GPT has selected a token id, the tokenizer looks up this id in the token vocabulary, retrieves the corresponding text piece, and writes that piece to the output text. This reverse step is called detokenization. For example, for a character-level tokenizer it is just joining characters; for BPE it is more complicated, but still the same idea.
A GPT cannot process an arbitrarily long text, such as a book, at once. After tokenization, the sequence of token ids must fit inside the model’s context window, whose maximum length is \(N_{\max}\). If a document is too long, surrounding software may split it into chunks or compact parts of the text. Thus the model itself only sees the tokens that are placed in its current context window. Similarly, when a conversation becomes too long, the software may drop older tokens.
8.2.2 Embeddings
As tokens are just symbols, they have to be converted to numbers so that the neural network8Recall, neural networks are just matrix multiplications on vectors with non-linear operations in between. downstream can act on them. For this, the GPT maps each token in the vocabulary to a vector, called its embedding. Stacking the embeddings of all tokens in the vocabulary gives the embedding matrix \[ E \in \R^{v \times d}. \] Here \(v\) is the size of the vocabulary and \(d\) is the chosen embedding dimension.9Don’t get confused. Row \(i\) of \(E\) is the embedding of the \(i\)-th token of the vocabulary; it is not the \(i\)-th token of the sentence being read. So to find the vector for a token that occurs in the text, we look up that token’s position in the vocabulary and select the matching row of \(E\).
Once tokens are vectors we can ask how similar two of them are. In a
word-level model the tokens cat and dog probably end up with nearby vectors
during training, because they occur in similar contexts (the ___ slept, feed
the ___), whereas dog and quantum would probably land far apart.10In
view of Schr\"odinger’s cat, cat and quantum might end up close. ’Closeness’
is a complicated concept. The natural measure of this geometric closeness is
the inner product. Write \(E_i\) for the embedding of the \(i\)-th vocabulary
token, that is, row \(i\) of \(E\). When the inner product \(\ip{E_i, E_j}\) is
large and positive, the two vectors point in a similar direction; when it is
near zero, they are unrelated. Thus the geometry of the vectors mirrors how the
tokens are actually used. This matters because the model computes only with
these vectors, through inner products and matrix multiplications: if related
tokens have related embeddings, whatever the model learns for one token carries
over to its neighbors.
A crude way to embed tokens would be to use one-hot encoding11See Chapter 3. for each token. That forces the embedding dimension to equal the vocabulary size, \(d = v\), and makes every two distinct tokens orthogonal.12Since the inner product between different one-hot encoded vectors is \(0\). Thus, it records only that two tokens differ, but not how much they differ. A learned embedding with \(d \ll v\) is both far smaller and, unlike one-hot vectors, able to place related tokens near each other.13In the sense of the inner product.
Observe that the embedding matrix \(E\) is not chosen by hand. Initially these entries are random, say \(E_{ij}\sim\Norm{0,1}\). Every entry of \(E\) is then learned: during training the model gradually adjusts these numbers so that tokens used in similar ways end up with similar vectors. Nobody decides in advance what an embedding of a token should be: the model discovers it. At the end of the chapter we discuss how this adjustment works.
Once each single token has an embedding, we represent a whole sequence of \(N\) tokens, for instance the characters of the sentence read so far, by stacking their embedding vectors as \(N\) rows in the context matrix \[ X \in \R^{N \times d}, \] that is, one token embedding vector per row. Here \(N\) is the length of the current token sequence. It cannot exceed the model’s maximum context length \(N_{\max}\).
Below we will see that from here on almost everything the model does is to multiply \(X\) by learned matrices and apply simple non-linear mappings.
There is an important aspect to the token sequences that we did not yet include:
the role of the position of tokens. In the cat bit the dog. and the dog bit
the cat., the animal that suffers is different. However, in token embedding,
the same symbol always receives the same token vector. Thus, the embedding
matrix \(E\) gives the model no information about where the token stands in the
sequence. We need to add this information with a second, position-dependent
\(d\)-dimensional vector. The position matrix has one row per possible position,
\[
P\in\R^{N_{\max}\times d}.
\]
If the token id at sequence position \(i\) is \(t_i\), then the \(i\)-th row of
the context matrix is
\[
x_i = E_{t_i}+P_i.
\]
Here \(E_{t_i}\) is the embedding of token \(t_i\), and \(P_i\) is the embedding
of position \(i\). Stacking these rows gives the final form of the context matrix \(X\).
A very naive fixed encoding for position, such as \(P_i=(i,0,\ldots,0)\), would tell the model the position, but it is a poor choice. The position signal grows with \(i\), can dominate the token embedding, and also uses only one coordinate to carry all position information. Practical position encodings therefore either learn the rows of \(P\) from data, or use some fixed vectors.14Wikipedia: Transformer (deep learning).
8.3 Predicting the next token
8.3.1 Next-token selection
Recall that the language model predicts the next token from the current context
matrix \(X\), whose rows represent the tokens that fit in the context window.
For this, it produces a pmf on the vocabulary.
After the tokens the do, a good character-level model puts high probability on
g (making dog), a little on e (doe), and almost none on z.
To get this pmf the model takes \(X\) as input and computes for each token in the vocabulary a plain real number called a score (or logit). Let \(s = (s_1,\dots,s_v)\) be the vector of scores, where \(s_i\) rates how well the \(i\)-th vocabulary token fits that one slot.15Again, the index \(i\) runs over the fixed vocabulary, not over the token positions of the sentence.
Because a score can be any real number, the score vector \(s\in\R^v\) is turned into a pmf \(p\in\R^v\) by the softmax function (5.2.10): \(p=\sm(s)\). During generation, implementations often divide the score vector by a temperature \(\tau>0\) before applying softmax: \[ p=\sm(s/\tau). \] A small \(\tau\) concentrates probability near the largest scores and makes the output more deterministic; a large \(\tau\) flattens the pmf and makes the output more variable.16Temperature is not the only knob. Implementations often also restrict sampling to the \(k\) most probable tokens (top-k sampling), or to the smallest set of tokens whose total probability exceeds a threshold (nucleus sampling), to prevent the occasional selection of a very unlikely token. Good values of \(\tau\) are found by testing.
One part of the computation of \(s\) is the same in every model we discuss below. The network condenses the context into a single summary vector \(h \in \R^{d}\), and then maps this vector to the scores by a learned matrix,17Many GPTs do not learn a separate \(W_{\mathrm{out}}\), but reuse the embedding matrix by setting \(W_{\mathrm{out}}=E^\top\). This is called weight tying: it saves parameters and couples unembedding to embedding.
\begin{align*} W_{\mathrm{out}}\in\R^{d\times v}, \tag{8.3.1} \end{align*}so that18Here we use the standard ML convention that a data matrix stores one observation per row. Thus each token vector is a row vector, and a linear layer multiplies on the right: \(h W_{\mathrm{out}}\).
\begin{align*} s = h W_{\mathrm{out}} \in \R^{v}. \tag{8.3.2} \end{align*}The matrix \(W_{\mathrm{out}}\) is often called the unembedding matrix.19Where the embedding matrix \(E\) maps a token into the model’s vector space, \(W_{\mathrm{out}}\) maps a vector back to scores on the vocabulary.
Thus, the problem is no longer how to use the score vector \(s\); the real problem is how to compute a good summary vector \(h\) from the whole context in \(X\). Answering this is the next topic.
8.3.2 Attention
Before GPTs and transformers, neural language models often used recurrent neural networks (RNNs) to compute the score vector \(s\) from a hidden vector \(h\). Just as a GPT, an RNN starts from the context matrix \(X\). However, instead of using the entire \(X\), it reads the rows \(x_1,\ldots,x_N\) of \(X\) from top to bottom, in the order of the token sequence. It keeps hidden vectors \(h_i\) updated recursively according to the rule
\begin{align*} h_i = F(h_{i-1}, x_i), \qquad i=1,\ldots,N, \tag{8.3.3} \end{align*}with \(h_0=0\). Here \(F\) denotes the recurrent cell20It is called a cell because the same computational unit is reused at each position in the sequence. The cell \(F\) is a small neural network, with learned matrices, biases, and nonlinearities. For instance, a simple RNN cell could use \(F(h_{i-1},x_i)=\tanh(h_{i-1}W_h+x_iW_x+b)\). that combines the previous hidden vector \(h_{i-1}\) with the current token vector \(x_i\). After the last token, the final hidden state \(h_N\) serves as the summary vector for (8.3.2).
This recurrent approach has two weaknesses. First, all information from the
earlier tokens has to pass through a single hidden state \(h_N\); for long
sequences, important information can be lost in the repeated updating of this
hidden vector. This is a serious problem when dealing with long-range
dependencies such as understanding text, because conceptually and grammatically
related words are not always nearby in a sentence. For instance, in the dog
that chased the cat ran, the word ran belongs to dog, not to cat, even
though cat lies closer in the sentence. Second, by (8.3.3), the rows of
\(X\) are processed one after another:
Thus the computation of \(h_N\) must wait for \(h_{N-1}\), which must wait for \(h_{N-2}\), and so on. This serial dependence precludes evaluation of all positions at the same time by large parallel matrix operations on a GPU, hence slowing down the computations.
A better design should have a token look directly at the other
tokens in the current context and learn which ones are relevant. Since the rows
of \(X\) are vectors, the first thing one might try is the matrix \(X
X^{\top}\). Its \((i,j)\) entry is the inner product \(\ip{x_i,x_j}\). This
gives a relation score between position \(i\) and position \(j\), but it has a
fundamental problem: as the inner product is symmetric, the score is symmetric,
\[
\ip{x_i,x_j}=\ip{x_j,x_i}.
\]
Thus how much token \(i\) attends to token \(j\) would always equal how much
token \(j\) attends to token \(i\). We do not want this. Attention21Wikipedia: Attention Is All You Need. is about directional
relations between the tokens in the sequence: the role of token \(j\) for
understanding token \(i\) need not be the same as the role of token \(i\) for
understanding token \(j\). Moreover, attention is not just ordinary similarity.
In the dog ... ran, the word ran must attend to dog, yet ran and dog
are not similar tokens, so their embeddings need not lie close at all. We
therefore need to transform the rows of \(X\) before using inner products as
relation scores.
Suppose we transform the matrix \(X\) with a matrix \(U\) and score token \(i\) against token \(j\) by the inner product of \( x_i U\) and \(x_j U\), that is \(\ip{ x_i U, x_j U}\). But notice that \(UU^{\top}\) is symmetric, so that the score of \(i\) with \(j\) under this transformation is always equal to the score of \(j\) with \(i\):
\begin{align*} \ip{x_{i}U , x_{j} U} &= \ip{x_{i}, x_{j} U U^{\top}} = \ip{x_{j} U U^{\top}, x_{i}} = \ip{x_{j} U, x_{i}U} \end{align*}Consequently, this type of transformation still does not capture sequential relations.
Thus, instead of using one matrix \(U\) to transform \(X\), the model builds two vectors for each token by multiplying \(X\) with two different \(d \times d\) matrices \(W_Q\) and \(W_K\), \[ Q = X W_Q, \qquad K = X W_K; \] and \(Q\) and \(K\) are again \(N \times d\). With two separate matrices the scores are generally not equal because, in general,
\begin{align*} \ip{x_i W_Q, x_j W_K} \neq \ip{x_j W_Q, x_i W_K}. \end{align*}
The \(i\)th row of \(Q\) is called the query of token \(i\), and the \(j\)th
row of \(K\) is called the key of token \(j\). The score \(\ip{x_i W_Q, x_j
W_K}\) quantifies how much token \(i\) attends to token \(j\), because, when
this inner product is large, the transformed vector for token \(i\) is aligned
with the transformed vector for token \(j\). The important point here is that
using two matrices \(W_Q\) and \(W_K\) offers the asymmetry22The asymmetry
could also be obtained with one matrix, for instance through a score like
\(\ip{x_i, x_j W}\). The separate matrices \(W_Q\) and \(W_K\) are used because
they give this convenient query–key interpretation. that enables the query of
ran to point toward the key of dog while the two embeddings of these words
can stay far apart. Note that, like the embedding matrix \(E\), the matrices
\(W_Q\) and \(W_K\) are not set by hand; they are learned from enormous amounts
of text.
The queries and the keys are both built from the same sequence \(X\), so every
token is compared against every other token of the same input. This is called
self-attention and is used throughout GPTs. Self-attention matters because the
context that counts for the next token is rarely the nearest token; it may lie
many tokens back, as dog does for ran. By letting every token look at every
other and learn which ones matter, the model captures such long-range links
directly. We remark that this only works because the rows of \(X\) include
position information. Without position embeddings, self-attention could treat
the input essentially as an unordered set: permuting the token rows would merely
permute the output rows in the same way.23Strictly speaking this holds for
unmasked self-attention; the causal mask introduced below also depends on
position, because it tells each token which later positions are forbidden. But
the mask only gives this before/after restriction. The richer information about
where a token sits in the sequence comes from the position embeddings.
We use \(QK^\top\) to form the attention matrix \[ A = \sm\!\left(\frac{QK^{\top}}{\sqrt{d}} + M\right), \] where the softmax is applied row by row, so that each row of \(A\) sums to one. This form needs two comments. First, each element of \(QK^{\top}\) is an inner product of two vectors with \(d\) components, hence a sum of \(d\) products. The more components, the larger such a sum tends to be. To keep the scores in a sensible range, and to prevent the softmax in the next step from reacting too sharply, we divide every entry of \(QK^{\top}\) by \(\sqrt{d}\).24Why \(\sqrt d\) and not \(d\)? If the entries of the query and the key are uncorrelated with mean \(0\) and variance \(1\), their inner product is a sum of \(d\) such products, and so has mean \(0\) and variance \(d\), hence standard deviation \(\sqrt d\). Second, a GPT may use only the current and earlier positions. When predicting from position \(i\), it must not use positions \(j>i\), because those are future tokens. We enforce this with a causal mask matrix \(M\) where \(M_{ij}=-\infty \1{j > i}\). Adding \(M\) before the softmax makes the softmax weight of every future position equal to zero. The mask also explains why GPT training can be parallel over positions. Since position \(i\) never sees tokens after \(i\), the model can compute predictions for all \(N\) positions in one forward pass without leaking the answers.25Here leaking means that, when predicting the token after position \(i\), the model would be allowed to use that same future token as input. That would make the training task meaningless. This is the key computational difference with the recurrent model, which must process the positions one after another.
To see what \(A\) does, first ignore attention. In a regular neural-network layer, we transform the rows of \(X\) by a learned matrix \(W_V\): \[ V=XW_V, \] which, in transformer terminology, is called the value matrix. Then \(V\) would be passed through a nonlinearity and on to the next layer. The problem is that this operation does not by itself mix information between tokens. For that purpose, we built the attention matrix \(A\). Instead of passing on \(V\), the attention head passes on the head output \[ H=AV = A X W_{V}\in \R^{N \times d}. \]
On a personal note: I would assess the attention matrix \(A\) as the real novelty here. As for the fancy query–key–value terminology itself, I don’t think one should attribute too much of a meaning to it, despite the evocative naming. At the time of writing,262026 the success of attention in language models is just an empirical fact. The meaning of text, or information more generally, has not (yet) been explained in query–key–value terms.
8.3.3 A one-head GPT
The construction above makes one attention head: it consists of one set of matrices \(W_Q,W_K,W_V\) combined in an attention matrix \(A\), and one head matrix \(H=AV\). In principle, one such head can already serve as a minimal one-head GPT.
Read Fig. 8.1 from bottom to top. The lower box, Make \(X\), starts with the text from the conversation. It tokenizes this text with the vocabulary, looks up the corresponding rows of \(E\), adds the position embeddings from \(P\), and produces the context matrix \(X\). The attention-head box then takes \(X\) as input. It forms \(Q=XW_Q\), \(K=XW_K\), and \(V=XW_V\), builds the attention matrix \(A\), and produces the head output \(H=AV\). The next-token-selection box uses the last row \(H_N \in \R^{d}\) of \(H\), because this row corresponds to the end of the current context. With (8.3.2) we compute the score \(s\), using \(H_N\) as the summary vector \(h\). From there, the remaining steps are exactly those of the list in Section 8.1.
8.3.4 From one head to a full decoder-only GPT
Fig. 8.2 shows how the one-head GPT is extended to a single transformer block with several heads. The input side is now collapsed to the single box \(X\), and the next-token-selection box is also collapsed: their internal details are exactly the ones from Fig. 8.1. The new part is the transformer in between.
The input \(X\) is passed through layer normalization27Wikipedia: Normalization (machine learning). before it enters the attention heads. Write
\begin{align*} Z=\LN{X}, \end{align*}where
\begin{align*} z_{ik} &= \gamma_{k}\frac{x_{ik}-\bar x_i}{\sigma_i} +\beta_{k}, &\bar x_i &= \frac1d\sum_{k=1}^d x_{ik}, & \sigma_i^{2} &= \frac{1}{d}\sum_{k=1}^d (x_{ik}-\bar x_i)^2. \end{align*}Thus, besides the standard normalization, transformers28Layer normalization is less important in a single-layer toy GPT than in a real GPT. Real GPTs stack many transformer blocks, so repeated transformations can make scales drift; normalization keeps the row vectors on a comparable scale. usually include the learned parameters \(\gamma_k\) and \(\beta_k\) to make the normalization less rigid: if coordinate \(k\) should be larger, smaller, or shifted after standardization, training can pick this up.
Why do this before attention?29Placing the normalization before each sublayer, as we do, is the pre-norm convention of GPT-2 and later models. The original Transformer instead normalized after the residual addition (post-norm). Both appear in the literature; pre-norm turns out to be easier to train for deep stacks. Attention scores are built from inner products. If the row scales drift after several transformations, then an inner product can become large because a row has a large norm, not because the tokens are especially relevant to each other. With this scaling, attention heads see vectors on a comparable scale.
After layer normalization, \(Z\) is sent to \(m\) attention heads in parallel.30This is another reason not to assign much meaning to the term value matrix. If the value matrix were capable of selecting what is “of value” in a text, then a single attention head would suffice. Empirically, GPTs perform better when several such heads are used. Heuristically, since each head has its own matrices, each head is free to pick up different kinds of relation between tokens.31One sometimes reads explanations in human terms, such as: one head attends to emotion, another to meaning. In my opinion, this is misleading. We do not know in such simple terms what individual heads learn. What we do know is that the trained GPT, as a whole, can produce text or computer code that is helpful to us. In head \(r\), the model forms its own queries, keys, and values,
\begin{align*} Q^{(r)} &= ZW_Q^{(r)}, & K^{(r)} &= ZW_K^{(r)},& V^{(r)} &= ZW_V^{(r)}. \end{align*}It then computes its own causal attention matrix \(A^{(r)}\)32With one change: the scaling inside the softmax is now \(\sqrt{d_h}\) instead of \(\sqrt d\), because the per-head queries and keys have \(d_h\) components. and produces \(A^{(r)}V^{(r)}\).
As the \(m\) heads have produced \(m\) separate answers for each token position, the head outputs are concatenated: \[ C=\operatorname{concat}(A^{(1)}V^{(1)},\ldots,A^{(m)}V^{(m)}). \] We want \(C\) to be an \(N\times d\) matrix, because those dimensions are expected by the rest of the model. Therefore the learned matrices of each head have dimensions \[ W_Q^{(r)},W_K^{(r)},W_V^{(r)}\in\R^{d\times d_h}, \] where \(d_h=d/m\).33In standard implementations \(d\) is chosen as a multiple of \(m\), so that all heads can have the same integer width \(d_h=d/m\). After concatenation, the model mixes the outputs of the different heads by the learned linear map \(C W_O\).
As in neural networks, a skip connection adds the attention update to the current representation: \[ X'=X+C W_O. \] The attention block therefore learns a change to \(X\), not a complete replacement of \(X\).
We remark in passing that the sum \(X + CW_O\) is not normalized: under the pre-norm convention adopted above, normalization is applied to the input of each sublayer, never to the residual sum itself.
The matrix \(X'\) is normalized again and sent through a feed-forward network:34This step was omitted in the one-head GPT because that example was meant to show the smallest attention-based prediction mechanism. That model still contains nonlinear operations, for instance the softmax in attention and the final softmax that turns token scores into a pmf, but it does not contain this extra learned row-wise neural-network layer. \[ X''=\LN{X'},\qquad \operatorname{FFN}(X'')=\phi(X''W_1+b_1)W_2+b_2. \] This is the ordinary neural-network part of the transformer block: first an affine transformation \(X''W_1+b_1\), then a componentwise nonlinearity \(\phi\),35GPT uses the GELU. and then another affine transformation. The attention step and the feed-forward step therefore do different jobs. Attention mixes information between token positions: each row can use information from earlier rows. The feed-forward network works row by row, so each token position gets a nonlinear transformation of the information that attention has collected. It is common to expand each row temporarily to dimension \(4d\): then \(W_1\in\R^{d\times 4d}\) and \(W_2\in\R^{4d\times d}\). The first matrix gives the nonlinearity \(\phi\) more coordinates36Empirically this larger intermediate dimension turns out to work well in transformer architectures. to act on; the second matrix maps back to dimension \(d\).
Finally, the feed-forward update is added back through a residual connection: \[ H=X' + \operatorname{FFN}(X''). \] The single output matrix \(H \in \R^{N\times d}\) is now ready for the same next-token-selection box used in the one-head GPT.
The full model applies \(L\) such transformer blocks37These are the layers of neural networks. in turn, each with its own matrices, as shown in Fig. 8.3. We write \(H^{(0)}=X\). Transformer block \(l\) takes \(H^{(l-1)}\) as input and returns \(H^{(l)}\). After the last block, GPTs usually apply one more layer normalization, \[ H=\LN{H^{(L)}}. \] This final matrix \(H\) is sent to the same next-token-selection box used in the one-head GPT.
Now that the architecture is complete, one remark on the word transformer is in order. The original transformer of Vaswani et al.38Wikipedia: Attention Is All You Need. was built for machine translation and consists of two coupled stacks of blocks, one for the source language, the other for the target language. A GPT has no separate source text to encode, so it needs no encoder. For this reason the GPT architecture is called decoder-only.
8.4 Training
We have introduced several matrices. The embedding matrix \(E\), the position matrix \(P\), and the unembedding matrix \(W_{\mathrm{out}}\) from (8.3.1) belong to the input and output sides of the GPT. In addition, each transformer block has its own head matrices \(W_Q^{(r)}\), \(W_K^{(r)}\), \(W_V^{(r)}\) for \(r=1,\ldots,m\), its own output matrix \(W_O\), and its own feed-forward matrices \(W_1\) and \(W_2\). It remains to explain in some detail how their entries are learned.
Training starts with filling the learned matrices with random numbers, for instance \(\sim \Norm{0,1}\). At this point the GPT has no useful relation to language.
The model is then shown a large amount of real text.39Real GPTs also use dropout Chapter 7 during training: the outputs of the attention and the feed-forward steps are randomly thinned before each residual addition. During text generation, dropout is switched off. Training does not use this text all at once. In one training step we pick a mini-batch \(\Bset\) of training sequences from the corpus. Write these sequences as \[ (t_{b,1},\ldots,t_{b,N+1}),\qquad b\in\Bset. \] For ease we assume that all sequences have the same length \(N+1\). For one sequence \(b\), the first \(N\) tokens form the input context. The GPT constructs \(X_b\), applies the transformer stack, and obtains \(H_b\in\R^{N\times d}\). From this matrix it computes the score matrix \[ S_b=H_bW_{\mathrm{out}}\in\R^{N\times v}. \] Thus, row \(S_{b,i}\) contains the scores for the token id after position \(i\) for the sequence \(b\). Because of the causal mask, these scores are based only on \(t_{b,1},\ldots,t_{b,i}\), not on future tokens. Applying softmax row by row gives the predicted pmf \[ \hat p_{b,i}=\sm(S_{b,i})\in\R^v,\qquad i=1,\ldots,N. \]
The observed next token is \(t_{b,i+1}\). Using the label-lifting map from (3.1.2), represent it by the one-hot vector \(q_{b,i}=r(t_{b,i+1})\). The cross-entropy loss at this position is \[ \lscr(q_{b,i},\hat p_{b,i}) = -\sum_{k=1}^{v} q_{b,i,k}\log \hat p_{b,i,k} = -\log \hat p_{b,i,t_{b,i+1}}. \] The last equality uses that \(q_{b,i}\) is one-hot.40All components are zero except the component corresponding to the observed next token \(t_{b,i+1}\). For sequence \(b\), the empirical risk over its \(N\) positions is
\begin{align*} \hat R_b &= \frac1N\sum_{i=1}^{N}\lscr(q_{b,i},\hat p_{b,i}) = -\frac1N\sum_{i=1}^{N} \log \hat p_{b,i,t_{b,i+1}}. \end{align*}The empirical risk of the mini-batch is the average over its \(|\Bset|\) sequences: \[ \hat R_{\Bset} = \frac{1}{|\Bset|}\sum_{b\in\Bset}\hat R_b. \] Backpropagation computes the gradient of this one number with respect to all parameters. The optimizer then updates the parameters once. That whole procedure is one training step. Training then consists of repeating many training steps.41The number of steps depends on the model, the corpus, and the mini-batch size. Larger GPTs may use millions of optimizer steps.
The training just described is called pre-training; it explains the P in GPT. For systems such as ChatGPT and Claude this is only the first stage in training: after pre-training on a large corpus, the model is trained further on smaller, curated datasets, so that it follows instructions and behaves as a helpful assistant. This second stage is called fine-tuning or post-training, and falls outside the scope of this chapter.
Above we already discussed transformers. It remains to explain what generative means in GPT. The trained model computes just one object: the pmf \(p=\sm(s)\) over the vocabulary for the position right after the current context. It writes text by using this pmf repeatedly: sample a token from \(p\), append it to the context, recompute \(p\) from the extended context, and continue until \(\cn{Eos}\) is drawn. These are steps 5–8 of Section 8.1. So the response is not produced in one piece; it is built one token at a time, and each token generated becomes part of the input used for the next.
The word large in large language model refers to the number of training parameters. A rough count explains the scale. Start with one transformer block. Each of the \(m\) heads has its query, key, and value matrices \(W_Q^{(r)},W_K^{(r)},W_V^{(r)}\in\R^{d\times d_h}\), giving \(3 d d_h\) entries per head, and \(3 m d d_h\) over all \(m\) heads. As \(d_h = d/m\), this amounts to \(3d^2\) parameters. The output matrix \(W_O\in\R^{d\times d}\) contributes another \(d^2\). Thus the multi-head attention part has about \(4d^2\) learned entries. The feed-forward network is wider; as \(W_1\in\R^{d\times 4d}\) and \(W_2\in\R^{4d\times d}\), they contribute \(8d^2\) parameters. Therefore one transformer block has roughly \(4d^2+8d^2 = 12d^2\) parameters.42We ignore biases, the \(E\) and \(P\) matrices, the unembedding matrix \(W_{\mathrm{out}}\), and some other parameters. With \(L\) transformer blocks, the stack contributes about \(12Ld^2\) parameters.43\(L=32,d=4096\) for GPT-3 6.7B and \(L=96,d=12288\) for GPT-3 175B are reported in Wikipedia: GPT-3. For example, in GPT-3, \(L=96\) and \(d=12288\) which gives \(\approx 174\) billion parameters from the transformer stack alone.
8.5 Exercises
What is the role of the token vocabulary in a GPT?
Solution
Solution, for real
The vocabulary is the fixed list of tokens used by the tokenizer. It maps text to token ids at the input and maps generated token ids back to text at the output. It also determines the number of possible next-token scores.
Why does self-attention use separate query and key matrices?
Solution
Solution, for real
The matrices transform token representations into queries and keys whose inner products measure how relevant one token is to another. This lets attention learn relations that are not determined by similarity of the original embeddings.
What is an attention head?
Solution
Solution, for real
An attention head is one set of query, key, and value matrices together with the resulting attention computation and head output.
Where in a GPT is the vocabulary used?
Solution
Solution, for real
It is used by the tokenizer and detokenizer, by the embedding lookup at the input, and by the final score vector and softmax, which assign probabilities to the vocabulary tokens.
What is the relation between the vocabulary and the embedding matrix \(E\)?
Solution
Solution, for real
The embedding matrix has one row for each vocabulary token. If the vocabulary has size \(v\) and the embedding dimension is \(d\), then \(E\in\R^{v\times d}\), and the row indexed by a token id is that token’s embedding.
Suppose \(X\in\R^{N\times d}\) is processed by five attention heads. What is the dimension of the output of one head, assuming all heads have equal width?
Solution
Solution, for real
Each head has width \(d_h=d/5\). Its query, key, and value matrices have shape \(d\times d_h\), and its output has shape \(N\times d_h=N\times(d/5)\).
Let \(t_i\) be the token id at sequence position \(i\). Is the \(i\)-th row of the context matrix \(x_i = E_{t_i}+P_i\) or \(x_i = E_{i}+P_{t_i}\)?
Solution
Solution, for real
\(x_i = E_{t_i}+P_i\). Row \(t_i\) of \(E\) is the embedding of the token that occurs at position \(i\), and row \(i\) of \(P\) is the embedding of position \(i\). The other expression indexes \(E\) by a position and \(P\) by a token id, which is meaningless.
What is the dimension of the position matrix \(P\)? Relate it to the embedding matrix \(E\) and to the context matrix \(X\).
Solution
Solution, for real
\(P\in\R^{N_{\max}\times d}\): one row per position in the context window, each row a \(d\)-dimensional vector. It has the same number of columns as the embedding matrix \(E\in\R^{v\times d}\), but its rows are indexed by position instead of by token id. For a sequence of length \(N\le N_{\max}\), the first \(N\) rows of \(P\) are added to the corresponding rows of \(X\in\R^{N\times d}\).
Which steps of the list in Section 8.1 make a GPT generative, and what turns these steps into a loop? #+beginsolution Steps 5–8. Read in that list what each of them does with the token that was just sampled, and when the loop stops.
One training step draws a mini-batch \(\Bset\) of sequences from the corpus. At which positions is the cross-entropy loss evaluated, and how is it used in gradient descent?
Solution
Solution, for real
At every position \(i=1,\ldots,N\) of every sequence \(b\in\Bset\), by comparing the predicted pmf \(\hat p_{b,i}\) with the observed next token \(t_{b,i+1}\). Look up in the text which two averages lead from these losses to \(\hat R_{\Bset}\), and which two operations turn that one number into a parameter update.
Explain the tokenizing process of a longest-match tokenizer. Give an example to demonstrate how it works.
Solution
Solution, for real
add followed by s in the first case, the single token adds in the second.
Reread the rule in the text to see which token is chosen at a position, and how
far the tokenizer then moves forward.
Starting from the context matrix \(X\), how does an attention head produce its head output \(H\)? Name all matrices that are used in this process.
Solution
Solution, for real
It forms \(Q=XW_Q\), \(K=XW_K\) and \(V=XW_V\), builds the attention matrix \(A\) from \(Q\), \(K\) and the causal mask \(M\), and returns \(H=AV\). Besides \(X\) and \(H\), the matrices are the learned \(W_Q,W_K,W_V\) and \(Q,K,V,M,A\). Look up in the text why \(A\) divides by \(\sqrt d\) and why \(M\) is added.
Describe how the next-token selection works, starting from the output matrix \(H\).
Solution
Solution, for real
The last row \(H_N\) serves as the summary vector \(h\); the unembedding matrix gives the scores \(s=hW_{\mathrm{out}}\in\R^v\), and the softmax turns them into the pmf \(p=\sm(s)\), from which a token is sampled. Look up in the text why it is the last row that is used, and what a temperature \(\tau\) does to \(p\).
Explain how a GPT turns text into the context matrix \(X\).
Solution
Solution, for real
The tokenizer maps the text to token ids with the vocabulary. If \(t_i\) is the id at position \(i\), then \(x_i = E_{t_i}+P_i\), and stacking these rows gives \(X\in\R^{N\times d}\). Look up in the text what goes wrong when the term \(P_i\) is left out.
When there are \(m\) attention heads, what are the dimensions of the query matrix \(W_Q^{(r)}\) of head \(r\) and of the queries \(Q^{(r)}\) it produces?
Solution
Solution, for real
\(W_Q^{(r)}\in\R^{d\times d_h}\) and \(Q^{(r)}\in\R^{N\times d_h}\), with \(d_h=d/m\). Look up in the text which requirement on the concatenated head outputs forces the width \(d_h\) to be \(d/m\).
Intuitively, why does a transformer block use several attention heads instead of one?
Solution
Solution, for real
Each head has its own \(W_Q^{(r)},W_K^{(r)},W_V^{(r)}\), hence its own attention matrix \(A^{(r)}\), so each head can pick up a different kind of relation between the tokens. Read in the text how much of this is an empirical observation, and what it does not allow us to claim about a single head.