02 Pytorch Classification
02 Pytorch Classification
PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
Open in Colab
[Link] 1/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
In other words, taking a set of inputs and predicting what class those set of inputs
belong to.
Except instead of trying to predict a straight line (predicting a number, also called a
regression problem), we'll be working on a classification problem.
[Link] 2/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
Topic Contents
3. Fitting the model to data We've got data and a model, now let's let the
(training) model (try to) find patterns in the (training) data.
4. Making predictions and Our model's found patterns in the data, let's
evaluating a model compare its findings to the actual (testing) data.
(inference)
5. Improving a model We've trained and evaluated a model but it's not
(from a model perspective) working, let's try a few things to improve it.
6. Non-linearity So far our model has only had the ability to model
straight lines, what about non-linear (non-straight)
lines?
8. Putting it all together Let's put everything we've done so far for binary
with multi-class classification together with a multi-class
classification classification problem.
And if you run into trouble, you can ask a question on the Discussions page there too.
[Link] 3/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
There's also the PyTorch developer forums, a very helpful place for all things PyTorch.
Output layer shape 1 (one class or the other) 1 per class (e.g. 3 for
( out_features ) food, person or dog
photo)
[Link] 4/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
Of course, this ingredient list of classification neural network components will vary
depending on the problem you're working on.
We're going to get hands-on with this setup throughout this notebook.
We'll use the make_circles() method from Scikit-Learn to generate two circles with
different coloured dots.
# Create circles
X, y = make_circles(n_samples,
noise=0.03, # a little bit of noise
to the dots
random_state=42) # keep random
state so we get the same values
First 5 y labels:
[1 1 1 1 0]
Let's keep following the data explorer's motto of visualize, visualize, visualize and put
them into a pandas DataFrame.
[Link] 5/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
Out[3]:
X1 X2 label
0 0.754246 0.231481 1
1 -0.756159 0.153259 1
2 -0.815392 0.173282 1
3 -0.393731 0.692883 1
4 0.442208 -0.896723 0
5 -0.479646 0.676435 1
6 -0.013648 0.803349 1
7 0.771513 0.147760 1
8 -0.169322 -0.793456 1
9 -0.121486 1.021509 0
It looks like each pair of X features ( X1 and X2 ) has a label ( y ) value of either 0 or 1.
This tells us that our problem is binary classification since there's only two options (0 or
1).
Out[4]: label
1 500
0 500
Name: count, dtype: int64
[Link] 6/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
c=y,
cmap=[Link]);
Let's find out how we could build a PyTorch neural network to classify dots into red (0) or
blue (1).
Note: This dataset is often what's considered a toy problem (a problem that's used to
try and test things out on) in machine learning.
But it represents the major key of classification, you have some kind of data
represented as numerical values and you'd like to build a model that's able to classify
it, in our case, separate it into red or blue dots.
Mismatching the shapes of tensors and tensor operations will result in errors in your
models.
And there's no surefire way to make sure they won't happen, they will.
What you can do instead is continually familiarize yourself with the shape of the data
you're working with.
[Link] 7/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
Ask yourself:
It often helps to view the values and shapes of a single sample (features and labels).
Doing so will help you understand what input and output shapes you'd be expecting from
your model.
This tells us the second dimension for X means it has two features (vector) where as y
has a single feature (scalar).
1.2 Turn data into tensors and create train and test splits
We've investigated the input and output shapes of our data, now let's prepare it for being
used with PyTorch and for modelling.
1. Turn our data into tensors (right now our data is in NumPy arrays and PyTorch
prefers to work with PyTorch tensors).
[Link] 8/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
2. Split our data into training and test sets (we'll train a model on the training set to
learn the patterns between X and y and then evaluate those learned patterns on
the test dataset).
Now our data is in tensor format, let's split it into training and test sets.
We'll use test_size=0.2 (80% training, 20% testing) and because the split happens
randomly across the data, let's use random_state=42 so the split is reproducible.
Nice! Looks like we've now got 800 training samples and 200 testing samples.
2. Building a model
[Link] 9/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
We've got some data ready, now it's time to build a model.
1. Setting up device agnostic code (so our model can run on CPU or GPU if it's
available).
The good news is we've been through all of the above steps before in notebook 01.
Except now we'll be adjusting them so they work with a classification dataset.
Let's start by importing PyTorch and [Link] as well as setting up device agnostic
code.
Out[10]: 'cuda'
Excellent, now device is setup, we can use it for any data or models we create and
PyTorch will handle it on the CPU (default) or GPU if it's available.
We'll want a model capable of handling our X data as inputs and producing something
in the shape of our y data as outputs.
This setup where you have features and labels is referred to as supervised learning.
Because your data is telling your model what the outputs should be given a certain input.
To create such a model it'll need to handle the input and output shapes of X and y .
Remember how I said input and output shapes are important? Here we'll see why.
[Link] 10/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
2. Creates 2 [Link] layers in the constructor capable of handling the input and
output shapes of X and y .
Out[11]: CircleModelV0(
(layer_1): Linear(in_features=2, out_features=5, bias=
True)
(layer_2): Linear(in_features=5, out_features=1, bias=
True)
)
The only major change is what's happening between self.layer_1 and self.layer_2 .
[Link] 11/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
This layer turns the input data from having 2 features to 5 features.
Why do this?
This allows the model to learn patterns from 5 numbers rather than just 2 numbers,
potentially leading to better outputs.
The number of hidden units you can use in neural network layers is a hyperparameter (a
value you can set yourself) and there's no set in stone value you have to use.
Generally more is better but there's also such a thing as too much. The amount you
choose will depend on your model type and dataset you're working with.
The only rule with hidden units is that the next layer, in our case, self.layer_2 has to
take the same in_features as the previous layer out_features .
A visual example of what a similar classification neural network to the one we've just built
looks like. Try creating one of your own on the TensorFlow Playground website.
[Link] 12/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
[Link] performs a forward pass computation of the input data through the
layers in the order they appear.
model_0
Out[12]: Sequential(
(0): Linear(in_features=2, out_features=5, bias=True)
(1): Linear(in_features=5, out_features=1, bias=True)
)
Woah, that looks much simpler than subclassing [Link] , why not just always use
[Link] ?
So if you'd like something else to happen (rather than just straight-forward sequential
computation) you'll want to define your own custom [Link] subclass.
Now we've got a model, let's see what happens when we pass some data through it.
First 10 predictions:
tensor([[0.0555],
[0.0169],
[0.2254],
[0.0071],
[0.3345],
[0.3101],
[0.1151],
[0.1840],
[0.2205],
[Link] 13/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
Hmm, it seems there are the same amount of predictions as there are test labels but the
predictions don't look like they're in the same form or shape as the test labels.
We've got a couple steps we can do to fix this, we'll see these later on.
We've setup a loss (also called a criterion or cost function) and optimizer before in
notebook 01.
For example, for a regression problem (predicting a number) you might use mean
absolute error (MAE) loss.
And for a binary classification problem (like ours), you'll often use binary cross entropy
as the loss function.
However, the same optimizer function can often be used across different problem
spaces.
For example, the stochastic gradient descent optimizer (SGD, [Link]() ) can
be used for a range of problems, and the same applies to the Adam optimizer
( [Link]() ).
[Link] 14/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
Table of various loss functions and optimizers, there are more but these are some common
ones you'll see.
Since we're working with a binary classification problem, let's use a binary cross entropy
loss function.
Note: Recall a loss function is what measures how wrong your model predictions are,
the higher the loss, the worse your model.
So generally, implementation 2 is a better option. However for advanced usage, you may
want to separate the combination of [Link] and [Link]() but that is
beyond the scope of this notebook.
For the optimizer we'll use [Link]() to optimize the model parameters with
learning rate 0.1.
Note: There's a discussion on the PyTorch forums about the use of [Link] vs.
[Link] . It can be confusing at first but as with many things, it
[Link] 15/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
# Create an optimizer
optimizer =
[Link](params=model_0.parameters(),
lr=0.1)
An evaluation metric can be used to offer another perspective on how your model is
going.
If a loss function measures how wrong your model is, I like to think of evaluation metrics
as measuring how right it is.
Of course, you could argue both of these are doing the same thing but evaluation metrics
offer a different perspective.
After all, when evaluating your models it's good to look at things from multiple points of
view.
There are several evaluation metrics that can be used for classification problems but
let's start out with accuracy.
Accuracy can be measured by dividing the total number of correct predictions over the
total number of predictions.
For example, a model that makes 99 correct predictions out of 100 will have an accuracy
of 99%.
Excellent! We can now use this function whilst training our model to measure it's
performance alongside the loss.
[Link] 16/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
3. Train model
Okay, now we've got a loss function and optimizer ready to go, let's train a model.
Steps in training:
1. Forward pass - The model goes through all of the training data once, performing its
forward() function calculations ( model(x_train) ).
2. Calculate the loss - The model's outputs (predictions) are compared to the ground
truth and evaluated to see how wrong they are ( loss = loss_fn(y_pred, y_train ).
3. Zero gradients - The optimizers gradients are set to zero (they are accumulated by
default) so they can be recalculated for the specific training step
( optimizer.zero_grad() ).
4. Perform backpropagation on the loss - Computes the gradient of the loss with
respect for every model parameter to be updated (each parameter with
requires_grad=True ). This is known as backpropagation, hence "backwards"
( [Link]() ).
3.1 Going from raw model outputs to predicted labels (logits ->
prediction probabilities -> prediction labels)
Before the training loop steps, let's see what comes out of our model during the forward
pass (the forward pass is defined by the forward() method).
[Link] 17/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
Out[16]: tensor([[0.0555],
[0.0169],
[0.2254],
[0.0071],
[0.3345]], device='cuda:0', grad_fn=<SliceBackwa
rd0>)
Since our model hasn't been trained, these outputs are basically random.
Which implements two layers of [Link]() which internally calls the following
equation:
The raw outputs (unmodified) of this equation ($y$) and in turn, the raw outputs of our
model are often referred to as logits.
That's what our model is outputing above when it takes in the input data ($x$ in the
equation or X_test in the code), logits.
We'd like some numbers that are comparable to our truth labels.
To get our model's raw outputs (logits) into such a form, we can use the sigmoid
activation function.
Out[ ]: tensor([[0.5139],
[0.5042],
[0.5561],
[0.5018],
[0.5829]], device='cuda:0', grad_fn=<SigmoidBack
ward0>)
Okay, it seems like the outputs now have some kind of consistency (even though they're
still random).
In our case, since we're dealing with binary classification, our ideal outputs are 0 or 1.
[Link] 18/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
The closer to 0, the more the model thinks the sample belongs to class 0, the closer to 1,
the more the model thinks the sample belongs to class 1.
More specificially:
To turn our prediction probabilities into prediction labels, we can round the outputs of the
sigmoid activation function.
# In full
y_pred_labels =
[Link]([Link](model_0(X_test.to(device))
[:5]))
Excellent! Now it looks like our model's predictions are in the same form as our truth
labels ( y_test ).
In [19]: y_test[:5]
This means we'll be able to compare our model's predictions to the test labels to see
how well it's performing.
To recap, we converted our model's raw outputs (logits) to prediction probabilities using
a sigmoid activation function.
And then converted the prediction probabilities to prediction labels by rounding them.
[Link] 19/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
Note: The use of the sigmoid activation function is often only for binary classification
logits. For multi-class classification, we'll be looking at using the softmax activation
function (this will come later on).
And the use of the sigmoid activation function is not required when passing our
model's raw outputs to the [Link] (the "logits" in logits loss is
because it works on the model's raw logits output), this is because it has a sigmoid
function built-in.
Alright, we've discussed how to take our raw model outputs and convert them to
prediction labels, now let's build a training loop.
Let's start by training for 100 epochs and outputing the model's progress every 10
epochs.
In [20]: torch.manual_seed(42)
# 2. Calculate loss/accuracy
# loss = loss_fn([Link](y_logits), # Using
[Link] you need [Link]()
# y_train)
loss = loss_fn(y_logits, # Using
[Link] works with raw logits
y_train)
acc = accuracy_fn(y_true=y_train,
[Link] 20/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
y_pred=y_pred)
# 4. Loss backwards
[Link]()
# 5. Optimizer step
[Link]()
### Testing
model_0.eval()
with torch.inference_mode():
# 1. Forward pass
test_logits = model_0(X_test).squeeze()
test_pred =
[Link]([Link](test_logits))
# 2. Caculate loss/accuracy
test_loss = loss_fn(test_logits,
y_test)
test_acc = accuracy_fn(y_true=y_test,
y_pred=test_pred)
[Link] 21/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
It looks like it went through the training and testing steps fine but the results don't seem
to have moved too much.
And because we're working with a balanced binary classification problem, it means our
model is performing as good as random guessing (with 500 samples of class 0 and
class 1 a model predicting class 1 every single time would achieve 50% accuracy).
Let's make a plot of our model's predictions, the data it's trying to predict on and the
decision boundary it's creating for whether something is class 0 or class 1.
To do so, we'll write some code to download and import the helper_functions.py
script from the Learn PyTorch for Deep Learning repo.
[Link] 22/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
[Link]([Link])
Oh wow, it seems like we've found the cause of model's performance issue.
It's currently trying to split the red and blue dots using a straight line...
That explains the 50% accuracy. Since our data is circular, drawing a straight line can at
best cut it down the middle.
In machine learning terms, our model is underfitting, meaning it's not learning predictive
patterns from the data.
Focusing specifically on the model (not the data), there are a few ways we could do this.
[Link] 23/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
Add more layers Each layer potentially increases the learning capabilities
of the model with each layer being able to learn some
kind of new pattern in the data. More layers are often
referred to as making your neural network deeper.
Add more hidden Similar to the above, more hidden units per layer means
units a potential increase in learning capabilities of the model.
More hidden units are often referred to as making your
neural network wider.
Fitting for longer Your model might learn more if it had more
(more epochs) opportunities to look at the data.
Changing the Some data just can't be fit with only straight lines (like
activation functions what we've seen), using non-linear activation functions
can help with this (hint, hint).
Change the learning Less model specific, but still related, the learning rate of
rate the optimizer decides how much a model should change
its parameters each step, too much and the model
overcorrects, too little and it doesn't learn enough.
Change the loss Again, less model specific but still important, different
function problems require different loss functions. For example, a
binary cross entropy loss function won't work with a
multi-class classification problem.
Note: *because you can adjust all of these by hand, they're referred to as
hyperparameters.
And this is also where machine learning's half art half science comes in, there's no
real way to know here what the best combination of values is for your project, best to
follow the data scientist's motto of "experiment, experiment, experiment".
Let's see what happens if we add an extra layer to our model, fit for longer ( epochs=1000
instead of epochs=100 ) and increase the number of hidden units from 5 to 10 .
[Link] 24/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
We'll follow the same steps we did above but with a few changed hyperparameters.
model_1 = CircleModelV1().to(device)
model_1
Out[23]: CircleModelV1(
(layer_1): Linear(in_features=2, out_features=10, bias
=True)
(layer_2): Linear(in_features=10, out_features=10, bia
s=True)
(layer_3): Linear(in_features=10, out_features=1, bias
=True)
)
Now we've got a model, we'll recreate a loss function and optimizer instance, using the
same settings as before.
Beautiful, model, optimizer and loss function ready, let's make a training loop.
[Link] 25/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
This time we'll train for longer ( epochs=1000 vs epochs=100 ) and see if it improves our
model.
In [25]: torch.manual_seed(42)
# 2. Calculate loss/accuracy
loss = loss_fn(y_logits, y_train)
acc = accuracy_fn(y_true=y_train,
y_pred=y_pred)
# 4. Loss backwards
[Link]()
# 5. Optimizer step
[Link]()
### Testing
model_1.eval()
with torch.inference_mode():
# 1. Forward pass
test_logits = model_1(X_test).squeeze()
test_pred =
[Link]([Link](test_logits))
# 2. Caculate loss/accuracy
test_loss = loss_fn(test_logits,
y_test)
test_acc = accuracy_fn(y_true=y_test,
y_pred=test_pred)
[Link] 26/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
What? Our model trained for longer and with an extra layer but it still looks like it didn't
learn any patterns better than random guessing.
Let's visualize.
[Link] 27/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
Hmmm.
Our model is still drawing a straight line between the red and blue dots.
If our model is drawing a straight line, could it model linear data? Like we did in notebook
01?
5.1 Preparing data to see if our model can model a straight line
Let's create some linear data to see if our model's able to model it and we're not just
using a model that can't learn anything.
# Create data
X_regression = [Link](start, end,
step).unsqueeze(dim=1)
y_regression = weight * X_regression + bias # linear
regression formula
[Link] 28/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
[0.0400]]),
tensor([[0.3000],
[0.3070],
[0.3140],
[0.3210],
[0.3280]]))
Wonderful, now let's split our data into training and test sets.
It's contained within the helper_functions.py script on the Learn PyTorch for Deep
Learning repo which we downloaded above.
In [29]: plot_predictions(train_data=X_train_regression,
train_labels=y_train_regression,
test_data=X_test_regression,
test_labels=y_test_regression
);
[Link] 29/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
Now we've got some data, let's recreate model_1 but with a loss function suited to our
regression data.
model_2
Out[30]: Sequential(
(0): Linear(in_features=1, out_features=10, bias=True)
(1): Linear(in_features=10, out_features=10, bias=Tru
e)
(2): Linear(in_features=10, out_features=1, bias=True)
)
We'll setup the loss function to be nn.L1Loss() (the same as mean absolute error) and
the optimizer to be [Link]() .
[Link] 30/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
optimizer = [Link](model_2.parameters(),
lr=0.1)
Now let's train the model using the regular training loop steps for epochs=1000 (just like
model_1 ).
Note: We've been writing similar training loop code over and over again. I've made it
that way on purpose though, to keep practicing. However, do you have ideas how we
could functionize this? That would save a fair bit of coding in the future. Potentially
there could be a function for training and a function for testing.
# 4. Loss backwards
[Link]()
# 5. Optimizer step
[Link]()
### Testing
model_2.eval()
with torch.inference_mode():
# 1. Forward pass
test_pred = model_2(X_test_regression)
# 2. Calculate the loss
[Link] 31/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
test_loss = loss_fn(test_pred,
y_test_regression)
Okay, unlike model_1 on the classification data, it looks like model_2 's loss is actually
going down.
And remember, since our model and data are using the target device , and this device
may be a GPU, however, our plotting function uses matplotlib and matplotlib can't handle
data on the GPU.
To handle that, we'll send all of our data to the CPU using .cpu() when we pass it to
plot_predictions() .
train_labels=y_train_regression.cpu(),
test_data=X_test_regression.cpu(),
test_labels=y_test_regression.cpu(),
predictions=y_preds.cpu());
[Link] 32/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
Alright, it looks like our model is able to do far better than random guessing on straight
lines.
Note: A helpful troubleshooting step when building deep learning models is to start as
small as possible to see if the model works before scaling it up.
This could mean starting with a simple neural network (not many layers, not many
hidden neurons) and a small dataset (like the one we've made) and then overfitting
(making the model perform too well) on that small example before increasing the
amount of data or the model size/design to reduce overfitting.
But how about we give it the capacity to draw non-straight (non-linear) lines?
How?
[Link] 33/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
First, let's recreate the data to start off fresh. We'll use the same setup as before.
n_samples = 1000
X, y = make_circles(n_samples=1000,
noise=0.03,
random_state=42,
)
Nice! Now let's split it into training and test sets using 80% of the data for training and
20% for testing.
In [35]: # Convert to tensors and split into train and test sets
import torch
from sklearn.model_selection import train_test_split
[Link] 34/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
test_size=0.2,
random_state=42
)
X_train[:5], y_train[:5]
What kind of pattern do you think you could draw with unlimited straight (linear) and non-
straight (non-linear) lines?
So far our neural networks have only been using linear (straight) line functions.
What do you think will happen when we introduce the capability for our model to use
non-linear activation functions?
PyTorch has a bunch of ready-made non-linear activation functions that do similar but
different things.
One of the most common and best performing is ReLU) (rectified linear-unit,
[Link]() ).
Rather than talk about it, let's put it in our neural network between the hidden layers in
the forward pass and see what happens.
[Link] 35/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
super().__init__()
self.layer_1 = [Link](in_features=2,
out_features=10)
self.layer_2 = [Link](in_features=10,
out_features=10)
self.layer_3 = [Link](in_features=10,
out_features=1)
[Link] = [Link]() # <- add in ReLU
activation function
# Can also put sigmoid in the model
# This would mean you don't need to use it on
the predictions
# [Link] = [Link]()
model_3 = CircleModelV2().to(device)
print(model_3)
CircleModelV2(
(layer_1): Linear(in_features=2, out_features=10, bias=T
rue)
(layer_2): Linear(in_features=10, out_features=10, bias=
True)
(layer_3): Linear(in_features=10, out_features=1, bias=T
rue)
(relu): ReLU()
)
[Link] 36/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
A visual example of what a similar classification neural network to the one we've just built
(using ReLU activation) looks like. Try creating one of your own on the TensorFlow
Playground website.
Question: Where should I put the non-linear activation functions when constructing a
neural network?
A rule of thumb is to put them in between hidden layers and just after the output layer,
however, there is no set in stone option. As you learn more about neural networks and
deep learning you'll find a bunch of different ways of putting things together. In the
meantime, best to experiment, experiment, experiment.
Now we've got a model ready to go, let's create a binary classification loss function as
well as an optimizer.
Wonderful!
[Link] 37/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
You know the drill, model, loss function, optimizer ready to go, let's create a training and
testing loop.
# 4. Loss backward
[Link]()
# 5. Optimizer step
[Link]()
### Testing
model_3.eval()
with torch.inference_mode():
# 1. Forward pass
test_logits = model_3(X_test).squeeze()
test_pred =
[Link]([Link](test_logits)) # logits ->
prediction probabilities -> prediction labels
# 2. Calculate loss and accuracy
test_loss = loss_fn(test_logits, y_test)
test_acc = accuracy_fn(y_true=y_test,
y_pred=test_pred)
[Link] 38/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
Remember how our circle data is non-linear? Well, let's see how our models predictions
look now the model's been trained with non-linear activation functions.
Out[39]: (tensor([1., 0., 1., 0., 0., 1., 0., 0., 1., 0.], device
='cuda:0'),
tensor([1., 1., 1., 1., 0., 1., 1., 1., 1., 0.]))
[Link] 39/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
model_1 = no non-linearity
[Link](1, 2, 2)
[Link]("Test")
plot_decision_boundary(model_3, X_test, y_test) #
model_3 = has non-linearity
Potentially you could try a few tricks to improve the test accuracy of the model? (hint:
head back to section 5 for tips on improving the model)
Note: Much of the data you'll encounter in the wild is non-linear (or a combination of
linear and non-linear). Right now we've been working with dots on a 2D plot. But
imagine if you had images of plants you'd like to classify, there's a lot of different plant
shapes. Or text from Wikipedia you'd like to summarize, there's lots of different ways
words can be put together (linear and non-linear patterns).
[Link] 40/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
Now let's see how the ReLU activation function influences it.
The ReLU function turns all negatives to 0 and leaves the positive values as they are.
Out[43]: tensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 1.,
2., 3., 4., 5., 6., 7.,
8., 9.])
It looks like our ReLU function worked, all of the negative values are zeros.
[Link] 41/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
Nice! That looks exactly like the shape of the ReLU function on the Wikipedia page for
ReLU).
$$ out_i = \frac{1}{1+e^{-input_i}} $$
$$ S(x) = \frac{1}{1+e^{-x_i}} $$
Where $S$ stands for sigmoid, $e$ stands for exponential ( [Link]() ) and $i$
stands for a particular element in a tensor.
[Link] 42/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
Woah, those values look a lot like prediction probabilities we've seen earlier, let's see
what they look like visualized.
Now there's plenty more non-linear activation functions that exist in PyTorch that we
haven't tried.
And the point remains, what patterns could you draw using an unlimited amount of linear
(straight) and non-linear (not straight) lines?
That's exactly what our model is doing when we combine linear and non-linear functions.
Instead of telling our model what to do, we give it tools to figure out how to best discover
patterns in the data.
[Link] 43/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
But now let's put it all together using a multi-class classification problem.
Recall a binary classification problem deals with classifying something as one of two
options (e.g. a photo as a cat photo or a dog photo) where as a multi-class
classification problem deals with classifying something from a list of more than two
options (e.g. classifying a photo as a cat a dog or a chicken).
Example of binary vs. multi-class classification. Binary deals with two classes (one thing or
another), where as multi-class classification can deal with any number of classes over two,
for example, the popular ImageNet-1k dataset is used as a computer vision benchmark and
has 1000 classes.
This method will create however many classes (using the centers parameter) we want.
[Link] 44/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
2. Turn the data into tensors (the default of make_blobs() is to use NumPy arrays).
3. Split the data into training and test sets using train_test_split() .
# 4. Plot data
[Link](figsize=(10, 7))
[Link](X_blob[:, 0], X_blob[:, 1], c=y_blob,
cmap=[Link]);
tensor([[-8.4134, 6.9352],
[-5.7665, -6.4312],
[-6.0421, -6.7661],
[Link] 45/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
[ 3.9508, 0.6984],
[ 4.2505, -0.2815]]) tensor([3, 2, 2, 1, 1])
Nice! Looks like we've got some multi-class data ready to go.
Question: Does this dataset need non-linearity? Or could you draw a succession of
straight lines to separate it?
You might also be starting to get an idea of how flexible neural networks are.
How about we build one similar to model_3 but this is still capable of handling multi-
class data?
output_features - the ideal numbers of output features we'd like (this will be
equivalent to NUM_CLASSES or the number of classes in your multi-class
classification problem).
hidden_units - the number of hidden neurons we'd like each hidden layer to use.
Since we're putting things together, let's setup some device agnostic code (we don't have
to do this again in the same notebook, it's only a reminder).
[Link] 46/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
Then we'll create the model class using the hyperparameters above.
Out[48]: 'cuda'
# Build model
class BlobModel([Link]):
def __init__(self, input_features,
output_features, hidden_units=8):
"""Initializes all required hyperparameters
for a multi-class classification model.
Args:
input_features (int): Number of input
features to the model.
out_features (int): Number of output
features of the model
(how many classes there are).
hidden_units (int): Number of hidden units
between layers, default 8.
"""
super().__init__()
self.linear_layer_stack = [Link](
[Link](in_features=input_features,
out_features=hidden_units),
# [Link](), # <- does our dataset require
non-linear layers? (try uncommenting and see if the
results change)
[Link](in_features=hidden_units,
out_features=hidden_units),
# [Link](), # <- does our dataset require
non-linear layers? (try uncommenting and see if the
results change)
[Link](in_features=hidden_units,
out_features=output_features), # how many classes are
there?
)
[Link] 47/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
model_4 = BlobModel(input_features=NUM_FEATURES,
output_features=NUM_CLASSES,
hidden_units=8).to(device)
model_4
Out[49]: BlobModel(
(linear_layer_stack): Sequential(
(0): Linear(in_features=2, out_features=8, bias=Tru
e)
(1): Linear(in_features=8, out_features=8, bias=Tru
e)
(2): Linear(in_features=8, out_features=4, bias=Tru
e)
)
)
Excellent! Our multi-class model is ready to go, let's create a loss function and optimizer
for it.
And we'll stick with using SGD with a learning rate of 0.1 for optimizing our model_4
parameters.
Alright, we've got a loss function and optimizer ready, and we're ready to train our model
but before we do let's do a single forward pass with our model to see if it works.
[Link] 48/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
Out[52]: ([Link]([4]), 4)
Wonderful, our model is predicting one value for each class that we have.
Do you remember what the raw outputs of our model are called?
Hint: it rhymes with "frog splits" (no animals were harmed in the creation of these
materials).
So right now our model is outputing logits but what if we wanted to figure out exactly
which label is was giving the sample?
The softmax function calculates the probability of each prediction class being the actual
predicted class compared to all other possible classes.
[Link] 49/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
It may still look like the outputs of the softmax function are jumbled numbers (and they
are, since our model hasn't been trained and is predicting using random patterns) but
there's a very specific thing different about each sample.
After passing the logits through the softmax function, each individual sample now adds
to 1 (or very close to).
Let's check.
These prediction probabilities are essentially saying how much the model thinks the
target X sample (the input) maps to each class.
Since there's one value for each class in y_pred_probs , the index of the highest value is
the class the model thinks the specific data sample most belongs to.
We can check which index has the highest value using [Link]() .
You can see the output of [Link]() returns 3, so for the features ( X ) of the
sample at index 0, the model is predicting that the most likely class value ( y ) is 3.
[Link] 50/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
Of course, right now this is just random guessing so it's got a 25% chance of being right
(since there's four classes). But we can improve those chances by training the model.
For a multi-class classification problem, to turn the logits into prediction probabilities,
you use the softmax activation function ( [Link] ).
The index of the value with the highest prediction probability is the class number the
model thinks is most likely given the input features for that sample (although this is a
prediction, it doesn't mean it will be correct).
8.5 Creating a training and testing loop for a multi-class PyTorch model
Alright, now we've got all of the preparation steps out of the way, let's write a training and
testing loop to improve and evaluate our model.
We've done many of these steps before so much of this will be practice.
The only difference is that we'll be adjusting the steps to turn the model outputs (logits)
to prediction probabilities (using the softmax activation function) and then to prediction
labels (by taking the argmax of the output of the softmax activation function).
Let's train the model for epochs=100 and evaluate it every 10 epochs.
# 1. Forward pass
y_logits = model_4(X_blob_train) # model outputs
raw logits
y_pred = [Link](y_logits,
dim=1).argmax(dim=1) # go from logits -> prediction
probabilities -> prediction labels
# print(y_logits)
[Link] 51/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
# 4. Loss backwards
[Link]()
# 5. Optimizer step
[Link]()
### Testing
model_4.eval()
with torch.inference_mode():
# 1. Forward pass
test_logits = model_4(X_blob_test)
test_pred = [Link](test_logits,
dim=1).argmax(dim=1)
# 2. Calculate test loss and accuracy
test_loss = loss_fn(test_logits, y_blob_test)
test_acc = accuracy_fn(y_true=y_blob_test,
y_pred=test_pred)
[Link] 52/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
But to make sure of this, let's make some predictions and visualize them.
Alright, looks like our model's predictions are still in logit form.
Though to evaluate them, they'll have to be in the same form as our labels
( y_blob_test ) which are in integer form.
Note: It's possible to skip the [Link]() function and go straight from
predicted logits -> predicted labels by calling [Link]() directly on the
logits.
[Link] 53/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
Nice! Our model predictions are now in the same form as our test labels.
[Link] 54/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
These are some of the most common methods you'll come across and are a good
starting point.
However, you may want to evaluate your classification model using more metrics such
as the following:
[Link] 55/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
In [60]: try:
from torchmetrics import Accuracy
except:
!pip install torchmetrics==0.9.3 # this is the
version we're using in this notebook (later versions
exist here:
[Link]
from torchmetrics import Accuracy
# Calculate accuracy
torchmetrics_accuracy(y_preds, y_blob_test)
Exercises
All of the exercises are focused on practicing the code in the sections above.
[Link] 56/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
You should be able to complete them by referencing each section or by following the
resource(s) linked.
Resources:
Example solutions notebook for 02 (try the exercises before looking at this)
Turn the data into PyTorch tensors. Split the data into training and test sets
using train_test_split with 80% training and 20% testing.
Feel free to use any combination of PyTorch layers (linear and non-linear) you
want.
3. Setup a binary classification compatible loss function and optimizer to use when
training the model.
4. Create a training and testing loop to fit the model you created in 2 to the data you
created in 1.
To measure model accuracy, you can create your own accuracy function or use
the accuracy function in TorchMetrics.
Train the model for long enough for it to reach over 96% accuracy.
The training loop should output progress every 10 epochs of the model's training
and test set loss and accuracy.
5. Make predictions with your trained model and plot them using the
plot_decision_boundary() function created in this notebook.
7. Create a multi-class dataset using the spirals data creation function from CS231n
(see below for the code).
Construct a model capable of fitting the data (you may need a combination of
linear and non-linear layers).
[Link] 57/58
6/19/26, 12:43 PM 02. PyTorch Neural Network Classification - Zero to Mastery Learn PyTorch for Deep Learning
Make a training and testing loop for the multi-class data and train a model on it
to reach over 95% testing accuracy (you can use any accuracy measuring
function here that you like).
Plot the decision boundaries on the spirals dataset from your model predictions,
the plot_decision_boundary() function should work for this dataset too.
Extra-curriculum
Write down 3 problems where you think machine classification could be useful
(these can be anything, get creative as you like, for example, classifying credit card
transactions as fraud or not fraud based on the purchase amount and purchase
location features).
Spend 10-minutes reading the Wikipedia page for different activation functions, how
many of these can you line up with PyTorch's activation functions?
Research when accuracy might be a poor metric to use (hint: read "Beyond
Accuracy" by Will Koehrsen for ideas).
Watch: For an idea of what's happening within our neural networks and what they're
doing to learn, watch MIT's Introduction to Deep Learning video.
[Link] 58/58