Dockerで環境構築してYOLOv5を動かす（PyTorch）

# はじめに

**YOLOv5**を使いたくて環境構築してたら，**CUDAやcuDNNのバージョン**などいろいろ合わせなくちゃいけないものが出てきた
前まではYOLOv3を使ってたのでそちらに合わせていたためである
一応どちらの環境も残しておきたいため，今回は**Dockerを使って複数の環境を構築する**ことにしたので忘れないように備忘録を作っておく

自分の環境は以下の通り
| 名前 | 種類 | 確認コマンド |
|:-:|:-:|:-:|
| GPU | NVIDIA GeForce RTX3090 | nvidia-smi --query-gpu=name --format=csv          |
| OS | Ubuntu20.04 | cat /etc/issue |

今回は以下の記事を大変参考にしたため，そちらも一読してほしい

https://qiita.com/tatsuya11bbs/items/3af03e704812b6c89965


# 手順

1．NVIDIAドライバのインストール
2．Dockerのインストール
3．Dockerで使用したい環境を構築
4．DockerコンテナでYOLOv5を使用

# 1．NIVIDIAドライバのインストール

DockerをインストールするときはNIVIDIAドライバというものが必要になる

詳しくは↓

https://qiita.com/karaage0703/items/e79a8ad2f57abc6872aa

今回は自分の環境での結果を示す

まず，そもそもドライバがインストールされているかを確認
```
$ nvidia-smi
```
インストールされている場合は
![スクリーンショット 2022-03-05 9.56.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2525698/745c31a0-d1d6-e7ec-c144-95ecfb96e6f9.png)
このような画面になる

ここで，現在あるドライバを消したい場合は
```
$ sudo apt-get --purge remove nvidia-*
$ sudo apt autoremove
```
で消える(らしい)
試してないので，できなかったら解決策を教えてほしい

次に自分の環境に合ったドライバを確認
```
// リポジトリの追加
$ sudo add-apt-repository ppa:graphics-drivers/ppa
$ ubuntu-drivers devices
```
![スクリーンショット 2022-03-05 10.03.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2525698/61e9c076-26ea-6965-1a20-89efc9140c61.png)

今回，自分はrecommendedのバージョンをインストールした
```
// ドライバインストール
$ sudo apt install nvidia-driver-510

// 再起動
$ sudo reboot
```
これで再度ドライバがインストールされているかを確認
```
$ nvidia-smi
```
![スクリーンショット 2022-03-05 9.56.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2525698/745c31a0-d1d6-e7ec-c144-95ecfb96e6f9.png)
右上にCUDA Versionが書いてあるが，これはドライバが対応している最新CUDAが書いてあるだけで，まだインストールはされていない
つまり，Driver Version: 510.47.03だとCUDAが11.6まで対応してるよ，という感じなので，別に11.1とかをインストールしても大丈夫

# 2．Dockerのインストール
これでドライバがインストールできたので，次にDockerをインストール

参考元↓

https://sid-fm.com/support/vm/guide/install-docker-ubuntu.html

↑実行結果とかも載せてあるので，心配性な人はこちらで

今回は詳しい説明はせずに，コマンドを羅列する
```
// aptを更新
$ sudo apt update

// 必要なパッケージをインストール
$ sudo apt install \
    apt-transport-https \
    ca-certificates \
    curl \
    gnupg-agent \
    software-properties-common

// Docker公式ページから公式GPG公開鍵をインストール
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -

// 公開鍵のフィンガープリントを確認
$ sudo apt-key fingerprint 0EBFCD88

// リポジトリの追加
$ sudo add-apt-repository \
   "deb [arch=amd64] https://download.docker.com/linux/ubuntu \
   $(lsb_release -cs) \
   stable"

// aptを更新
$ sudo apt update

// Dockerのインストール
$ sudo apt-get install docker-ce docker-ce-cli containerd.io
```

これでDockerが使用可能になる

確認として
```
$ sudo docker run hello-world
```
コマンドを打って，↓のようになれば完了
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2525698/54b24acf-92c5-8ea0-d6c5-ada3ee2801a2.png)

### オプション
dockerコマンドを使うときは必ずsudoが必要になるが，面倒なのでなしでもできるようにする
調べれば解決方法は大量に出てくるが，一応書いておく
```
// dockerグループの情報表示
$ getent group docker

// Dockerグループにユーザ追加
$ sudo gpasswd -a [name] docker

// 権限を付与
$ sudo chgrp docker /var/run/docker.sock

// 追加されているか確認
$ id [name]

// 再起動
$ sudo reboot
```
今回，[name]には自分のパソコンのユーザ名を入力した
なんでもいいのかもしれないが，試してないので分からない

