keras-yolo3 学習環境の構築【Ubuntu 20.04版】

## はじめに

Ubuntu20.04インストール直後のマシンにTensorflow 1.6.0とGPUを用いた学習環境を構築したときの記録です。
学習に使用したプログラムはqqwweee/keras-yolo3で公開されているものです。


[qqwweee/keras-yolo3](https://github.com/qqwweee/keras-yolo3)


このリポジトリで公開されているコードはtensorflowのバージョンが1.6.0のときに作成されたものでそれ以降更新がされていません。そのため，最新のtensorflow(2.4.0)では動作せずtensorflowのバージョンが1.6.0の環境を作る必要があります。
ここで注意する必要があるのが，TensorFlowとcuDNN，CUDAのバージョンの互換性です。下記の表の通り，tensorflow-gpu-1.6.0が使用できるバージョンはcuDNNが7，CUDAが9です。
![[https://www.tensorflow.org/install/source#gpu](https://www.tensorflow.org/install/source#gpu)](https://dl.dropbox.com/s/bqydanuv0q9hu5b/tensorflow-gpu.png?dl=0)
[https://www.tensorflow.org/install/source#gpu](https://www.tensorflow.org/install/source#gpu)
どのプログラムも動く環境を作るため、基本的にはホストとなるマシンにCUDAは入れず、CUDAとcuDNNが入ったDockerイメージを活用します。Dockerを使用することでホストマシンに必要なのはNVIDIAドライバだけになります。
Dockerを用いたTensorflow学習環境の構築は[Docker | TensorFlow](https://www.tensorflow.org/install/docker)を参考にしました。

## 1. GPUドライバのインストール
 [NVIDIA Driver Installation Quickstart Guide](https://docs.nvidia.com/datacenter/tesla/tesla-installation-notes/index.html#abstract)にある手順に沿ってインストールします。
### インストールの準備
参考: [Pre-installation Actions](https://docs.nvidia.com/cuda/cuda-installation-guide-linux/index.html#pre-installation-actions)
1. CUDAに対応したGPUか確認
  ```bash
  $ lspci | grep -i nvidia
  01:00.0 3D controller: NVIDIA Corporation Device 1f95 (rev a1)
  
  ```
  NVIDIA製のGPUであれば、[リンク先](https://developer.nvidia.com/cuda-gpus)にリストされていればOK。
2. Linuxのバージョンは対応しているのか
  ```bash
  $ uname -m && cat /etc/*release
  x86_64
  DISTRIB_ID=Ubuntu
  DISTRIB_RELEASE=20.04
  DISTRIB_CODENAME=focal
  DISTRIB_DESCRIPTION="Ubuntu 20.04.2 LTS"
  ...(略)...
  ```
3. makeやgccなどビルドに必要なツールがインストール済みか

4. 正しいLinuxカーネルヘッダーを備えているか
```bash
$ uname -r
5.8.0-48-generic

```

### makeやgccなどの開発ツールのインストール

下記のコマンドを実行します。
```bash
sudo apt update
sudo apt install build-essential
```
インストールできたか確認
```bash
$ gcc --version
gcc (Ubuntu 9.3.0-17ubuntu1~20.04) 9.3.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ make --version
GNU Make 4.2.1
このプログラムは x86_64-pc-linux-gnu 用にビルドされました
Copyright (C) 1988-2016 Free Software Foundation, Inc.
ライセンス GPLv3+: GNU GPL バージョン 3 以降 <http://gnu.org/licenses/gpl.html>
これはフリーソフトウェアです: 自由に変更および配布できます.
法律の許す限り、　無保証　です

```
### インストール
> How do I install the NVIDIA driver?The recommended way is to use your package manager and install the cuda-drivers package (or equivalent).
> When no packages are available, you should use an official "runfile".Alternatively, the NVIDIA driver can be deployed through a container.
> Refer to the documentation for more information.NVIDIA/nvidia-docker
[https://github.com/NVIDIA/nvidia-docker/wiki/Frequently-Asked-Questions#how-do-i-install-the-nvidia-driver](https://github.com/NVIDIA/nvidia-docker/wiki/Frequently-Asked-Questions#how-do-i-install-the-nvidia-driver)

nvidia-dockerのGithubのWikiによると、パッケージマネージャーを使ってインストールするほうが推奨されているので、ここは素直にパッケージマネージャーを使ってインストールしていきます。

参考：[NVIDIA Driver Installation Quickstart Guide](https://docs.nvidia.com/datacenter/tesla/tesla-installation-notes/index.html#ubuntu-lts)

```bash
$ sudo apt-get install linux-headers-$(uname -r)
$ distribution=$(. /etc/os-release;echo $ID$VERSION_ID | sed -e 's/\.//g')
$ wget https://developer.download.nvidia.com/compute/cuda/repos/$distribution/x86_64/cuda-$distribution.pin
$ sudo mv cuda-$distribution.pin /etc/apt/preferences.d/cuda-repository-pin-600
$ sudo apt-key adv --fetch-keys https://developer.download.nvidia.com/compute/cuda/repos/$distribution/x86_64/7fa2af80.pub
$ echo "deb http://developer.download.nvidia.com/compute/cuda/repos/$distribution/x86_64 /" | sudo tee /etc/apt/sources.list.d/cuda.list
$ sudo apt-get update
$ sudo apt-get -y install cuda-drivers
```
※途中で`Configure Secure Boot`というタイトルのメッセージが表示された場合は、指示に従ってパスワードを設定し、再起動後に設定したパスワードを入力します。

インストールできたことを確認します。

```bash
$ nvidia-smi
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 460.32.03    Driver Version: 460.32.03    CUDA Version: 11.2     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  GeForce GTX 165...  On   | 00000000:01:00.0 Off |                  N/A |
| N/A   50C    P8     3W /  N/A |    220MiB /  3911MiB |      0%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
+-----------------------------------------------------------------------------+
```

## 2. Dockerのインストール
参考元: [Install Docker Engine on Ubuntu | Docker Documentation](https://docs.docker.com/engine/install/ubuntu/)
```bash
$ sudo apt-get update
$ sudo apt-get install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg \
    lsb-release
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /usr/share/keyrings/docker-archive-keyring.gpg
$  echo \
  "deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://download.docker.com/linux/ubuntu \
  $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
$ sudo apt-get update
$ sudo apt-get install docker-ce docker-ce-cli containerd.io
```

インストールできたことを確認します。

```bash
$ sudo docker run hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
b8dfde127a29: Pull complete 
Digest: sha256:9f6ad537c5132bcce57f7a0a20e317228d382c3cd61edae14650eec68b2b345c
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
 1. The Docker client contacted the Docker daemon.
 2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
    (amd64)
 3. The Docker daemon created a new container from that image which runs the
    executable that produces the output you are currently reading.
 4. The Docker daemon streamed that output to the Docker client, which sent it
    to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
 $ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
 https://hub.docker.com/

For more examples and ideas, visit:
 https://docs.docker.com/get-started/
```

sudoコマンドなしでDockerを実行できるように設定します。

```bash
$ sudo groupadd docker
[sudo] msi のパスワード:
groupadd: グループ 'docker' は既に存在します
$ sudo gpasswd -a $USER docker
ユーザ msi をグループ docker に追加
$ sudo systemctl restart docker
$ reboot

```

## 3. NVIDIA Container Toolkitのインストール

参考元: [Setting up NVIDIA Container Toolkit - Installation Guide](https://docs.nvidia.com/datacenter/cloud-native/container-toolkit/install-guide.html#setting-up-nvidia-container-toolkit)

```bash
$ distribution=$(. /etc/os-release;echo $ID$VERSION_ID) \
   && curl -s -L https://nvidia.github.io/nvidia-docker/gpgkey | sudo apt-key add - \
   && curl -s -L https://nvidia.github.io/nvidia-docker/$distribution/nvidia-docker.list | sudo tee /etc/apt/sources.list.d/nvidia-docker.list
```

```bash
$ sudo apt-get update
$ sudo apt-get install -y nvidia-docker2

```

Dockerを再起動させます。

```bash
$ sudo systemctl restart docker

```

正しくインストールできたことを確認します。

(ここで表示されるCUDA VersionはインストールされているCUDAのバージョンではないことに注意してください)

```bash
$ sudo docker run --rm --gpus all nvidia/cuda:11.0-base nvidia-smi
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 460.32.03    Driver Version: 460.32.03    CUDA Version: 11.2     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  GeForce GTX 165...  On   | 00000000:01:00.0 Off |                  N/A |
| N/A   50C    P8     3W /  N/A |    220MiB /  3911MiB |      0%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
+-----------------------------------------------------------------------------+

```

## 4. Visual Studio Codeのインストールと設定

インストール方法にはいくつか手段がありますが、ここでは公式サイトから.debファイルを入手してインストールしていきます。

1. [公式サイト](https://code.visualstudio.com/download)から.debファイルをダウンロードします。
2. ダウンロードしたファイルの上で右クリックし、メニューから「ソフトウェアのインストールで開く」を選択し、インストールします。
   ※Firefoxでダウンロードした場合、ダウンロードしたフォルダからファイルを移動させずにインストールすると失敗する場合があります。この場合はファイルを違うフォルダに移動させてからインストールすると成功します。
3. Visual Studio Codeをインストールできたら、以下の拡張機能をインストールします。
   - Remote Development
   - Python
   - Docker
## 5. Dockerの実行

Dockerfileを用意します。

```docker
FROM tensorflow/tensorflow:1.6.0-devel-gpu-py3
RUN apt-get update && apt-get install -y python3-pip tzdata language-pack-ja libjpeg-dev libjpeg8-dev
ENV TZ Asia/Tokyo
ENV LC_ALL=ja_JP.UTF-8
# RUN pip3 uninstall -y tensorflow pillow && pip3 install tensorflow-gpu==1.6.0 keras==2.1.5 pillow==2.8.1 h5py==2.8.0
RUN pip3 uninstall -y tensorflow && pip3 install tensorflow-gpu==1.6.0 keras==2.1.5 pillow==2.8.1 h5py==2.8.0
```

作成したDockerfileを指定してビルドを行い、コンテナを起動します。

```bash
$ docker build -t keras-yolo3 .
$ docker run --runtime=nvidia --name tf36 --rm -d -it -v <共有元ディレクトリ>:<共有先ディレクトリ> keras-yolo3:latest
```

Visual Studio Codeのリモートコンテナから起動したコンテナを開き、学習を実行します。

学習中に下記のコマンドを実行することでGPUを使用していることが確認できました。

```bash
$ watch -d -n 0.5 nvidia-smi  # 学習中
Every 0.5s: nvidia-smi                                               Mon Mar 29 20:36:56 2021

Mon Mar 29 20:36:56 2021
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 460.32.03    Driver Version: 460.32.03    CUDA Version: 11.2     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  GeForce GTX 165...  On   | 00000000:01:00.0 Off |                  N/A |
| N/A   77C    P0    50W /  N/A |   3757MiB /  3911MiB |     60%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
+-----------------------------------------------------------------------------+

$ watch -d -n 0.5 nvidia-smi  # 学習終了後
Every 0.5s: nvidia-smi                                               Mon Mar 29 20:38:50 2021

Mon Mar 29 20:38:50 2021
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 460.32.03    Driver Version: 460.32.03    CUDA Version: 11.2     |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|                               |                      |               MIG M. |
|===============================+======================+======================|
|   0  GeForce GTX 165...  On   | 00000000:01:00.0 Off |                  N/A |
| N/A   57C    P0    16W /  N/A |    393MiB /  3911MiB |      4%      Default |
|                               |                      |                  N/A |
+-------------------------------+----------------------+----------------------+

+-----------------------------------------------------------------------------+
| Processes:                                                                  |
|  GPU   GI   CI        PID   Type   Process name                  GPU Memory |
|        ID   ID                                                   Usage      |
|=============================================================================|
+-----------------------------------------------------------------------------+
```