自前のGPUでPyTorchを動かすための前準備(Windows)

# はじめに
念願のGPU(RTX3070)を積んだPC(Windows)を購入したので、PyTorchを動かしてディープラーニングすることにしました。
色々と前準備が必要だったので、メモとして残しました。

# 手順
1. CUDA on WSLの導入
2. PyTorchのインストール

# 1. CUDA on WLSの導入
これについては、素晴らしい記事があるので[こちら](https://qiita.com/ksasaki/items/ee864abd74f95fea1efa)を参照すると良いです。
こちらの記事の *CUDA Toolkit のインストール* までを行えばOKです。

インストールするパッケージについては、[公式のページ](https://docs.nvidia.com/cuda/wsl-user-guide/index.html)を参考にしました。


# 2. PyTorchのインストール
PyTorchのインストールですが、ローカルのAnacondaを使う方法とdockerを使う方法の両方を試しました。
お好きな方を参照してください。

## ローカルのAnacondaでのインスール
Anacondaはすでにwsl上に導入されているものとします。
CUDA11.1に対応しているPyTorchは、以下のコマンドでインストールできます。

```bash
pip install torch==1.9.0+cu111 torchvision==0.10.0+cu111 torchaudio===0.9.0 -f https://download.pytorch.org/whl/torch_stable.html
```

どのバージョンのPyTorchをインストールすればよいのかについては、[こちら](https://pytorch.org/)で確認できます。


## dockerを使用したインストール
dockerを使用する場合は、[nvidiaが用意しているPyTorchのイメージを使用する方法](https://ngc.nvidia.com/catalog/containers/nvidia:pytorch)と、pythonイメージを使用してPyTorchをインストールする方法があります。
前者の方法だと、追加でライブラリをインストールしたい場合、condaとpipのコンフリクトが起きるケースがあったので、ここでは後者の方法で実施します。

以下のような、pythonイメージのDockerfileを用意します。

```Dockerfile:Dockerfile
FROM python:3

# 必要なライブラリのインストール
ADD requirements.txt .
RUN pip install --upgrade pip && \
    pip install --no-cache-dir --default-timeout=1000  -r requirements.txt

# PyTorchのインストール
RUN pip install --no-cache-dir --default-timeout=1000 \
    torch==1.9.0+cu111 \
    torchvision==0.10.0+cu111 \
    -f https://download.pytorch.org/whl/torch_stable.html
```

Dockerfileを使用してビルドします。

```bash
docker build -t {image_name} .
```

`image_name`には好きな名前を入れて下さい。
このイメージを以下のコマンドで起動します。

```bash
docker run \
    --gpus all \
    -it \
    --rm \
    -v {local_dir}:{container_dir} \
    --ipc=host \
    {image_name}
```

`local_dir`、`container_dir`は絶対パスで指定して下さい。


## GPUが使用できるか確認
PyTorchの[公式ページ](https://pytorch.org/tutorials/beginner/pytorch_with_examples.html)にサンプルのコードがあったので、動作するか確認してみます。

```py
# -*- coding: utf-8 -*-

import torch
import math


dtype = torch.float
device = torch.device("cuda:0")
print(device)

# Create random input and output data
x = torch.linspace(-math.pi, math.pi, 2000, device=device, dtype=dtype)
y = torch.sin(x)

# Randomly initialize weights
a = torch.randn((), device=device, dtype=dtype)
b = torch.randn((), device=device, dtype=dtype)
c = torch.randn((), device=device, dtype=dtype)
d = torch.randn((), device=device, dtype=dtype)

learning_rate = 1e-6
for t in range(2000):
    # Forward pass: compute predicted y
    y_pred = a + b * x + c * x ** 2 + d * x ** 3

    # Compute and print loss
    loss = (y_pred - y).pow(2).sum().item()
    if t % 100 == 99:
        print(t, loss)

    # Backprop to compute gradients of a, b, c, d with respect to loss
    grad_y_pred = 2.0 * (y_pred - y)
    grad_a = grad_y_pred.sum()
    grad_b = (grad_y_pred * x).sum()
    grad_c = (grad_y_pred * x ** 2).sum()
    grad_d = (grad_y_pred * x ** 3).sum()

    # Update weights using gradient descent
    a -= learning_rate * grad_a
    b -= learning_rate * grad_b
    c -= learning_rate * grad_c
    d -= learning_rate * grad_d


print(f'Result: y = {a.item()} + {b.item()} x + {c.item()} x^2 + {d.item()} x^3')
```

下図のように問題なく動いていれば、導入完了です。
![実行結果](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/241394/6cfb9ce8-1a24-af5f-80a0-d4c47df9be79.png)