Convolutional neural network code exercises
MNIST data classification
- First construct a simple convolutional neural network (CNN)
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torchvision import datasets, transforms
import matplotlib.pyplot as plt
import numpy
A function that calculates how many parameters there are in the model
def get_n_params(model) :
np=0
for p in list(model.parameters()):
np += p.nelement()
return np
Use GPU training, which can be set in the menu "Code Execution Tools" -> "Change Runtime Type"
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
Copy the code
-
Torchvision.datasets can be called to download the data locally. MINIST’s prototype method is as follows:
-
Root: the data set is downloaded to the local Root directory
-
Train: data set is created from train.pt when True, or from test.pt otherwise
-
Download: If set to True, thieves will download data from the Internet and store it in the Root folder
-
Transform: A function or transform that inputs PIL images and returns the transformed data.
-
Target_transform: A function or transform that takes a target and transforms it.
We do this in code
-
input_size = 28*28 The image size on # MNIST is 28x28
output_size = 10 # A number of categories 0 through 9, and therefore of ten categories
train_loader = torch.utils.data.DataLoader(
datasets.MNIST('./data', train=True, download=True,
transform=transforms.Compose(
[transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))])),
batch_size=64, shuffle=True)
test_loader = torch.utils.data.DataLoader(
datasets.MNIST('./data', train=False, transform=transforms.Compose([
transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))])),
batch_size=1000, shuffle=True)
Copy the code
-
Visualization of data sets:
plt.figure(figsize=(8.5))
for i in range(20):
plt.subplot(4.5, i + 1)
image, _ = train_loader.dataset.__getitem__(i)
plt.imshow(image.squeeze().numpy(),'gray')
plt.axis('off');
Copy the code
- Create a network
class FC2Layer(nn.Module) :
def __init__(self, input_size, n_hidden, output_size) :
# nn.Module Subclass functions must execute the parent class constructor in the constructor
Nn.module. __init__(self)
super(FC2Layer, self).__init__()
self.input_size = input_size
Sequential is used directly to define a network, which should be distinguished from the following code for CNN
self.network = nn.Sequential(
nn.Linear(input_size, n_hidden),
nn.ReLU(),
nn.Linear(n_hidden, n_hidden),
nn.ReLU(),
nn.Linear(n_hidden, output_size),
nn.LogSoftmax(dim=1))def forward(self, x) :
# View typically occurs in the Forward function of the Model class and is used to change the shape of the input or output
# x.view(-1, self.input_size) means multidimensional data spread out into two dimensions
Input_size =784; input_size= 1
# in the DataLoader section, we can see that batch_size is 64, so the number of rows in x is 64
Print (x.pu ().numpy().shape)
# During training, we will see the output of (64, 784), which is consistent with our expectations
The # forward function specifies the operation of the network. This fully connected network may not make much sense.
# The following CNN network can see the effect of forward.
x = x.view(-1, self.input_size)
return self.network(x)
class CNN(nn.Module) :
def __init__(self, input_size, n_feature, output_size) :
# execute the parent constructor, as is required for all networks
super(CNN, self).__init__()
# Here are some definitions of typical structures in networks, generally convolution and full connection
Pooling, ReLU, etc. are not defined here
self.n_feature = n_feature
self.conv1 = nn.Conv2d(in_channels=1, out_channels=n_feature, kernel_size=5)
self.conv2 = nn.Conv2d(n_feature, n_feature, kernel_size=5)
self.fc1 = nn.Linear(n_feature*4*4.50)
self.fc2 = nn.Linear(50.10)
# The following forward function defines the structure of the network and organizes the above structures in some order
Conv1, conv2, etc., can be reused multiple times
def forward(self, x, verbose=False) :
x = self.conv1(x)
x = F.relu(x)
x = F.max_pool2d(x, kernel_size=2)
x = self.conv2(x)
x = F.relu(x)
x = F.max_pool2d(x, kernel_size=2)
x = x.view(-1, self.n_feature*4*4)
x = self.fc1(x)
x = F.relu(x)
x = self.fc2(x)
x = F.log_softmax(x, dim=1)
returnX defines the training and test function In [0] :# training function
def train(model) :
model.train()
From the train_loader, each batch of 64 samples is extracted for training
for batch_idx, (data, target) in enumerate(train_loader):
# Send data to GPU
data, target = data.to(device), target.to(device)
optimizer.zero_grad()
output = model(data)
loss = F.nll_loss(output, target)
loss.backward()
optimizer.step()
if batch_idx % 100= =0:
print('Train: [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format(
batch_idx * len(data), len(train_loader.dataset),
100. * batch_idx / len(train_loader), loss.item()))
def test(model) :
model.eval()
test_loss = 0
correct = 0
for data, target in test_loader:
# Send data to GPU
data, target = data.to(device), target.to(device)
# Feed the data into the model to get the predicted results
output = model(data)
# Calculate the loss of this batch and add it to test_loss
test_loss += F.nll_loss(output, target, reduction='sum').item()
# get the index of the Max log-probability,
The # with the highest value corresponds to the classification result, which is then stored in preD
pred = output.data.max(1, keepdim=True) [1]
# Compare pred with target to get the number of correct predictions and add them to correct
# view_as = preD = target = pred
correct += pred.eq(target.data.view_as(pred)).cpu().sum().item()
test_loss /= len(test_loader.dataset)
accuracy = 100. * correct / len(test_loader.dataset)
print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format(
test_loss, correct, len(test_loader.dataset),
accuracy))
Copy the code
- Training on a small fully connected network:
n_hidden = 8 # number of hidden units
model_fnn = FC2Layer(input_size, n_hidden, output_size)
model_fnn.to(device)
optimizer = optim.SGD(model_fnn.parameters(), lr=0.01, momentum=0.5)
print('Number of parameters: {}'.format(get_n_params(model_fnn)))
train(model_fnn)
test(model_fnn)
Copy the code
- Training on convolutional neural network
n_features = 6 # number of feature maps
model_cnn = CNN(input_size, n_features, output_size)
model_cnn.to(device)
optimizer = optim.SGD(model_cnn.parameters(), lr=0.01, momentum=0.5)
print('Number of parameters: {}'.format(get_n_params(model_cnn)))
train(model_cnn)
test(model_cnn)
Copy the code
It can be found that the effect of CNN with parameters is significantly better than that of a simple fully connected network, which is related to two characteristics of CNN
- convolution
- pooling
- Shuffling pixel order and training and testing again on both networks, the image after shuffling pixel order is visualized in the figure below
perm = torch.randperm(784)
plt.figure(figsize=(8.4))
for i in range(10):
image, _ = train_loader.dataset.__getitem__(i)
# permute pixels
image_perm = image.view(-1.28*28).clone()
image_perm = image_perm[:, perm]
image_perm = image_perm.view(-1.1.28.28)
plt.subplot(4.5, i + 1)
plt.imshow(image.squeeze().numpy(), 'gray')
plt.axis('off')
plt.subplot(4.5, i + 11)
plt.imshow(image_perm.squeeze().numpy(), 'gray')
plt.axis('off')
Copy the code
-
To redefine the training and testing functions, we wrote two functions, train_perm and test_perm, corresponding to the addition of pixel shuffling training function and test function respectively.
Basically the same training and test functions as before, except that data is added with an out-of-order operation.
Shuffle the pixel order function for each batch of data def perm_pixel(data, perm) : # Convert to a two-dimensional matrix data_new = data.view(-1.28*28) # Shuffle pixel order data_new = data_new[:, perm] Restore to the original 4-dimensional tensor data_new = data_new.view(-1.1.28.28) return data_new # training function def train_perm(model, perm) : model.train() for batch_idx, (data, target) in enumerate(train_loader): data, target = data.to(device), target.to(device) # Pixels out of order data = perm_pixel(data, perm) optimizer.zero_grad() output = model(data) loss = F.nll_loss(output, target) loss.backward() optimizer.step() if batch_idx % 100= =0: print('Train: [{}/{} ({:.0f}%)]\tLoss: {:.6f}'.format( batch_idx * len(data), len(train_loader.dataset), 100. * batch_idx / len(train_loader), loss.item())) # test function def test_perm(model, perm) : model.eval() test_loss = 0 correct = 0 for data, target in test_loader: data, target = data.to(device), target.to(device) # Pixels out of order data = perm_pixel(data, perm) output = model(data) test_loss += F.nll_loss(output, target, reduction='sum').item() pred = output.data.max(1, keepdim=True) [1] correct += pred.eq(target.data.view_as(pred)).cpu().sum().item() test_loss /= len(test_loader.dataset) accuracy = 100. * correct / len(test_loader.dataset) print('\nTest set: Average loss: {:.4f}, Accuracy: {}/{} ({:.0f}%)\n'.format( test_loss, correct, len(test_loader.dataset), accuracy)) Copy the code
- Train and test on a fully connected network
perm = torch.randperm(784) n_hidden = 8 # number of hidden units model_fnn = FC2Layer(input_size, n_hidden, output_size) model_fnn.to(device) optimizer = optim.SGD(model_fnn.parameters(), lr=0.01, momentum=0.5) print('Number of parameters: {}'.format(get_n_params(model_fnn))) train_perm(model_fnn, perm) test_perm(model_fnn, perm) Copy the code
- Training and testing are carried out on convolutional neural network
perm = torch.randperm(784)
n_features = 6 # number of feature maps
model_cnn = CNN(input_size, n_features, output_size)
model_cnn.to(device)
optimizer = optim.SGD(model_cnn.parameters(), lr=0.01, momentum=0.5)
print('Number of parameters: {}'.format(get_n_params(model_cnn)))
train_perm(model_cnn, perm)
test_perm(model_cnn, perm)
Copy the code