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

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

1つ前のチュートリアル「Neural Networks」は[こちら](https://qiita.com/mckeeeen/items/e255b4ac1efba88d0ca1)。


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

尚、公式ページの日本語翻訳ではありませんので悪しからず。
シンプルに日本語翻訳を読みたい方は[こちら](http://caffe.classcat.com/2017/04/14/pytorch-tutorial-neural-networks/)のサイトに載っています。



モジュールのインポート


```python
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データセットのインポート


```python
# 画像の変換処理を定義する（画像を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


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


```python
# 可視化の関数を定義する
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)
# ミニバッチを１つ取得する
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](https://qiita-image-store.s3.amazonaws.com/0/235450/3f0eee4a-d400-5171-2863-5feb7e93eac5.png)



# 2. Define a Convolution Neural Network

1つ前のチュートリアル「Neural Networks」と同じCNNを、3チャネルにする
Neural Networksについては[こちら](https://qiita.com/mckeeeen/items/e255b4ac1efba88d0ca1)に記事を載せてあります


```python
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
```


```python
net=Net()
```

# 3. Define a Loss function and optimizer

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


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

# 4. Train the network

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


```python
# エポック数を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

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


```python
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](https://qiita-image-store.s3.amazonaws.com/0/235450/a9b434ef-8919-93c1-555f-e730d085be72.png)



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


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

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


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

    Predicted:    cat  ship  ship  ship


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


```python
# 予測したデータの総数
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%）よりも良い予測精度になっている

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


```python
# クラス毎の正解したデータの総数
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でトレーニングするためには、

```python
net.cuda()
```
でNNのインスタンスを、

```python
inputs,labels=Variable(inputs.cuda()),Variable(labels.cuda())
```
で入力データと教師データを、CUDAのテンソルに変換しておく必要がある

# 参考サイト
[PyTorchドキュメント](http://pytorch.org/docs/0.3.1/)
[PyTorchのGitHubリポジトリ](https://github.com/pytorch/pytorch)