Background
Sections
IntroductionModule 01 β€” Tensors 🧊01 Β· Creating Tensors 🧊02 Β· Indexing & Reshaping πŸ”ͺ03 Β· Tensor Math βž—04 Β· Device Placement πŸ–₯️⚑Module 02 β€” Autograd βš™οΈ01 Β· The Computational Graph πŸ•ΈοΈ02 Β· Backward Pass & Gradients ⬅️03 Β· Turning Autograd Off πŸ›‘04 Β· Gradient Gotchas πŸͺ€Module 03 β€” Neural Networks01 Β· The `nn.Module` Basics 🧱02 Β· Common Layers 🧩03 Β· Building a Network πŸ—οΈ04 Β· Inspecting Models πŸ”Module 04 β€” Data Handling πŸ—‚οΈ01 Β· Dataset Basics πŸ“‡02 Β· The DataLoader 🚚03 Β· Transforms 🎨04 Β· Splits & Built-in Datasets βœ‚οΈModule 05 β€” The Training Loop πŸ”01 Β· Loss Functions 🎯02 Β· Optimizers βš™οΈ03 Β· The Training Loop πŸ”04 Β· Evaluation & Metrics πŸ“ŠModule 06 β€” Saving & Loading πŸ’Ύ01 Β· `state_dict` Basics πŸ’Ύ02 Β· Checkpoints & Resuming ⏸️03 Β· Loading for Inference πŸš€04 Β· Best Model & Early Stopping πŸ…Module 07 β€” Computer Vision πŸ‘οΈ01 Β· Convolutions πŸ”²02 Β· CNN Architecture πŸ›οΈ03 Β· Transfer Learning πŸ”04 Β· Image Classification Project πŸ§ͺModule 08 β€” NLP & Transformers πŸ’¬01 Β· Text Data & Tokenization πŸ”€02 Β· Embeddings 🧭03 Β· Recurrent Layers & LSTMs πŸ”„04 Β· Intro to Transformers ⚑Module 09 β€” Ecosystem: PyTorch Lightning ⚑01 Β· Why Lightning? πŸ€”02 Β· The LightningModule 🧩03 Β· The Trainer πŸŽ›οΈ04 Β· DataModules & Callbacks 🧰Module 10 β€” Deployment & Optimization πŸš€01 Β· Exporting Models πŸ“¦02 Β· `torch.compile` ⚑03 Β· Inference Optimization πŸͺΆ04 Β· Serving & Next Steps πŸŽ“

04 Β· Best Model & Early Stopping πŸ…

4 min read

The model at the last epoch is rarely the best model. This sub-module is how you capture the best one β€” and stop wasting time once it stops improving.


1. The 'Why'

Module 05 ended with an uncomfortable truth revealed by the training curves: as training continues, the model's performance on the validation set improves for a while, then plateaus, then often gets worse as the model begins to overfit. This means the model you happen to have when the loop finishes β€” the last-epoch model β€” is frequently not the best one you trained. Somewhere in the middle was a version that generalized better, and if you only saved at the end, you threw it away. Best-model checkpointing fixes this: you watch a validation metric every epoch and save a checkpoint only when it improves, so at the end you still hold the single best version regardless of how the run finished.

Its natural companion is early stopping. If validation performance hasn't improved for several epochs, continuing is usually a waste of compute and risks drifting further into overfitting. Early stopping watches for that stagnation and halts training automatically once a "patience" window passes with no improvement. Together these two techniques turn the raw training loop from Module 05 into a disciplined procedure that reliably produces β€” and preserves β€” a good model. They lean on everything from this module (state_dict saving, checkpoints) and close the arc that began with reading train/validation curves.


2. Core Concepts

Best-model checkpointing. Track the best validation score seen so far. Each epoch, if the current score beats it, save the model's state_dict (overwriting best_model.pt) and update the record. At the end, that file holds the best model.

Which metric to monitor. Pick one validation metric as the criterion β€” lower is better for loss, higher is better for accuracy. Be consistent about the direction.

Early stopping & "patience". Count epochs since the last improvement. When that counter reaches your patience threshold, break out of the training loop. Patience trades a little extra training against the risk of stopping too soon on a noisy plateau.

Restore the best at the end. After the loop (whether it finished or stopped early), load best_model.pt back into the model so you're using the best version, not the last.


3. Code in Action

Saving the best model each epoch

import torch

best_val = float("inf")            # we're monitoring validation LOSS (lower is better)

for epoch in range(100):
    train_loss = train_one_epoch(model, train_loader, criterion, optimizer, device)
    val_loss, val_acc = evaluate(model, val_loader, criterion, device)

    if val_loss < best_val:        # improved!
        best_val = val_loss
        torch.save(model.state_dict(), "best_model.pt")   # keep only the best
        print(f"epoch {epoch}: new best val_loss {val_loss:.4f} β€” saved")

Adding early stopping with patience

import torch

best_val = float("inf")
patience = 5                        # stop after 5 epochs with no improvement
epochs_no_improve = 0

for epoch in range(100):
    train_loss = train_one_epoch(model, train_loader, criterion, optimizer, device)
    val_loss, val_acc = evaluate(model, val_loader, criterion, device)

    if val_loss < best_val:
        best_val = val_loss
        epochs_no_improve = 0                              # reset the counter
        torch.save(model.state_dict(), "best_model.pt")
    else:
        epochs_no_improve += 1                             # another stagnant epoch
        if epochs_no_improve >= patience:
            print(f"Early stopping at epoch {epoch}")
            break

Restoring the best model after training

import torch

# The loop may have ended on a worse epoch β€” load the best one back in
model.load_state_dict(torch.load("best_model.pt", map_location=device))
model.eval()
print("Restored best model for evaluation / deployment.")

4. Common Pitfalls

Pitfall 1 β€” Reporting the last-epoch model as your result. Without best-model saving, you present whatever the loop ended on, which may be overfit. Always monitor validation and keep the best checkpoint; report that model.

Pitfall 2 β€” Monitoring the training metric for stopping. Training loss almost always keeps falling, so early stopping on it never triggers (and misses overfitting entirely). Monitor a validation metric β€” that's the whole point.

Pitfall 3 β€” Patience too small (or too large). Too small a patience stops on normal validation noise before the model has converged; too large wastes compute and overfits. A few epochs (e.g. 5–10) is a reasonable starting point; tune it to how noisy your validation curve is.


5. Further Reading & Watch List


⬅️ Prev: 03 Β· Loading for Inference Β· 🏁 Module & Phase 2 complete! Back to the module index or on to Module 07 β€” Computer Vision.