Overview

4 Implementing a GPT model from Scratch To Generate Text

This chapter walks from a top-down sketch to a working, GPT-like language model that generates text token by token. Building on the prior chapter’s masked multi-head attention, it sets a clear target—recreating the “GPT‑2 small” configuration—and outlines how tokenization, token and positional embeddings, stacked transformer blocks, and a final projection layer fit together. The author adopts a pragmatic approach: start with a placeholder backbone to map inputs to logits, then progressively replace stubs with real components, aiming at an implementation that mirrors GPT‑2’s design while remaining easy to scale to larger variants.

The core building blocks are implemented and motivated in turn. Layer normalization is introduced to stabilize training (used in a pre-layer norm setup), followed by a feed-forward subnetwork that expands the embedding dimension by 4x, applies a GELU nonlinearity, and projects back to the model width. Residual (shortcut) connections are emphasized as crucial for healthy gradient flow in deep stacks. These pieces are composed into a reusable TransformerBlock that combines attention, dropout, layer norm, residual paths, and the GELU-based feed-forward. The full GPTModel then stacks multiple blocks between embeddings and an output head. The chapter also explains parameter accounting, the notion of weight tying in GPT‑2 (why “124M” counts differ from naive totals), and the resulting memory footprint, highlighting how large even a “small” LLM can be.

Finally, the chapter shows how model outputs (logits shaped by batch, sequence length, and vocabulary size) are turned into text with a simple greedy decoding loop that repeatedly selects the most probable next token and appends it to the context. This illustrates the end-to-end path from input text to generated output and clarifies why an untrained model produces gibberish—training is covered next. Throughout, the configuration remains the single source of truth (vocab size, context length, embedding width, heads, layers, dropout, and QKV bias), making it straightforward to scale the same codebase from GPT‑2 small to larger variants and to experiment with architectural options.

A mental model of the three main stages of coding an LLM, pretraining the LLM on a general text dataset, and finetuning it on a labeled dataset. This chapter focuses on implementing the LLM architecture, which we will train in the next chapter.
A mental model of a GPT model. Next to the embedding layers, it consists of one or more transformer blocks containing the masked multi-head attention module we implemented in the previous chapter.
A mental model outlining the order in which we code the GPT architecture. In this chapter, we will start with the GPT backbone, a placeholder architecture, before we get to the individual core pieces and eventually assemble them in a transformer block for the final GPT architecture.
A big-picture overview showing how the input data is tokenized, embedded, and fed to the GPT model. Note that in our DummyGPTClass coded earlier, the token embedding is handled inside the GPT model. In LLMs, the embedded input token dimension typically matches the output dimension. The output embeddings here represent the context vectors we discussed in chapter 3.
An illustration of layer normalization where the 5 layer outputs, also called activations, are normalized such that they have a zero mean and variance of 1.
An illustration of the dim parameter when calculating the mean of a tensor. For instance, if we have a 2D tensor (matrix) with dimensions [rows, columns], using dim=0 will perform the operation across rows (vertically, as shown at the bottom), resulting in an output that aggregates the data for each column. Using dim=1 or dim=-1 will perform the operation across columns (horizontally, as shown at the top), resulting in an output aggregating the data for each row.
A mental model listing the different building blocks we implement in this chapter to assemble the GPT architecture.
The output of the GELU and ReLU plots using matplotlib. The x-axis shows the function inputs and the y-axis shows the function outputs.
provides a visual overview of the connections between the layers of the feed forward neural network. It is important to note that this neural network can accommodate variable batch sizes and numbers of tokens in the input. However, the embedding size for each token is determined and fixed when initializing the weights.
An illustration of the expansion and contraction of the layer outputs in the feed forward neural network. First, the inputs expand by a factor of 4 from 768 to 3072 values. Then, the second layer compresses the 3072 values back into a 768-dimensional representation.
A mental model showing the topics we cover in this chapter, with the black checkmarks indicating those that we have already covered.
A comparison between a deep neural network consisting of 5 layers without (on the left) and with shortcut connections (on the right). Shortcut connections involve adding the inputs of a layer to its outputs, effectively creating an alternate path that bypasses certain layers. The gradient illustrated in Figure 1.1 denotes the mean absolute gradient at each layer, which we will compute in the code example that follows.
An illustration of a transformer block. The bottom of the diagram shows input tokens that have been embedded into 768-dimensional vectors. Each row corresponds to one token's vector representation. The outputs of the transformer block are vectors of the same dimension as the input, which can then be fed into subsequent layers in an LLM.
A mental model of the different concepts we have implemented in this chapter so far.
An overview of the GPT model architecture. This figure illustrates the flow of data through the GPT model. Starting from the bottom, tokenized text is first converted into token embeddings, which are then augmented with positional embeddings. This combined information forms a tensor that is passed through a series of transformer blocks shown in the center (each containing multi-head attention and feed forward neural network layers with dropout and layer normalization), which are stacked on top of each other and repeated 12 times.
This diagram illustrates the step-by-step process by which an LLM generates text, one token at a time. Starting with an initial input context ("Hello, I am"), the model predicts a subsequent token during each iteration, appending it to the input context for the next round of prediction. As shown, the first iteration adds "a", the second "model", and the third "ready", progressively building the sentence.
details the mechanics of text generation in a GPT model by showing a single iteration in the token generation process. The process begins by encoding the input text into token IDs, which are then fed into the GPT model. The outputs of the model are then converted back into text and appended to the original input text.
An illustration showing six iterations of a token prediction cycle, where the model takes a sequence of initial token IDs as input, predicts the next token, and appends this token to the input sequence for the next iteration. (The token IDs are also translated into their corresponding text for better understanding.)