以下のコマンドを打ってみてエラーがでなければ無事にできている
```
$ docker ps
```

最後にNVIDIA container toolkitをインストール
```
$ 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

$ sudo apt-get update && sudo apt-get install -y nvidia-container-toolkit
$ sudo systemctl restart docker
```

↓を打って確認
```
$ nvidia-container-cli info
```

# 3．Dockerで使用したい環境を構築

結構ここで詰まったので，今までより詳しく書く

まずそもそもDockerでは2つの手順が必要
1．イメージを作成
2．コンテナを作成（環境の箱，さまざまな種類のコンテナを作ることで複数の環境を共存させることができる）

この2つが混同してよく分からなくなっていた
というわけで

https://blog.codecamp.jp/programming-docker-image-container

こちらの記事を参考にどうぞ

まずコンテナを作るための基盤となるイメージを作成(pull)する
```
// 一旦このコマンドは打たないで，それぞれの環境にあったcudaとcudnnのバージョンを確認すること
docker pull nvidia/cuda:11.3-cudnn8-devel-ubuntu20.04
```

このとき，どのCUDAをインストールしてどのcuDNNをインストールすればいいのか分からず，適当にコマンドをうっていた

なので一旦ここで落ち着いて足を止めてほしい

### 自分の環境に合ったCUDA，cuDNN，Pytorch
まず，この記事のはじめにubuntuのバージョンを書いたと思うが，ぶっちゃけdockerがあればどんなubuntuバージョンでも作れるので関係ない

だが，今回はYOLOv5を使うので最新の方がいいだろう，ということで20.04で環境を作成する

YOLOv5はPythonで動かそうと思うので，Pythonで機械学習をするときに必要なライブラリである **Pytorch** が必要になる

GPUとPytorch公式ホームページの情報に合わせて，CUDAとcuDNNのバージョンを決定するのがいいだろう

https://pytorch.org/

### それぞれの自分の決定方法

