PytorchはCIFAR 10のCNN集積ニューラルネットワークを構築する


ソース参照https://github.com/zergtant/pytorch-handbook/blob/master/chapter1/4_cifar10_tutorial.ipynb少し修正する

CIFAR 10データ


CIFAR 10は基本的なピクチャーデータベースで、全部で10種類の分類で、訓練セットは50000枚のピクチャーがあって、テストセットは10000枚のピクチャーがあって、ピクチャーはすべて32*32解像度です.PytorchのtorchvisionはCIFAR 10を使ったデータを簡単にダウンロードできます.コードは以下の通りです.
import torch
import torchvision
import torchvision.transforms as transforms

# 
BATCH_SIZE = 4
EPOCH = 2

#torchvision CIFAR10 , transform [0,1]
transform = transforms.Compose([transforms.ToTensor(),
                                transforms.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))])
trainset = torchvision.datasets.CIFAR10(root='./data',train = True,
                                        download=True, transform=transform)
trainloader = torch.utils.data.DataLoader(trainset,batch_size = BATCH_SIZE,
                                          shuffle = True, num_workers=2)

testset = torchvision.datasets.CIFAR10(root='./data',train = False,
                                        download=True, transform=transform)
testloader = torch.utils.data.DataLoader(testset,batch_size = BATCH_SIZE,
                                          shuffle = False, num_workers=2)
classes = ('plane', 'car', 'bird', 'cat',
           'deer', 'dog', 'frog', 'horse', 'ship', 'truck')                                          

コードコメント


