[Pytorch practice] using CNN image classification

Posted Jun 15, 20206 min read

[Pytorch practice hands]Use CNN image classification

demand

In 4*4 pictures, compare the number of black pixels on the periphery and black pixels on the inner circle to classify the pictures

Screenshot 2020-06-15 18.01.24.png

As shown in the above picture, there are 5 black pixels on the periphery that are larger than the black pixels on the inner circle, and 1 is divided into 0 categories.

idea

  1. Construct 4*4 image data set by numpy and PIL
  2. Construct your own data set class
  3. Read the data set to select the data set to reduce skew
  4. Because the cnn design has few features, the direct 1*1 convolution layer
  5. Or add padding to 6*6 on the periphery of 4*4, design the convolution kernel of 2_*2 to get 3_*3, and then connect the fully connected layer

Code

import torch
import torchvision
import torchvision.transforms as transforms
import numpy as np
from PIL import Image

Constructing a data set

import csv
import collections
import os
import shutil

def buildDataset(root, dataType, dataSize):
    """ Construct Data Set
    Save the constructed picture to root/{dataType}Data
    Save the csv file of the image address and label to root/{dataType}DataInfo.csv
    Args:
        root:str
            Project directory
        dataType:str
            'train' or'test'
        dataNum:int
            Data size
    Returns:
    """
    dataInfo = []
    dataPath = f'{root}/{dataType}Data'
    if not os.path.exists(dataPath):
        os.makedirs(dataPath)
    else:
        shutil.rmtree(dataPath)
        os.mkdir(dataPath)

    for i in range(dataSize):
        # Create 0, 1 array
        imageArray=np.random.randint(0,2,(4,4))
        # Calculate the number of 0 and 1 to get the label
        allBlackNum = collections.Counter(imageArray.flatten())[0]
        innerBlackNum = collections.Counter(imageArray[1:3,1:3].flatten())[0]
        label = 0 if(allBlackNum-innerBlackNum)>innerBlackNum else 1
        # Save the picture
        path = f'{dataPath}/{i}.jpg'
        dataInfo.append([path,label])
        im = Image.fromarray(np.uint8(imageArray*255))
        im = im.convert('1')
        im.save(path)
    # Save the image address and label in the csv file
    filePath = f'{root}/{dataType}DataInfo.csv'
    with open(filePath,'w') as f:
        writer = csv.writer(f)
        writer.writerows(dataInfo)


root=r'/Users/null/Documents/PythonProject/Classifier'

Construct training data set

buildDataset(root,'train',20000)

Construct a test data set

buildDataset(root,'test',10000)

Read the data set

class MyDataset(torch.utils.data.Dataset):

    def __init__(self, root, datacsv, transform=None):
        super(MyDataset, self).__init__()
        with open(f'{root}/{datacsv}','r') as f:
            imgs = []
            # Read csv information to imgs list
            for path,label in map(lambda line:line.rstrip().split(','),f):
                imgs.append((path, int(label)))
        self.imgs = imgs
        self.transform = transform if transform is not None else lambda x:x

    def __getitem__(self, index):
        path, label = self.imgs[index]
        img = self.transform(Image.open(path).convert('1'))
        return img, label

    def __len__(self):
        return len(self.imgs)

trainData=MyDataset(root = root,datacsv='trainDataInfo.csv', transform=transforms.ToTensor())
testData=MyDataset(root = root,datacsv='testDataInfo.csv', transform=transforms.ToTensor())

Processing the data set so that the data set is not skewed

import itertools

def chooseData(dataset,scale):
    # Sort category 1 to the front
    dataset.imgs.sort(key=lambda x:x[1],reverse=True)
    # Get the number of category 1, take the array of scale times, the data is not so skewed
    trueNum =collections.Counter(itertools.chain.from_iterable(dataset.imgs))[1]
    end = min(trueNum*scale,len(dataset))
    dataset.imgs=dataset.imgs[:end]

scale = 4
chooseData(trainData,scale)
chooseData(testData,scale)
len(trainData), len(testData)

(2250, 1122)

import torch.utils.data as Data

# Hyperparameter
batchSize = 50
lr = 0.1
numEpochs = 20

