Welcome back! In our last lesson, we explored the theoretical foundations of Convolutional Neural Networks (CNNs), breaking down the core operations of convolution, pooling, and stride. You learned why these building blocks are so effective for analyzing images.
Today, we transition from theory to practice. This lesson is all about implementation. Our goal is to build and train your first CNN for image classification using PyTorch. We'll take the concepts from the previous lesson and translate them into a functional PyTorch model. We'll cover the entire workflow: loading data, defining the network architecture, writing the training loop, and evaluating the model's performance.
Let's get started!
The End-to-End Workflow
Building and training a neural network follows a standard, repeatable process. We will implement the following five steps, which you'll use in almost every deep learning project:
- Load and normalize the data: We'll use the popular CIFAR-10 image dataset and prepare it for our network.
- Define the CNN architecture: We'll construct the network by stacking convolutional, pooling, and fully connected layers.
- Define a loss function and optimizer: We'll choose the appropriate tools to measure our model's error and update its weights.
- Train the network: We'll feed the training data to the model and let it learn.
- Test the network: We'll evaluate how well our trained model performs on unseen data.
This entire pipeline is visualized below, showing how an image flows through the layers to produce a final classification.

1. Loading and Preparing the Data
The first step is to get our data ready. PyTorch's torchvision library makes it incredibly simple to work with standard image datasets. We will use the CIFAR-10 dataset, which consists of 60,000 32x32 color images in 10 classes (e.g., 'plane', 'car', 'bird').
When loading the data, we also need to apply some transformations:
transforms.ToTensor(): This converts the images from their original format (PILImage) into PyTorch tensors. It also scales the pixel values from a range of [0, 255] to [0, 1].transforms.Normalize(): This normalizes the tensor's values. By providing the mean and standard deviation for each of the three color channels (Red, Green, Blue), it scales the values to be in the range [-1, 1]. This helps the network train more stably and efficiently.
Finally, we'll wrap our dataset in a DataLoader, which is a utility that provides batches of data, shuffles it for each epoch, and can even use multiple subprocesses to load data in parallel.
For a clear, code-driven walkthrough of this process, please read the first section of the official PyTorch tutorial on this topic.
This official PyTorch tutorial demonstrates the standard way to load and prepare the CIFAR-10 dataset using torchvision. Pay close attention to how transforms.Compose is used to chain the preprocessing steps.
Read the section titled '1. Load and normalize CIFAR10'. The code here is a template you'll reuse in many computer vision projects. You can ignore the download progress bar and the sample images for now.
2. Defining the CNN Architecture in PyTorch
This is where we build our network. In PyTorch, a model is defined as a Python class that inherits from torch.nn.Module.
The architecture consists of two main parts:
__init__(): The constructor where we define all the layers our network will use. We'll be usingnn.Conv2dfor convolutional layers,nn.MaxPool2dfor pooling, andnn.Linearfor the final classification layers.forward(): This method defines the "forward pass"—how an input tensorxflows through the layers we defined in__init__().
Let's watch a detailed video that walks through creating a CNN class, paying special attention to a common challenge: calculating the correct input size for the first fully connected (Linear) layer.
PyTorch Tutorial 14 - Convolutional Neural Network (CNN)
Patrick Loeber's tutorial provides an exceptionally clear explanation of how to define a CNN in PyTorch. He methodically demonstrates how to track the tensor's shape as it passes through each layer, which is crucial for connecting the convolutional part of the network to the fully connected part.
Please watch the following segments carefully: Defining the Layers (06:46 - 10:52): See how nn.Conv2d, nn.MaxPool2d, and nn.Linear layers are instantiated in the __init__ method. Note the arguments for nn.Conv2d: in_channels (3 for an RGB image), out_channels (number of filters), and kernel_size. Calculating Flattened Size (10:52 - 17:18): This is the most important part. Follow along as he explains how the tensor's dimensions change after each convolution and pooling operation. Understanding how to calculate 16 * 5 * 5 is a key practical skill. The Forward Pass (17:18 - 20:06): Watch how the forward method applies the layers in sequence, including using the ReLU activation function and flattening the tensor before the linear layers.
To solidify your understanding, here is the complete code for the network architecture from the official PyTorch tutorial. You can see it matches the structure described in the video.
import torch.nn as nn
import torch.nn.functional as F
class Net(nn.Module):
def __init__(self):
super().__init__()
# 3 input image channels (RGB), 6 output channels, 5x5 square convolution kernel
self.conv1 = nn.Conv2d(3, 6, 5)
# Max pooling over a (2, 2) window
self.pool = nn.MaxPool2d(2, 2)
self.conv2 = nn.Conv2d(6, 16, 5)
# an affine operation: y = Wx + b
self.fc1 = nn.Linear(16 * 5 * 5, 120) # <-- The result of the calculation from the video
self.fc2 = nn.Linear(120, 84)
self.fc3 = nn.Linear(84, 10) # 10 output classes
def forward(self, x):
# Apply conv1, then ReLU, then pooling
x = self.pool(F.relu(self.conv1(x)))
# Apply conv2, then ReLU, then pooling
x = self.pool(F.relu(self.conv2(x)))
# Flatten all dimensions except the batch dimension
x = torch.flatten(x, 1)
# Apply linear layers with ReLU activations
x = F.relu(self.fc1(x))
x = F.relu(self.fc2(x))
# Final output layer (no activation, as CrossEntropyLoss will handle it)
x = self.fc3(x)
return x
net = Net()
print(net)
3. Training the Network
With our data prepared and our network defined, the final steps are to set up and run the training process. This part will be very similar to what you saw in the "Deep Learning Foundations" module.
-
Define Loss Function and Optimizer: For multi-class classification,
nn.CrossEntropyLossis the standard choice. It combines aLogSoftmaxlayer and aNegative Log-Likelihoodloss in one class. For the optimizer,torch.optim.SGDwith momentum is a classic and solid choice. -
The Training Loop: We will loop over our dataset for a specified number of epochs. In each epoch, we loop through the
trainloader, which gives us batches of[inputs, labels]. For each batch, we perform the following steps:- Zero the gradients (
optimizer.zero_grad()). - Perform a forward pass to get the model's predictions (
outputs = net(inputs)). - Calculate the loss (
loss = criterion(outputs, labels)). - Perform backpropagation to compute gradients (
loss.backward()). - Update the model's weights (
optimizer.step()).
- Zero the gradients (
Let's watch a video that brings all these pieces together and shows the full implementation from start to finish.
Convolutional Neural Nets Explained and Implemented in Python (PyTorch)
This video from James Briggs provides a complete, hands-on walkthrough of training a CNN on CIFAR-10. It reinforces the concepts you've just learned and shows them in a continuous script.
Watch the following two segments: Model Definition (25:14 - 28:15): This provides another example of defining a CNN architecture. Notice the pattern: Conv -> ReLU -> Pool. Training Setup and Loop (28:15 - 30:19): Observe how the CrossEntropyLoss and SGD optimizer are set up, and how the training loop iterates through epochs and batches to train the model.
As a reference, you can find the corresponding code for defining the loss/optimizer and the training loop in sections 3 and 4 of the PyTorch tutorial we looked at earlier ([LINK](https://docs.pytorch.org/tutorials/beginner/blitz/cifar10_tutorial.html)).
4. Evaluating the Model
After training, we need to see how well our model has learned. We do this by making predictions on the test set—data the model has never seen before.
The process is straightforward:
- Set the model to evaluation mode with
net.eval(). This disables layers like Dropout that behave differently during training and inference. - Use a
with torch.no_grad():block. This tells PyTorch not to calculate gradients, which saves memory and computation since we are not training. - Loop through the
testloader, pass the images through the network, and get the predicted class by finding the index of the highest output value usingtorch.max(). - Compare the
predictedclasses to the truelabelsand calculate the accuracy.
This final step is covered in the last part of the PyTorch tutorial.
This section of the PyTorch tutorial shows how to use the trained network to predict class labels for the test images and calculate the overall accuracy.
Read section '5. Test the network on the test data'. Focus on the code block that calculates the accuracy on the whole dataset.
The accuracy you get might be around 50-60%. While not state-of-the-art, it's significantly better than the 10% accuracy of random guessing, proving that our network has successfully learned features from the images!
Conclusion
Congratulations! You have now walked through the entire process of building and training a Convolutional Neural Network in PyTorch. You've combined the theoretical concepts from the last lesson with the practical coding skills from your Python and PyTorch foundation.
Key Takeaways:
- The entire process follows a standard workflow: Data -> Model -> Loss/Optimizer -> Train -> Evaluate.
torchvisionandDataLoaderare powerful tools for loading and preparing image datasets.- A CNN model is defined as a class inheriting from
nn.Module, with layers defined in__init__and the data flow defined inforward. - A critical step is to calculate the flattened feature size to correctly connect
nn.Conv2dlayers tonn.Linearlayers. - The training loop iterates through batches of data, performing the forward pass, loss calculation, backpropagation, and weight updates.
- Evaluation is done on a separate test set with gradients turned off (
torch.no_grad()).
Preview of the Next Lesson:
In this lesson, we used a standard dataset with minimal preparation. However, real-world datasets often require more work. In the next lesson, "Prepare image datasets using DataLoader and apply data augmentation," we will dive deeper into preparing custom datasets and explore data augmentation—a powerful technique to artificially expand your dataset and improve model performance.