03 Pytorch Computer Vision
03 Pytorch Computer Vision
PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
Open in Colab
For example, it could involve building a model to classify whether a photo is of a cat or a
dog (binary classification).
Camera and photo apps use computer vision to enhance and sort images.
[Link] 1/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
Modern cars use computer vision to avoid other cars and stay within lane lines.
In essence, anything that can be described in a visual sense can be a potential computer
vision problem.
Topic Contents
2. Prepare data We've got some images, let's load them in with a
PyTorch DataLoader so we can use them with our
training loop.
[Link] 2/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
Topic Contents
8. Comparing our models We've built three different models, let's compare
them.
11. Saving and loading Since we might want to use our model for later,
the best performing let's save it and make sure it loads back in
model correctly.
If you run into trouble, you can ask a question on the course GitHub Discussions page
there too.
[Link] 3/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
And of course, there's the PyTorch documentation and PyTorch developer forums, a very
helpful place for all things PyTorch.
Now we've covered some of the most important PyTorch computer vision libraries, let's
import the relevant dependencies.
[Link] 4/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
# Import torchvision
import torchvision
from torchvision import datasets
from [Link] import ToTensor
# Check versions
# Note: your PyTorch version shouldn't be lower than
1.10.0 and torchvision version shouldn't be lower than
0.11
print(f"PyTorch version:
{torch.__version__}\ntorchvision version:
{torchvision.__version__}")
PyTorch version: 2.0.1+cu118
torchvision version: 0.15.2+cu118
1. Getting a dataset
To begin working on a computer vision problem, let's get a computer vision dataset.
The original MNIST dataset contains thousands of examples of handwritten digits (from
0 to 9) and was used to build computer vision models to identify numbers for postal
services.
[Link] 5/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
[Link] contains a lot of example datasets you can use to practice writing
computer vision code on. FashionMNIST is one of those datasets. And since it has 10
different image classes (different types of clothing), it's a multi-class classification
problem.
Later, we'll be building a computer vision neural network to identify the different styles of
clothing in these images.
root: str - which folder do you want to download the data to?
target_transform - you can transform the targets (labels) if you like too.
[Link] 6/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
Downloading [Link]
[Link]/[Link]
Downloading [Link]
[Link]/[Link] to data/FashionMNI
ST/raw/[Link]
100%|██████████| 29515/29515 [00:00<00:00, 269809.67it/s]
Extracting data/FashionMNIST/raw/train-labels-idx1-ubyte.g
z to data/FashionMNIST/raw
Downloading [Link]
[Link]/[Link]
Downloading [Link]
[Link]/[Link] to data/FashionMNIS
T/raw/[Link]
100%|██████████| 4422102/4422102 [00:00<00:00, 4950701.58i
t/s]
Extracting data/FashionMNIST/raw/[Link]
to data/FashionMNIST/raw
Downloading [Link]
[Link]/[Link]
Downloading [Link]
[Link]/[Link] to data/FashionMNIS
T/raw/[Link]
100%|██████████| 5148/5148 [00:00<00:00, 4744512.63it/s]
Extracting data/FashionMNIST/raw/[Link]
to data/FashionMNIST/raw
[Link] 7/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
[Link] 8/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
[Link] 9/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
[Link] 10/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
[Link] 11/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
We've got a big tensor of values (the image) leading to a single value for the target (the
label).
The shape of the image tensor is [1, 28, 28] or more specifically:
Various problems will have various input and output shapes. But the premise remains:
encode data into numbers, build a model to find patterns in those numbers, convert those
patterns into something meaningful.
If color_channels=3 , the image comes in pixel values for red, green and blue (this is
also known as the RGB color model).
The order of our current tensor is often referred to as CHW (Color Channels, Height,
Width).
[Link] 12/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
There's debate on whether images should be represented as CHW (color channels first)
or HWC (color channels last).
Note: You'll also see NCHW and NHWC formats where N stands for number of images.
For example if you have a batch_size=32 , your tensor shape may be [32, 1, 28,
28] . We'll cover batch sizes later.
PyTorch generally accepts NCHW (channels first) as the default for many operators.
However, PyTorch also explains that NHWC (channels last) performs better and is
considered best practice.
For now, since our dataset and models are relatively small, this won't make too much of
a difference.
But keep it in mind for when you're working on larger image datasets and using
convolutional neural networks (we'll see these later).
Out[6]: ['T-shirt/top',
'Trouser',
'Pullover',
'Dress',
'Coat',
'Sandal',
'Shirt',
'Sneaker',
'Bag',
'Ankle boot']
[Link] 13/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
Because we're working with 10 different classes, it means our problem is multi-class
classification.
We can turn the image into grayscale using the cmap parameter of [Link]() .
[Link] 14/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
[Link] 15/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
But the principles we're going to learn on how to build a model for it will be similar
across a wide range of computer vision problems.
In essence, taking pixel values and building a model to find patterns in them to use on
future pixel values.
Plus, even for this small dataset (yes, even 60,000 images in deep learning is considered
quite small), could you write a program to classify each one of them?
Question: Do you think the above data can be modeled with only straight (linear)
lines? Or do you think you'd also need non-straight (non-linear) lines?
2. Prepare DataLoader
Now we've got a dataset ready to go.
[Link] 16/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
These smaller chunks are called batches or mini-batches and can be set by the
batch_size parameter.
Why do this?
In an ideal world you could do the forward pass and backward pass across all of your
data at once.
But once you start using really large datasets, unless you've got infinite computing
power, it's easier to break them up into batches.
With mini-batches (small portions of the data), gradient descent is performed more
often per epoch (once per mini-batch rather than once per epoch).
But since this is a value you can set (a hyperparameter) you can try all different kinds of
values, though generally powers of 2 are used most often (e.g. 32, 64, 128, 256, 512).
[Link] 17/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
Batching FashionMNIST with a batch size of 32 and shuffle turned on. A similar batching
process will occur for other datasets but will differ depending on the batch size.
Let's create DataLoader 's for our training and test sets.
test_dataloader = DataLoader(test_data,
batch_size=BATCH_SIZE,
shuffle=False # don't necessarily have to shuffle
the testing data
)
[Link] 18/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
And we can see that the data remains unchanged by checking a single sample.
[Link] 19/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
You use the baseline as a starting point and try to improve upon it with subsequent,
more complicated models.
We've done this in a previous section but there's going to be one slight difference.
Because we're working with image data, we're going to use a different layer to start
things off.
[Link] 20/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
The [Link]() layer took our shape from [color_channels, height, width] to
[color_channels, height*width] .
Why do this?
Because we've now turned our pixel data from height and width dimensions into one
long feature vector.
And [Link]() layers like their inputs to be in the form of feature vectors.
Let's create our first model using [Link]() as the first layer.
[Link] 21/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
Wonderful!
We've got a baseline model class we can use, now let's instantiate a model.
input_shape=784 - this is how many features you've got going in the model, in our
case, it's one for every pixel in the target image (28 pixels high by 28 pixels wide =
784 features).
Let's create an instance of our model and send to the CPU for now (we'll run a small test
for running model_0 on CPU vs. a similar model on GPU soon).
In [15]: torch.manual_seed(42)
Out[15]: FashionMNISTModelV0(
(layer_stack): Sequential(
(0): Flatten(start_dim=1, end_dim=-1)
(1): Linear(in_features=784, out_features=10, bias=T
rue)
(2): Linear(in_features=10, out_features=10, bias=Tr
ue)
)
)
[Link] 22/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
Note: Rather than importing and using our own accuracy function or evaluation
metric(s), you could import various evaluation metrics from the TorchMetrics
package.
I mean, let's make a timing function to measure the time it takes our model to train on
CPU versus using a GPU.
We'll train this model on the CPU but the next one on the GPU and see what happens.
[Link] 23/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
Our timing function will import the timeit.default_timer() function from the Python
timeit module.
Args:
start (float): Start time of computation
(preferred in timeit format).
end (float): End time of computation.
device ([type], optional): Device that compute
is running on. Defaults to None.
Returns:
float: time between start and end in seconds
(higher is longer).
"""
total_time = end - start
print(f"Train time on {device}: {total_time:.3f}
seconds")
return total_time
Beautiful!
Looks like we've got all of the pieces of the puzzle ready to go, a timer, a loss function, an
optimizer, a model and most importantly, some data.
Let's now create a training loop and a testing loop to train and evaluate our model.
We'll be using the same steps as the previous notebook(s), though since our data is now
in batch form, we'll add another loop to loop through our data batches.
Our data batches are contained within our DataLoader s, train_dataloader and
test_dataloader for the training and test data splits respectively.
And since we're computing on batches of data, our loss and evaluation metrics will be
calculated per batch rather than across the whole dataset.
This means we'll have to divide our loss and accuracy values by the number of batches
in each dataset's respective dataloader.
[Link] 24/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
2. Loop through training batches, perform training steps, calculate the train loss per
batch.
3. Loop through testing batches, perform testing steps, calculate the test loss per
batch.
# 4. Loss backward
[Link]()
# 5. Optimizer step
[Link]()
[Link] 25/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
### Testing
# Setup variables for accumulatively adding up loss
and accuracy
test_loss, test_acc = 0, 0
model_0.eval()
with torch.inference_mode():
for X, y in test_dataloader:
# 1. Forward pass
test_pred = model_0(X)
end=train_time_end_on_cpu,
device=str(next(model_0.parameters()).device))
[Link] 26/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
Epoch: 1
-------
Looked at 0/60000 samples
Looked at 12800/60000 samples
Looked at 25600/60000 samples
Looked at 38400/60000 samples
Looked at 51200/60000 samples
Epoch: 2
-------
Looked at 0/60000 samples
Looked at 12800/60000 samples
Looked at 25600/60000 samples
Looked at 38400/60000 samples
Looked at 51200/60000 samples
It didn't take too long to train either, even just on the CPU, I wonder if it'll speed up on the
GPU?
Namely, let's create a function that takes in a trained model, a DataLoader , a loss
function and an accuracy function.
[Link] 27/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
The function will use the model to make predictions on the data in the DataLoader and
then we can evaluate those predictions using the loss function and accuracy function.
In [20]: torch.manual_seed(42)
def eval_model(model: [Link],
data_loader:
[Link],
loss_fn: [Link],
accuracy_fn):
"""Returns a dictionary containing the results of
model predicting on data_loader.
Args:
model ([Link]): A PyTorch model
capable of making predictions on data_loader.
data_loader ([Link]): The
target dataset to predict on.
loss_fn ([Link]): The loss function
of model.
accuracy_fn: An accuracy function to compare
the models predictions to the truth labels.
Returns:
(dict): Results of model making predictions on
data_loader.
"""
loss, acc = 0, 0
[Link]()
with torch.inference_mode():
for X, y in data_loader:
# Make predictions with the model
y_pred = model(X)
[Link] 28/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
Looking good!
We can use this dictionary to compare the baseline model results to other models later
on.
Note: Model training time is dependent on hardware used. Generally, more processors
means faster training and smaller models on smaller datasets will often train faster
than large models and large datasets.
Now let's setup some device-agnostic code for our models and data to run on GPU if it's
available.
If you're running this notebook on Google Colab, and you don't have a GPU turned on yet,
it's now time to turn one on via Runtime -> Change runtime type -> Hardware
accelerator -> GPU . If you do this, your runtime will likely reset and you'll have to run
all of the cells above by going Runtime -> Run before .
Out[21]: 'cuda'
Beautiful!
[Link] 29/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
Seeing the data we've been working with, do you think it needs non-linear functions?
We'll do so by recreating a similar model to before, except this time we'll put non-linear
functions ( [Link]() ) in between each linear layer.
We'll need input_shape=784 (equal to the number of features of our image data),
hidden_units=10 (starting small and the same as our baseline model) and
output_shape=len(class_names) (one output unit per class).
Note: Notice how we kept most of the settings of our model the same except for one
change: adding non-linear layers. This is a standard practice for running a series of
machine learning experiments, change one thing and see what happens, then do it
again, again, again.
In [23]: torch.manual_seed(42)
model_1 = FashionMNISTModelV1(input_shape=784, #
[Link] 30/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
As usual, we'll setup a loss function, an optimizer and an evaluation metric (we could do
multiple evaluation metrics but we'll stick with accuracy for now).
So far we've been writing train and test loops over and over.
Let's write them again but this time we'll put them in functions so they can be called
again and again.
And because we're using device-agnostic code now, we'll be sure to call .to(device) on
our feature ( X ) and target ( y ) tensors.
For the training loop we'll create a function called train_step() which takes in a model,
a DataLoader a loss function and an optimizer.
The testing loop will be similar but it'll be called test_step() and it'll take in a model, a
DataLoader , a loss function and an evaluation function.
Note: Since these are functions, you can customize them in any way you like. What
we're making here can be considered barebones training and testing functions for our
specific classification use case.
[Link] 31/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
accuracy_fn,
device: [Link] = device):
train_loss, train_acc = 0, 0
[Link](device)
for batch, (X, y) in enumerate(data_loader):
# Send data to GPU
X, y = [Link](device), [Link](device)
# 1. Forward pass
y_pred = model(X)
# 2. Calculate loss
loss = loss_fn(y_pred, y)
train_loss += loss
train_acc += accuracy_fn(y_true=y,
# 4. Loss backward
[Link]()
# 5. Optimizer step
[Link]()
def test_step(data_loader:
[Link],
model: [Link],
loss_fn: [Link],
accuracy_fn,
device: [Link] = device):
test_loss, test_acc = 0, 0
[Link](device)
[Link]() # put model in eval mode
# Turn on inference context manager
with torch.inference_mode():
for X, y in data_loader:
# Send data to GPU
X, y = [Link](device), [Link](device)
# 1. Forward pass
[Link] 32/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
test_pred = model(X)
Woohoo!
Now we've got some functions for training and testing our model, let's run them.
That way, for each epoch, we're going through a training step and a testing step.
Note: You can customize how often you do a testing step. Sometimes people do them
every five epochs or 10 epochs or in our case, every epoch.
Let's also time things to see how long our code takes to run on the GPU.
In [26]: torch.manual_seed(42)
# Measure time
from timeit import default_timer as timer
train_time_start_on_gpu = timer()
epochs = 3
for epoch in tqdm(range(epochs)):
print(f"Epoch: {epoch}\n---------")
train_step(data_loader=train_dataloader,
model=model_1,
loss_fn=loss_fn,
optimizer=optimizer,
accuracy_fn=accuracy_fn
)
test_step(data_loader=test_dataloader,
model=model_1,
loss_fn=loss_fn,
accuracy_fn=accuracy_fn
)
train_time_end_on_gpu = timer()
[Link] 33/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
total_train_time_model_1 =
print_train_time(start=train_time_start_on_gpu,
end=train_time_end_on_gpu,
device=device)
0%| | 0/3 [00:00<?, ?it/s]
Epoch: 0
---------
Train loss: 1.09199 | Train accuracy: 61.34%
Test loss: 0.95636 | Test accuracy: 65.00%
Epoch: 1
---------
Train loss: 0.78101 | Train accuracy: 71.93%
Test loss: 0.72227 | Test accuracy: 73.91%
Epoch: 2
---------
Train loss: 0.67027 | Train accuracy: 75.94%
Test loss: 0.68500 | Test accuracy: 75.02%
Excellent!
Note: The training time on CUDA vs CPU will depend largely on the quality of the
CPU/GPU you're using. Read on for a more explained answer.
Question: "I used a GPU but my model didn't train faster, why might that be?"
Answer: Well, one reason could be because your dataset and model are both so small
(like the dataset and model we're working with) the benefits of using a GPU are
outweighed by the time it actually takes to transfer the data there.
There's a small bottleneck between copying data from the CPU memory (default) to
the GPU memory.
So for smaller models and datasets, the CPU might actually be the optimal place to
compute on.
But for larger datasets and models, the speed of computing the GPU can offer usually
far outweighs the cost of getting the data there.
However, this is largely dependent on the hardware you're using. With practice, you will
get used to where the best place to train your models is.
[Link] 34/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
Let's evaluate our trained model_1 using our eval_model() function and see how it
went.
In [27]: torch.manual_seed(42)
----------------------------------------------------------
-----------------
RuntimeError Traceback (most
recent call last)
<ipython-input-27-93fed76e63a5> in <cell line: 4>()
2
3 # Note: This will error due to `eval_model()` not
using device agnostic code
----> 4 model_1_results = eval_model(model=model_1,
5 data_loader=test_dataloader,
6 loss_fn=loss_fn,
/usr/local/lib/python3.10/dist-packages/torch/nn/modules/m
[Link] in _call_impl(self, *args, **kwargs)
1499 or _global_backward_pre_hooks or _
global_backward_hooks
1500 or _global_forward_hooks or _globa
l_forward_pre_hooks):
-> 1501 return forward_call(*args, **kwargs)
1502 # Do not call functions when jit is used
1503 full_backward_hooks, non_full_backward_hoo
ks = [], []
<ipython-input-22-a46e692b8bdd> in forward(self, x)
12
13 def forward(self, x: [Link]):
---> 14 return self.layer_stack(x)
[Link] 35/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
/usr/local/lib/python3.10/dist-packages/torch/nn/modules/m
[Link] in _call_impl(self, *args, **kwargs)
1499 or _global_backward_pre_hooks or _
global_backward_hooks
1500 or _global_forward_hooks or _globa
l_forward_pre_hooks):
-> 1501 return forward_call(*args, **kwargs)
1502 # Do not call functions when jit is used
1503 full_backward_hooks, non_full_backward_hoo
ks = [], []
/usr/local/lib/python3.10/dist-packages/torch/nn/modules/c
[Link] in forward(self, input)
215 def forward(self, input):
216 for module in self:
--> 217 input = module(input)
218 return input
219
/usr/local/lib/python3.10/dist-packages/torch/nn/modules/m
[Link] in _call_impl(self, *args, **kwargs)
1499 or _global_backward_pre_hooks or _
global_backward_hooks
1500 or _global_forward_hooks or _globa
l_forward_pre_hooks):
-> 1501 return forward_call(*args, **kwargs)
1502 # Do not call functions when jit is used
1503 full_backward_hooks, non_full_backward_hoo
ks = [], []
/usr/local/lib/python3.10/dist-packages/torch/nn/modules/l
[Link] in forward(self, input)
112
113 def forward(self, input: Tensor) -> Tensor:
--> 114 return [Link](input, [Link], self.b
ias)
115
116 def extra_repr(self) -> str:
Oh no!
[Link] 36/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
It's because we've setup our data and model to use device-agnostic code but not our
evaluation function.
How about we fix that by passing a target device parameter to our eval_model()
function?
Args:
model ([Link]): A PyTorch model
capable of making predictions on data_loader.
data_loader ([Link]): The
target dataset to predict on.
loss_fn ([Link]): The loss function
of model.
accuracy_fn: An accuracy function to compare
the models predictions to the truth labels.
device (str, optional): Target device to
compute on. Defaults to device.
Returns:
(dict): Results of model making predictions on
data_loader.
"""
loss, acc = 0, 0
[Link]()
with torch.inference_mode():
for X, y in data_loader:
# Send data to the target device
X, y = [Link](device), [Link](device)
y_pred = model(X)
loss += loss_fn(y_pred, y)
acc += accuracy_fn(y_true=y,
y_pred=y_pred.argmax(dim=1))
[Link] 37/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
"model_loss": [Link](),
"model_acc": acc}
Woah, in this case, it looks like adding non-linearities to our model made it perform
worse than the baseline.
That's a thing to note in machine learning, sometimes the thing you thought should work
doesn't.
And then the thing you thought might not work does.
From the looks of things, it seems like our model is overfitting on the training data.
Overfitting means our model is learning the training data well but those patterns aren't
generalizing to the testing data.
1. Using a smaller or different model (some models fit certain kinds of data better than
others).
2. Using a larger dataset (the more data, the more chance a model has to learn
generalizable patterns).
There are more, but I'm going to leave that as a challenge for you to explore.
Try searching online, "ways to prevent overfitting in machine learning" and see what
comes up.
[Link] 38/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
CNN's are known for their capabilities to find patterns in visual data.
And since we're dealing with visual data, let's see if using a CNN model can improve
upon our baseline.
The CNN model we're going to be using is known as TinyVGG from the CNN Explainer
website.
Input layer -> [Convolutional layer -> activation layer -> pooling layer] ->
Output layer
Where the contents of [Convolutional layer -> activation layer -> pooling
layer] can be upscaled and repeated multiple times, depending on requirements.
Question: Wait, you say CNN's are good for images, are there any other model types I
should be aware of?
Good question.
This table is a good general guide for which model to use (though there are exceptions).
Note: The table above is only for reference, the model you end up using will be highly
dependent on the problem you're working on and the constraints you have (amount of
[Link] 39/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
Enough talking about models, let's now build a CNN that replicates the model on the
CNN Explainer website.
To do so, we'll leverage the nn.Conv2d() and nn.MaxPool2d() layers from [Link] .
[Link] 40/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
torch.manual_seed(42)
model_2 = FashionMNISTModelV2(input_shape=1,
hidden_units=10,
output_shape=len(class_names)).to(device)
model_2
Out[30]: FashionMNISTModelV2(
(block_1): Sequential(
(0): Conv2d(1, 10, kernel_size=(3, 3), stride=(1,
1), padding=(1, 1))
(1): ReLU()
(2): Conv2d(10, 10, kernel_size=(3, 3), stride=(1,
1), padding=(1, 1))
(3): ReLU()
(4): MaxPool2d(kernel_size=2, stride=2, padding=0, d
ilation=1, ceil_mode=False)
)
(block_2): Sequential(
(0): Conv2d(10, 10, kernel_size=(3, 3), stride=(1,
1), padding=(1, 1))
(1): ReLU()
[Link] 41/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
Nice!
We could start using our model above and see what happens but let's first step through
the two new layers we've added:
The 2d is for 2-dimensional data. As in, our images have two dimensions: height and
width. Yes, there's color channel dimension but each of the color channel dimensions
have two dimensions too: height and width.
For other dimensional data (such as 1D for text or 3D for 3D objects) there's also
nn.Conv1d() and nn.Conv3d() .
To test the layers out, let's create some toy data just like the data used on CNN Explainer.
In [31]: torch.manual_seed(42)
[Link] 42/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
[Link] 43/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
stride (int or tuple, optional) - How big of a step the convolving kernel takes at a
time. Default: 1.
padding (int, tuple, str) - Padding added to all four sides of input. Default: 0.
Example of what happens when you change the hyperparameters of a nn.Conv2d() layer.
In [32]: torch.manual_seed(42)
[Link] 44/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
809, -0.2741],
[ 1.2664, -1.4054, 0.3727, ..., -0.3409, 1.2
191, -0.0463],
...,
[-0.1541, 0.5132, -0.3624, ..., -0.2360, -0.4
609, -0.0035],
[ 0.2981, -0.2432, 1.5012, ..., -0.6289, -0.7
283, -0.5767],
[-0.0386, -0.0781, -0.0388, ..., 0.2842, 0.4
228, -0.1802]],
...,
[Link] 45/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
219, 0.1513],
[ 0.0119, 0.1017, 0.7839, ..., -0.3752, -0.8
127, -0.1257]],
This is because our nn.Conv2d() layer expects a 4-dimensional tensor as input with
size (N, C, H, W) or [batch_size, color_channels, height, width] .
Right now our single image test_image only has a shape of [color_channels,
height, width] or [3, 64, 64] .
We can fix this for a single image using test_image.unsqueeze(dim=0) to add an extra
dimension for N .
[Link] 46/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
Hmm, notice what happens to our shape (the same shape as the first layer of TinyVGG
on CNN Explainer), we get different channel sizes as well as different pixel sizes.
In [35]: torch.manual_seed(42)
# Create a new conv_layer with different values (try
setting these to whatever you like)
conv_layer_2 = nn.Conv2d(in_channels=3, # same number
of color channels as our input image
out_channels=10,
kernel_size=(5, 5), # kernel
is usually a square so a tuple also works
stride=2,
padding=0)
Now our image is of shape [1, 10, 30, 30] (it will be different if you use different
values) or [batch_size=1, color_channels=10, height=30, width=30] .
Behind the scenes, our nn.Conv2d() is compressing the information stored in the
image.
It does this by performing operations on the input (our test image) against its internal
parameters.
The goal of this is similar to all of the other neural networks we've been building.
[Link] 47/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
Data goes in and the layers try to update their internal parameters (patterns) to lower the
loss function thanks to some help of the optimizer.
The only difference is how the different layers calculate their parameter updates or in
PyTorch terms, the operation present in the layer forward() method.
If we check out our conv_layer_2.state_dict() we'll find a similar weight and bias
setup as we've seen before.
[Link] 48/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
[Link] 49/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
[Link] 50/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
[Link] 51/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
Look at that! A bunch of random numbers for a weight and bias tensor.
The shapes of these are manipulated by the inputs we passed to nn.Conv2d() when we
set it up.
That's a good one. But similar to many other things in machine learning, the values of
these aren't set in stone (and recall, because these values are ones we can set
ourselves, they're referred to as "hyperparameters").
The best way to find out is to try out different values and see how they effect your
model's performance.
Or even better, find a working example on a problem similar to yours (like we've done
with TinyVGG) and copy it.
We're working with a different of layer here to what we've seen before.
But the premise remains the same: start with random numbers and update them to
better represent the data.
Now let's check out what happens when we move data through nn.MaxPool2d() .
[Link] 52/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
Notice the change in the shapes of what's happening in and out of a nn.MaxPool2d()
layer.
The kernel_size of the nn.MaxPool2d() layer will affect the size of the output shape.
In our case, the shape halves from a 62x62 image to 31x31 image.
In [39]: torch.manual_seed(42)
# Create a random tensor with a similar number of
dimensions to our images
random_tensor = [Link](size=(1, 1, 2, 2))
print(f"Random tensor:\n{random_tensor}")
print(f"Random tensor shape: {random_tensor.shape}")
[Link] 53/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
Notice the final two dimensions between random_tensor and max_pool_tensor , they
go from [2, 2] to [1, 1] .
And the change would be different for different values of kernel_size for
nn.MaxPool2d() .
Also notice the value leftover in max_pool_tensor is the maximum value from
random_tensor .
Essentially, every layer in a neural network is trying to compress data from higher
dimensional space to lower dimensional space.
In other words, take a lot of numbers (raw data) and learn patterns in those numbers,
patterns that are predictive whilst also being smaller in size than the original values.
From an artificial intelligence perspective, you could consider the whole goal of a neural
network to compress information.
This means, that from the point of view of a neural network, intelligence is compression.
This is the idea of the use of a nn.MaxPool2d() layer: take the maximum value from a
portion of a tensor and disregard the rest.
[Link] 54/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
Except instead of just taking the maximum, the nn.Conv2d() performs a convolutional
operation on the data (see this in action on the CNN Explainer webpage).
Exercise: What do you think the nn.AvgPool2d() layer does? Try making a random
tensor like we did above and passing it through. Check the input and output shapes as
well as the input and output values.
Pick a single layer of a model, pass some data through it and see what happens.
We'll use the functions as before, [Link]() as the loss function (since
we're working with multi-class classification data).
7.4 Training and testing model_2 using our training and test functions
[Link] 55/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
In [41]: torch.manual_seed(42)
# Measure time
from timeit import default_timer as timer
train_time_start_model_2 = timer()
train_time_end_model_2 = timer()
total_train_time_model_2 =
print_train_time(start=train_time_start_model_2,
end=train_time_end_model_2,
device=device)
0%| | 0/3 [00:00<?, ?it/s]
Epoch: 0
---------
Train loss: 0.59302 | Train accuracy: 78.41%
Test loss: 0.39771 | Test accuracy: 86.01%
Epoch: 1
---------
Train loss: 0.36149 | Train accuracy: 87.00%
Test loss: 0.35713 | Test accuracy: 87.00%
Epoch: 2
---------
Train loss: 0.32354 | Train accuracy: 88.28%
Test loss: 0.32857 | Test accuracy: 88.38%
[Link] 56/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
Woah! Looks like the convolutional and max pooling layers helped improve performance
a little.
2. model_1 - the same setup as our baseline model except with [Link]() layers in
between the [Link]() layers.
3. model_2 - our first CNN model that mimics the TinyVGG architecture on the CNN
Explainer website.
Building multiple models and performing multiple training experiments to see which
performs best.
Let's combine our model results dictionaries into a DataFrame and find out.
Out[43]:
model_name model_loss model_acc
[Link] 57/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
Nice!
total_train_time_model_1,
total_train_time_model_2]
compare_results
Out[44]:
model_name model_loss model_acc training_time
It looks like our CNN ( FashionMNISTModelV2 ) model performed the best (lowest loss,
highest accuracy) but had the longest training time.
Performance-speed tradeoff
Generally, you get better performance out of a larger, more complex model (like we did
with model_2 ).
However, this performance increase often comes at a sacrifice of training speed and
inference speed.
Note: The training times you get will be very dependent on the hardware you use.
Generally, the more CPU cores you have, the faster your models will train on CPU. And
similar for GPUs.
Newer hardware (in terms of age) will also often train models faster due to
incorporating technological advances.
[Link] 58/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
To do so, let's create a function make_predictions() where we can pass the model and
some data for it to predict on.
[Link] 59/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
[Link] 60/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
Excellent!
And now we can go from prediction probabilities to prediction labels by taking the
[Link]() of the output of the [Link]() activation function.
Now our predicted classes are in the same format as our test labels, we can compare.
Since we're dealing with image data, let's stay true to the data explorer's motto.
[Link] 61/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
[Link] 62/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
A confusion matrix shows you where your classification model got confused between
predictions and true labels.
1. Make predictions with our trained model, model_2 (a confusion matrix compares
predictions to true labels).
[Link] 63/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
Wonderful!
Now we've got predictions, let's go through steps 2 & 3: 2. Make a confusion matrix using
[Link] . 3. Plot the confusion matrix using
[Link].plot_confusion_matrix() .
First we'll need to make sure we've got torchmetrics and mlxtend installed (these two
libraries will help us make and visualize a confusion matrix).
Note: If you're using Google Colab, the default version of mlxtend installed is 0.14.0
(as of March 2022), however, for the parameters of the plot_confusion_matrix()
function we'd like use, we need 0.19.0 or higher.
To plot the confusion matrix, we need to make sure we've got and mlxtend version of
0.19.0 or higher.
[Link] 64/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
Then we'll create a confusion matrix (in tensor format) by passing our instance our
model's predictions ( preds=y_pred_tensor ) and targets ( target=test_data.targets ).
Finally we can plot our confusion matrix using the plot_confusion_matrix() function
from [Link] .
[Link] 65/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
We can see our model does fairly well since most of the dark squares are down the
diagonal from top left to bottom right (and ideal model will have only values in these
squares and 0 everywhere else).
The model gets most "confused" on classes that are similar, for example predicting
"Pullover" for images that are actually labelled "Shirt".
And the same for predicting "Shirt" for classes that are actually labelled "T-shirt/top".
This kind of information is often more helpful than a single accuracy metric because it
tells use where a model is getting things wrong.
It also hints at why the model may be getting certain things wrong.
It's understandable the model sometimes predicts "Shirt" for images labelled "T-
shirt/top".
We can use this kind of information to further inspect our models and data to see how it
could be improved.
Exercise: Use the trained model_2 to make predictions on the test FashionMNIST
dataset. Then plot some predictions where the model was wrong alongside what the
label of the image should've been. After visualizing these predictions do you think it's
[Link] 66/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
more of a modelling error or a data error? As in, could the model do better or are the
labels of the data too close to each other (e.g. a "Shirt" label is too close to "T-
shirt/top")?
Recall from notebook 01 we can save and load a PyTorch model using a combination of:
You can see more of these three in the PyTorch saving and loading models
documentation.
For now, let's save our model_2 's state_dict() then load it back in and evaluate it to
make sure the save and load went correctly.
[Link] 67/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
Now we've got a saved model state_dict() we can load it back in using a combination
of load_state_dict() and [Link]() .
And now we've got a loaded model we can evaluate it with eval_model() to make sure
its parameters work similarly to model_2 prior to saving.
loaded_model_2_results = eval_model(
model=loaded_model_2,
data_loader=test_dataloader,
loss_fn=loss_fn,
accuracy_fn=accuracy_fn
)
loaded_model_2_results
In [60]: model_2_results
[Link] 68/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
We can find out if two tensors are close to each other using [Link]() and
passing in a tolerance level of closeness via the parameters atol (absolute tolerance)
and rtol (relative tolerance).
If our model's results are close, the output of [Link]() should be true.
[Link](loaded_model_2_results["model_loss"]),
atol=1e-08, # absolute tolerance
rtol=0.0001) # relative tolerance
Out[61]: tensor(True)
Exercises
All of the exercises are focused on practicing the code in the sections above.
You should be able to complete them by referencing each section or by following the
resource(s) linked.
Resources:
Example solutions notebook for 03 (try the exercises before looking at this)
1. What are 3 areas in industry where computer vision is currently being used?
2. Search "what is overfitting in machine learning" and write down a sentence about
what you find.
3. Search "ways to prevent overfitting in machine learning", write down 3 of the things
you find and a sentence about each. Note: there are lots of these, so don't worry too
much about all of them, just pick 3 and start with those.
4. Spend 20-minutes reading and clicking through the CNN Explainer website.
Upload your own example image using the "upload" button and see what
happens in each layer of a CNN as your image passes through it.
[Link] 69/70
6/19/26, 12:44 PM 03. PyTorch Computer Vision - Zero to Mastery Learn PyTorch for Deep Learning
7. Turn the MNIST train and test datasets into dataloaders using
[Link] , set the batch_size=32 .
8. Recreate model_2 used in this notebook (the same model from the CNN Explainer
website, also known as TinyVGG) capable of fitting on the MNIST dataset.
9. Train the model you built in exercise 8. on CPU and GPU and see how long it takes
on each.
10. Make predictions using your trained model and visualize at least 5 of them
comparing the prediction to the target label.
11. Plot a confusion matrix comparing your model's predictions to the truth labels.
12. Create a random tensor of shape [1, 3, 64, 64] and pass it through a
nn.Conv2d() layer with various hyperparameter settings (these can be any settings
you choose), what do you notice if the kernel_size parameter goes up and down?
13. Use a model similar to the trained model_2 from this notebook to make predictions
on the test [Link] dataset.
Then plot some predictions where the model was wrong alongside what the
label of the image should've been.
After visualizing these predictions do you think it's more of a modelling error or
a data error?
As in, could the model do better or are the labels of the data too close to each
other (e.g. a "Shirt" label is too close to "T-shirt/top")?
Extra-curriculum
Watch: MIT's Introduction to Deep Computer Vision lecture. This will give you a great
intuition behind convolutional neural networks.
Spend 10-minutes clicking through the different options of the PyTorch vision library,
what different modules are available?
For a large number of pretrained PyTorch computer vision models as well as many
different extensions to PyTorch's computer vision functionalities check out the
PyTorch Image Models library timm (Torch Image Models) by Ross Wightman.
[Link] 70/70