trainIter = Data.DataLoader(dataset=trainData, batch_size=batchSize, shuffle=True)
testIter = Data.DataLoader(dataset=testData, batch_size=batchSize)

Define the model

from torch import nn
from torch.autograd import Variable
from torch.nn import Module,Linear,Sequential,Conv2d,ReLU,ConstantPad2d
import torch.nn.functional as F

class Net(Module):
    def __init__(self):
        super(Net, self).__init__()

        self.cnnLayers = Sequential(
            # padding add 1 layer constant 1, set the convolution kernel to 2*2
            ConstantPad2d(1, 1),
            Conv2d(1, 1, kernel_size=2, stride=2,bias=True)
       )
        self.linearLayers = Sequential(
            Linear(9, 2)
       )

    def forward(self, x):
        x = self.cnnLayers(x)
        x = x.view(x.shape[0], -1)
        x = self.linearLayers(x)
        return x


class Net2(Module):
    def __init__(self):
        super(Net2, self).__init__()

        self.cnnLayers = Sequential(
            Conv2d(1, 1, kernel_size=1, stride=1,bias=True)
       )
        self.linearLayers = Sequential(
            ReLU(),
            Linear(16, 2)
       )

    def forward(self, x):
        x = self.cnnLayers(x)
        x = x.view(x.shape[0], -1)
        x = self.linearLayers(x)
        return x

Define loss function

# Cross entropy loss function
loss = nn.CrossEntropyLoss()

loss2 = nn.CrossEntropyLoss()

Define optimization algorithm

net = Net()
optimizer = torch.optim.SGD(net.parameters(),lr = lr)

net2 = Net2()
optimizer2 = torch.optim.SGD(net2.parameters(),lr = lr)

Training model

# Calculate accuracy
def evaluateAccuracy(dataIter, net):
    accSum, n = 0.0, 0
    with torch.no_grad():
        for X, y in dataIter:
            accSum +=(net(X).argmax(dim=1) == y).float().sum().item()
            n += y.shape[0]
    return accSum/n

def train(net, trainIter, testIter, loss, numEpochs, batchSize,
             optimizer):
    for epoch in range(numEpochs):
        trainLossSum, trainAccSum, n = 0.0, 0.0, 0
        for X,y in trainIter:
            yHat = net(X)
            l = loss(yHat,y).sum()
            optimizer.zero_grad()
            l.backward()
            optimizer.step()
            # Calculate training accuracy and loss
            trainLossSum += l.item()
            trainAccSum +=(yHat.argmax(dim=1) == y).sum().item()
            n += y.shape[0]
        # Assess test accuracy
        testAcc = evaluateAccuracy(testIter, net)
        print('epoch {:d}, loss {:.4f}, train acc {:.3f}, test acc {:.3f}'.format(epoch + 1, trainLossSum/n, trainAccSum/n, testAcc))

Net model training

train(net, trainIter, testIter, loss, numEpochs, batchSize, optimizer)

epoch 1, loss 0.0128, train acc 0.667, test acc 0.667
epoch 2, loss 0.0118, train acc 0.683, test acc 0.760
epoch 3, loss 0.0104, train acc 0.742, test acc 0.807
epoch 4, loss 0.0093, train acc 0.769, test acc 0.772
epoch 5, loss 0.0085, train acc 0.797, test acc 0.745
epoch 6, loss 0.0084, train acc 0.798, test acc 0.807
epoch 7, loss 0.0082, train acc 0.804, test acc 0.816
epoch 8, loss 0.0078, train acc 0.816, test acc 0.812
epoch 9, loss 0.0077, train acc 0.818, test acc 0.817
epoch 10, loss 0.0074, train acc 0.824, test acc 0.826
epoch 11, loss 0.0072, train acc 0.836, test acc 0.819
epoch 12, loss 0.0075, train acc 0.823, test acc 0.829
epoch 13, loss 0.0071, train acc 0.839, test acc 0.797
epoch 14, loss 0.0067, train acc 0.849, test acc 0.824
epoch 15, loss 0.0069, train acc 0.848, test acc 0.843
epoch 16, loss 0.0064, train acc 0.864, test acc 0.851
epoch 17, loss 0.0062, train acc 0.867, test acc 0.780
epoch 18, loss 0.0060, train acc 0.871, test acc 0.864
epoch 19, loss 0.0057, train acc 0.881, test acc 0.890
epoch 20, loss 0.0055, train acc 0.885, test acc 0.897

