社会人研究者が色々頑張るブログ

pythonで画像処理やパターン認識をやっていきます

pytorchのお勉強(6):GPUの利用

はじめに

Pytorchのお勉強の続きです。
前回までに学んだ内容でDeepNeural Networkでやりたい一通りの事はできるようになりましたが、肝心のGPUを用いた高速化を学んでいませんでした。

GPUはGraphic Processing Unitと呼ばれる並列計算が得意な演算装置です。
ゲームやグラフィック・ツールを高速に動作させる為に作られた外部演算装置であり、CPUで構成されたホスト計算機にくっつけて動作させます。
現在では並列演算の性能の高さを活かし、Deep Learningや物理シミュレーションなどの科学計算や、仮想通貨のマイニングなどにも応用されています。

今回は、pytorchの学習、推論にGPUを用いる方法を学んでいきます。
この公式ドキュメントを参考に進めていきます。

GPUが使えるか確認

GPUを学習に用いるためには、以下を満たしている必要があります。
1.ホストPCにGPUNvidia推奨)を接続する
2.GPUのドライバをインストールする
3.condaでcudatoolkitを導入する

これらについては実験環境のOS名とNivida Driverで検索すると参考になる資料が沢山出てくると思います。

これらが導入済とした時、次のコードを実行することでPytorch上でGPUが使用可能か確認できます。

device = "cuda" if torch.cuda.is_available() else "cpu"
print("Using {} device".format(device))

GPUが使える状態だと

Using cuda device

と出力されます。

学習にGPUを使う

公式ドキュメントによると「学習データとネットワークモデルをGPUで使いますよ!」と宣言すれば、簡単にGPUで学習できるみたいです。

# ネットワークモデルをGPUで利用する事を宣言する
import torch
device = "cuda" if torch.cuda.is_available() else "cpu"

model = Network()
model = model.to(device)
# データをGPU上に展開する事を宣言する
def batch in dataloader:
    X = batch["image"].to(device)
    Y = batch["label"].to(device)
    
    pred = model(X)

dataloaderに関しては過去の記事を参照してください。
nsr-9.hatenablog.jp

非常に簡単に利用できますね!

実際にGPUを使ってみる

前回作成した学習用のコードをGPUに対応させます。
nsr-9.hatenablog.jp

変更点はnetwork modelとbatchに関してGPUで使うことを宣言している部分だけです。

from dataset import CustomImageDataset
from network import Net
import torch
from torch.utils.data import DataLoader
import torchvision.models as models


device = "cuda"
def train_loop(dataloader, model, loss_fn, opt):
    data_size = len(dataloader.dataset)

    for batch, D in enumerate(dataloader):
        X = D["image"].to(device)
        y = D["label"].to(device)
        pred = model(X)
        loss = loss_fn(pred, y)

        # バックプロパゲーション
        opt.zero_grad()
        loss.backward()
        opt.step()

        if batch % 100 == 0:
            loss, current = loss.item(), batch*len(X)
            print(f"loss: {loss:>7f}  [{current:>5d}/{data_size:>5d}]")


if __name__ == "__main__":
    
    loss_fn = torch.nn.CrossEntropyLoss()
    model = Net().to(device)
    optimizer = torch.optim.Adam(model.parameters())
    train_data = CustomImageDataset()
    train_dataloader = DataLoader(train_data, batch_size=128, shuffle=True)
    epochs = 20 
    import time
    start = time.time()
    for t in range(epochs):
        print(f"Epoch {t+1}\n-------------------------------")
        train_loop(train_dataloader, model, loss_fn, optimizer)

    #input_image = torch.zeros((1, 1, 64, 64))
    #onnx.export(model, input_image, "model.onnx")
    print("time: "+str(time.time()-start))

    torch.save(model, "model.pth")

コードの先頭の方にあるdevice="cuda"を"cpu"に変更すると、CPUで学習できるようになります。

GPU/CPUでそれぞれ20 epoch学習させた際の学習時間は次のようになりました。

device 学習時間(秒)
CPU 78
GPU 68

学習時間が12%くらい削減されていますね。
普段使ってるライブラリとタスクではGPUを用いると劇的に学習時間が改善されるのですが、あまり改善効果が見られませんね。
こうなった理由としては、ネットワークの規模が小さい(計算コストが小さい)事と、入力画像が比較的小さい(32x32pix)事が挙げられます。
計算量が小さいとGPUの並列効果よりも、GPUメモリへのデータ転送に時間がかかってしまう事があります。
計算量の多いタスクで改善効果を確認してみたいですね。

また、GPUで学習を行っている際に、別のターミナルから以下のコマンドを実行すると、GPUの状態を確認することができます。

nvidia-smi

上記の学習を行っていた際の出力は次のようになります。
f:id:nsr_9:20210830182941p:plain

GPUのメモリが1GByte程度だったので、結構昔のGPUでも学習することができそうですね。

まとめ

今回はpytorchでGPU学習する方法を勉強しました。
pytorchは非常に簡単にGPU学習とCPU学習を切り替えられるのでとても便利だと思いました。(他のライブラリだとこうはいかない)
今回は小さいタスクだったのでGPU利用による改善効果が小さかったのですが、次は大きなタスクで実験してみたいと思います。