Miniconda上のTensorflow と WSL2+Miniconda上のTensorflow のインストールとパフォーマンスの比較

# はじめに
Tensorflow 2.11からWindowsのGPU対応バージョンがリリースされなくなりました。Tensorflow 2.11以降のバージョンを動かしたければOSをLinuxにするかWin+WSL2上で動かす(か自力でTensorflowをビルドする)しかありません。

そこでWSL2上で動かすとどれほどパフォーマンスが変わるか比較してみます。どちらもGPU対応されているTensorflow 2.10で比較を行いました。また最後のGPU対応バージョンなのでインストール方法もメモとして残しておきますので参考になればと思います。

# 環境
Windows10Pro 22H2
Intel Core i7-6850K
NVIDIA GeForce GTX 1070
WSL2
CUDA 11.8
Miniconda3(Python 3.10)
Tensorflow 2.10
上記の環境とは別のWin11マシンでもインストールしましたが、インストールの流れは同じです。

# 環境① Miniconda+Tensorflowインストール
まずはWindowsにMinicondaとTensorflowをインストールする手順です。
## CUDAインストール
Tensorflow 2.10はCUDA 12では動かないようなので、CUDA 11.8をインストールします。
以下からダウンロードしてインストールします。
・CUDA 11.8
https://developer.nvidia.com/cuda-11-8-0-download-archive
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/231036/aa06b51c-c27b-e3e6-4071-431f6f06d016.jpeg" width=70%>

・cuDNN v8.9.1 (May 5th, 2023), for CUDA 11.x
https://developer.nvidia.com/rdp/cudnn-download
CUDAをインストールした場所(C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.8)にコピーします
・zlibwapi.dll
https://docs.nvidia.com/deeplearning/cudnn/install-guide/index.html#install-zlib-windows

zlibwapi.dllをC:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v11.8\binなどパスの通ったところに入れておきます。

## Minicondaインストール
機械学習の環境を構築する際にPython単独でインストールするのではなく、numpyなどの数値計算パッケージが高速に動作するAnacondaを用いている方も多いと思いますが、2020年にAnacondaの利用が200人以上の企業では有償となりました。そのため、Minicondaをインストールし、conda-forgeのチャンネルを使用すれば無償として使えるということでこの方法でインストールしてみます。

・Miniconda3-py310_23.3.1-0-Windows-x86_64.exe
https://docs.conda.io/en/latest/miniconda.html
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/231036/f5539e1c-62c5-a943-2b75-02626ed2b103.jpeg" width=70%>

PowerShellを起動して
PS ><font color="CornflowerBlue ">conda update conda</font>
を実行

もし、CondaSSLErrorというエラーが出たら、
C:/Users/ユーザー名/AppData/Local/miniconda3/Library/bin/libcrypto-1_1-x64.dll
C:/Users/ユーザー名/AppData/Local/miniconda3/Library/bin/libssl-1_1-x64.dll
の2つのファイルを
C:/Users/ユーザー名/AppData/Local/miniconda3/DLLs
にコピーしてPCを再起動(←必須)
PowerShellから
PS ><font color="CornflowerBlue ">conda config --set ssl_verify false</font>
とするとSSLの証明なしでアクセスできます。

### チャンネルの変更
企業でなければこの手順は不要です。
PS ><font color="CornflowerBlue ">conda config --show channels</font>
channels:
&nbsp;&nbsp;- defaults
チャンネルを conda-forge に変更します。
PS ><font color="CornflowerBlue ">conda config --add channels conda-forge</font>
PS ><font color="CornflowerBlue ">conda config --remove channels defaults</font>
確認する。
PS ><font color="CornflowerBlue ">conda config --show channels</font>
channels:
&nbsp;&nbsp;- conda-forge
設定は以下に保存されています。
C:/Users/ユーザー名/.condarc

### 数値計算ライブラリなどのインストール
とりあえず以下をインストールしておけば十分でしょう。
PS ><font color="CornflowerBlue ">conda install numpy scipy sympy matplotlib scikit-learn pandas jupyter notebook ipykernel jupyterlab graphviz pydot</font>

### Tensorflow 2.10のインストール
PS ><font color="CornflowerBlue ">pip install tensorflow==2.10</font>
SSLエラーがでたら以下のように実行します。
PS ><font color="CornflowerBlue ">pip install --trusted-host pypi.org --trusted-host files.pythonhosted.org tensorflow==2.10</font>

以下のスクリプトを実行してGPUデバイス名が表示されればOKです。
```python:Device.py
from tensorflow.python.client import device_lib
print(device_lib.list_local_devices())
```
PS ><font color="CornflowerBlue ">python Device.py</font>

# 環境② WSL2+Miniconda+Tensorflowインストール
同じWindowsシステムにWSL2環境を構築し、その上にMinicondaをインストールします。

## WSL2のインストール
[コントロールパネル]-[プログラムと機能]-[Windowsの機能]を選択し、以下にチェックを入れて再起動します。
☑Hyper-V
☑Linux用Windowsサブシステム
☑Windowsハイパーバイザープラットフォーム
☑仮想マシンプラットフォーム

