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
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
表示したミニバッチのラベルを予測する
# 画像を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のテンソルに変換しておく必要がある