Net2 model training

# batchSize = 50
# lr = 0.1
# numEpochs = 15
train(net2, trainIter, testIter, loss2, numEpochs, batchSize, optimizer2)

epoch 1, loss 0.0119, train acc 0.638, test acc 0.676
epoch 2, loss 0.0079, train acc 0.823, test acc 0.986
epoch 3, loss 0.0046, train acc 0.987, test acc 0.977
epoch 4, loss 0.0030, train acc 0.983, test acc 0.973
epoch 5, loss 0.0023, train acc 0.981, test acc 0.976
epoch 6, loss 0.0019, train acc 0.980, test acc 0.988
epoch 7, loss 0.0016, train acc 0.984, test acc 0.984
epoch 8, loss 0.0014, train acc 0.985, test acc 0.986
epoch 9, loss 0.0013, train acc 0.987, test acc 0.992
epoch 10, loss 0.0011, train acc 0.989, test acc 0.993
epoch 11, loss 0.0010, train acc 0.989, test acc 0.996
epoch 12, loss 0.0010, train acc 0.992, test acc 0.994
epoch 13, loss 0.0009, train acc 0.993, test acc 0.994
epoch 14, loss 0.0008, train acc 0.995, test acc 0.996
epoch 15, loss 0.0008, train acc 0.994, test acc 0.998

Test

test = torch.Tensor([[[[0,0,0,0],[0,1,1,0],[0,1,1,0],[0,0,0,0]]],
                  [[[1,1,1,1],[1,0,0,1],[1,0,0,1],[1,1,1,1]]],
                  [[[0,1,0,1],[1,0,0,1],[1,0,0,1],[0,0,0,1]]],
                  [[[0,1,1,1],[1,0,0,1],[1,0,0,1],[0,0,0,1]]],
                  [[[0,0,1,1],[1,0,0,1],[1,0,0,1],[1,0,1,0]]],
                  [[[0,0,1,0],[0,1,0,1],[0,0,1,1],[1,0,1,0]]],
                  [[[1,1,1,0],[1,0,0,1],[1,0,1,1],[1,0,1,1]]]
                ])

target=torch.Tensor([0,1,0,1,1,0,1])

test

tensor([[[[0., 0., 0., 0.],
          [0., 1., 1., 0.],
          [0., 1., 1., 0.],
          [0., 0., 0., 0.]]],

​​

        [[[1., 1., 1., 1.],
          [1., 0., 0., 1.],
          [1., 0., 0., 1.],
          [1., 1., 1., 1.]]],

​​

        [[[0., 1., 0., 1.],
          [1., 0., 0., 1.],
          [1., 0., 0., 1.],
          [0., 0., 0., 1.]]],

​​

        [[[0., 1., 1., 1.],
          [1., 0., 0., 1.],
          [1., 0., 0., 1.],
          [0., 0., 0., 1.]]],

​​

        [[[0., 0., 1., 1.],
          [1., 0., 0., 1.],
          [1., 0., 0., 1.],
          [1., 0., 1., 0.]]],

​​

        [[[0., 0., 1., 0.],
          [0., 1., 0., 1.],
          [0., 0., 1., 1.],
          [1., 0., 1., 0.]]],

​​

        [[[1., 1., 1., 0.],
          [1., 0., 0., 1.],
          [1., 0., 1., 1.],
          [1., 0., 1., 1.]]]])





with torch.no_grad():
    output = net(test)
    output2 = net2(test)
predictions =output.argmax(dim=1)
predictions2 =output2.argmax(dim=1)


# Comparing results
print(f'Net test results {predictions.eq(target)}')
print(f'Net2 test result {predictions2.eq(target)}')


Net test result tensor([True, True, False, True, True, True, True])
Net2 test result tensor([False, True, False, True, True, False, True])