Ubuntuをインストールします。
PS ><font color="CornflowerBlue ">wsl --update</font>
PS ><font color="CornflowerBlue ">wsl --install -d Ubuntu</font>
以下を実行しVERSIONが2になっていればOKです。
PS ><font color="CornflowerBlue ">wsl -l -v</font>

スタートメニューから Ubuntu を起動する。<font color="Red">”WslRegisterDistribution failed with error:”</font>というエラーが出てきてしまった場合は、以下の順にインストールします。
・上記[Windowsの機能]4つをアンインストール
・PS ><font color="CornflowerBlue ">wsl --install</font>
&nbsp;&nbsp;これで[仮想マシンプラットフォーム]とUbuntuがインストールされます。
・最後に以下をチェックする
☑Hyper-V
☑Linux用Windowsサブシステム
☑Windowsハイパーバイザープラットフォーム

Ubuntu を起動しapt-getをアップデートしておきます。
$ <font color="MediumOrchid ">sudo apt-get update</font>
$ <font color="MediumOrchid ">sudo apt-get upgrade</font>

## WSL2上にCUDAをインストール
以下のページから [Linux]-[x86_64]-[WSL-Ubuntu]-[2.0]-[deb (local)] を選択すると
https://developer.nvidia.com/cuda-11-8-0-download-archive
次のようなコマンドが出てくるので、そのままWSLのUbuntuにコピー＆ペーストします。

```
wget https://developer.download.nvidia.com/compute/cuda/repos/wsl-ubuntu/x86_64/cuda-wsl-ubuntu.pin
sudo mv cuda-wsl-ubuntu.pin /etc/apt/preferences.d/cuda-repository-pin-600
wget https://developer.download.nvidia.com/compute/cuda/11.8.0/local_installers/cuda-repo-wsl-ubuntu-11-8-local_11.8.0-1_amd64.deb
sudo dpkg -i cuda-repo-wsl-ubuntu-11-8-local_11.8.0-1_amd64.deb
sudo cp /var/cuda-repo-wsl-ubuntu-11-8-local/cuda-*-keyring.gpg /usr/share/keyrings/
sudo apt-get update
sudo apt-get -y install cuda
```

パスを通しておきます。
```
echo 'export CUDA_PATH=/usr/local/cuda-11.8' >> ${HOME}/.bashrc
echo 'export LD_LIBRARY_PATH=/usr/local/cuda-11.8/lib64:${LD_LIBRARY_PATH}' >> ${HOME}/.bashrc
echo 'export PATH=/usr/local/cuda-11.8/bin:${PATH}' >> ${HOME}/.bashrc
```
以下を実行してバージョンが表示されればOkです。
$ <font color="MediumOrchid ">source ~/.bashrc</font>
$ <font color="MediumOrchid ">nvcc --version</font>

## WSL2上にcuDNNをインストール

cuDNNをダウンロードします。
https://developer.nvidia.com/rdp/cudnn-download
[Download cuDNN v8.9.1 (May 5th, 2023), for CUDA 11.x]-[Local Installer for Ubuntu22.04 x86_64 (Deb)]
エクスプローラで、cudnn-local-repo-ubuntu2204-8.9.1.23_1.0-1_amd64.deb を Linux/Ubuntu/home/ユーザー名/ に移動
$ <font color="MediumOrchid ">sudo dpkg -i cudnn-local-repo-ubuntu2204-8.9.1.23_1.0-1_amd64.deb</font>
以下のようなコマンドが出てくるので、コピー＆ペースト。
$ <font color="MediumOrchid ">sudo cp /var/cudnn-local-repo-ubuntu2204-8.9.1.23/cudnn-local-*-keyring.gpg /usr/share/keyrings/</font>
ライブラリをインストールします。
$ <font color="MediumOrchid ">sudo apt-get update</font>
$ <font color="MediumOrchid ">sudo apt-get -y install libcudnn8 libcudnn8-dev libcudnn8-samples</font>

## WSL2上にMinicondaをインストール

$ <font color="MediumOrchid ">wget https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh</font>
$ <font color="MediumOrchid ">bash Miniconda3-latest-Linux-x86_64.sh</font>
ライセンスを読んで home ディレクトリにインストール。全てyesでOK。
環境変数を更新
$ <font color="MediumOrchid ">source ~/.bashrc</font>

チャンネルをconda-forgeに変更します。
$ <font color="MediumOrchid ">conda config --show channels</font>
channels:
&nbsp;&nbsp;- defaults
$ <font color="MediumOrchid ">conda config --add channels conda-forge</font>
$ <font color="MediumOrchid ">conda config --remove channels defaults</font>
$ <font color="MediumOrchid ">conda config --show channels</font>
channels:
&nbsp;&nbsp;- conda-forge

condaをアップデートします。
$ <font color="MediumOrchid ">conda update conda</font>

### 数値計算ライブラリなどのインストール
環境①と同様にパッケージをインストールします。
$ <font color="MediumOrchid ">conda install numpy scipy sympy matplotlib scikit-learn pandas jupyter notebook ipykernel jupyterlab graphviz pydot</font>

