eGPU+Ubuntu20.04LTSを使って、機械学習環境(TensorFlow, PyTorch)を作成してみた。

eGPU＋Ubuntu20.04LTS環境上で動作する機械学習環境(TensorFlow、PyTorch)を構築するまでの手順の記録（my備忘録）です。
環境構築を着手してから、想定以上に途中失敗することが多く、何度もやり直しを行い、ようやく両方の環境が使えるようになったので、忘れないように記録しました。


#使用した機材
（ディスプレイ・マウス・キーボード・ケーブルは除く）
    - Intel NUC [NUC10i5FNH]　(環境を構築するPC・ディスプレイ接続)
    - Ubuntu20.04LTSをインストールするためのUSBメモリ（インストーラを作成する環境はMacbookを利用）
    - Razer Core X（eGPU BOX）
    - Nvidia GeForce RTX 2060（今回、eGPU BOXに格納したGPUボード・ディスプレイ未接続）

# 参考にさせていただいたサイト

- [eGPUでハイスペックLinuxデスクトップをDeep Learning Workstation化計画(eGPUセットアップ編)](https://qiita.com/y-vectorfield/items/8960c804441d2ebd605e, "")
- [Ubuntu 20.04 をインストールして GPU環境でTensorflow、Pytorch を動かすメモ (備忘録)](https://qiita.com/k-uchi/items/8643ab5fa9962de8f1d7, "")
- [Ubuntu20.04LTSのブートUSBをMacで作成する](https://qiita.com/seigot/items/faea0998e17c40b3a63e "")
- [Tensorflow 2.x & PyTorch 1.6でGPUを使う【Ubuntu 20.04】](https://note.com/mokuichi/n/na8cd8e707635#eov85 "")
- [Python 3.xの環境構築【Ubuntu 20.04】](https://note.com/mokuichi/n/n627eb6773aad "")

#作業手順
以下の順番で実施する。

- 前準備１：インストールするバージョンの確認
- 前準備２：USBインストーラの準備
- 前準備３：eGPU BOXの接続状態の確認
- BIOS設定
- Ubuntu 20.04.02LTSのインストール
- eGPUの接続
- nouveauの無効化
- CUDAのインストール
- cuDNNのインストール
- python/pyenv/pipenvのインストール
- TensorFlowのインストール 
- PyTorchのインストール

#実施
## 前準備１：インストールするバージョンの確認
作業に入る前に利用したいTensorFlowとPyTorchのバージョンに合わせて、インストールするCUDAとPythonのバージョンを決めます。

TensorFlowとCUDA、pythonの対応は[こちらの対応表](https://www.tensorflow.org/install/source?hl=ja#gpu "TensorFlow")を参考にしました。
PyTorchは[公式サイト](https://pytorch.org/get-started/locally/ "")を参考にしました。

今回は「Tensorflow-2.4.0」「PyTorch 1.8.1」を使うため、以下のバージョンに定めることにしました。

- CUDA:11.0
- Python:3.8.X

## 前準備２：USBインストーラの準備
私は手持ちのMacbookがありましたので、[こちらの記事](https://qiita.com/seigot/items/faea0998e17c40b3a63e "")を参考にさせていただき、Bootable USBインストーラを作成しました。

https://qiita.com/seigot/items/faea0998e17c40b3a63e

本記事はUSBインストーラを使ってUbuntu20.04LTSをインストールしますが、他の手段を使っていただいても大丈夫かと。

## 前準備３：eGPU BOXの接続状態の確認
少なくともUbuntuインストールが完了するまでは、Intel UNCとeGPU Boxを接続していない&eGPU OBXの電源OFF状態にしています。（接続した状態で実施すると不安定だったりしたため）

## BIOS設定
利用する環境によって、設定方法は異なりますが、Intel NUC(NUC10i5FNH)を使う場合ではUbuntuをインストールする前にいくつかBIOS操作を行う必要があります。

- BIOSのバージョンアップ（手順は省略）
- SecureBootをdisableにします。
    - [Boot]>[SecureBoot]>[SecureBoot]を"Disabled"にします。
- USBメモリからのブート（USBメモリによるインストールの場合）
    - [Boot]>[Boot Priority]>[Boot USB Devices First]を"ON"にします。
    - [Boot]>[Boot Priority]>[USB]を"ON"にします。
- 3rd partyのドライバのロードを許可します。
    - [Security]>[Allow UEFI 3rd party driver loaded]を"ON"にします。
- Thunderbolt Security Levelを変更します。
    - [Security]>[Thunderbolt Security Level]を"Legacy Mode"にします。 


## Ubuntu 20.04LTSのインストール
画面のガイダンスに従い、Ubuntu 20.04LTSをインストールします。
ガイダンス途中で以下のオプション選択が表示されます。

- 「通常のインストール」を選択します。
- 「Ubuntuのインストール中にアップデートをダウンロードする。」にチェックします（ON）
- 「グラフィックスとWiFiハードウェアと追加のメディアフォーマットのサードパーティ製ソフトウェアをインストールする。」のチェックを外します（OFF）

インストール完了後、ログインし、以下のコマンドを実施します。

    $ sudo apt update
    $ sudo apt upgrade
    $ sudo reboot

## eGPUの接続
再起動後からのログイン後、Thunderbolt3ケーブルを使って、eGPU BOXとIntel UNCを接続し、eGPU BOXの電源を入れる。

以下のコマンドを使って、eGPU BOXとの接続を確認する。

    $ boltctl

実行すると、以下のようにeGPU BOXの情報が表示され、接続できていることが確認できます。
![boltctlの実行結果](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194963/f35d07b8-fa16-570e-cda0-b6440d3ecae5.png)


## nouveauの無効化

 インストール直後はnouveauドライバが起動していることがあるので、CUDA/NVIDAドライバを使うためにnouveauを無効化する必要があります。まずはeGPU接続時にnouveauが起動しているかを以下のコマンドを使ってチェックします。

    $ lsmod | grep nouveau

コマンド実行後、何も表示されなければnouveauが無効化していることになり、表示された場合は以下の作業を実行します。
(私が実施した時にPC起動前からeGPU接続かつ電源ONにしていた場合、なぜかこのコマンドを実行しても何も表示されませんでした。それからeGPU　BOX再接続oreGPU再電源ONにして、コマンドを再実行したら、表示されましたが、原因はわからず。表示されなかった方は注意されたし)

    $ sudo touch /etc/modprobe.d/blacklist-nouveau.conf
    $ sudo echo "options nouveau modeset=0" >> /etc/modprobe.d/blacklist-nouveau.conf
    $ sudo update-initramfs -u
    update-initramfs: Generating /boot/initrd.img-5.8.0-36-generic
    $ sudo reboot

再度、以下のコマンドを実行し、nouveauが起動していないことを確認します。

    $ lsmod | grep nouveau 

## CUDAのインストール
[CUDA-Toolkitのアーカイブ](https://developer.nvidia.com/cuda-toolkit-archive "cuda-toolkit-archive")にアクセスし、利用したいバージョンを選択します。
今回はCUDA11.0を選択します。

![CUDA11.0](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194963/cd24a2fd-eb85-323c-e735-1f1f2cd53a72.png)


インストールする環境に対して選択すると、下部領域にインストールするまでのスクリプトが表示されるので、スクリプトをコピーして利用します。

![スクリーンショット 2021-05-05 18.33.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194963/b4d70597-7bb7-97d7-eda1-8cd52de0afed.png)

![スクリーンショット 2021-05-05 18.33.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194963/f8768b0d-96fb-1adb-3e48-63078363e688.png)



ただし、そのまま実行してしまうとCUDA 11.0ではなく最新バージョンがインストールされてしまうため、cudaインストールコマンド行にバージョン指定を追記する必要があります。

    $ wget https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86_64/cuda-ubuntu2004.pin
    $ sudo mv cuda-ubuntu2004.pin /etc/apt/preferences.d/cuda-repository-pin-600
    $ sudo apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86_64/7fa2af80.pub
    $ sudo add-apt-repository "deb https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2004/x86_64/ /"
    $ sudo apt-get update
    $ sudo apt-get -y install cuda-11-0
    $ sudo reboot

再起動後、CUDA Toolkitが無事インストールされているかを確認します。

    $ /usr/local/cuda/bin/nvcc -V

以下のような結果が表示されたら成功です。

![/usr/local/cuda/bin/nvccの実行結果](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194963/b47abea4-f50c-8b30-a9da-a4cb6530e798.png)

CUDA Toolkitのインストール途中にCUDAのバージョンに見合ったNvidiaドライバーも自動でインストールされています。以下のコマンドが実行可能になっているので、eGPU BOXとの接続状況を確認します。

    $ nvidia-smi

GPUボードを認識すると以下の結果が表示されます。ちなみにここに記載されている、CUDA Versionは実際にインストールされているCUDAのバージョンが表示されているようではないようです。

![nvidia-smiの実行結果](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/194963/7d6a0314-4f47-943a-0520-c5d909ec287d.png)


nvccコマンドのパスが通っているかを確認します。

    $ nvcc -V

パスが通っていなかった場合は/etc/bash.bashrcの末尾にパス設定を追記します。編集方法は以下だとviを使っていますが、変更できれば他のエディタ、echo操作での書き込みでも大丈夫です。

    $ sudo vi /etc/bash.bashrc

/etc/bash.bashrcの末尾行に以下を追記します。

```/etc/bash.bashrc
export PATH="/usr/local/cuda/bin:$PATH"
```

再起動します。

    $ sudo reboot

## cuDNNのインストール

[cuDNNアーカイブ](https://developer.nvidia.com/rdp/cudnn-archive "cuDNN-archive")にアクセスし、cuDNNをインストールします。（ダウンロードするためには登録が必要です。）
インストールしたCUDAのバージョンにあったバージョンを用います。
今回はcuDNN v8.1.1 を選択します。


cuDNN Library for Linux [x86_64] をクリックしてダウンロードします。
(ダウンロードファイル：cudnn-11.2-linux-x64-v8.1.1.33.tgz)
また、後ほど動作確認用として利用するため、以下もクリックしてダウンロードする
cuDNN Code Samples and User Guide for Ubuntu for Ubuntu20.04 x86_64 [Deb]
(ダウンロードファイル名：libcudnn8-samples_8.1.1.33-1+cuda11.2_amd64.deb)


ダウンロード後、以下のコマンドでダウンロードしたファイルを解凍します。

    $ tar -xzvf cudnn-11.2-linux-x64-v8.1.1.33.tgz

解凍後、./cudaディレクトリがあることを確認します。
このディレクトリの中のディレクトリ・ファイルをコピーし、アクセス権を設定します。

    $ sudo cp cuda/include/cudnn*.h /usr/local/cuda/include
    $ sudo cp -P cuda/lib64/libcudnn* /usr/local/cuda/lib64
    $ sudo chmod a+r /usr/local/cuda/include/cudnn*.h /usr/local/cuda/lib64/libcudnn*

再び、/etc/bash.bashrcの末尾にパス設定を追記します。

    $ sudo vi /etc/bash.bashrc

/etc/bash.bashrcの末尾行に以下を追記します。

```/etc/bash.bashrc
export LD_LIBRARY_PATH="/usr/local/cuda/lib64:$LD_LIBRARY_PATH"
export LD_LIBRARY_PATH="/usr/lib/cuda/include:$LD_LIBRARY_PATH"
```

再起動します。

    $sudo reboot

インストールが成功ができているかを確認する。

    $ mkdir libcudnn8-samples
    $ dpkg-deb -x libcudnn8-samples_8.0.4.30-1+cuda11.0_amd64.deb libcudnn8-samples
    $ cd libcudnn8-samples/usr/src/cudnn_samples_v8/mnistCUDNN
    $ make clean && make
    $ ./mnistCUDNN

make clean && makeを実行した際、私の環境ではfreeimageが入っていないと怒られたので、以下のコマンドでインストールし、再実行したら成功しました。

    $ sudo apt-get install libfreeimage3 libfreeimage-dev

## python/pyenv/pipenvのインストール


[Python 3.xの環境構築【Ubuntu 20.04】](https://note.com/mokuichi/n/n627eb6773aad "")を参考にさせていただき、Python環境(pyenv/pipenv)をインストールしました。

まずは必要なソフトウェアのインストールを実施。

     $sudo apt install git gcc make zlib1g-dev libffi-dev libbz2-dev libssl-dev libreadline-dev libsqlite3-dev python3-tk tk-dev


pyenvをインストールします。

    $ git clone https://github.com/pyenv/pyenv.git ~/.pyenv
    $ git clone https://github.com/pyenv/pyenv-update.git ~/.pyenv/plugins/pyenv-update
    $ sudo vi ~/.bashrc

.bashrcの末尾の行に以下を追記します。

```~/.bashrc
export PYENV_ROOT="$HOME/.pyenv"
export PATH="$PYENV_ROOT/bin:$PATH"
eval "$(pyenv init -)"
```

設定を反映します。

     $ source ~/.bashrc

pyenvにインストールできるpythonのバージョンは以下のコマンドで確認ができます。

     $ pyenv install --list

CUDA 11.0とTensorFlow とPyTorchを使いたいので、Pythonを3.8.Xのものをインストールする。今回はPython3.8.10を指定。

     $ pyenv install 3.8.10
     $ pyenv versions

pythonコマンドをインストールしたバージョンにする。

     $ pyenv global 3.8.10


Pythonコマンドのバージョンが3.8.10であることを確認します。

     $ python -V

Pipenvをインストールします。

     $ pip install --upgrade pip setuptools
     $ pip install pipenv

仮想環境で作業するためのディレクトリを作成し移動します。

     $ mkdir samplePython
     $ cd samplePython

Pythonバージョンを指定します。
     
     $ pipenv —python 3.8.10

## TensorFlowのインストール

[Tensorflow 2.x & PyTorch 1.6でGPUを使う【Ubuntu 20.04】](https://note.com/mokuichi/n/na8cd8e707635#eov85 "")を参考にさせていただき、TensorFlowとPyTorchをインストールしました。

以下のコマンドを実行し、TensorFlow(2.4.0)をインストールします。

    $ pipenv install tensorflow==2.4.0

GPUドライバが認識できているかを確認します。

    $ python
    >>>import tensorflow as tf
    >>>print(tf.config.list_physical_devices('GPU'))

上記実行により、出力されるメッセージの最後の行に以下の行が表示されていれば成功のようです。

    [PhysicalDevice(name='/physical_device:GPU:0', device_type='GPU')]

## pytorchのインストール

今回はpipenv環境でインストールしたいので、PyTorchをインストールする前に以下を実行します。

    $ pipenv shell
    $ pipenv install future numpy pillow

[PyTorch公式サイト](https://pytorch.org/get-started/locally/ "")からインストールする環境に合わせたPyTorchバージョンのインストールするコマンドを入手します。

    $ pip install torch==1.8.1+cu111 torchvision==0.9.1+cu111 torchaudio==0.8.1 -f https://download.pytorch.org/whl/torch_stable.html

以下のコマンドを実施し、GPUドライバが正しく認識できているかを確認します。以下を実行した結果、Trueと表示されれば、成功です。

    $ python
    >>> import torch
    >>> print(torch.cuda.is_available())