ML.NET による画像分類 GPU 編

# はじめに
[前回の投稿](https://qiita.com/hiromasa-masuda/items/e1a3ea60f0c838f17589)では、ML.NET 1.4.0 で、画像分類がサポートされたことと、ML.NET による画像分類の実装方法を説明しました。
また、ML.NET の画像分類は、[Tensorflow.NET](https://github.com/SciSharp/TensorFlow.NET) を介して、Tensorflow が利用されていることにも触れました。
ML.NET 1.4.0 では、画像分類のサポートに加えて GPU サポートも追加されています。サポートされている GPU の種類は、NVIDIA CUDA 互換の GPU となります。
今回は、ML.NET での画像分類において、GPU を使った学習の方法を説明します。

# TensorFlow.NET ライブラリのインストール
GPU を使った学習を行うためには、TensorFlow.NET 再配布ライブラリの GPU バージョンをインストールします。
[前回の投稿の ML.NET のインストール](https://qiita.com/hiromasa-masuda/items/e1a3ea60f0c838f17589#mlnet-%E3%81%AE%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB) では、TensorFlow.NET ライブラリの CPU バージョンとして "SciSharp.TensorFlow.Redist" をインストールしていましたが、代わりに、TensorFlow.NET ライブラリの GPU バージョン "SciSharp.TensorFlow.Redist-Windows-GPU" をインストールします。(Linux 環境の場合は、SciSharp.TensorFlow.Redist-Linux-GPU)

[ソリューション エクスプローラー] から、プロジェクトを選択し、右クリックメニューから、[NuGet パッケージの管理] を選択します。
[参照] タブの [検索]　テキスト ボックスに "SciSharp.TensorFlow.Redist-Windows-GPU" と入力し、[SciSharp.TensorFlow.Redist-Windows-GPU] を選択、[インストール] を選択し、以降画面の指示に従い "SciSharp.TensorFlow.Redist-Windows-GPU" をインストールします。(*) "SciSharp.TensorFlow.Redist" がインストール済の場合は、アンインストールしておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172205/ea637e4b-af14-0a35-f9d1-4156074f54f2.png)

# CUDA と cuDNN のインストール
次に、CUDA と cuDNN をインストールします。もちろん、先に CUDA と cuDNN をインストールしておいても良いのですが、TensorFlow のバージョンに対応する CUDA と cuDNN のバージョンも異なるため、今回は、TensorFlow インストール後に、CUDA と cuDNN をインストールしています。
TensorFlow.NET ライブラリのバージョンが、TensorFlow のバージョンと一致しています。先の手順では、TensorFlow.NET(SciSharp.TensorFlow.Redist-Windows-GPU) 2.3.1 をインストールしたので、TensorFlow 2.3.1 に対応する CUDA と cuDNN の各バージョンをインストールします。

[TensorFlow GPU サポート ソフトウェア要件](https://www.tensorflow.org/install/gpu#software_requirements) にあるように、TensorFlow 2.1.0 以降では、CUDA 10.1 と cuDNN 7.6 が対応するバージョンとなります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172205/a70992c2-9c74-e4cd-1c0d-4c02c8648961.png)

TensorFlow 2.1.0 より前のバージョンの場合は、以下に TensorFlow と CUDA, cuDNN とのテスト済のビルド構成の対応表があるので、ここで TensorFlow のバージョンに対応する CUDA, cuDNN のバージョンを確認します。

- [TensorFlow テスト済みのビルド構成](https://www.tensorflow.org/install/source_windows#gpu)

ここでは、TensorFlow 2.3.1 に対して、CUDA 10.1 と cuDNN 7.6 をインストールします。

[NVIDIA CUDA 10.1 update2 ダウンロード サイト](https://developer.nvidia.com/cuda-10.1-download-archive-update2) から、CUDA 10.1 update2 をダウンロードします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172205/a81d7dae-852c-4dd3-2f13-9d814808f6be.png)

ダウンロードしたモジュールを起動し、以降、インストーラーの指示に従い、インストールを完了します。この時、Visual Studio 2019 が起動状態だとインストールが中断してしまうため、Visual Studio 2019 を起動している場合は、先に Visual Studio 2019 を終了しておきます。

[NVIDIA cuDNN ダウンロードサイト](https://developer.nvidia.com/rdp/cudnn-download) へアクセスし、[Archived cuDNN Releases] から、CUDA 10.1 に対応する最新の cuDNN v7.6.x をダウンロードします。今回は、cuDNN 7.6.5 が該当するので、これをダウンロードします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/172205/8944bcbe-a36d-6e79-b6aa-4537f3da2735.png)

ダウンロードした zip ファイルを解凍すると、以下のような構造になっています。

```
cuda
│  NVIDIA_SLA_cuDNN_Support.txt
│
├─bin
│      cudnn64_7.dll
│
├─include
│      cudnn.h
│
└─lib
    └─x64
       cudnn.lib
```
cudnn64_7.dll、cudnn.h、cudnn.lib を CUDA のインストール フォルダーへ移動します。
cudnn64_7.dll を %HOMEDRIVE%\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.1\bin 以下へ、
cudnn.h を %HOMEDRIVE%\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.1\include 以下へ、
cudnn.lib を %HOMEDRIVE%\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v10.1\lib\x64 以下へそれぞれ移動します。

これで、セットアップは完了です。

# GPU による学習の実行

あとは、[前回の投稿](https://qiita.com/hiromasa-masuda/items/e1a3ea60f0c838f17589)と同様にプログラムを実行するのみです。
Visual Studio でプログラムを実行すると、以下のように、コンソール出力に、GPU を検出し、CUDA の DLL がロードされていることがわかります。

```dosbatch
2020-11-10 00:47:15.081708: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library nvcuda.dll
2020-11-10 00:47:15.133701: I tensorflow/core/common_runtime/gpu/gpu_device.cc:1716] Found device 0 with properties:
pciBusID: 0000:01:00.0 name: GeForce GTX 1650 Ti computeCapability: 7.5
coreClock: 1.485GHz coreCount: 16 deviceMemorySize: 4.00GiB deviceMemoryBandwidth: 178.84GiB/s
2020-11-10 00:47:15.153863: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library cudart64_101.dll
2020-11-10 00:47:15.164522: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library cublas64_10.dll
2020-11-10 00:47:15.175347: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library cufft64_10.dll
2020-11-10 00:47:15.185020: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library curand64_10.dll
2020-11-10 00:47:15.195719: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library cusolver64_10.dll
2020-11-10 00:47:15.202931: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library cusparse64_10.dll
2020-11-10 00:47:15.216774: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library cudnn64_7.dll
```

私の環境では、CPU では、20分くらいかかっていたものが、GPU では、3 分ほどで終わるようになりました。

もし、学習の実行時に以下のように CUDNN_STATUS_ALLOC_FAILED エラーが出力される場合は、環境変数で、"TF_FORCE_GPU_ALLOW_GROWTH=true" とすると改善されます。

```dosbatch

2020-11-04 01:01:27.824001: I tensorflow/stream_executor/platform/default/dso_loader.cc:48] Successfully opened dynamic library cudnn64_7.dll
2020-11-04 01:01:29.049615: E tensorflow/stream_executor/cuda/cuda_dnn.cc:328] Could not create cudnn handle: CUDNN_STATUS_ALLOC_FAILED
2020-11-04 01:01:29.056108: E tensorflow/stream_executor/cuda/cuda_dnn.cc:328] Could not create cudnn handle: CUDNN_STATUS_ALLOC_FAILED
2 root error(s) found.
  (0) Unknown: Failed to get convolution algorithm. This is probably because cuDNN failed to initialize, so try looking to see if a warning log message was printed above.
         [[{{node resnet_v2_50/conv1/Conv2D}}]]
         [[resnet_v2_50/SpatialSqueeze/_7]]
  (1) Unknown: Failed to get convolution algorithm. This is probably because cuDNN failed to initialize, so try looking to see if a warning log message was printed above.
```

環境変数の設定方法は、コマンドプロンプトを [管理者として実行] で、起動して、以下のコマンドを実行する。

```dosbatch

>setx TF_FORCE_GPU_ALLOW_GROWTH true -m
```

もしくは、"sysdm.cpl" を起動して、[詳細設定] - [環境変数..] を選択、[システム環境変数] - [新規...] を選択し、[変数名] に "TF_FORCE_GPU_ALLOW_GROWTH"、[変数値] に "true" を設定して、[OK] - [OK] - [OK] を選択する。

環境変数設定後に、Visual Studio を再起動して、プログラムを再実行すると、エラーが改善されるはずです。

# 参考サイト
- [ML.NET による画像分類](https://qiita.com/hiromasa-masuda/items/e1a3ea60f0c838f17589)
- [Announcing ML.NET 1.4 general availability (Machine Learning for .NET)](https://devblogs.microsoft.com/dotnet/announcing-ml-net-1-4-global-availability-machine-learning-for-net/)
- [TensorFlow GPU サポート](https://www.tensorflow.org/install/gpu)
- [Could not create cudnn handle: CUDNN_STATUS_ALLOC_FAILED #39989](https://github.com/tensorflow/tensorflow/issues/39989)