The images are of size 32x32 pixels (tiny!), which makes it very lightweight, quick to load and easy to play with.
Create a DataLoader
A DataLoader is an iterable feeding data to a model at each iteration. The data loader transforms the data to the proper format, sets the batch size, whether the data is shuffled or not, and how the I/O is parallelized. You can create DataLoaders with the torch.utils.data.DataLoader class.
Let’s create 2 DataLoaders: one for the train set and one for the test set.
Load packages
import torchimport torchvisionimport torchvision.transforms as transforms
---------------------------------------------------------------------------ModuleNotFoundError Traceback (most recent call last)
CellIn[1], line 1----> 1importtorch 2importtorchvision 3importtorchvision.transformsastransformsModuleNotFoundError: No module named 'torch'
---------------------------------------------------------------------------NameError Traceback (most recent call last)
CellIn[2], line 1----> 1 transform = transforms.Compose(
2 [transforms.ToTensor(),
3transforms.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))])NameError: name 'transforms' is not defined
Choose a batch size
Remember that the data move forward through the network (forward pass), outputting some estimates which are used to calculate some loss (or error) value. Then we get gradients through automatic differentiation and the model parameters are adjusted a little through gradient descent.
You do not have to have the entire training set go through this process each time: you can use batches.
The batch size is the number of items from the data that are processed before the model is updated. There is no hard rule to set good batch sizes and sizes tend to be picked through trial and error.
Here are some rules to choose a batch size:
make sure that the batch fits in the CPU or GPU,
small batches give faster results (each training iteration is very fast), but give less accuracy,
large batches lead to slower training, but better accuracy.
Though not necessary, it can be useful to have a look at the data:
# Load the packages for thisimport matplotlib.pyplot as pltimport numpy as np# Define a function to display an imagedef show_img(img): img = img /2+0.5# unnormalize npimg = img.numpy() plt.figure(figsize=(9.5, 5)) plt.axis('off') plt.imshow(np.transpose(npimg, (1, 2, 0))) plt.show()# Get a batch of random training imagesdataiter =iter(trainloader)images, labels =next(dataiter)# Display the imagesshow_img(torchvision.utils.make_grid(images))# Print the labelsprint(' '.join(f'{classes[labels[j]]:5s}'for j inrange(batch_size)))
---------------------------------------------------------------------------ModuleNotFoundError Traceback (most recent call last)
CellIn[6], line 2 1# Load the packages for this----> 2importmatplotlib.pyplotasplt 3importnumpyasnp 4 5# Define a function to display an imageModuleNotFoundError: No module named 'matplotlib'
The model
Architecture
First, we need to define the architecture of the network. There are many types of architectures. For images, CNN are well suited.
In Python, you can define a subclass of an existing class with:
class YourSubclass(BaseClass):<definition of your subclass>
The subclass is derived from the base class and inherits its properties. PyTorch contains the class torch.nn.Module which is used as the base class when defining a neural network.
import torch.nn as nnimport torch.nn.functional as Fclass Net(nn.Module):# Define the architecture of the networkdef__init__(self):super().__init__()# 3 input image channel (3 colour channels)# 6 output channels,# 5x5 square convolution kernelself.conv1 = nn.Conv2d(3, 6, 5)# Max pooling over a (2, 2) windowself.pool = nn.MaxPool2d(2, 2)self.conv2 = nn.Conv2d(6, 16, 5)# 5*5 from image dimensionself.fc1 = nn.Linear(16*5*5, 120)self.fc2 = nn.Linear(120, 84)# 10 is the size of the output layer# since there are 10 classesself.fc3 = nn.Linear(84, 10)# Set the flow of data through the network for the forward pass# x represents the datadef forward(self, x):# F.relu is the rectified-linear activation function x =self.pool(F.relu(self.conv1(x))) x =self.pool(F.relu(self.conv2(x)))# flatten all dimensions except the batch dimension x = torch.flatten(x, 1) x = F.relu(self.fc1(x)) x = F.relu(self.fc2(x)) x =self.fc3(x)return x
---------------------------------------------------------------------------ModuleNotFoundError Traceback (most recent call last)
CellIn[7], line 1----> 1importtorch.nnasnn 2importtorch.nn.functionalasF 3 4classNet(nn.Module):ModuleNotFoundError: No module named 'torch'
Let’s create an instance of Net and print its structure:
net = Net()print(net)
---------------------------------------------------------------------------NameError Traceback (most recent call last)
CellIn[8], line 1----> 1net=Net() 2print(net)NameError: name 'Net' is not defined
Loss function and optimizer
We need to choose a loss function that will be used to calculate the gradients through backpropagation as well as an optimizer to do the gradient descent.
SGD with momentum has proved a very efficient optimizing technique and is widely used.
import torch.optim as optimcriterion = nn.CrossEntropyLoss()optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
---------------------------------------------------------------------------ModuleNotFoundError Traceback (most recent call last)
CellIn[9], line 1----> 1importtorch.optimasoptim 2 3criterion=nn.CrossEntropyLoss() 4optimizer=optim.SGD(net.parameters(),lr=0.001,momentum=0.9)ModuleNotFoundError: No module named 'torch'
Training
We can now train the model:
for epoch inrange(2): # loop over the dataset twice running_loss =0.0for i, data inenumerate(trainloader, 0):# get the inputs; data is a list of [inputs, labels] inputs, labels = data# zero the parameter gradients optimizer.zero_grad()# forward + backward + optimize outputs = net(inputs) loss = criterion(outputs, labels) loss.backward() optimizer.step()# print statistics running_loss += loss.item()if i %2000==1999: # print every 2000 mini-batchesprint(f'[{epoch +1}, {i +1:5d}] loss: {running_loss /2000:.3f}') running_loss =0.0print('Finished Training')
---------------------------------------------------------------------------NameError Traceback (most recent call last)
CellIn[10], line 5 1forepochinrange(2):# loop over the dataset twice 2 3running_loss=0.0 4----> 5fori,datainenumerate(trainloader,0): 6# get the inputs; data is a list of [inputs, labels] 7inputs,labels=data 8NameError: name 'trainloader' is not defined
Testing
Little test on one batch for fun
Let’s now test our model on one batch of testing data.
---------------------------------------------------------------------------NameError Traceback (most recent call last)
CellIn[11], line 1----> 1dataiter=iter(testloader) 2images,labels=next(dataiter)NameError: name 'testloader' is not defined
---------------------------------------------------------------------------NameError Traceback (most recent call last)
CellIn[12], line 1----> 1show_img(torchvision.utils.make_grid(images)) 2print('Real: ',' '.join(f'{classes[labels[j]]:5s}'forjinrange(4)))NameError: name 'show_img' is not defined
Now, let’s run the same batch of testing images through our model:
outputs = net(images)
---------------------------------------------------------------------------NameError Traceback (most recent call last)
CellIn[13], line 1----> 1outputs=net(images)NameError: name 'net' is not defined
---------------------------------------------------------------------------NameError Traceback (most recent call last)
CellIn[14], line 1----> 1_,predicted=torch.max(outputs,1) 2 3 print('Predicted: ', ' '.join(f'{classes[predicted[j]]:5s}'
4forjinrange(4)))NameError: name 'torch' is not defined
More serious testing
This was fun, but of course, with a sample of one, we can’t say anything about how good our model is. We need to test it on many more images from the test set.
Let’s use the entire test set:
correct =0total =0# since we're not training, we don't need to calculate the gradients for our outputswith torch.no_grad():for data in testloader: images, labels = data# calculate outputs by running images through the network outputs = net(images)# the class with the highest energy is what we choose as prediction _, predicted = torch.max(outputs.data, 1) total += labels.size(0) correct += (predicted == labels).sum().item()print(f'Accuracy of the network on the 10000 test images: {100* correct // total} %')
---------------------------------------------------------------------------NameError Traceback (most recent call last)
CellIn[15], line 4 1correct=0 2total=0 3# since we're not training, we don't need to calculate the gradients for our outputs----> 4withtorch.no_grad(): 5fordataintestloader: 6images,labels=data 7# calculate outputs by running images through the networkNameError: name 'torch' is not defined
Per class testing
We could see whether the model seems to perform better for some classes than others:
# prepare to count predictions for each classcorrect_pred = {classname: 0for classname in classes}total_pred = {classname: 0for classname in classes}# again no gradients neededwith torch.no_grad():for data in testloader: images, labels = data outputs = net(images) _, predictions = torch.max(outputs, 1)# collect the correct predictions for each classfor label, prediction inzip(labels, predictions):if label == prediction: correct_pred[classes[label]] +=1 total_pred[classes[label]] +=1# print accuracy for each classfor classname, correct_count in correct_pred.items(): accuracy =100*float(correct_count) / total_pred[classname]print(f'Accuracy for class: {classname:5s} is {accuracy:.1f} %')
---------------------------------------------------------------------------NameError Traceback (most recent call last)
CellIn[16], line 6 2correct_pred={classname:0forclassnameinclasses} 3total_pred={classname:0forclassnameinclasses} 4 5# again no gradients needed----> 6withtorch.no_grad(): 7fordataintestloader: 8images,labels=data 9outputs=net(images)NameError: name 'torch' is not defined