Help us understand the problem. What is going on with this article?

#2 Training a classifier : PyTorchチュートリアルをやってみた

More than 1 year has passed since last update.

PyTorchのチュートリアル(Deep Learning with PyTorch: A 60 Minute Blitz)の中のTraining a classifierに取り組んだところ、DeepLearning初心者には少々分かりづらいところがあり躓いたので、ここにまとめておきます。

1つ前のチュートリアル「Neural Networks」はこちら

PyTorchドキュメントやGitHubのソースコードを参考にして、元々のチュートリアルのコードにコメントを加えています。少しだけコードを変更しているところもあります。
細かくコメントを入れてあるので冗長ですが、私と同じように躓いた人の参考になれば幸いです。

尚、公式ページの日本語翻訳ではありませんので悪しからず。
シンプルに日本語翻訳を読みたい方はこちらのサイトに載っています。

モジュールのインポート

import numpy as np
import matplotlib.pyplot as plt

import torch as t
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
from torch.autograd import Variable
import torchvision
import torchvision.transforms as transforms

1. Loading and normalizing CIFAR10

CIFAR10データセットのインポート

# 画像の変換処理を定義する(画像をtorch.Tensorに変換し、3つあるチャネル毎に標準化(平均0.5、標準偏差0.5)する)
transform=transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.5,0.5,0.5),(0.5,0.5,0.5))])
# CIFAR10のトレーニングデータをダウンロードし、transformで変換する
trainset=torchvision.datasets.CIFAR10(root='./input',train=True,download=True,transform=transform)
# トレーニングデータの読み込み方を定義する(ミニバッチに分け(バッチ毎のサンプル数は4)、エポック毎にシャッフルして再度ミニバッチを作る)
# ここでは、2つのサブプロセスに分けて並列処理するようにしている
trainloader=t.utils.data.DataLoader(trainset,batch_size=4,shuffle=True,num_workers=0)
# CIFAR10のテストデータをダウンロードし、transformで変換する
testset=torchvision.datasets.CIFAR10(root='./input',train=False,download=True,transform=transform)
# テストデータの読み込み方を定義する(トレーニングデータと同様)
# テストデータはシャッフルする必要はない
testloader=t.utils.data.DataLoader(testset,batch_size=4,shuffle=False,num_workers=0)
# CIFAR10のクラスラベル
classes=('plane','car','bird','cat','deer','dog','frog','horse','ship','truck')
Downloading https://www.cs.toronto.edu/~kriz/cifar-10-python.tar.gz to ./input/cifar-10-python.tar.gz
Files already downloaded and verified

ミニバッチを可視化してみる(学習には必要ない部分)

# 可視化の関数を定義する
def imshow(img):
    # 標準化した画像を元に戻す
    img=img/2+0.5
    # torch.Tensorからnumpy.ndarrayに型変換
    npimg=img.numpy()
    # np.transposeで軸を入れ替え、matplotlib.pyplot.imshowを使って画像を表示
    plt.imshow(np.transpose(npimg,(1,2,0)))

# ミニバッチのイテレータを生成する
dataiter=iter(trainloader)
# ミニバッチを1つ取得する
images, labels = dataiter.next()

# ミニバッチから横並びのグリッドを作り、可視化する
imshow(torchvision.utils.make_grid(images))
# ラベルを表示する
print(' '.join('%5s'%classes[labels[j]] for j in range(4)))
 ship   car  deer horse

output_6_1.png

2. Define a Convolution Neural Network

1つ前のチュートリアル「Neural Networks」と同じCNNを、3チャネルにする
Neural Networksについてはこちらに記事を載せてあります

class Net(nn.Module):

    def __init__(self):
        super(Net,self).__init__()
        # チャネル数を3に設定する
        self.conv1=nn.Conv2d(3,6,5)
        self.conv2=nn.Conv2d(6,16,5)
        # 今回はここでプーリング層を定義している
        self.pool=nn.MaxPool2d(2,2)
        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, self.num_flat_features(x))
        x=F.relu(self.fc1(x))
        x=F.relu(self.fc2(x))
        x=self.fc3(x)
        return x

    # 本編にはないが、Neural Networkで定義したものをそのまま使う
    def num_flat_features(self,x):
        size=x.size()[1:]
        num_features=1
        for s in size:
            num_features*=s
        return num_features
net=Net()

3. Define a Loss function and optimizer

損失関数に交差エントロピー、最適化にMomentumSGDを使う

criterion=nn.CrossEntropyLoss()
# 学習率を0.001、モメンタムのパラメータを0.9に設定する
optimizer=optim.SGD(net.parameters(),lr=0.001,momentum=0.9)

4. Train the network

ネットワークを学習させる