### Tensorflow 2.10のインストール
こちらも環境①と同様です。
$ <font color="MediumOrchid ">pip install tensorflow==2.10</font>

<!--
環境によっては
$ <font color="MediumOrchid ">pip install tensorrt</font>
$ <font color="MediumOrchid ">ln -s libnvinfer_plugin.so.8 libnvinfer_plugin.so.7</font>
$ <font color="MediumOrchid ">ln -s libnvinfer.so.8 libnvinfer.so.7</font>
-->
Device.py
```python:Device.py
from tensorflow.python.client import device_lib
print(device_lib.list_local_devices())
```
PS ><font color="CornflowerBlue ">python Device.py</font>


# パフォーマンスの比較

以下のような CIFAR-10 のコードを動かしてみます。
```python:CIFAR-10.py
import tensorflow as tf
import numpy as np

cifar10 = tf.keras.datasets.cifar10
(x_train, y_train), (x_test, y_test) = cifar10.load_data()
y_train = y_train.flatten()
y_test = y_test.flatten()

input_shape = (32, 32, 3)

x_train=x_train.reshape(x_train.shape[0], x_train.shape[1], x_train.shape[2], 3)
x_train=x_train / 255.0
x_test = x_test.reshape(x_test.shape[0], x_test.shape[1], x_test.shape[2], 3)
x_test=x_test / 255.0

y_train = tf.one_hot(y_train.astype(np.int32), depth=10)
y_test = tf.one_hot(y_test.astype(np.int32), depth=10)

batch_size = 32
num_classes = 10
epochs = 50

model = tf.keras.models.Sequential([
    tf.keras.layers.Conv2D(32, 3, padding='same', input_shape=x_train.shape[1:], activation='relu'),
    tf.keras.layers.Conv2D(32, 3, activation='relu'),
    tf.keras.layers.MaxPooling2D(),
    tf.keras.layers.Dropout(0.25),

    tf.keras.layers.Conv2D(64, 3, padding='same', activation='relu'),
    tf.keras.layers.Conv2D(64, 3, activation='relu'),
    tf.keras.layers.MaxPooling2D(),
    tf.keras.layers.Dropout(0.25),

    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(512, activation='relu'),
    tf.keras.layers.Dropout(0.5),
    tf.keras.layers.Dense(num_classes, activation='softmax'),
])

model.compile(optimizer=tf.keras.optimizers.RMSprop(learning_rate=0.0001, decay=1e-06),
            loss='categorical_crossentropy', metrics=['acc'])

history = model.fit(x_train, y_train, batch_size=batch_size,
                    epochs=epochs)
```

まずはMiniconda上のTensorflowをCPUだけで動作させてみます。

```:Tensorflow CPUバージョン
Epoch 1/50
1563/1563 [==============================] - 69s 44ms/step - loss: 1.8482 - acc: 0.3217
Epoch 2/50
1563/1563 [==============================] - 69s 44ms/step - loss: 1.4917 - acc: 0.4600
Epoch 3/50
1563/1563 [==============================] - 69s 44ms/step - loss: 1.3445 - acc: 0.5182
Epoch 4/50
1563/1563 [==============================] - 71s 46ms/step - loss: 1.2434 - acc: 0.5605
Epoch 5/50
1563/1563 [==============================] - 71s 45ms/step - loss: 1.1599 - acc: 0.5917
```

次にMiniconda上のTensorflowをGPUで動かしてみます。
```:Tensorflow GPUバージョン
Epoch 1/50
1563/1563 [==============================] - 20s 11ms/step - loss: 1.8236 - acc: 0.3341
Epoch 2/50
1563/1563 [==============================] - 17s 11ms/step - loss: 1.4996 - acc: 0.4562
Epoch 3/50
1563/1563 [==============================] - 19s 12ms/step - loss: 1.3622 - acc: 0.5111
Epoch 4/50
1563/1563 [==============================] - 21s 14ms/step - loss: 1.2606 - acc: 0.5508
Epoch 5/50
1563/1563 [==============================] - 21s 14ms/step - loss: 1.1751 - acc: 0.5865
```
GPUは効いているようです。

最後にWSL2+MinicondaでTensorflowをGPUで動かしてみます。
```:WSL2+Tensorflow GPUバージョン
1563/1563 [==============================] - 24s 14ms/step - loss: 1.8202 - acc: 0.3296
Epoch 2/50
1563/1563 [==============================] - 23s 15ms/step - loss: 1.5129 - acc: 0.4499
Epoch 3/50
1563/1563 [==============================] - 25s 16ms/step - loss: 1.3676 - acc: 0.5090
Epoch 4/50
1563/1563 [==============================] - 26s 16ms/step - loss: 1.2706 - acc: 0.5476
Epoch 5/50
1563/1563 [==============================] - 26s 16ms/step - loss: 1.1897 - acc: 0.5770
```

Miniconda上で動かした場合に比べると1.2倍くらい遅くなる結果となりました。仮想環境だと若干遅くなることは避けられないみたいですね。今のところTensorflow 2.10で十分ですが、どのタイミングでTensorflowをアップデートしようか悩みどころですね。