Summary

  • Layer normalization stabilizes training by ensuring that each layer's outputs have a consistent mean and variance.
  • Shortcut connections are connections that skip one or more layers by feeding the output of one layer directly to a deeper layer, which helps mitigate the vanishing gradient problem when training deep neural networks, such as LLMs.
  • Transformer blocks are a core structural component of GPT models, combining masked multi-head attention modules with fully connected feed-forward networks that use the GELU activation function.
  • GPT models are LLMs with many repeated transformer blocks that have millions to billions of parameters.
  • GPT models come in various sizes, for example, 124, 345, 762, and 1542 million parameters, which we can implement with the same GPTModel Python class.
  • The text generation capability of a GPT-like LLM involves decoding output tensors into human-readable text by sequentially predicting one token at a time based on a given input context.
  • Without training, a GPT model generates incoherent text, which underscores the importance of model training for coherent text generation, which is the topic of subsequent chapters.

FAQ

What are the main building blocks of the GPT model implemented in this chapter?The model consists of token and positional embeddings, a stack of Transformer blocks (each combining masked multi-head self-attention, a feed forward network with GELU activation, layer normalization, dropout, and residual/shortcut connections), a final layer normalization, and a linear output head that produces logits over the vocabulary.
What does each entry in GPT_CONFIG_124M control?- vocab_size: size of the tokenizer vocabulary (50,257 for GPT-2 BPE)
- context_length: maximum input sequence length handled by positional embeddings (1024)
- emb_dim: token embedding size (768)
- n_heads: number of attention heads (12)
- n_layers: number of Transformer blocks (12)
- drop_rate: dropout probability (0.1)
- qkv_bias: whether to use bias terms in Q/K/VP linear layers (disabled by default for modern LLMs; revisited when loading GPT-2 weights)
Why start with a DummyGPTModel and placeholder classes?Using DummyGPTModel with placeholder TransformerBlock and LayerNorm provides a top-down, big-picture wiring of the architecture and data flow (embeddings → blocks → norm → output head). This clarifies interfaces and shapes before filling in the real components later (actual LayerNorm, FeedForward, attention, and complete TransformerBlock).
What does layer normalization do here, and how is it implemented?Layer normalization stabilizes and speeds up training by normalizing activations per example across the feature (embedding) dimension to zero mean and unit variance, then applying learned scale and shift. Implementation details: operate over the last dimension, add a small epsilon for numerical stability, and use unbiased=False (divide by n, not n−1) to match GPT-2/TensorFlow behavior and pretrained weight compatibility.
How does GELU differ from ReLU, and why is GELU used?ReLU is piecewise linear and zeroes all negative inputs; GELU is smooth and yields small, non-zero outputs for negatives, which can improve optimization and gradient flow. GPT-2 uses a fast GELU approximation: 0.5·x·(1 + tanh(√(2/π)·(x + 0.044715·x³))).
What is the purpose and shape behavior of the FeedForward module?The FeedForward sublayer expands the embedding dimension 4× (emb_dim → 4·emb_dim), applies GELU, then projects back to emb_dim. This expand-then-contract pattern enriches token-wise representations while preserving the overall input/output shape [batch, tokens, emb_dim], enabling easy stacking.
What are shortcut (residual) connections and why are they important?Residual connections add a layer’s input back to its output, creating alternate gradient paths that mitigate vanishing gradients in deep networks. In practice, they keep gradient magnitudes more stable across layers, improving trainability in stacked Transformer blocks.
What is Pre-LayerNorm vs Post-LayerNorm, and which is used?Pre-LayerNorm applies layer norm before attention and feed-forward sublayers; Post-LayerNorm applies it after. This chapter implements Pre-LayerNorm because it yields better training dynamics and stability in modern Transformers compared to the older Post-LayerNorm design.
Why does the parameter count show about 163M when GPT-2 “small” is 124M?The original GPT-2 ties (shares) the token embedding matrix with the output projection weights (“weight tying”), reducing unique parameters. Our implementation keeps separate tok_emb and out_head weights (both shaped [vocab_size, emb_dim]), so total parameters are ~163M; subtracting the output head to emulate weight tying yields ~124M, matching GPT-2 small. Modern LLMs often keep them separate for performance.
How does the simple text generation loop work, and why is untrained output gibberish?At each step: (1) crop to the last context_length tokens, (2) forward pass to get logits, (3) take the last time-step logits, (4) optionally softmax, (5) select next token (argmax = greedy decoding), (6) append token to the input and repeat. With randomly initialized weights (no training), logits are uninformative, so generated text is nonsensical. Training (next chapter) makes outputs coherent.

pro $24.99 per month

  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose one free eBook per month to keep
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime

lite $19.99 per month

  • access to all Manning books, including MEAPs!

team

5, 10 or 20 seats+ for your team - learn more


choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Build a Large Language Model (From Scratch) ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Build a Large Language Model (From Scratch) ebook for free
choose your plan

team

monthly
annual
$49.99
$499.99
only $41.67 per month
  • five seats for your team
  • access to all Manning books, MEAPs, liveVideos, liveProjects, and audiobooks!
  • choose another free product every time you renew
  • choose twelve free products per year
  • exclusive 50% discount on all purchases
  • renews monthly, pause or cancel renewal anytime
  • renews annually, pause or cancel renewal anytime
  • Build a Large Language Model (From Scratch) ebook for free