【AIプログラミング】PyTorchで畳み込みニューラルネットワーク、チュートリアル Part4-3

AIプログラミング

これでPyTorchの60分チュートリアルも最後になります。

60分と言いつつもかなり色々予備知識が必要だったね。

今回で、やっとPyTorchのチュートリアルである「A 60 MINUTE BLITZ」を完了することができます。最後は、テストデータで検証を行い、GPUでの処理を行ってみる、というチュートリアルです。今回も知識の補足を行いながら進めました。

やっているチュートリアルへのリンクはこちらです。

「Training a Classifier」チュートリアルの次の項目で、今回の記事は以下項目の「5」をやっていきます。

1.データ読み込みと前処理(前々回)
2.畳み込みニューラルネットワークを定義(前回)
3.損失関数を定義(前回)
4.訓練データでネットワークを訓練(前回)
5.テストデータで評価(今回)

最後に、チュートリアル全体を実行するためのGoogleColaboファイルをGitHubにてアップロードさせていただいていますので、ぜひご活用ください。

こんな人の役にたつかも

・機械学習プログラミングを勉強している人

・PyTorchプログラミングのチュートリアルに挑戦している人

・PyTorchで畳み込みニューラルネットワークをプログラミングしている人

スポンサーリンク

ニューラルネットワークをテストデータで評価

まずは、訓練データと同様、テストデータの中身を確認です。

以前の記事で作成した「imshow」関数が必要になります。

テストデータの確認

matplotlibで画像を確認しますので、前回までの記事のプログラムを実行しておく必要があります。

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)))

小さな画像が4つ、みれました。

訓練モデルのロード

前回記事の最後で、ニューラルネットワークの訓練結果をファイルとして保存しました。次のプログラムで、訓練パラメータをファイルから読み出すことができます。

これで、色々なところで訓練を行わずに使いまわすことができます。

loadメソッドの引数「PATH」も前回の記事で指定したディレクトリです。

net = Net()
net.load_state_dict(torch.load(PATH))

テストデータの予測

次のようにテストデータ画像をニューラルネットワークに与えます。

outputs = net(images)
print(outputs)
tensor([[-2.2456, -3.0535,  0.9128,  2.4592,  2.1587,  2.1558,  2.8599,  0.5596,
         -2.9859, -2.6490],
        [-2.6678, -3.3542,  1.4564,  3.3314, -0.7947,  5.2420, -0.3214,  2.1732,
         -2.1843, -2.5228],
        [ 2.5839, -1.2858,  2.6275, -0.7872,  4.8332, -0.4881, -1.2820, -0.2292,
         -1.9573, -3.4837],
        [-0.0959, -3.2768,  1.0982, -0.0127,  4.6545,  0.6341, -1.4771,  6.0890,
         -4.3654, -1.5090]], grad_fn=<AddmmBackward>)

1枚の画像に対して、10個(行の方向)の出力が出てきています。それが1バッチ4枚分の回答がありますので、outputsは4×10の行列になっています。

ニューラルネットの答えとしては、行の中でもっとも数値の大きな値のものが予測の回答となりますので、次のようにmaxメソッドで一番大きな値を取り出してあげる必要があります。

maxの第二引数の「1」は、行方向に対して最大値を取り出すように指定している1です。0にすると縦方向になってしまうので、今回は1と指定します。

_, predicted = torch.max(outputs, 1)

print(predicted)

print('Predicted: ', ' '.join('%5s' % classes[predicted[j]]
                              for j in range(4)))
tensor([3, 8, 0, 0])
Predicted:    cat  ship plane plane

maxメソッドを通すことで、predictedに4枚の画像の回答を取り出すことができました。1枚目は「3」なので、cat、2枚目は「8」なのでshipといった感じです。これは、訓練をさせ直すと変わります。

テストデータ全体としてどれくらいの精度が出るかを見てみます。

correct = 0
total = 0
with torch.no_grad():
    for data in testloader:
        images, labels = data
        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))
Accuracy of the network on the 10000 test images: 54 %

もっとエポック数あげたりするといいのかな?試す余地ありそう。

GPUに処理をさせる

GPUは本来グラフィックの重い処理をCPUに負担をかけず高速に演算できるように利用されていたコンピュータの構成要素になります。最近では、GPUをより汎用的な演算に利用することができるようになってきています。C言語やPythonなどのプログラミング言語からもGPUが気軽に利用できるような仕組み、プラットフォームがNVIDIAのCUDAになります。

PyTorchには、CUDAを利用して計算を分散させて、処理を行うことができます。。

Macではどうやら結構前にCUDAが排除されているみたいです・・・ということは、本格的にニューラルネットワークを訓練してGPUを拡張する場合は、WindowsやLinuxマシンが良いということでしょうか。PythonなのでどのOSでも良いと思いますが。

また、GoogleColaboratoryでは設定を変更することでGPUでの計算が利用でき、CUDAを試すこともできます。

GoogleColaboでGPU利用設定

Google colaboでGPUを利用するためには、「ランタイム」→「ランタイムのタイプの変更」メニューで、GPUを選択する必要があります。

device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

print(device)

GPUが利用できると、以下のようにcudaと表示されます。利用できないと「cpu」と表示されます。

cuda:0

作成したニューラルネットワークは、次のようにデバイスを指定してGPUで処理を行うようにできます。

net.to(device)
Net(
  (conv1): Conv2d(3, 6, kernel_size=(5, 5), stride=(1, 1))
  (pool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
  (conv2): Conv2d(6, 16, kernel_size=(5, 5), stride=(1, 1))
  (fc1): Linear(in_features=400, out_features=120, bias=True)
  (fc2): Linear(in_features=120, out_features=84, bias=True)
  (fc3): Linear(in_features=84, out_features=10, bias=True)
)

最後に、入力画像データ「inputs」と答えのラベルデータ「labels」は、それぞれのミニバッチごとに場所を渡してあげる必要があるとのことです。

inputs, labels = data[0].to(device), data[1].to(device)
 
次のように、訓練プログラムのミニバッチのfor文を書き換えます。
for epoch in range(2):#エポック

    running_loss = 0.0
    for i, data in enumerate(trainloader, 0):#ミニバッチ
        #ここの行をGPU用に変更する必要がある。
        inputs, labels = data[0].to(device), data[1].to(device)

        optimizer.zero_grad()

        outputs = net(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

        running_loss += loss.item()
        if i % 2000 == 1999:
            print('[%d, %5d] loss: %.3f' %
                  (epoch + 1, i + 1, running_loss / 2000))
            running_loss = 0.0
        
print('Finished Training')

これでGPUの処理で訓練をさせることができました。しかし、あまり速度的に変わっていないように感じます。

ネットワークの規模が小さすぎるとのことです。畳み込み層のノードを増やしたりすることで、GPUの速度アップが体感できるようになるようです。今後また試してみたいと思いました。

GitHubのプログラムも参考に

記事で行なってきたPyTorchnの「Training a Classifire」チュートリアルのGoogleColaboファイルをGitHubに配置しましたので、ご参考ください。

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