Convolutional neural network code exercises

MNIST data classification

  1. 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
  1. 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
  1. 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
  1. 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
  1. 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
  1. 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
  1. 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
  1. 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
    1. 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
    1. 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