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 Β· Evaluation & Metrics πŸ“Š

5 min read

Loss is what the model optimizes; metrics are what you actually care about. And the gap between training and validation curves tells you whether your model is really learning.


1. The 'Why'

The loss function is a mathematical convenience β€” it's smooth and differentiable so the optimizer can work with it β€” but it's rarely the number a human cares about. Nobody ships a product because the cross-entropy hit 0.31; they ship it because it classifies images correctly 94% of the time. That human-meaningful number is a metric, and computing metrics like accuracy is how you translate training progress into a claim you can actually stand behind. A model can have a respectable-looking loss and still be useless, so tracking the right metric on held-out data is what separates "the loss went down" from "the model works."

Just as important is watching how metrics evolve on training versus validation data. This is your window into the two failure modes that haunt every deep learning project. Overfitting shows up as training loss that keeps falling while validation loss stalls or rises β€” the model is memorizing the training set instead of learning general patterns. Underfitting shows up as both staying stubbornly high β€” the model isn't capable or trained enough. Learning to read these curves turns training from guesswork into diagnosis. And it sets up Module 06 perfectly: once you can tell which epoch had the best validation performance, you'll want to save that exact model β€” the checkpoint problem.


2. Core Concepts

Loss vs. metric. The loss is optimized by gradient descent (must be differentiable). A metric (accuracy, F1, etc.) is for human interpretation and can be non-differentiable. You compute both, but only the loss goes into .backward().

Accuracy for classification. The fraction of predictions that match the true label. Get the predicted class with logits.argmax(dim=1), compare to targets, and average the matches.

Evaluate under eval() + no_grad(). All metric computation happens with the model in eval() mode and inside torch.no_grad() β€” you're measuring, not learning.

Overfitting vs. underfitting (reading the curves). Overfitting: training keeps improving, validation plateaus or worsens (a widening gap). Underfitting: both are poor. The goal is the sweet spot where validation is as good as it gets.

Tracking history. Store per-epoch train and validation numbers in lists so you can print or plot the learning curves and pick the best epoch.


3. Code in Action

Computing accuracy

import torch

def accuracy(logits, targets):
    preds = logits.argmax(dim=1)             # predicted class = highest logit
    correct = (preds == targets).sum().item()# count matches
    return correct / targets.size(0)         # fraction correct

logits = torch.tensor([[2.0, 0.1, 0.3],      # sample 0 -> predicts class 0
                       [0.2, 0.1, 1.9]])     # sample 1 -> predicts class 2
targets = torch.tensor([0, 1])               # true: class 0, class 1
print(accuracy(logits, targets))             # 0.5 -> one of two correct

A full evaluation pass

import torch

@torch.no_grad()                             # decorator = wrap whole fn in no_grad
def evaluate(model, loader, criterion, device):
    model.eval()                             # inference mode
    total_loss, total_correct, total = 0.0, 0, 0
    for X, y in loader:
        X, y = X.to(device), y.to(device)
        logits = model(X)
        total_loss += criterion(logits, y).item() * y.size(0)   # sum, weighted by batch
        total_correct += (logits.argmax(1) == y).sum().item()
        total += y.size(0)
    return total_loss / total, total_correct / total            # (avg loss, accuracy)

Tracking history to spot overfitting

history = {"train_loss": [], "val_loss": [], "val_acc": []}

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

    history["train_loss"].append(train_loss)
    history["val_loss"].append(val_loss)
    history["val_acc"].append(val_acc)
    print(f"epoch {epoch}: train {train_loss:.3f} | val {val_loss:.3f} | acc {val_acc:.3f}")

# Overfitting signal: train_loss keeps dropping while val_loss starts climbing.
best_epoch = min(range(len(history["val_loss"])), key=lambda i: history["val_loss"][i])
print(f"Best epoch by val loss: {best_epoch}")   # <-- this motivates saving a checkpoint (Module 06)

4. Common Pitfalls

Pitfall 1 β€” Judging the model by training accuracy. High training accuracy can just mean memorization. Always report the metric on a held-out validation/test set; that's the only number that reflects generalization.

Pitfall 2 β€” Computing metrics with gradients on. Forgetting eval() and no_grad() during evaluation wastes memory, slows things down, and (without eval()) gives noisy numbers because Dropout is still active. Wrap every evaluation in both.

Pitfall 3 β€” Ignoring the train/validation gap. Watching only one curve hides the diagnosis. A model with 0.99 train accuracy and 0.70 validation accuracy is overfitting badly β€” the gap is the story. Track both every epoch so you can catch it early (and later, stop or checkpoint at the best point).


5. Further Reading & Watch List


⬅️ Prev: 03 Β· The Training Loop Β· 🏁 Module complete! Back to the module index or on to Module 06 β€” Saving & Loading.