**CUDA**：RTX30xxのGPUだとCUDA11.1以降じゃないと動かないらしい，pytorchの[公式ホームページ](https://pytorch.org/)にCUDA10.2とCUDA11.3を選ぶ部分があったので，11.3にした

**cuDNN**：7と8があるが新しい方の8にした．[cuDNN8の対応CUDAバージョン](https://docs.nvidia.com/deeplearning/cudnn/support-matrix/index.html)を見るとCUDA11.0~11.5と書いてあった

**Pytorch**：[公式ホームページ](https://pytorch.org/)の画面を選択していって，指定されたバージョンにした
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2525698/ab74f57f-d309-7d47-e8f2-601147391971.png)

こんな感じでバージョンを決定したのだが，正しいやり方なのかは分からない
調べながら試行錯誤してみてほしい

### Dockerでの環境構築
では，Dockerでの環境構築に戻る

↑で調べて決定したバージョンに基づきイメージを作成する
ここで，DockerHubを参考にpullするイメージを決定する

https://hub.docker.com/r/nvidia/cuda/tags

Dockerで環境構築をする際には先ほど説明した様にイメージを作成する必要がある
このイメージにはさまざまなものがあるのだが，docker-hubでpullできるイメージを検索することができる

今回は， **11.3.1-cudnn8-devel-ubuntu20.04** をpullした
かならず**develがついたもの**を選ぶ様に
のちのち使う nvcc コマンドはdevelがついたものでないと動作しなかった

```
// イメージをpull
$ docker pull nvidia/cuda:11.3.1-cudnn8-devel-ubuntu20.04
```
Pytorchのインストールはいつするんだと思った方もいるだろうが，これは環境構築した後にその環境に入ってから行う

次にイメージをもとにコンテナを作成する．環境の箱作りだ．
```
// コンテナを作成
$ docker run -it -d --gpus all --name [name] nvidia/cuda:11.3.1-cudnn8-devel-ubuntu20.04 bash
```

dockerコンテナにホスト側（dockerコンテナ外）のディレクトリをマウントするとdockerにデータを渡しやすくなる
詳しいことは以下の記事で書いている．
https://qiita.com/liker59/items/e66e1aa81a9edcf0fa30


これでコンテナが作成された
[name]には好きな名前をつける

最後にコンテナに入る
```
$ docker exec -it [name] bash
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2525698/5bc81285-5e6f-b59c-2557-69fbb19b64d5.png)
こんな感じの表記になったら，無事コンテナに入ることができている

### その他
dockerで便利なコマンド
```
// dockerのイメージを確認
$ docker images

// イメージの削除
$ docker rmi [TAG]

// dockerで起動中のコンテナを確認
$ docker ps -a

// コンテナを起動
$ docker start [name]

// コンテナを停止
$ docker stop [name]

// コンテナの削除
$ docker rm [name]

// コンテナ名の変更
$ docker rename [oldname] [newname]

// コンテナ上のファイルをコピーしてくる
$ docker cp [name]:/ディレクトリ名/ファイル名 移動先ディレクトリ名

// コンテナ上にファイルをコピーする
$ docker cp ファイル名 [name]:/ディレクトリ名

// コンテナをイメージ化
$ docker commit [name] リポジトリ名:TAG
```


# 4．DockerコンテナでYOLOv5を使用
ようやくYOLOv5を動かせる環境コンテナが作成できた

まずはじめにきちんと環境構築ができているかを確認する
- ドライバの認識
```
root@b726bd02dd2e:/# nvidia-smi
```
- CUDAの確認
```
root@b726bd02dd2e:/# nvcc -V
```
このnvccコマンドがdevel以外だと動かなかった
- cuDNNの確認
```
root@b726bd02dd2e:/#　dpkg -l | grep cudnn
```


確認できたら，次にpythonとpipをインストールする
```
root@b726bd02dd2e:/# apt-get update
root@b726bd02dd2e:/# apt-get install -y python3 python3-pip
```
- pipでPytorchをインストール
ここでPytorchの[公式ホームページ](https://pytorch.org/)で選択してでてきたコマンドを打つ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2525698/ab74f57f-d309-7d47-e8f2-601147391971.png)
```
root@b726bd02dd2e:/# pip3 install torch==1.10.2+cu113 torchvision==0.11.3+cu113 torchaudio==0.10.2+cu113 -f https://download.pytorch.org/whl/cu113/torch_stable.html
```
ではpytorchを使ってGPUがきちんと認識しているかを確認する
```
root@b726bd02dd2e:/# python3
>>> import torch
>>> torch.__version__
>>> torch.cuda.is_available()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2525698/d47c4485-c0bb-219d-0992-bce8a6940562.png)
これでtorchのバージョンとGPUが認識できていることが確認できる

### YOLOv5のインストール
では本命のYOLOv5のインストールに入っていく

参考にしたサイト↓

https://www.alpha.co.jp/blog/202108_02

このサイトではvenvを使って仮想環境をつくっているが，dockerがあるので飛ばしてもいいだろう

- YOLOv5のインストール
```
root@b726bd02dd2e:/# apt install -y git
root@b726bd02dd2e:/# git clone https://github.com/ultralytics/yolov5
```
これで **yolov5** というディレクトリができる

yolov5のディレクトリに移動して
```
root@b726bd02dd2e:/yolov5# pip3 install -r requirements.txt
```
と打つとYOLOv5が使えるようになる

では，YOLOv5に用意してあるサンプルを用いて実際に物体認識を行なってみよう
```
// 必要なパッケージをインストール
root@b726bd02dd2e:/yolov5# apt-get install -y libgl1-mesa-dev
root@b726bd02dd2e:/yolov5# apt-get install -y libglib2.0-0

// 実行
root@b726bd02dd2e:/yolov5# python3 detect.py --source data/images/bus.jpg --conf 0.5 --weights yolov5s.pt
```
実行結果は，runs/detect/exp に保存される

しかし，docker上では画像ファイルを開くことができない（できるのかもしれないが，現時点では分からない，分かる人がいたら教えてほしい，ぜひ）

* 2022/03/19 追記
dockerコンテナにホスト側（dockerコンテナ外）のディレクトリをマウントすることで解決
詳しいことは以下の記事で書いている．

https://qiita.com/liker59/items/e66e1aa81a9edcf0fa30

そこで今回はdockerのファイルをdocker外にコピーすることでこの問題を解決する
まず，dockerから抜け出す
```
root@b726bd02dd2e:/yolov5# exit
```
次に保存先のディレクトリを作成しておく
ディレクトリ先に移動して
```
$ docker cp [name]:/yolov5/runs/detect/exp/bus.jpg .
```
と打つと画像がコピーされる

そしてファイルから開くと
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2525698/29f584b5-ef04-3f2a-6b85-014f187028c1.png)
というように物体認識ができていることが確認できた

以上がDockerで環境構築〜YOLOv5で物体認識までの流れである

# おわりに
今回はYOLOv5を使うためにDockerで環境構築を行った
今後は自分が用意したデータで学習をさせたいのでなにか残しておきたいと思ったら，また追加しようと思う