First time studying this topic?

Launch the interactive walkthrough to discover each component, including the 3D Q-Sphere Lab & Python in Colab.

Back to Deep Learning
Topic 3.10

Building a Deep Neural Network: Module 3 Capstone

Synthesize Module 3: construct, train, validate, and evaluate a multi-layer deep neural network on handwritten digits (MNIST).

Learning Objectives

  • ✓Build a full Multi-Layer Perceptron (MLP) for computer vision digit recognition
  • ✓Implement PyTorch DataLoader mini-batch pipelines
  • ✓Track training and validation loss curves across epochs
  • ✓Evaluate confusion matrix and per-class digit test accuracy

📖Let's Begin with a Story

In 1998, Yann LeCun, Corinna Cortes, and Christopher Burges released the MNIST dataset: 70,000 scanned 28x28 grayscale images of handwritten digits (0 through 9) written by US Census Bureau employees and high school students. MNIST became the "Hello World" of deep learning. For decades, researchers benchmarked every major breakthrough on this dataset. In this capstone, you will construct a deep neural network that ingests 784 raw pixel inputs, passes them through multiple hidden non-linear layers, and achieves over 95% accuracy in recognizing human handwriting.

Why Should You Learn Building a Deep Neural Network: Module 3 Capstone?

🎯 Practical & Engineering Stakes

The definitive computer vision milestone

Taking raw pixel arrays and transforming them through layered representation into accurate semantic classifications marks your graduation into practical deep learning engineering.

What Is It?

Simple Explanation

We are building a brain that can read messy human handwriting: 1. Each 28x28 image has 784 pixels. 2. We flatten the pixels into a row of 784 numbers. 3. We send them through a Hidden Layer of 128 neurons (which look for lines and curves). 4. We send them through a second Hidden Layer of 64 neurons (which combine the curves into loops and strokes). 5. The 10 output neurons vote on which digit it is (0 through 9).

Technical Explanation

End-to-end deep learning architecture implementing an MLP classifier: R784→R128→R64→R10\mathbb{R}^{784} \rightarrow \mathbb{R}^{128} \rightarrow \mathbb{R}^{64} \rightarrow \mathbb{R}^{10} trained via mini-batch SGD with momentum on Categorical Cross-Entropy objective. Inputs are normalized to zero-mean and unit variance.

📚 Key Terms

DataLoaderDataLoader

Simple: A high-speed helper that shuffles data and serves it in bite-sized batches.
Technical: PyTorch utility managing mini-batch construction, multi-process workers, and memory pinning.

FlatteningFlattening

Simple: Unrolling a 2D square image (28x28) into a single flat line of 784 numbers.
Technical: Reshaping tensor from spatial dimensions (B,H,W)(B, H, W) to flattened feature vector (B,H⋅W)(B, H \cdot W).

📐 Mathematical Foundation

LaTeX KaTeX
y^=arg⁡max⁡c∈{0,…,9}Softmax(fMLP(x))c\hat{y} = \arg\max_{c \in \{0,\dots,9\}} \text{Softmax}\big(f_{\text{MLP}}(\mathbf{x})\big)_c

What does it mean?

Final classification decision: selecting the digit class index c that achieves the highest Softmax probability.

Understanding the Symbols

Symbol / TermMeaningBeginner Interpretation
x\mathbf{x}Flattened 784-pixel imageInput vector
y^\hat{y}Predicted digit (0-9)Final model decision

📊 Numerical Example

Image of digit "7" generates highest probability at index 7: P(7) = 0.96.

\hat{y} = \arg\max([0.01, 0.0, 0.0, 0.01, 0.01, 0.0, 0.01, 0.96, 0.0, 0.0]) = 7

Result: Classified as Digit 7

⚙️ How Does It Work?

1

Data Loading & Flattening

28x28 pixel images are unrolled into 784-element vectors.

2

Forward Propagation

Vectors pass through Layer 1 (784->128, ReLU), Layer 2 (128->64, ReLU), and Output (64->10).