pytorchは、MNIST、CIFAR 10など、汎用データベースのダウンロードに便利なインタフェースを提供し、トレーニングセットとテストセットを出力します.
  • torchvision.datasets.CIFAR 10()はすべてのデータを直接ダウンロードし、train=True/Falseによって訓練セットまたはテストセットに割り当てられた[0255]RGBイメージ画像を決定することができる.
  • 訓練に用いるデータセットは通常正規化の必要があり、データを読み取る際にtransform=transformで直接実現することができ、一般的にtransform=torchvisionである.transforms.ToTensor()はtorchvisionに[0255]を[0,1]のfloat RGBに出力させることができ、本論文では正規化normalizationを継続した.
  • transforms.Componentは複数のtransformコマンドの組み合わせを実現し、ここではtransforms.ToTensor()実装出力は[0,1]であり、transformsに続く.Normalize(0.5,0.5,0.5),(0.5,0.5,0.5)は[0,1]のRGBを[−1,1]に正規化した.最初のパラメータは(0,0,0)が平均値0に正規化されるべきであると考えられていたことに注意してください.しかしtransformsのソースコードにより,output[channel] = (input[channel] - mean[channel]) / std[channel],すなわち,((0,1)−0.5)/0.5=(−1,1);
  • torchvision.datasets.CIFAR 10の出力は実際にはdatasetになっていて、直接dataloaderでバッチロードすることができて、BATCH_SIZEはバッチごとのデータであり、本明細書では計算のたびに4枚の図を反復する.
  • torch.utils.data.DataLoaderもう一つ注意すべき点はnum_workers=2は2つのコアを同時に並行するが、直接実行するとRuntimeError:An attempt has been made to start a new process before the current process has finished its bootstrapping phase.これはpythonがパラレルコンピューティングマルチプロセスを使用するにはメインプログラムにif name==「main」:メインプログラムを実行する必要があるためです.詳細は、既知の記事を参照してください.https://zhuanlan.zhihu.com/p/39542342;

  • 画像を表示

    plt.imshow(trainset.data[86]) #trainset.data , array 
    plt.show()
    
    dataiter = iter(trainloader)
    images, labels = dataiter.next()
    images_comb = torchvision.utils.make_grid(images)
    images_comb_unnor = (images_comb*0.5+0.5).numpy()
    plt.imshow(np.transpose(images_comb_unnor, (1, 2, 0)))
    plt.show()
    

    Pythonのモジュールmatplotlibは便利な絵です.
  • plt.imshow()はnumpy arrayをサポートし、(M,N,3)のRGBに対して画像を出力することができ、RGB値は[0,1]のfloatであってもよいし、[255]のintであってもよい.
  • 興味深いのは、trainsetがtransformを通って出てくるとすでに[-1,1]のtensorだと思っていたが、実際にはtrainset.dataには元のarray[0.255](32323)が残っていますplt.imshow()は直接画像を生成します.
  • trainset自体が伝達するのはメタグループであり、それぞれimageとlabelであり、imageはtorchである.tensor,[−1,1](33232),以下:
  • trainset[0][1]
    Out[13]: 6
    trainset[0][0]
    Out[14]: 
    tensor([[[-0.5373, -0.6627, -0.6078,  ...,  0.2392,  0.1922,  0.1608],
             [-0.8745, -1.0000, -0.8588,  ..., -0.0353, -0.0667, -0.0431],
             [-0.8039, -0.8745, -0.6157,  ..., -0.0745, -0.0588, -0.1451],
             ...,
             [ 0.6314,  0.5765,  0.5529,  ...,  0.2549, -0.5608, -0.5843],
             [ 0.4118,  0.3569,  0.4588,  ...,  0.4431, -0.2392, -0.3490],
             [ 0.3882,  0.3176,  0.4039,  ...,  0.6941,  0.1843, -0.0353]],
            [[-0.5137, -0.6392, -0.6235,  ...,  0.0353, -0.0196, -0.0275],
             [-0.8431, -1.0000, -0.9373,  ..., -0.3098, -0.3490, -0.3176],
             [-0.8118, -0.9451, -0.7882,  ..., -0.3412, -0.3412, -0.4275],
             ...,
             [ 0.3333,  0.2000,  0.2627,  ...,  0.0431, -0.7569, -0.7333],
             [ 0.0902, -0.0353,  0.1294,  ...,  0.1608, -0.5137, -0.5843],
             [ 0.1294,  0.0118,  0.1137,  ...,  0.4431, -0.0745, -0.2784]],
            [[-0.5059, -0.6471, -0.6627,  ..., -0.1529, -0.2000, -0.1922],
             [-0.8431, -1.0000, -1.0000,  ..., -0.5686, -0.6078, -0.5529],
             [-0.8353, -1.0000, -0.9373,  ..., -0.6078, -0.6078, -0.6706],
             ...,
             [-0.2471, -0.7333, -0.7961,  ..., -0.4510, -0.9451, -0.8431],
             [-0.2471, -0.6706, -0.7647,  ..., -0.2627, -0.7333, -0.7333],
             [-0.0902, -0.2627, -0.3176,  ...,  0.0980, -0.3412, -0.4353]]])
    trainset[0][0].shape
    Out[15]: torch.Size([3, 32, 32])
    type(trainset[0])
    Out[16]: tuple
    type(trainset[0])
    Out[19]: tuple
    type(trainset[0][0])
    Out[20]: torch.Tensor
    type(trainset[0][1])
    Out[21]: int
    
  • (images_comb*0.5+0.5).numpy()は、実際には逆正規化プロセスであり、[−1,1]を[0,1]のarrayに復元し、[32,32,3](np.transpose(images_comb_unnor,(1,2,0))に変換してimshow()入力に供する.

  • ボリュームニューラルネットワークの定義

    class CNN_NET(torch.nn.Module):
        def __init__(self):
            super(CNN_NET,self).__init__()
            self.conv1 = torch.nn.Conv2d(in_channels = 3,
                                         out_channels = 6,
                                         kernel_size = 5,
                                         stride = 1,
                                         padding = 0)
            self.pool = torch.nn.MaxPool2d(kernel_size = 2,
                                           stride = 2)
            self.conv2 = torch.nn.Conv2d(6,16,5)
            self.fc1 = torch.nn.Linear(16*5*5,120)
            self.fc2 = torch.nn.Linear(120,84)
            self.fc3 = torch.nn.Linear(84,10)
    
        def forward(self,x):
            x=self.pool(F.relu(self.conv1(x)))
            x=self.pool(F.relu(self.conv2(x)))
            x=x.view(-1,16*5*5) # batchsize 16*5*5 , sample,16*5*5 
            x = F.relu(self.fc1(x))
            x = F.relu(self.fc2(x))
            x = self.fc3(x)
            return x
    
    net = CNN_NET()
    
  • ネットワークの構築は継承torchに基づいている.nn.Moduleの親、基本フォーマットは以下の通りです.
  • class CNN_NET(torch.nn.Module):
        def __init__(self):
            super(CNN_NET,self).__init__()
            ...
        def forward(self,x):
        	return x  
    
  • CNNのネットワーク基本構造は、ボリューム末端、アクティブ層、プール化層、全接続層である.torch.nn.Conv 2 d()で定義された入力レイヤ数in_チャンネル、出力レイヤ数out_channels,ボリュームコアサイズkernel_size、ボリュームステップstride、paddingなどのパラメータを充填します.torch.nn.MaxPool 2 d()は最大プール化され、同様にボリュームサイズやステップ長などのパラメータを定義する.
  • 全接続層は、巻き上げられた画像を平らにし、全接続ニューラルネットワークに接続する.全接続層の確立には、入力特徴数が畳み込み出力と一致し、畳み込み出力のタイルサイズがW=MMC、Mが縦横寸法、Cが層数(チャネル数)であることに注意する必要がある.具体的な計算方法は、畳み込みまたはプール化計算方法に共通し、M=(N-kernel_size+2*padding)/stride+1が下向きに整頓され、Nが入力Nである×N画像のアスペクト寸法.
  • 全接続層の最終出力は10であり、CIFAR 10分類ラベルの10クラスに対応する.

  • 損失関数とオプティマイザ

    import torch.optim as optim
    
    optimizer = optim.SGD(net.parameters(),lr=0.001,momentum=0.9)
    loss_func =torch.nn.CrossEntropyLoss() #   ( )
    
  • ランダム勾配降下を最適化器とし,クロスエントロピーは損失関数
  • である.

    CNNトレーニング

    for epoch in range(EPOCH):
        running_loss = 0.0
        for step, (b_x,b_y)in enumerate(trainloader):
            outputs = net(b_x) #   net   x,  
            loss = loss_func(outputs, b_y) #  
            optimizer.zero_grad() #  
            loss.backward() #  ,  
            optimizer.step() #   net   parameters  
            #  
            running_loss += loss.item()
            if step % 1000 == 999:    #  2000 
                print('[%d, %5d] loss: %.3f' %
                      (epoch + 1, step + 1, running_loss / 2000))
                running_loss = 0.0
    
    print('Finished Training')
    
  • EPOCHはサンプルを何度も訓練した.
  • loss = loss_func(outputs,b_y)は予測値と実際のlabelとのクロスエントロピー誤差を計算するが,計算誤差の場合,one-hot形式ではなく1 D Tensor,(batch,),torchが伝わることに注意が必要である.nn.CrossEntropyLoss()は、受信したtensorをonehotし、softmaxで計算誤差をアクティブにします.
  • ではlossは減少し続けていることがわかるが,EPOCHは2にすぎないためlossの低下はまだ不十分である.また、合計50000枚の画像、batchsizeは4で、epochごとに12500 step
  • があります.
    [1,  2000] loss: 2.186
    [1,  4000] loss: 1.879
    [1,  6000] loss: 1.671
    [1,  8000] loss: 1.594
    [1, 10000] loss: 1.537
    [1, 12000] loss: 1.479
    [2,  2000] loss: 1.408
    [2,  4000] loss: 1.400
    [2,  6000] loss: 1.360
    [2,  8000] loss: 1.342
    [2, 10000] loss: 1.337
    [2, 12000] loss: 1.283
    

    テストセット精度の検証

    correct = 0
    total = 0
    with torch.no_grad():
        # , 
        for (images,labels) in testloader:
            outputs = net(images)
            numbers,predicted = torch.max(outputs.data,1)
            total +=labels.size(0)
            correct+=(predicted==labels).sum().item()
    
    print('Accuracy of the network on the 10000 test images: %d %%' % (
        100 * correct / total))
    
    
  • with torch.no_grad()は主に予測時にtorchの下の変数に勾配を計算する必要がなく、計算時間と空間を節約できることを教え、それ以外にこのコードを加えなくても結果に影響しない.
  • outputもtensor
  • torch.max(input) → Tensor torch.max(a,1)は、各行の最大値の要素を返し、そのインデックス(この行の最大要素の列インデックスを返す)torchを返す.max(a,0)は各列の最大値の要素を返し、インデックス(この列の最大要素の行インデックスを返す)はtensorであることに注意する.
  • はoutputsに対してもよい.dataはF.softmax()をアクティブにしてから最大値を求めるが、出力インデックスは変化しない.torch.max(F.softmax(outputs),1)
  • 最新torch 1.0バージョン以上、torch.Tensorとtorch.Tensor.dataはマージされているようなので不要です.dataはtensorの値を取得します.
  • 現在のネットワークの計算精度は53%であり、CNNネットワークのいくつかのパラメータを修正することで精度を向上させることができる.

  • メッシュパラメータの調整

    class CNN_NET(torch.nn.Module):
        def __init__(self):
            super(CNN_NET,self).__init__()
            self.conv1 = torch.nn.Conv2d(in_channels = 3,
                                         out_channels = 64,
                                         kernel_size = 5,
                                         stride = 1,
                                         padding = 0)
            self.pool = torch.nn.MaxPool2d(kernel_size = 3,
                                           stride = 2)
            self.conv2 = torch.nn.Conv2d(64,64,5)
            self.fc1 = torch.nn.Linear(64*4*4,384)
            self.fc2 = torch.nn.Linear(384,192)
            self.fc3 = torch.nn.Linear(192,10)
    
        def forward(self,x):
            x=self.pool(F.relu(self.conv1(x)))
            x=self.pool(F.relu(self.conv2(x)))
            x=x.view(-1,64*4*4)
            x = F.relu(self.fc1(x))
            x = F.relu(self.fc2(x))
            x = self.fc3(x)
            return x
    
  • ネットワークの複雑さを増加させ、ボリューム出力層数、全接続層のノード数などを含め、精度を63%に向上させることができる.
  • epochを8に増加すれば、73%に上昇し続けることができる.

  • 完全なコード

    ######################## ############################
    import numpy as np
    import torch
    import torchvision
    import torchvision.transforms as transforms
    import matplotlib.pyplot as plt
    import torch.nn.functional as F
    
    #hyper parameter
    BATCH_SIZE = 4
    EPOCH = 2
    
    transform = transforms.Compose([transforms.ToTensor(),
                                    transforms.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))])
    trainset = torchvision.datasets.CIFAR10(root='./data',train = True,
                                            download=True, transform=transform)
    trainloader = torch.utils.data.DataLoader(trainset,batch_size = BATCH_SIZE,
                                              shuffle = True, num_workers=1)
    
    testset = torchvision.datasets.CIFAR10(root='./data',train = False,
                                            download=True, transform=transform)
    testloader = torch.utils.data.DataLoader(testset,batch_size = BATCH_SIZE,
                                              shuffle = False, num_workers=1)
    
    classes = ('plane', 'car', 'bird', 'cat',
               'deer', 'dog', 'frog', 'horse', 'ship', 'truck')
    
    # plt.imshow(trainset.data[86]) #trainset.data , array 
    # plt.show()
    
    # dataiter = iter(trainloader)
    # images, labels = dataiter.next()
    # images_comb = torchvision.utils.make_grid(images)
    # images_comb_unnor = (images_comb*0.5+0.5).numpy()
    # plt.imshow(np.transpose(images_comb_unnor, (1, 2, 0)))
    # plt.show()
    
    class CNN_NET(torch.nn.Module):
        def __init__(self):
            super(CNN_NET,self).__init__()
            self.conv1 = torch.nn.Conv2d(in_channels = 3,
                                         out_channels = 64,
                                         kernel_size = 5,
                                         stride = 1,
                                         padding = 0)
            self.pool = torch.nn.MaxPool2d(kernel_size = 3,
                                           stride = 2)
            self.conv2 = torch.nn.Conv2d(64,64,5)
            self.fc1 = torch.nn.Linear(64*4*4,384)
            self.fc2 = torch.nn.Linear(384,192)
            self.fc3 = torch.nn.Linear(192,10)
    
        def forward(self,x):
            x=self.pool(F.relu(self.conv1(x)))
            x=self.pool(F.relu(self.conv2(x)))
            x=x.view(-1,64*4*4)
            x = F.relu(self.fc1(x))
            x = F.relu(self.fc2(x))
            x = self.fc3(x)
            return x
    
    net = CNN_NET()
    
    import torch.optim as optim
    
    optimizer = optim.SGD(net.parameters(),lr=0.001,momentum=0.9)
    loss_func =torch.nn.CrossEntropyLoss()
    
    for epoch in range(EPOCH):
        running_loss = 0.0
        for step, data in enumerate(trainloader):
            b_x,b_y=data
            outputs = net.forward(b_x)
            loss = loss_func(outputs, b_y)
            optimizer.zero_grad()
            loss.backward()
            optimizer.step()
            #  
            running_loss += loss.item()
            if step % 1000 == 999:    #  2000 
                print('[%d, %5d] loss: %.3f' %
                      (epoch + 1, step + 1, running_loss / 2000))
                running_loss = 0.0
    
    print('Finished Training')
    
    dataiter = iter(trainloader)
    images, labels = dataiter.next()
    images_comb = torchvision.utils.make_grid(images)
    images_comb_unnor = (images_comb*0.5+0.5).numpy()
    plt.imshow(np.transpose(images_comb_unnor, (1, 2, 0)))
    plt.show()
    
    predicts=net.forward(images)
    
    
    
    
    ######## #######
    correct = 0
    total = 0
    with torch.no_grad():
        # , 
        for (images,labels) in testloader:
            outputs = net(images)
            numbers,predicted = torch.max(outputs.data,1)
            total +=labels.size(0)
            correct+=(predicted==labels).sum().item()
    
    print('Accuracy of the network on the 10000 test images: %d %%' % (
        100 * correct / total))