Phase 5: Complete Training Pipeline
Module 13: Complete Training Pipeline
What You Will Learn
In this module, you will learn:
- Complete Training Loop
- Validation Loop
- Testing Loop
- Evaluation Metrics
- Accuracy
- Precision
- Recall
- F1 Score
- Model Saving
- Checkpoints
- Early Stopping
- Building a Complete MNIST Classifier
What is a Training Pipeline?
A Training Pipeline is the complete workflow used to train, evaluate, and save a deep learning model.
Instead of writing code randomly, we follow a structured pipeline.
1Dataset 2 │ 3 ▼ 4DataLoader 5 │ 6 ▼ 7Model 8 │ 9 ▼ 10Loss Function 11 │ 12 ▼ 13Optimizer 14 │ 15 ▼ 16Training Loop 17 │ 18 ▼ 19Validation 20 │ 21 ▼ 22Testing 23 │ 24 ▼ 25Save Best Model
Complete Workflow
1Load Dataset 2 │ 3Create DataLoader 4 │ 5Build Model 6 │ 7Choose Loss 8 │ 9Choose Optimizer 10 │ 11Train 12 │ 13Validate 14 │ 15Save Best Model 16 │ 17Test
Training Loop
The training loop is where the neural network learns.
Every epoch consists of four steps:
- Forward Pass
- Compute Loss
- Backpropagation
- Update Weights
Training Loop Diagram
1Input Batch 2 │ 3 ▼ 4Forward Pass 5 │ 6 ▼ 7Loss 8 │ 9 ▼ 10Backward Pass 11 │ 12 ▼ 13Optimizer Step 14 │ 15 ▼ 16Next Batch
Complete Training Loop
1import torch 2 3def train_one_epoch(model, 4 dataloader, 5 criterion, 6 optimizer, 7 device): 8 9 model.train() 10 11 running_loss = 0.0 12 13 correct = 0 14 total = 0 15 16 for images, labels in dataloader: 17 18 images = images.to(device) 19 labels = labels.to(device) 20 21 optimizer.zero_grad() 22 23 outputs = model(images) 24 25 loss = criterion(outputs, labels) 26 27 loss.backward() 28 29 optimizer.step() 30 31 running_loss += loss.item() 32 33 _, predicted = outputs.max(1) 34 35 total += labels.size(0) 36 37 correct += predicted.eq(labels).sum().item() 38 39 epoch_loss = running_loss / len(dataloader) 40 41 accuracy = 100 * correct / total 42 43 return epoch_loss, accuracy
Validation Loop
Validation evaluates the model without updating weights.
Important:
- No gradient calculation
- No optimizer update
- Faster execution
Validation Loop
1def validate(model, 2 dataloader, 3 criterion, 4 device): 5 6 model.eval() 7 8 running_loss = 0 9 10 correct = 0 11 total = 0 12 13 with torch.no_grad(): 14 15 for images, labels in dataloader: 16 17 images = images.to(device) 18 labels = labels.to(device) 19 20 outputs = model(images) 21 22 loss = criterion(outputs, labels) 23 24 running_loss += loss.item() 25 26 _, predicted = outputs.max(1) 27 28 total += labels.size(0) 29 30 correct += predicted.eq(labels).sum().item() 31 32 loss = running_loss / len(dataloader) 33 34 accuracy = 100 * correct / total 35 36 return loss, accuracy
Why Use model.train() and model.eval()?
| Function | Purpose |
|---|---|
| model.train() | Enable training mode |
| model.eval() | Disable Dropout & use BatchNorm statistics |
Example
1model.train() 2 3# Training 4 5model.eval() 6 7# Validation/Test
Testing Loop
Testing is identical to validation.
It is performed only after training is complete.
1def test(model, 2 dataloader, 3 device): 4 5 model.eval() 6 7 correct = 0 8 total = 0 9 10 with torch.no_grad(): 11 12 for images, labels in dataloader: 13 14 images = images.to(device) 15 16 labels = labels.to(device) 17 18 outputs = model(images) 19 20 _, predicted = outputs.max(1) 21 22 total += labels.size(0) 23 24 correct += predicted.eq(labels).sum().item() 25 26 print(f"Test Accuracy: {100*correct/total:.2f}%")
Evaluation Metrics
Accuracy alone is not always enough.
Suppose
11000 Images 2 3990 Cats 4 510 Dogs
A model predicting only cats gets:
1Accuracy = 99%
But it never detects dogs.
Therefore we use additional metrics.
Accuracy
Formula
1Correct Predictions 2------------------- 3Total Predictions
Example
1accuracy = correct / total
Precision
Precision answers:
Out of all predicted positives, how many were correct?
Formula
1TP 2 3--------- 4 5TP + FP
Using Scikit-Learn
1from sklearn.metrics import precision_score 2 3precision = precision_score( 4 y_true, 5 y_pred, 6 average="macro" 7)
Recall
Recall answers:
Out of all actual positives, how many did we detect?
Formula
1TP 2 3--------- 4 5TP + FN
Example
1from sklearn.metrics import recall_score 2 3recall = recall_score( 4 y_true, 5 y_pred, 6 average="macro" 7)
F1 Score
F1 combines Precision and Recall.
Formula
12 × Precision × Recall 2 3------------------------ 4 5Precision + Recall
Example
1from sklearn.metrics import f1_score 2 3f1 = f1_score( 4 y_true, 5 y_pred, 6 average="macro" 7)
Complete Metrics Example
1from sklearn.metrics import ( 2 accuracy_score, 3 precision_score, 4 recall_score, 5 f1_score 6) 7 8y_true = [0,1,1,0,1] 9 10y_pred = [0,1,0,0,1] 11 12print("Accuracy:", 13 accuracy_score(y_true,y_pred)) 14 15print("Precision:", 16 precision_score(y_true,y_pred)) 17 18print("Recall:", 19 recall_score(y_true,y_pred)) 20 21print("F1:", 22 f1_score(y_true,y_pred))
Model Saving
After training, save the model.
1torch.save( 2 model.state_dict(), 3 "model.pth" 4)
Loading Saved Model
1model.load_state_dict( 2 3 torch.load("model.pth") 4) 5 6model.eval()
Why Save state_dict()?
Instead of saving the entire model object, PyTorch recommends saving only the learned parameters.
Benefits:
- Smaller file size
- Better compatibility
- Easier to load across projects
Checkpoints
A checkpoint saves:
- Model
- Optimizer
- Current Epoch
- Loss
Useful when training large models.
Save Checkpoint
1torch.save({ 2 3 "epoch": epoch, 4 5 "model_state_dict": 6 model.state_dict(), 7 8 "optimizer_state_dict": 9 optimizer.state_dict(), 10 11 "loss": loss 12 13}, "checkpoint.pth")
Load Checkpoint
1checkpoint = torch.load( 2 "checkpoint.pth" 3) 4 5model.load_state_dict( 6 7 checkpoint["model_state_dict"] 8) 9 10optimizer.load_state_dict( 11 12 checkpoint["optimizer_state_dict"] 13) 14 15epoch = checkpoint["epoch"]
Early Stopping
Sometimes validation loss stops improving.
Continuing training only causes overfitting.
Example
1Epoch 2 310 4 5Validation Loss = 0.20 6 711 8 90.20 10 1112 12 130.20 14 1513 16 170.21 18 19↓ 20 21Stop Training
Simple Early Stopping
1best_loss = float("inf") 2 3patience = 5 4 5counter = 0 6 7for epoch in range(epochs): 8 9 train_loss, _ = train_one_epoch( 10 model, 11 train_loader, 12 criterion, 13 optimizer, 14 device 15 ) 16 17 val_loss, _ = validate( 18 model, 19 val_loader, 20 criterion, 21 device 22 ) 23 24 if val_loss < best_loss: 25 26 best_loss = val_loss 27 28 counter = 0 29 30 torch.save( 31 model.state_dict(), 32 "best_model.pth" 33 ) 34 35 else: 36 37 counter += 1 38 39 if counter >= patience: 40 41 print("Early stopping!") 42 43 break
Complete Training Pipeline
1for epoch in range(10): 2 3 train_loss, train_acc = train_one_epoch( 4 model, 5 train_loader, 6 criterion, 7 optimizer, 8 device 9 ) 10 11 val_loss, val_acc = validate( 12 model, 13 val_loader, 14 criterion, 15 device 16 ) 17 18 print( 19 f"Epoch {epoch+1}" 20 21 f" Train Loss:{train_loss:.4f}" 22 23 f" Train Acc:{train_acc:.2f}%" 24 25 f" Val Loss:{val_loss:.4f}" 26 27 f" Val Acc:{val_acc:.2f}%" 28 )
Practice Project
MNIST Classifier
Step 1: Import Libraries
1import torch 2import torch.nn as nn 3import torch.optim as optim 4 5from torchvision import datasets 6from torchvision import transforms 7 8from torch.utils.data import DataLoader
Step 2: Load Dataset
1transform = transforms.ToTensor() 2 3train_dataset = datasets.MNIST( 4 root="data", 5 train=True, 6 download=True, 7 transform=transform 8) 9 10test_dataset = datasets.MNIST( 11 root="data", 12 train=False, 13 download=True, 14 transform=transform 15) 16 17train_loader = DataLoader( 18 train_dataset, 19 batch_size=64, 20 shuffle=True 21) 22 23test_loader = DataLoader( 24 test_dataset, 25 batch_size=64 26)
Step 3: Create Model
1class MNISTModel(nn.Module): 2 3 def __init__(self): 4 5 super().__init__() 6 7 self.network = nn.Sequential( 8 9 nn.Flatten(), 10 11 nn.Linear(28*28,128), 12 13 nn.ReLU(), 14 15 nn.Linear(128,10) 16 ) 17 18 def forward(self,x): 19 20 return self.network(x)
Step 4: Initialize
1device = torch.device( 2 3 "cuda" 4 5 if torch.cuda.is_available() 6 7 else "cpu" 8) 9 10model = MNISTModel().to(device) 11 12criterion = nn.CrossEntropyLoss() 13 14optimizer = optim.Adam( 15 16 model.parameters(), 17 18 lr=0.001 19)
Step 5: Train
1epochs = 5 2 3for epoch in range(epochs): 4 5 train_loss, train_acc = train_one_epoch( 6 model, 7 train_loader, 8 criterion, 9 optimizer, 10 device 11 ) 12 13 print( 14 15 f"Epoch {epoch+1}" 16 17 f" Loss:{train_loss:.4f}" 18 19 f" Accuracy:{train_acc:.2f}%" 20 )
Step 6: Test
1test( 2 model, 3 test_loader, 4 device 5)
Complete Training Pipeline
1Dataset 2 │ 3 ▼ 4DataLoader 5 │ 6 ▼ 7Model 8 │ 9 ▼ 10Loss Function 11 │ 12 ▼ 13Optimizer 14 │ 15 ▼ 16Training 17 │ 18 ▼ 19Validation 20 │ 21 ▼ 22Checkpoint 23 │ 24 ▼ 25Testing 26 │ 27 ▼ 28Best Model
Best Practices
- Always split data into training, validation, and testing sets.
- Use
model.train()during training andmodel.eval()during validation and testing. - Wrap evaluation code with
torch.no_grad()to reduce memory usage and speed up inference. - Monitor both training and validation metrics to detect overfitting.
- Save the best-performing model based on validation performance rather than the final epoch.
- Save optimizer and scheduler states in checkpoints to resume interrupted training.
- Apply Early Stopping when validation loss stops improving.
- Report multiple metrics such as Accuracy, Precision, Recall, and F1 Score, especially for imbalanced datasets.
Module Summary
In this module, you learned:
- ✅ How to build a complete PyTorch training, validation, and testing pipeline.
- ✅ The purpose of
model.train(),model.eval(), andtorch.no_grad(). - ✅ How to compute key evaluation metrics including Accuracy, Precision, Recall, and F1 Score.
- ✅ How to save and reload model weights using
state_dict(). - ✅ How to create and restore training checkpoints.
- ✅ How Early Stopping helps prevent overfitting and saves the best model.
- ✅ How to implement a complete MNIST image classifier using PyTorch.
After completing this module, you have all the essential building blocks needed to train, evaluate, save, and deploy deep learning models using a professional PyTorch training pipeline.