[Pytorch練手]使用CNN圖像分類

[Pytorch練手]使用CNN圖像分類

需求

在4*4的圖片中,比較外圍黑色像素點和內圈黑色像素點個數的大小將圖片分類python

截屏2020-06-15 18.01.24.png

如上圖圖片外圍黑色像素點5個大於內圈黑色像素點1個分爲0類反之1類算法

想法

  1. 經過numpy、PIL構造4*4的圖像數據集
  2. 構造本身的數據集類
  3. 讀取數據集對數據集選取減小偏斜
  4. cnn設計由於特徵少,直接1*1卷積層
  5. 或者在4*4外圍添加padding成6*6,設計2*2的卷積核得出3*3再接上全鏈接層

代碼

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

構造數據集

import csv
import collections
import os
import shutil

def buildDataset(root,dataType,dataSize):
    """構造數據集
    構造的圖片存到root/{dataType}Data
    圖片地址和標籤的csv文件存到 root/{dataType}DataInfo.csv
    Args:
        root:str
            項目目錄
        dataType:str
            'train'或者‘test'
        dataNum:int
            數據大小
    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):
        # 建立0,1 數組
        imageArray=np.random.randint(0,2,(4,4))
        # 計算0,1數量獲得標籤
        allBlackNum = collections.Counter(imageArray.flatten())[0]
        innerBlackNum = collections.Counter(imageArray[1:3,1:3].flatten())[0]
        label = 0 if (allBlackNum-innerBlackNum)>innerBlackNum else 1
        # 將圖片保存
        path = f'{dataPath}/{i}.jpg'
        dataInfo.append([path,label])
        im = Image.fromarray(np.uint8(imageArray*255))
        im = im.convert('1') 
        im.save(path)
    # 將圖片地址和標籤存入csv文件
    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'

構造訓練數據集

buildDataset(root,'train',20000)

構造測試數據集

buildDataset(root,'test',10000)

讀取數據集

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 = []
            # 讀取csv信息到imgs列表
            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())

處理數據集使得數據集不偏斜

import itertools

def chooseData(dataset,scale):
    # 將類別爲1的排序到前面
    dataset.imgs.sort(key=lambda x:x[1],reverse=True)
    # 獲取類別1的數目 ,取scale倍的數組,得數據不那麼偏斜
    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

# 超參數
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)

定義模型

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添加1層常數1,設定卷積核爲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

定義損失函數

# 交叉熵損失函數
loss = nn.CrossEntropyLoss()
loss2 = nn.CrossEntropyLoss()

定義優化算法

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

訓練模型

# 計算準確率
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()
            # 計算訓練準確度和loss
            trainLossSum += l.item()
            trainAccSum += (yHat.argmax(dim=1) == y).sum().item()
            n += y.shape[0]
        # 評估測試準確度
        testAcc = evaluateAccuracy(testIter, net)
        print('epoch {:d}, loss {:.4f}, train acc {:.3f}, test acc {:.3f}'.format(epoch + 1, trainLossSum / n, trainAccSum / n, testAcc))

Net模型訓練

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模型訓練

# 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 = 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)
# 比較結果
print(f'Net測試結果{predictions.eq(target)}')
print(f'Net2測試結果{predictions2.eq(target)}')
Net測試結果tensor([ True,  True, False,  True,  True,  True,  True])
Net2測試結果tensor([False,  True, False,  True,  True, False,  True])
相關文章
相關標籤/搜索