【AIプログラミング】PyTorchのCNNを改善中、チュートリアルプログラムを整理してエポック数の検証

AIプログラミング

PyTorchのチュートリアルで作り上げたCNNについて、ブロック単位にプログラムが実行できるように整理してみました。

チュートリアルだと、色々と文字が多くて後からコピペも煩雑だね。

チュートリアルで作り上げたCNNについて、内容は理解できたのですが、色々と試したいときに、文章などが間に入り、コピペしづらかったりしましたので、まずはプログラムの整理を行いました。

PyTorchのチュートリアルを真面目にやる記事は、こちらをご参考ください。

また、プログラムの整理ついでに、最適化手法をSGDからAdamに変更してみました。Adamについては内容はあまり理解できていないのですが、現在一般的に利用される最適化手法なので入れてみました。

スポンサーリンク

プログラムの整理

CIFAR10の読み込み

CIFAR10データセットを読み込みます。画像の表示などを削除しました。

#CIFAR10の読みこみ
import torch
import torchvision
import torchvision.transforms as transforms

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=4,
                                          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=4,
                                         shuffle=False, num_workers=2)

classes = ('plane', 'car', 'bird', 'cat',
           'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

畳み込みニューラルネットワークの定義

ここでニューラルネットワークの層などの構造を作り上げます。

#畳み込みニューラルネットワークの定義
import torch.nn as nn
import torch.nn.functional as F

class Net(nn.Module):
    def __init__(self):
        super(Net, self).__init__()
        self.conv1 = nn.Conv2d(3, 6, 5)
        self.pool = nn.MaxPool2d(2, 2)
        self.conv2 = nn.Conv2d(6, 16, 5)
        self.fc1 = nn.Linear(16 * 5 * 5, 120)
        self.fc2 = nn.Linear(120, 84)
        self.fc3 = 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)
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))
        x = self.fc3(x)
        return x

net = Net()

損失関数、最適化手法の定義

損失関数と最適化手法を定義します。現在、一般的になっているAdamを入れてみました。

#損失関数の定義
import torch.optim as optim

criterion = nn.CrossEntropyLoss()
#optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
optimizer = optim.Adam(net.parameters(), lr=0.001)

GPU利用の場合

GPUで処理を行う場合は次のブロックを実行します。また、訓練パート等でもGPU利用に修正する行があります。Colaboノートの場合、ハードウェアアクセラレータもGPUに変更する必要があります。

#GPU化するためにはこのブロックを実行
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print(device)
net.to(device)

訓練

訓練で最初のrange(2)の2を返納することで訓練のエポック数を指定できます。

#訓練
for epoch in range(2):  #チュートリアルでは2エポック訓練を行う。

    running_loss = 0.0
    for i, data in enumerate(trainloader, 0):
        #inputs, labels = data #CPUのとき
        inputs, labels = data[0].to(device), data[1].to(device) #GPU

        #勾配のパラメータを初期化
        optimizer.zero_grad()

        #順伝播し、逆伝播して重みを更新
        outputs = net(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

        # print statistics
        running_loss += loss.item()
        if i % 2000 == 1999:    #ミニバッチ2000ごとに状況の表示
            print('[%d, %5d] loss: %.3f' %
                  (epoch + 1, i + 1, running_loss / 2000))
            running_loss = 0.0
        
print('Finished Training')

テストデータの評価

テストデータに対して、どれだけ正解したかを評価します。

correct = 0
total = 0
with torch.no_grad():
    for data in testloader:
        #images, labels = data #CPU
        images, labels = data[0].to(device), data[1].to(device) #GPU
        outputs = net(images)
        _, 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))

モデルの保存

時間をかけて訓練したパラメータを保存します。

#モデルの保存
PATH = './cifar_net.pth'
torch.save(net.state_dict(), PATH)

検証:エポック数を増やしてみる

エポック数5の時の訓練の推移です。

[1,  2000] loss: 1.142
[1,  4000] loss: 1.165
[1,  6000] loss: 1.167
[1,  8000] loss: 1.147
[1, 10000] loss: 1.177
[1, 12000] loss: 1.178
[2,  2000] loss: 1.064
[2,  4000] loss: 1.109
[2,  6000] loss: 1.072
[2,  8000] loss: 1.118
[2, 10000] loss: 1.128
[2, 12000] loss: 1.097
[3,  2000] loss: 1.020
[3,  4000] loss: 1.033
[3,  6000] loss: 1.043
[3,  8000] loss: 1.072
[3, 10000] loss: 1.044
[3, 12000] loss: 1.067
[4,  2000] loss: 0.962
[4,  4000] loss: 1.005
[4,  6000] loss: 1.036
[4,  8000] loss: 1.017
[4, 10000] loss: 1.038
[4, 12000] loss: 1.009
[5,  2000] loss: 0.950
[5,  4000] loss: 0.958
[5,  6000] loss: 0.971
[5,  8000] loss: 0.992
[5, 10000] loss: 1.026
[5, 12000] loss: 1.000

テストデータへは61%の正解率でした。2000のミニバッチ単位でも、1前後で損失が下げ止まっている感が出ています。

これは、時間との戦いです。

エポック数10にしてみました。結構時間がかかりますが、まだ大丈夫です。Colaboには連続実行時間があるらしいので、ちょっと注意ですね。

[1,  2000] loss: 0.942
[1,  4000] loss: 0.943
[1,  6000] loss: 0.967
[1,  8000] loss: 0.947
[1, 10000] loss: 0.971
[1, 12000] loss: 1.001
[2,  2000] loss: 0.911
============略===========
[8,  2000] loss: 0.821
[8,  4000] loss: 0.835
[8,  6000] loss: 0.839
[8,  8000] loss: 0.906
[8, 10000] loss: 0.885
[8, 12000] loss: 0.875
[9,  2000] loss: 0.785
[9,  4000] loss: 0.842
[9,  6000] loss: 0.846
[9,  8000] loss: 0.879
[9, 10000] loss: 0.849
[9, 12000] loss: 0.860
[10,  2000] loss: 0.782
[10,  4000] loss: 0.826
[10,  6000] loss: 0.823
[10,  8000] loss: 0.851
[10, 10000] loss: 0.835
[10, 12000] loss: 0.877
Finished Training

損失が下げ止まっている感が否めません。ちなみに、テストデータ精度は61%と、それほど精度向上しませんでした。

検討してみて感じたこと

ニューラルネットワークの精度検証は、とにかく時間がかかる、ということがわかりました。また、実行するたびに、算出される結果が異なってくるので、ある意味ガチャのような要素があって面白いです 笑

よく、一週間学習させるみたいなことを聞きますが、学習するエポックを増やせばおそらくそれなりに精度が向上していくような気がしています。

GitHubにプログラムをアップロードします。ブロック単位でのコピペなど、役に立てばと思います。

タイトルとURLをコピーしました