3

Loss & Backpropagation

Cross-Entropy compares output logits against target digit labels and computes gradients.

4

Validation

Accuracy is audited on unseen test images.

🌍 Where Is It Used?

Automated Postal Mail Sorting

Problem: Sorting millions of handwritten envelope zip codes per hour in postal centers.

Solution: Deep MLPs and CNNs read handwritten numbers directly on conveyor belts.

Bank Check Deposit Scanning

Problem: Reading check dollar amounts from mobile camera photos.

Solution: Digit recognition networks parse handwriting on mobile banking applications.

🎯 Try It Yourself

A grayscale image has dimensions of 28 pixels high by 28 pixels wide. When flattened into a 1D vector for an MLP, how many input features does it have?

Interactive activity component

🐍 Python Hands-On: PyTorch CNN Handwritten Digit Classifier (MNIST)

Build, train, and evaluate a Convolutional Neural Network (CNN) with Conv2d, MaxPool2d, and Linear layers on the MNIST dataset using PyTorch.

Open Colab

1Install Required Packages

For Google Colab or Jupyter notebooks, run this shell command to install the required dependencies:

Terminal / Notebook Install Commandbash
!pip install torch torchvision matplotlib
Packages:
torchtorchvisionmatplotlib

2Import Libraries

Import Statementspython
1import torch
2import torch.nn as nn
3import torchvision.transforms as transforms
4import torchvision.datasets as datasets
5from torch.utils.data import DataLoader
6import matplotlib.pyplot as plt

import torch → Core PyTorch tensor and autograd library

import torch.nn as nn → Neural network layers (Conv2d, MaxPool2d, Linear)

import torchvision.transforms as transforms → Image transformations and normalization

import torchvision.datasets as datasets → MNIST benchmark dataset loader

from torch.utils.data import DataLoader → Mini-batch generator

import matplotlib.pyplot as plt → Plotting utility

3Demonstration Code & Logic

Python Execution Codepython
1# Transform: Convert image to Tensor and normalize pixels to [-1, 1]
2transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])
3
4# Download and prepare MNIST training and test datasets
5train_data = datasets.MNIST(root='./data', train=True, download=True, transform=transform)
6test_data = datasets.MNIST(root='./data', train=False, download=True, transform=transform)
7
8# Create mini-batch DataLoaders (batch size = 64)
9train_loader = DataLoader(train_data, batch_size=64, shuffle=True)
10test_loader = DataLoader(test_data, batch_size=64, shuffle=False)
11
12# Define Convolutional Neural Network (CNN) Architecture
13class CNN(nn.Module):
14 def __init__(self):
15 super(CNN, self).__init__()
16 self.conv1 = nn.Conv2d(1, 32, kernel_size=3, stride=1, padding=1)
17 self.pool = nn.MaxPool2d(2, 2)
18 self.fc1 = nn.Linear(32 * 14 * 14, 10)
19
20 def forward(self, x):
21 x = self.pool(torch.relu(self.conv1(x)))
22 x = x.view(-1, 32 * 14 * 14)
23 x = self.fc1(x)
24 return x
25
26model = CNN()
27criterion = nn.CrossEntropyLoss()
28optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
29
30# Train for 5 epochs over the MNIST dataset
31for epoch in range(5):
32 for images, labels in train_loader:
33 optimizer.zero_grad()
34 outputs = model(images)
35 loss = criterion(outputs, labels)
36 loss.backward()
37 optimizer.step()
38 print(f"Epoch {epoch+1}, Loss: {loss.item():.4f}")
39
40# Evaluate model accuracy on unseen test set (10,000 images)
41correct = 0
42total = 0
43with torch.no_grad():
44 for images, labels in test_loader:
45 outputs = model(images)
46 _, predicted = torch.max(outputs.data, 1)
47 total += labels.size(0)
48 correct += (predicted == labels).sum().item()
49
50print(f"Test Accuracy: {100 * correct / total:.2f}%")
Step-by-Step Code Breakdown:

