CUDAバージョンの解決

**Note**:  
pytorchはCUDAとcuDNNを同梱したビルド済みバイナリで配布されているため、CUDAのメジャーバージョンが同一またはより新しければ使えます。  
tensorflowは同梱していないため、[現在のnvidia-driverでパッケージが要求するCUDAが使えるか](#現在のnvidia-driverでパッケージが要求するCUDAが使えるか)の確認後、
更に下記[コンテナを使わないCUDAのバージョン解決](#コンテナを使わないCUDAのバージョン解決)または[コンテナを用いたCUDAの解決](#コンテナを用いたCUDAの解決)を行う必要があります。

CUDAのバージョン:

- メジャーバージョン -> ポイントより前の数字 (11.x -> 11)
- マイナーバージョン -> ポイントより後ろが違う (11.x)

## 現在のnvidia-driverでパッケージが要求するCUDAが使えるか

- `nvidia-smi`を入力し表示されるCUDAバージョンは**利用できる最新**のCUDAバージョンを示します。
- パッケージが要求するCUDAバージョンと上記のバージョンの関係が以下の場合、CUDAが使用されます。
  - パッケージが要求するCUDAバージョン==`nvcc -V`で表示されるバージョン
  - CUDA 11系より新しい場合、マイナーバージョンは互換性があります。
- より新しいCUDAのバージョンを使用したい場合は、以下の場合に限り前方互換が保証されています。
  - nvidia-driverのライフサイクル([リンク先](https://docs.nvidia.com/datacenter/tesla/drivers/index.html))終了までに登場したCUDAバージョン
- 実際に使えるかの確認は以下を`python`で実行してください。
  - `pytorch`
    ```python
    import torch.cuda as cuda
    cuda.is_available()
    > True 使える
    > False 使えない
    ```
  - `tensorflow`
    ```python
    import tensorflow as tf
    tf.debugging.set_log_device_placement(True)
    tf.constant([1, 2, 3])
    > # device:GPU:<num>であればGPUが使用されている。以下はGPUではなくCPUが使用されている
    > I tensorflow/core/common_runtime/eager/execute.cc:1224] Executing op _EagerConst in device /job:localhost/replica:0/task:0/device:CPU:0
    ```

## CUDAの前方互換

1. [リンク先](https://www.nvidia.com/Download/index.aspx?lang=en-us)からドライバーをインストール

   - 選択項目は以下の通り

   | 項目             | -                                                        |
   | ---------------- | -------------------------------------------------------- |
   | Product Type     | Data Center / Tesla                                      |
   | Product-Series   | サーバーのGPUのシリーズ                                  |
   | Product          | サーバーのGPU                                            |
   | Operating System | Linux 64-bit                                             |
   | CUDA Toolkit     | ドライバーがサポートする最新もしくはそれ以前のバージョン |
   | Language         | English or Japanese                                      |

2. driverの抽出
   - 実行後、ディレクトリ配架に`./NVIDIA-Linux-x86_64-{version}`が作成されている
    ```sh
    chmod +x NVIDIA-Linux-x86_64-{version}.run
    ./NVIDIA-Linux-x86_64-{version}.run -x
    ```

3. 以下のコマンドでシンボリックリンクを作成

   - `libnvidia-nvvm.so.*`はCUDA 11.5 or laterのみ

   ```sh
   VERSION={version}
   CUDA_VERSION={cuda-ver}
   mv ./NVIDIA-Linux-x86_64-$VERSION ./cuda-compat-$CUDA_VERSION
   cd ./cuda-compat-$CUDA_VERSION
   ln -sf $PWD/libcuda.so.$VERSION libcuda.so
   ln -sf $PWD/libcuda.so.$VERSION libcuda.so.1
   ln -sf $PWD/libnvidia-nvvm.so.$VERSION libnvidia-nvvm.so
   ln -sf $PWD/libnvidia-nvvm.so.$VERSION libnvidia-nvvm.so.4
   ```

4. `LD_LIBRARY_PATH=/absolute/path/to/NVIDIA-Linux-x86_64-{version}:$LD_LIBRARY_PATH nvidia-smi`を実行し、CUDAのバージョンが変わっていれば成功
5. 次回以降も同様に使いたい場合は`.bashrc`に`export LD_LIBRARY_PATH=/absolute/path/to/NVIDIA-Linux-x86_64-{version}:$LD_LIBRARY_PATH`を追記
6. より新しいCUDAバージョンを使用する場合は`/path/to/NVIDIA-Linux-x86_64-{version}`をコンテナにマウントして環境変数`LD_LIBRARY_PATH=/absolute/path/to/NVIDIA-Linux-x86_64-{version}:$LD_LIBRARY_PATH`を設定する。

### トラブルシューティング

- Q. tensorflowやpytorchのテンソルをGPUに載せると`803`エラーが出る
  A. ホストドライバーがより新しいCUDAバージョンに対応していません。
  [ライフサイクル](https://docs.nvidia.com/datacenter/tesla/drivers/index.html)内にCUDAがリリースされていたか確認し、より古いバージョンがサポートされているか確認してください。

## コンテナを使わないCUDAのバージョン解決

1. CUDA Toolkitのインストール

- [リンク先](https://developer.nvidia.com/cuda-toolkit-archive)から任意のバージョンのCUDA Toolkitを選択
- `Installer Type`は**runfile (local)**を選択
- `wget`のコマンドが表示されるのでダウンロード

2. `*.run`の実行

   ```sh
   chmod +x ./cuda_{cuda-ver}_{driver-version}_linux.run
   ./cuda_{cuda-ver}_{driver-version}_linux.run --silent --toolkit --toolkitpath=$PWD/cuda-{cuda-ver}
   ```

3. [任意] cuDNNのダウンロード

   - cuDNNを用いると学習が高速になります。
   - cuDNNは[NVIDIA DEVELOPER](https://developer.nvidia.com/)への登録が必要です。
   - 登録とログイン後、[リンク先](https://developer.nvidia.com/rdp/cudnn-download)から任意のcudaバージョンに対応したcuDNNを`Local Installer for Linux x86_64 (Tar)`を指定してダウンロードしてください。
   - ログインが必要なためwgetを使用できません。ローカルにダウンロードした後、`rsync`等でサーバーへアップロードしてください。

4. [任意] cuDNNのファイルの配置

   ```sh
   tar -xvf cudnn-linux-$arch-8.x.x.x_cudaX.Y-archive.tar.xz
   cp cudnn-*-archive/include/cudnn*.h /path/to/user/cuda-{cuda-ver}/include
   cp -P cudnn-*-archive/lib/libcudnn* /path/to/user/cuda-{cuda-ver}/lib64
   chmod a+r /path/to/user/cuda-{cuda-ver}/include /path/to/user/cuda-{cuda-ver}/lib64
   ```

5. 環境変数の設定
   ```sh
   export CUDA_HOME=/path/to/user/cuda-{cuda-ver}
   export LD_LIBRARY_PATH=/path/to/user/cuda-{cuda-ver}/lib64:$LD_LIBRARY_PATH
   ```

以上で`tensorflow`、`pytorch`で任意バージョンのCUDAが使用できます。

## コンテナを用いたCUDAの解決

- nvidia-driveがサポートするCUDAであれば煩雑なセットアップ無しに使用できます。
  - より新しいCUDAバージョンを使用する場合は、[CUDAの前方互換](#CUDAの前方互換)の7. を参照してください。
- [dockerhub](https://hub.docker.com/)より下記のイメージから任意のCUDAバージョンのイメージをベースイメージにしてコンテナを作成してください。
  - [nvidia/cuda](https://hub.docker.com/r/nvidia/cuda/tags): `{cuda-ver}-cudnn8-devel-ubuntu{ubuntu-ver}`から選択
  - [pytorch/pytorch](https://hub.docker.com/r/pytorch/pytorch/tags): `{pytorch-ver}-cuda{cuda-ver}-cudnn8-devel`から選択
  - [tensorflow/tensorflow](https://hub.docker.com/r/tensorflow/tensorflow/tags): `-gpu`が付いているものから選択
  - 

# 参考

- [NVIDIAドライバを更新せずに新しいCUDAを使う（データセンタ向けGPU限定）](https://zenn.dev/yuyakato/articles/f93b35d269d245)
- [CUDA Compatibility :: GPU Deployment and Management Documentation](https://docs.nvidia.com/deploy/cuda-compatibility/index.html)
- [cuDNN Installation Guide](https://docs.nvidia.com/deeplearning/cudnn/install-guide/index.html)
- [NVIDIA Datacenter Drivers](https://docs.nvidia.com/datacenter/tesla/drivers/index.html)