
In this blog post, we will walk through the process of coding a simple fully connected neural network using the PyTorch framework. We will explain each step and build the basic architecture of the network. The goal of this network is to classify handwritten digits from the MNIST dataset.
Let's start by importing the necessary libraries:
import torch
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
from torch.utils.data import DataLoader
from torchvision import datasets
from torchvision import transforms
We will be using the torch library for creating and training the neural network, as well as the torchvision library for loading the MNIST dataset.
Creating the Fully Connected Neural Network
The first step is to create the architecture of our neural network. We will define a class called NN that inherits from the nn.Module class provided by PyTorch. This class will represent our neural network model.
class NN(nn.Module):
def __init__(self, input_size, num_classes):
super(NN, self).__init__()
self.fc1 = nn.Linear(input_size, 50)
self.fc2 = nn.Linear(50, num_classes)
def forward(self, x):
x = F.relu(self.fc1(x))
x = self.fc2(x)
return x
The __init__ method initializes the layers of the network. In this case, we have two fully connected layers (nn.Linear) with the input size and the number of classes as parameters. The forward method defines the forward pass of the network, specifying the operations to be applied to the input data.
Setting Up the Environment and hyperparameters
Before we proceed, we need to set up some environment configurations. We will define the device on which the network will be trained, whether it is a GPU or CPU. We will also set some hyperparameters for training the network.
device = 'cuda' if torch.cuda.is_available() else 'cpu'
input_size = 784
num_classes = 10
learning_rate = 0.001
batch_size = 64
num_epochs = 2
The device variable is set to 'cuda' if a GPU is available; otherwise, it is set to 'cpu'. We specify the input size, which corresponds to the flattened size of the input images (28x28 pixels). The num_classes variable represents the number of classes in the dataset (digits from 0 to 9). The learning_rate determines the step size at each iteration during training. The batch_size defines the number of samples processed in each iteration, and num_epochs represents the number of times the entire dataset will be passed through the network during training.
Loading the Dataset
Next, we will load the MNIST dataset. PyTorch provides a convenient API to download and load common datasets like MNIST.
train_dataset = datasets.MNIST(root='datasets/', train=True, transform=transforms.ToTensor(), download=True)
train_loader = DataLoader(dataset=train_dataset, batch_size=batch_size, shuffle=True)
test_dataset = datasets.MNIST(root='datasets/', train=False, transform=transforms.ToTensor(), download=True)
test_loader = DataLoader(dataset=test_dataset, batch_size=batch_size, shuffle=True)
We create two Dataset objects for training and testing, specifying the root directory for storing the dataset, whether it is for training or testing, and the transformation to be applied to the data (converting images to tensors). We also create two DataLoader objects, which allow us to iterate over the dataset in batches. The batch_size parameter determines the number of samples to be included in each batch, and shuffle=True shuffles the dataset before each epoch to ensure randomness in the training process.
Initializing the Network, Loss Function, and Optimizer
Now, we will initialize our neural network model, define the loss function, and choose an optimizer for training the network.
model = NN(input_size=input_size, num_classes=num_classes).to(device) loss_fn = nn.CrossEntropyLoss() optimizer = optim.Adam(model.parameters(), lr=learning_rate)
We create an instance of our NN class and move it to the chosen device using the .to(device) method. This ensures that the computations will be performed on either a GPU or CPU, depending on the availability. We use the nn.CrossEntropyLoss() function as our loss function since we are dealing with a classification task. The optim.Adam() function is used to initialize the Adam optimizer, which will update the weights of our model during training.
Training the Network
It's time to train our neural network. We will iterate over the dataset for the specified number of epochs and update the model's weights based on the calculated loss.
for epoch in range(num_epochs):
for batch_idx, (data, targets) in enumerate(train_loader):
# Get data to the device
data = data.to(device)
targets = targets.to(device)
# Reshape the data
data = data.reshape(data.shape[0], -1)
# Forward pass
scores = model(data)
# Calculate the loss
loss = loss_fn(scores, targets)
# Backward pass and optimization
optimizer.zero_grad()
loss.backward()
optimizer.step()
We iterate over each epoch and, within each epoch, iterate over the batches of data. For each batch, we move the data and targets to the device, reshape the data to match the network's input size, perform a forward pass through the network to obtain the predicted scores, calculate the loss between the predicted scores and the actual targets, perform backpropagation to compute the gradients, and update the weights of the model using the optimizer.
Evaluating the Model
After training the network, we want to evaluate its performance on both the training and testing datasets.
def check_accuracy(loader, model):
if loader.dataset.train:
print("Checking accuracy on the train dataset")
else:
print("Checking accuracy on the test dataset")
num_correct = 0
num_samples = 0
model.eval()
with torch.no_grad():
for x, y in loader:
x = x.to(device)
y = y.to(device)
x = x.reshape(x.shape[0], -1)
scores = model(x)
_, preds = scores.max(1)
num_correct += (preds == y).sum()
num_samples += preds.size(0)
accuracy = float(num_correct) / float(num_samples) * 100
print("Accuracy: {:.2f}%".format(accuracy))
model.train()
return
check_accuracy(train_loader, model)
check_accuracy(test_loader, model)
The check_accuracy function takes a data loader and a model as input and calculates the accuracy of the model's predictions on the given dataset. We iterate over the dataset, move the data to the device, reshape it, obtain the predicted scores from the model, calculate the number of correct predictions, and calculate the overall accuracy. The model is set to evaluation mode (model.eval()) to disable gradient computation and speed up inference.
Conclusion
In this blog post, we have walked through the process of coding a simple fully connected neural network using the PyTorch framework. We started by creating the network architecture using the nn.Module class and defined the forward pass operation. Then, we set up the environment by specifying the device for training, setting hyperparameters, and loading the MNIST dataset.
Next, we initialized the network, defined the loss function, and chose an optimizer for training. We used the Adam optimizer and the cross-entropy loss function appropriate for our classification task.
We then trained the network by iterating over the dataset for the specified number of epochs, performing forward and backward passes, and updating the model's weights.
Finally, we evaluated the model's performance on both the training and testing datasets using the check_accuracy function. This function calculated the accuracy of the model's predictions and printed the results.
By following this example, you can build and train a simple neural network using the PyTorch framework. Feel free to modify the architecture, hyperparameters, or dataset to suit your specific needs.
I hope this blog post has provided you with a clear understanding of how to code a simple neural network in PyTorch. Happy coding!
References:
https://pytorch.org/
https://pytorch.org/tutorials/
https://www.youtube.com/watch?v=Jy4wM2X21u0&list=PLhhyoLH6IjfxeoooqP9rhU3HJIAVAJ3Vz&index=4&ab_channel=AladdinPersson
Comments