end0tknr's kipple - 新web写経開発

http://d.hatena.ne.jp/end0tknr/ から移転しました

pytorch for pythonによる CIFAR10 に対する画像分類

https://github.com/miyamotok0105/pytorch_handbook

上記urlにある3章の写経.

srcの内容は理解できる. が、srcに記載されているロジック妥当性までは理解できていない。

そう考えると「まだまだ」というより「さっぱり」だ

ちなみに CIFAR10とは、次のurlにある10種類の画像群です。

CIFAR-10 and CIFAR-100 datasets

#!/usr/local/python3/bin/python3
# -*- coding: utf-8 -*-
import torch
import torchvision
import torch.nn as nn
import torch.optim as optim
import torch.nn.functional as F
import torchvision.transforms as transforms
import numpy as np
# from matplotlib import pyplot as plt
import matplotlib
matplotlib.use('Agg')
import matplotlib.pylab as plt

NUM_CLASSES = 10  # CIFAR10データは、10種類のdata
DEVICE = 'cuda' if torch.cuda.is_available() else 'cpu'
DATA_DIR = './data/'
IMG_SIZE_AND_CHANNEL = 0
TRAIN_NUM_EPOCHS = 50   #50エポック


def main():
    (train_loader, test_loader) = get_train_test_data()
    print("train_loader: ", train_loader)
    print("test_loader: ", test_loader)

    (net, criterion, optimizer) = get_net_criterion_optimizer()

    (train_loss_list, train_acc_list, val_loss_list, val_acc_list) = \
        train(train_loader, test_loader, net, criterion, optimizer)

    output_to_file(train_loss_list, train_acc_list, val_loss_list, val_acc_list)

    # torch.save(net.state_dict(), 'net.ckpt')

    # net2 = MLPNet().to(device)
    # net2.load_state_dict(torch.load('net.ckpt'))

    # net2.eval()
    # with torch.no_grad():
    #     total = 0
    #     test_acc = 0
    #     for images, labels in test_loader:
    #         images, labels = images.view(-1, 32*32*3).to(device), labels.to(device)
    #         outputs = net2(images)
    #         test_acc += (outputs.max(1)[1] == labels).sum().item()
    #         total += labels.size(0)
    #     print('精度: {} %'.format(100 * test_acc / total))


def get_train_test_data():
    # CIFAR10とは、ラベル付けされた5万枚の訓練画像と1万枚のテスト画像のデータセットで
    # 以下で、STEP1) www.cs.toronto.edu から自動的にダウンロード & 解凍します
    #         STEP2) 画像と正解ラベルのペアを返却 が行われます
    train_dataset = torchvision.datasets.CIFAR10(root=DATA_DIR,
                                                 train=True,
                                                 transform=transforms.ToTensor(),
                                                 download=True)
    test_dataset = torchvision.datasets.CIFAR10(root=DATA_DIR,
                                                train=False,
                                                transform=transforms.ToTensor(),
                                                download=True)
    # 試しに1つの訓練用データの内容を見ると、3チャネル , 32x32size だと分かります
    image, label = train_dataset[0]
    # print( image.size() )
    # print(label)
    global IMG_SIZE_AND_CHANNEL
    IMG_SIZE_AND_CHANNEL = image.size()[0] * image.size()[1] * image.size()[2]

    # DataLoader() は、batch_size分だけ、画像と正解ラベルを返します
    train_loader = torch.utils.data.DataLoader(dataset=train_dataset,
                                               batch_size=64,
                                               shuffle=True,
                                               num_workers=2)
    test_loader = torch.utils.data.DataLoader(dataset=test_dataset,
                                              batch_size=64,
                                              shuffle=False,
                                              num_workers=2)
    return train_loader, test_loader
    

# MLP ネットワークの定義
# (多層パーセプトロン, 入力層,隠れ層,出力層が全結合である最も単純なdeep learning)
class MLPNet (nn.Module):
    def __init__(self):
        super(MLPNet, self).__init__()
        # 32x32size & 3チャネル, 隠れ層のunit数は、600
        self.fc1 = nn.Linear(IMG_SIZE_AND_CHANNEL, 600)
        self.fc2 = nn.Linear(600, 600)
        self.fc3 = nn.Linear(600, NUM_CLASSES)
        self.dropout1 = nn.Dropout2d(0.2)
        self.dropout2 = nn.Dropout2d(0.2)
            
    def forward(self, x):
        x = F.relu(self.fc1(x))
        x = self.dropout1(x)
        x = F.relu(self.fc2(x))
        x = self.dropout2(x)
        return F.relu(self.fc3(x))

def get_net_criterion_optimizer():
    net = MLPNet().to(DEVICE)

    criterion = nn.CrossEntropyLoss()
    # 以下のparameterの妥当性は理解していません
    optimizer = optim.SGD(net.parameters(), lr=0.01, momentum=0.9, weight_decay=5e-4)
    return net, criterion, optimizer