# エポック数を2にセットする
for epoch in range(2):
    # 損失関数の累積値
    running_loss=0
    # ミニバッチ毎に学習をループさせる(インデックスは0スタート)
    for i,data in enumerate(trainloader,start=0):
        # ミニバッチのデータとラベルを取得する
        inputs,labels=data
        # torch.autograd.Variableに変換
        inputs,labels=Variable(inputs),Variable(labels)
        # 勾配を0に初期化
        optimizer.zero_grad()
        # 順伝播
        outputs=net(inputs)
        # 損失関数の計算
        loss=criterion(outputs,labels)
        # 逆伝播
        loss.backward()
        # パラメータ更新
        optimizer.step()
        # 損失関数の累積値を計算
        running_loss+=loss.data[0]
        # 2000バッチ毎に損失関数の平均値を表示
        if i%2000==1999:
            # エポック、バッチ、損失関数のバッチ平均値の順に表示
            print('[%d, %5d] loss: %.3f'%(epoch+1,i+1,running_loss/2000))
            # 損失関数の累積値を0で初期化
            running_loss=0

# 学習終了のメッセージ
print('Finished Training')
[1,  2000] loss: 2.293
[1,  4000] loss: 2.040
[1,  6000] loss: 1.789
[1,  8000] loss: 1.671
[1, 10000] loss: 1.574
[1, 12000] loss: 1.510
[2,  2000] loss: 1.433
[2,  4000] loss: 1.384
[2,  6000] loss: 1.358
[2,  8000] loss: 1.346
[2, 10000] loss: 1.334
[2, 12000] loss: 1.286
Finished Training

5. Test the network on the test data

テストデータのミニバッチを表示してみる

dataiter=iter(testloader)
images,labels=dataiter.next()

imshow(torchvision.utils.make_grid(images))
print('GroundTruth: ', ' '.join('%5s'%classes[labels[j]] for j in range(4)))
GroundTruth:    cat  ship  ship plane

output_19_1.png

表示したミニバッチのラベルを予測する

# 画像をtorch.autograd.Variableに変換してから計算
outputs=net(Variable(images))

予測されたラベルを確認する

# 最も出力値の大きいラベルを予測されたラベルとして取り出す
_,predicted=t.max(outputs.data,dim=1)
# 表示する
print('Predicted: ', ' '.join('%5s' % classes[predicted[j]] for j in range(4)))
Predicted:    cat  ship  ship  ship

テストデータセット全体を予測する

# 予測したデータの総数
total=0
# 正解したデータの総数
correct=0
# テストデータセットのミニバッチ毎に予測する
for data in testloader:
    images,labels=data
    outputs=net(Variable(images))
    _,predicted=t.max(outputs.data,dim=1)
    # 予測したデータ数を加算
    total+=labels.size(0)
    # 正解したデータ数を加算
    correct+=(predicted==labels).sum()

# 全体の正解率を表示    
print('Accuracy of the network on the 10000 test images: %d %%'%(100*correct/total))    
Accuracy of the network on the 10000 test images: 54 %

この結果は、ランダムに予測した場合(10クラスなので10%)よりも良い予測精度になっている

再度予測してクラス毎の正解率を計算する

# クラス毎の正解したデータの総数
class_correct=list(0 for i in range(10))
# クラス毎の予測したデータの総数
class_total=list(0 for i in range(10))
for data in testloader:
    images,labels=data
    outputs=net(Variable(images))
    _,predicted=t.max(outputs.data,dim=1)
    # ミニバッチ毎の正解したかどうかのboolianのリスト
    c=(predicted==labels).squeeze()
    # クラス毎の予測したデータ数、正解したデータ数を加算
    for i in range(4):
        label = labels[i]
        class_correct[label]+=c[i]
        class_total[label]+=1

# クラス毎の正解率を表示
for i in range(10):
    print('Accuracy of %5s : %2d %%'%(classes[i],100*class_correct[i]/class_total[i]))
Accuracy of plane : 42 %
Accuracy of   car : 73 %
Accuracy of  bird : 37 %
Accuracy of   cat : 27 %
Accuracy of  deer : 46 %
Accuracy of   dog : 40 %
Accuracy of  frog : 63 %
Accuracy of horse : 75 %
Accuracy of  ship : 79 %
Accuracy of truck : 59 %

Training on GPU

CPUではなくGPUでトレーニングするためには、

net.cuda()

でNNのインスタンスを、

inputs,labels=Variable(inputs.cuda()),Variable(labels.cuda())

で入力データと教師データを、CUDAのテンソルに変換しておく必要がある

参考サイト

PyTorchドキュメント
PyTorchのGitHubリポジトリ

Why do not you register as a user and use Qiita more conveniently?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away