Overview

Appendix D. Adding Bells and Whistles to the Training Loop

This appendix refines the training loop used for pretraining and finetuning by adding practical stability and convergence improvements. To keep the material self-contained, it reinitializes the GPT model and reconstructs the data loaders from earlier chapters, using a short story dataset for demonstration. The overarching goal is to introduce and then integrate three techniques—learning rate warmup, cosine decay, and gradient clipping—into a cohesive training function that can reliably pretrain a small LLM.

The first enhancement is learning rate warmup, which starts training with small updates and linearly raises the learning rate from an initial value to a user-defined peak over a fixed number of steps (for example, from 0.0001 to 0.01 over 20 steps). After warmup, a cosine decay schedule gradually reduces the learning rate from its peak toward a small floor (for example, 0.1 times the initial learning rate) following a half-cosine curve across the remaining training steps. Together, these schedules help avoid destabilizing updates early on and reduce the risk of overshooting minima later by slowing the learning rate as training progresses.

The appendix also adds gradient clipping to curb exploding gradients by constraining the L2 norm of all parameter gradients to a specified maximum (for example, max_norm = 1.0 via clip_grad_norm_). The text explains the L2 norm notionally and demonstrates, with a simple measurement of per-parameter gradients before and after clipping, that the largest gradients are reduced to a safer range. In the final step, all three techniques—warmup, cosine decay, and gradient clipping—are combined within the training loop to stabilize and improve LLM pretraining.

Figure D.1 The learning rate warmup increases the learning rate for the first 20 training steps. After 20 steps, the learning rate reaches the peak of 0.01 and remains constant for the rest of the training.
Figure D.2 The first 20 steps of linear learning rate warmup are followed by a cosine decay, which reduces the learning rate in a half-cosine cycle until it reaches its minimum point at the end of training.

D.4 The modified training function

In this final section of this appendix, we improve the train_model_simple training function we used in chapter 5 by adding the three concepts we introduced: linear warmup, cosine decay, and gradient clipping. Together, these methods help stabilize LLM training.

The code is as follows, with the changes compared to the train_model_simple annotated:

After defining the train_model function, we can use it in a similar fashion to train the model compared to the train_model_simple method in chapter 5:

The training will take about 5 minutes to complete on a MacBook Air or similar laptop and print the following outputs:

Like chapter 5, the model begins to overfit after a few epochs since it is a very small dataset, and we iterate over it multiple times. However, we can see that the function is working since it minimizes the training set loss.

Readers are encouraged to train the model on a larger text dataset and compare the results obtained with this more sophisticated training function to the results that can be obtained with the train_model_simple function used in chapter 5.

FAQ

What is learning rate warmup and why is it useful for training LLMs?Learning rate warmup gradually increases the learning rate from a small initial value (initial_lr) to a user-defined peak_lr over the first warmup_steps. This reduces the chance of destabilizing, large parameter updates early in training, which is especially helpful for large, deep models like LLMs.
How do I implement a simple linear warmup schedule in PyTorch?Compute a per-step increment lr_increment = (peak_lr - initial_lr) / warmup_steps. Track a global_step that starts at -1 and increments once per optimizer update. If global_step < warmup_steps, set lr = initial_lr + global_step * lr_increment; otherwise set lr = peak_lr. Apply the value to each optimizer parameter group via for pg in optimizer.param_groups: pg["lr"] = lr.
How should I choose the number of warmup steps and the peak learning rate?Warmup steps are often a small fraction of total steps (for example, 1–5%), but this depends on model size, batch size, and optimizer. The peak_lr should be the highest stable learning rate for your setup; err on the side of smaller values for larger models or noisier data and increase cautiously while monitoring loss stability.
What is cosine decay and how does it change the learning rate after warmup?Cosine decay smoothly reduces the learning rate from peak_lr toward a minimum value (min_lr) following a half-cosine curve after warmup. It slows updates later in training to avoid overshooting minima and to encourage stable convergence.
What is the formula used for cosine decay in this appendix?After warmup, compute progress = (global_step - warmup_steps) / (total_training_steps - warmup_steps), clamped to [0, 1]. Then set lr = min_lr + (peak_lr - min_lr) * 0.5 * (1 + cos(pi * progress)). Before warmup ends, use the linear warmup rule.
Why set a minimum learning rate (min_lr), and why 0.1 × initial_lr in the example?A minimum learning rate prevents the scheduler from driving the LR to exactly zero, which can stall learning or cause numerical issues. Using min_lr = 0.1 × initial_lr is a simple, conservative floor; it’s a heuristic and can be tuned based on validation performance.
How do I compute total_training_steps and track the schedule?Set total_training_steps = len(train_loader) * n_epochs. Maintain a global_step counter that increments once per optimizer update. Record the current optimizer.param_groups[0]["lr"] each step (for example, append to a list) and visualize it to verify the schedule.
What is gradient clipping and when should I apply it in the training loop?Gradient clipping limits the magnitude (norm) of gradients to a maximum value (max_norm) to prevent exploding updates. In PyTorch, call torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=...) after loss.backward() and before optimizer.step().
What’s the difference between clip_grad_norm_ and clip_grad_value_?clip_grad_norm_ rescales gradients so their global L2 norm does not exceed max_norm, keeping direction while reducing magnitude. clip_grad_value_ clamps individual gradient values to a specified range. For LLMs, norm-based clipping (clip_grad_norm_) is more commonly used.
How do I combine warmup, cosine decay, and gradient clipping in a complete training step?Typical order per step: set model.train(); optimizer.zero_grad(); compute lr using warmup/cosine schedule and assign to optimizer.param_groups; forward pass and compute loss; loss.backward(); torch.nn.utils.clip_grad_norm_(...); optimizer.step(); update global_step; optionally log lr and loss.

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