def train(train_loader, test_loader, net, criterion, optimizer):
    #最後にlossとaccuracyのグラフを出力する為
    train_loss_list = []
    train_acc_list =  []
    val_loss_list =   []
    val_acc_list =    []

    for epoch in range(TRAIN_NUM_EPOCHS):
        #エポックごとに初期化
        train_loss = 0
        train_acc = 0
        val_loss = 0
        val_acc = 0
    
        net.train()  #訓練モードへ切り替え
        
        for i, (images, labels) in enumerate(train_loader):  #ミニバッチで分割し読込み
            #viewで縦横32x32 & 3channelのimgを1次元に変換し、toでDEVICEに転送
            images, labels = images.view(-1, IMG_SIZE_AND_CHANNEL).to(DEVICE), labels.to(DEVICE)

            
            optimizer.zero_grad()               # 勾配をリセット
            outputs = net(images)               # 順伝播の計算
            loss = criterion(outputs, labels)   #lossの計算
            train_loss += loss.item()           #lossのミニバッチ分を溜め込む
            #accuracyをミニバッチ分を溜め込む
            #正解ラベル(labels)と予測値のtop1(outputs.max(1))が合っている場合に1が返る
            train_acc += (outputs.max(1)[1] == labels).sum().item()
            #逆伝播の計算
            loss.backward()
            #重みの更新
            optimizer.step()
            #平均lossと平均accuracyを計算
        avg_train_loss = train_loss / len(train_loader.dataset)
        avg_train_acc = train_acc / len(train_loader.dataset)
    

        net.eval()   #評価モードへ切り替え
        
        #評価するときに必要のない計算が走らないようtorch.no_gradを使用
        with torch.no_grad():
            for images, labels in test_loader:        
                images, labels = images.view(-1,IMG_SIZE_AND_CHANNEL).to(DEVICE),labels.to(DEVICE)
                outputs = net(images)
                loss = criterion(outputs, labels)
                val_loss += loss.item()
                val_acc += (outputs.max(1)[1] == labels).sum().item()
        avg_val_loss = val_loss / len(test_loader.dataset)
        avg_val_acc = val_acc / len(test_loader.dataset)
    

        # 訓練データのlossと検証データのlossとaccuracyをログ出力.
        print ("Epoch [{}/{}], Loss: {loss:.4f},val_loss: {val_loss:.4f},val_acc: {val_acc:.4f}"
               .format(epoch+1,
                       TRAIN_NUM_EPOCHS,
                       i+1,
                       loss=avg_train_loss,
                       val_loss=avg_val_loss, val_acc=avg_val_acc))

        train_loss_list.append(avg_train_loss)
        train_acc_list.append(avg_train_acc)
        val_loss_list.append(avg_val_loss)
        val_acc_list.append(avg_val_acc)

    return train_loss_list,train_acc_list,val_loss_list,val_acc_list

def output_to_file(train_loss_list,train_acc_list,val_loss_list,val_acc_list):

    plt.figure()
    plt.plot(range(TRAIN_NUM_EPOCHS),
             train_loss_list,
             color='blue',
             linestyle='-',
             label='train_loss')
    plt.plot(range(TRAIN_NUM_EPOCHS),
             val_loss_list,
             color='green',
             linestyle='--',
             label='val_loss')
    plt.legend()
    plt.xlabel('epoch')
    plt.ylabel('loss')
    plt.title('Training and validation loss')
    plt.grid()
    plt.savefig( 'test_1.png' )

    plt.figure()
    plt.plot(range(TRAIN_NUM_EPOCHS),
             train_acc_list,
             color='blue',
             linestyle='-',
             label='train_acc')
    plt.plot(range(TRAIN_NUM_EPOCHS),
             val_acc_list,
             color='green',
             linestyle='--',
             label='val_acc')
    plt.legend()
    plt.xlabel('epoch')
    plt.ylabel('acc')
    plt.title('Training and validation accuracy')
    plt.grid()
    plt.savefig( 'test_2.png' )

    
if __name__ == '__main__':
    main()

↑こう書くと、↓こう表示されます。

[end0tknr@cent80 PYTORCH]$ ./foo_3_2.py 
Files already downloaded and verified
Files already downloaded and verified
train_loader:  <torch.utils.data.dataloader.DataLoader object at 0x7f1f2821ab50>
test_loader:  <torch.utils.data.dataloader.DataLoader object at 0x7f1f28970e50>
Epoch [1/50], Loss: 0.0304,val_loss: 0.0276,val_acc: 0.3715
Epoch [2/50], Loss: 0.0274,val_loss: 0.0272,val_acc: 0.3773
 :
Epoch [10/50], Loss: 0.0233,val_loss: 0.0228,val_acc: 0.4799
 :
Epoch [40/50], Loss: 0.0197,val_loss: 0.0208,val_acc: 0.5325
 :
Epoch [50/50], Loss: 0.0191,val_loss: 0.0206,val_acc: 0.5276
$ 

f:id:end0tknr:20191012204621p:plain

f:id:end0tknr:20191012204629p:plain