# Transform: Convert image to Tensor and normalize pixels to [-1, 1]

Normalization pipeline

transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])

Normalize with mean 0.5 and std 0.5

# Download and prepare MNIST training and test datasets

Data loading

train_data = datasets.MNIST(root='./data', train=True, download=True, transform=transform)

Load 60,000 training images

test_data = datasets.MNIST(root='./data', train=False, download=True, transform=transform)

Load 10,000 test images

# Create mini-batch DataLoaders (batch size = 64)

DataLoaders

train_loader = DataLoader(train_data, batch_size=64, shuffle=True)

Shuffle training batches

test_loader = DataLoader(test_data, batch_size=64, shuffle=False)

Test batches

# Define Convolutional Neural Network (CNN) Architecture

CNN definition

class CNN(nn.Module):

Subclass nn.Module

def __init__(self):

Constructor

super(CNN, self).__init__()

Initialize parent

self.conv1 = nn.Conv2d(1, 32, kernel_size=3, stride=1, padding=1)

32 feature filters (3x3)

self.pool = nn.MaxPool2d(2, 2)

2x2 max-pooling (downsamples 28x28 to 14x14)

self.fc1 = nn.Linear(32 * 14 * 14, 10)

Dense output layer mapping to 10 classes

def forward(self, x):

Forward computation

x = self.pool(torch.relu(self.conv1(x)))

Conv -> ReLU -> MaxPool

x = x.view(-1, 32 * 14 * 14)

Flatten feature maps

x = self.fc1(x)

Linear output logits

return x

Return predictions

model = CNN()

Instantiate CNN model

criterion = nn.CrossEntropyLoss()

Cross-entropy loss function

optimizer = torch.optim.Adam(model.parameters(), lr=0.001)

Adam optimizer with lr=0.001

# Train for 5 epochs over the MNIST dataset

Training loop

for epoch in range(5):

Loop through epochs

for images, labels in train_loader:

Iterate mini-batches

optimizer.zero_grad()

Clear previous gradients

outputs = model(images)

Forward pass

loss = criterion(outputs, labels)

Compute batch loss

loss.backward()

Backpropagate error

optimizer.step()

Update weights

print(f"Epoch {epoch+1}, Loss: {loss.item():.4f}")

Log epoch progress

# Evaluate model accuracy on unseen test set (10,000 images)

Testing loop

correct = 0

Counter for correct predictions

total = 0

Total test samples

with torch.no_grad():

Disable gradient tracking for inference

for images, labels in test_loader:

Loop over test batches

outputs = model(images)

Predict on test images

_, predicted = torch.max(outputs.data, 1)

Select highest logit index

total += labels.size(0)

Tally batch size

correct += (predicted == labels).sum().item()

Count correct matches

print(f"Test Accuracy: {100 * correct / total:.2f}%")

Display final test accuracy

📋 Complete Script (All-in-One)

Click "Copy for Colab", open Google Colab, paste into a single cell, and run.

