アイキャッチ画像

PyTorch チュートリアルにトライ 4 (GPUを使う)

PyTorchへの入門として、公式のチュートリアルをなぞってみました。 PyTorch本家のチュートリアル のCIFAR-10の画像分類を、GPUで実行してみたいと思います。

目次

  1. 使用可能なGPUの確認
  2. CIFAR-10の学習をGPUで行う

使用可能なGPUの確認

本稿では、GeForceなどのCUDAが使えるGPUを対象とします。

まず、GPUが利用可能かどうかの確認をします。

>python
Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import torch
>>> torch.cuda.is_available()
True

Trueなので、CUDAが使えます。

GPUにはインデックスが振られます。ということは、いくつGPUが使えるか確認する必要があります。

>python
Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import torch
>>> torch.cuda.device_count()
2

>>> torch.cuda.current_device()
0

使用できるGPUが2つあって、現在のGPUはインデックスが0のものです。

実際に、試しているPCには2台のGPUが入っています。

各GPUにどのインデックスが割り振られているか確認してみます。具体的には、各インデックスのデバイスの名前を表示します。

>python
Python 3.7.3 (v3.7.3:ef4ec6ed12, Mar 25 2019, 22:22:05) [MSC v.1916 64 bit (AMD64)] on win32
Type "help", "copyright", "credits" or "license" for more information.
>>> import torch
>>> torch.cuda.get_device_name(0)
'TITAN RTX'
>>> torch.cuda.get_device_name(1)
'GeForce GTX 1060 6GB'
>>> torch.cuda.get_device_name(2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\Program Files\Python37\lib\site-packages\torch\cuda\__init__.py", line 257, in get_device_name
    return get_device_properties(device).name
  File "C:\Program Files\Python37\lib\site-packages\torch\cuda\__init__.py", line 281, in get_device_properties
    raise AssertionError("Invalid device id")
AssertionError: Invalid device id

GPUごとのインデックスがわかりました。また、存在しないインデックスを指定するとエラーになります。

CIFAR-10の学習をGPUで行う

では、公式チュートリアルに従ってCIFER-10の学習をGPUで行ってみます。

import torch
import torchvision
import torchvision.transforms as transforms
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim

# ニューラルネットワークの定義
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

# データセット読み込み
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=0)

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

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

# ネットワークのインスタンス作成
net = Net()

# 損失関数とオプティマイザーの定義
criterion = nn.CrossEntropyLoss()
optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)

# GPUを使用する
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
net.to(device) # モデルの転送

# 学習ループ
for epoch in range(2):  # 総データに対する学習回数

    running_loss = 0.0
    for i, data in enumerate(trainloader, 0):
        # データをリストに格納
        inputs, labels = data[0].to(device), data[1].to(device) # データの転送

        # パラメータを0にリセット
        optimizer.zero_grad()

        # 順方向の計算、損失計算、バックプロパゲーション、パラメータ更新
        outputs = net(inputs)
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()

        # 計算状態の出力
        running_loss += loss.item()
        if i % 2000 == 1999:    # print every 2000 mini-batches
            print('[%d, %5d] loss: %.3f' %
                  (epoch + 1, i + 1, running_loss / 2000))
            running_loss = 0.0

print('Finished Training')

# 計算結果のモデルを保存
torch.save(net.state_dict(), './cifar_net.pth')

以前のコードから変化したのは下記のデバイスの設定のところと、

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

ネットワークを転送するところと、

net.to(device)

計算用のデータを転送するところです。

inputs, labels = data[0].to(device), data[1].to(device)

実行すると下記のようになります。

Files already downloaded and verified
Files already downloaded and verified
[1,  2000] loss: 2.186
[1,  4000] loss: 1.879
[1,  6000] loss: 1.700
[1,  8000] loss: 1.628
[1, 10000] loss: 1.543
[1, 12000] loss: 1.473
[2,  2000] loss: 1.393
[2,  4000] loss: 1.386
[2,  6000] loss: 1.352
[2,  8000] loss: 1.326
[2, 10000] loss: 1.299
[2, 12000] loss: 1.279
Finished Training

ただ、計算時間に3分ほどかかっていて、期待したほど早くはない。(Core i5-3250Uで5分くらい。)

GPUの負荷率が概ね11%でメモリ使用量が1.6GB程度でしたので、計算よりもデータ転送に時間がかかったのかもしれません。バッチサイズを大きくしてみたいと思います。

ミニバッチサイズを64にして実行してみると、1分とかからず終了します。ただし、パラメータの更新の頻度が少なくなりますので、ロスは減りません。エポックを増やさないとだめですね。

Files already downloaded and verified
Files already downloaded and verified
[1,   750] loss: 2.298
[2,   750] loss: 2.144
Finished Training

というわけで20エポックの計算をしてみました。

Files already downloaded and verified
Files already downloaded and verified
[1,   750] loss: 2.300
[2,   750] loss: 2.215
[3,   750] loss: 1.879
[4,   750] loss: 1.679
[5,   750] loss: 1.585
[6,   750] loss: 1.519
[7,   750] loss: 1.458
[8,   750] loss: 1.404
[9,   750] loss: 1.356
[10,   750] loss: 1.319
[11,   750] loss: 1.284
[12,   750] loss: 1.253
[13,   750] loss: 1.224
[14,   750] loss: 1.200
[15,   750] loss: 1.174
[16,   750] loss: 1.147
[17,   750] loss: 1.125
[18,   750] loss: 1.102
[19,   750] loss: 1.082
[20,   750] loss: 1.067
Finished Training

精度を計算してみます。コードは以前作成したものです。計算はCPUで行いました。

Files already downloaded and verified
Accuracy of the network on the 10000 test images: 60 %

60%まで向上しました。

Files already downloaded and verified
Accuracy of plane : 75 %
Accuracy of   car : 67 %
Accuracy of  bird : 51 %
Accuracy of   cat : 36 %
Accuracy of  deer : 43 %
Accuracy of   dog : 41 %
Accuracy of  frog : 67 %
Accuracy of horse : 79 %
Accuracy of  ship : 70 %
Accuracy of truck : 69 %

ラベルごとの精度を見てみると、やっぱり猫が苦手なんですかね。

広告

PyTorchカテゴリの投稿