Google Colab Notebook Scriptpython
1# =========================================================
2# PyTorch CNN Handwritten Digit Classifier (MNIST)
3# Prepared for Google Colab (Run with Shift + Enter)
4# =========================================================
5
6# Step 1: Install required packages
7!pip install torch torchvision matplotlib
8
9# Step 2: Import libraries
10import torch
11import torch.nn as nn
12import torchvision.transforms as transforms
13import torchvision.datasets as datasets
14from torch.utils.data import DataLoader
15import matplotlib.pyplot as plt
16
17# Step 3: Run Demonstration
18# Transform: Convert image to Tensor and normalize pixels to [-1, 1]
19transform = transforms.Compose([transforms.ToTensor(), transforms.Normalize((0.5,), (0.5,))])
20
21# Download and prepare MNIST training and test datasets
22train_data = datasets.MNIST(root='./data', train=True, download=True, transform=transform)
23test_data = datasets.MNIST(root='./data', train=False, download=True, transform=transform)
24
25# Create mini-batch DataLoaders (batch size = 64)
26train_loader = DataLoader(train_data, batch_size=64, shuffle=True)
27test_loader = DataLoader(test_data, batch_size=64, shuffle=False)
28
29# Define Convolutional Neural Network (CNN) Architecture
30class CNN(nn.Module):
31 def __init__(self):
32 super(CNN, self).__init__()
33 self.conv1 = nn.Conv2d(1, 32, kernel_size=3, stride=1, padding=1)
34 self.pool = nn.MaxPool2d(2, 2)
35 self.fc1 = nn.Linear(32 * 14 * 14, 10)
36
37 def forward(self, x):
38 x = self.pool(torch.relu(self.conv1(x)))
39 x = x.view(-1, 32 * 14 * 14)
40 x = self.fc1(x)
41 return x
42
43model = CNN()
44criterion = nn.CrossEntropyLoss()
45optimizer = torch.optim.Adam(model.parameters(), lr=0.001)
46
47# Train for 5 epochs over the MNIST dataset
48for epoch in range(5):
49 for images, labels in train_loader:
50 optimizer.zero_grad()
51 outputs = model(images)
52 loss = criterion(outputs, labels)
53 loss.backward()
54 optimizer.step()
55 print(f"Epoch {epoch+1}, Loss: {loss.item():.4f}")
56
57# Evaluate model accuracy on unseen test set (10,000 images)
58correct = 0
59total = 0
60with torch.no_grad():
61 for images, labels in test_loader:
62 outputs = model(images)
63 _, predicted = torch.max(outputs.data, 1)
64 total += labels.size(0)
65 correct += (predicted == labels).sum().item()
66
67print(f"Test Accuracy: {100 * correct / total:.2f}%")
⚡ How to Run in Google Colab (3 Quick Steps)
  1. Click "Copy for Google Colab" above.
  2. Click Open New Google Colab Notebook (opens in a new tab).
  3. Click on the first empty code cell, press Ctrl + V (or Cmd + V) to paste, then press Shift + Enter to execute!

🔄 Algorithmic Execution Flow

1
Data Loading & Normalization

Downloads 70,000 MNIST images and scales grayscale pixels to [-1, 1] range.

→
2
Spatial Feature Extraction

Conv2d layer extracts 32 feature maps, followed by ReLU and 2x2 max-pooling to downsample spatial dimensions.

→
3
Mini-Batch Optimization

Adam optimizer minimizes CrossEntropyLoss across mini-batches of 64 images.

→
4
Unseen Test Generalization

Evaluates on 10,000 held-out test images, achieving >98% test accuracy.

📥 Input / Initial State
60,000 training and 10,000 test handwritten digit images (28x28 grayscale pixels)
📤 Expected Output
Epoch 1, Loss: 0.0842
Epoch 2, Loss: 0.0511
Epoch 3, Loss: 0.0324
Epoch 4, Loss: 0.0195
Epoch 5, Loss: 0.0128
Test Accuracy: 98.65%
💡 What Does This Demonstration Show?

The convolutional neural network effectively captures 2D spatial stroke geometries, correctly classifying unseen handwritten digits with over 98% accuracy.

Course Learning Resource • Slide DeckLesson 512 Slides

Lesson 5: Classical Deep Learning with PyTorch

Convolutional Neural Networks, MNIST dataset, backpropagation, loss functions, and optimization.

Author: Dr. Syed Muzamil Basha• FDP Approved Curriculum
Open in New Tab

📝 Learning Check

Test your understanding with these questions. Remember, this is for learning, not examination!

Question 1: In multi-class image classification with PyTorch, what does torch.argmax(logits, dim=1) do?

Question 2: What is the typical workflow for building a neural network?

Question 3: What is data augmentation in deep learning?

Question 4: What is early stopping?

Question 5: What is transfer learning?