SSD (Object Detection) をGoogle ColabでPyTorchをつかって動かしてみる。

#はじめに
ssd.pytorchと、mlperfのSSDを動かしてみた。両者とも計算資源をTesla T4でも、20時間以上使用する。このため、ssd.pytorchではiter数を削減することにより学習時間を3時間程度で終了するようにしている。もちろん、推論なら計算量もかなり減るので実用的とは思う。
(2019/10/31追記)
PyTorchのサイトに、[使い方の説明(SSD By NVIDIA)](https://pytorch.org/hub/nvidia_deeplearningexamples_ssd/)がある。


#環境設定(ssd.pytorch)
##環境設定
ssd.pytorchのアップストリームのコードでは、(片手間にサポートしており更新が間に合わず)動かない箇所が散見される(2019年3月現在)。このため、別途、レポジトリを修正したコードをgithubに上げた。修正は、Issueの中の議論を元に作った。本来はアップストリームの以下を実行すべきだが、とりあえず作成した暫定コードはその下に記載している。
アップストリーム

```
!git clone https://github.com/amdegroot/ssd.pytorch
```
暫定コード

```
!git clone https://github.com/sakaia/ssd.pytorch
```
###データセット
データセットは、VOCを使うことにした。ダウンロード時間は、4分かかる。計算時間は20時間近くかかると思われる。COCOは下記(mlperf)で記載の通り30分程度かかる。。

```
!bash ssd.pytorch/data/scripts/VOC2007.sh >& VOC2007.log
!bash ssd.pytorch/data/scripts/VOC2012.sh >& VOC2012.log
```
上記のファイルがない場合、以下からもダウンロードできる。[PASCAL VOC 2007 Dataset #48](https://github.com/rbgirshick/rcnn/issues/48)

```
!wget  http://pjreddie.com/media/files/VOCtrainval_06-Nov-2007.tar
!wget http://pjreddie.com/media/files/VOCtest_06-Nov-2007.tar
!tar -xvf VOCtrainval_06-Nov-2007.tar >& trainval.log
!tar -xvf VOCtest_06-Nov-2007.tar >& test.log
```

###モデルのダウンロード
モデルファイルのダウンロードを行う。これは数秒程度で終了する。ここでは、vgg16を使っているが、より計算効率の良いresnetを使う等の改善をする等を考えると良いと思う。

```
!cd ssd.pytorch;mkdir weights;cd weights;wget https://s3.amazonaws.com/amdegroot-models/vgg16_reducedfc.pth
```
プログラムを起動するとcocoファイルを勝手に見に行くのでそのファイルをコピーしておく。

```
!mkdir /root/data/coco
!cp  ssd.pytorch/data/coco_labels.txt /root/data/coco/
```
##プログラム実行
ラーニングレート(lr)は大きいと損失の値がnanになってしまう。このため、1e-3から1e-5に落として処理する。これは、ハイパーパラメータの値が不適切なために発生するためであり調整する必要がある。[^1][^2] また、iterは、[config.py](https://github.com/amdegroot/ssd.pytorch/blob/master/data/config.py)に設定されたmax_iterまで実行される。変更が必要な場合、[config.py](https://github.com/amdegroot/ssd.pytorch/blob/master/data/config.py)を編集する必要がある。暫定コードでは、iter数を削減している。

```
!cd ssd.pytorch;python train.py --dataset VOC --dataset_root ~/data/VOCdevkit --batch_size 16 --lr 1e-5
```

##推論の場合
推論は、300秒程度で終わるので容易に実行できる。ここでは、すでに提供されている学習済みモデルを使って行う。

```
!cd ssd.pytorch/weights;wget https://s3.amazonaws.com/amdegroot-models/ssd300_mAP_77.43_v2.pth
```
評価は以下のようにして行える。

```
!cd ssd.pytorch;python eval.py
```
#環境設定(mlperfのSSDの場合)
Google Colabでは、Tesla T4が使えるが、最大利用可能時間が12時間である。mlperfのSSDは、COCOという大規模データを使っていることもありデフォルト設定では800時間近い計算時間が必要である。なお、mlperfのSSDでは、チェックポイント機能により、学習モデルを読み書きできる機能があるのでそれでやるのが現実的かもしれない。とはいえ、GPGPUの計算は、より性能の高いGPGPUで行うことを薦める。
##環境設定
mlperfのソースコードをダウンロードする。

```
!git clone https://github.com/mlperf/training
```
Microsoft COCOのデータセットをダウンロードし展開する。大体30分程度かかる(帯域10MBの場合)。なお、ファイル展開時そのままJupyterにログ出力をすると、大変遅くなる。このためログをファイルに出力している。

```
!training/single_stage_detector/download_dataset.sh >& download.log
```
次に、ロギングツール[mlperf_compliance](https://github.com/mlperf/training/tree/master/compliance/mlperf_compliance)と、TensorCoreを活用する[NVIDIA/apex](https://github.com/NVIDIA/apex)をインストールする。

```
!pip3 install mlperf_compliance
!git clone https://github.com/NVIDIA/apex
!cd apex;pip3 install -v --no-cache-dir --global-option="--cpp_ext" --global-option="--cuda_ext" .
```
##プログラムの実行
1エポック1時間程度(Tesla T4で2773.34秒/Tesla P100で2645.56秒)かかった。デフォルトは800エポックなので、実行すると1月程度(720時間)かかると思われる。参考までに、V100だと1450秒程度かかる。

```
!python training/single_stage_detector/ssd/train.py --epochs 2
```

#その他
##DockerでのDataLoader利用の注意
DataLoaderでnum_workersを1以上に設定して、マルチプロセスを利用すると、[共有メモリ(/dev/shm)を用いてデータがやり取りされる](https://pytorch.org/docs/stable/multiprocessing.html#sharing-strategies)。共有メモリのサイズは、Dockerだと、[64MBが標準](https://docs.docker.com/engine/reference/run/)だが、Google Colabでは6GBとなっている(df -h /dev/shmで確認できる。)。この共有メモリ枯渇により、[DataLoaderの処理が失敗する](https://github.com/pytorch/pytorch/issues/2244)場合がある。この回避策は2つある。

- docker runの時点で/dev/shmを拡張する。(該当コンテナの拡張[`--shm-size=""`](https://docs.docker.com/engine/reference/run/#runtime-constraints-on-resources)もしくはホストメモリの利用[`--ipc=host`](https://docs.docker.com/engine/reference/run/#ipc-settings---ipc))
- DataLoaderのnum_workers設定を0にして、シングルプロセスで動かす。

前者の場合、データローダを含んだ処理性能が高いままである。
後者の場合、データローダも同一プロセスで動かすため、処理が2倍以上遅くなる。

なお、/dev/shmは以下で監視できる。

```
while true; do df -h /dev/shm | tail -1;sleep 1s; done
```


#参考資料
## ソースコード
+ [ssd.pytorch](https://github.com/amdegroot/ssd.pytorch)
   + [ssd.pytorch](https://github.com/sakaia/ssd.pytorch)
      + そのままでは動かないので、一部Issueの記事の議論からパッチを追加したもの
      + [StopIteration ERROR during training #214](https://github.com/amdegroot/ssd.pytorch/issues/214)
      + [Fix .data[0] and tensor index problem #322](https://github.com/amdegroot/ssd.pytorch/pull/322)
   + [ssd.pytorch/data/config.py](https://github.com/amdegroot/ssd.pytorch/blob/master/data/config.py)
       + SSD用のデータセット(VOC/COCO)の設定を記載したコード


+ [mlperf/training/single_stage_detector/](https://github.com/mlperf/training/tree/master/single_stage_detector)
  + [NVIDIA/apex](https://github.com/NVIDIA/apex)

+ [Pascal VOC mirror](https://pjreddie.com/projects/pascal-voc-dataset-mirror/)

##論文
- [SSD: Single Shot MultiBox Detector](https://arxiv.org/abs/1512.02325)
   - [Review: SSD — Single Shot Detector (Object Detection)](https://towardsdatascience.com/review-ssd-single-shot-detector-object-detection-851a94607d11)
- [Speed/accuracy trade-offs for modern convolutional object detectors](https://arxiv.org/abs/1611.10012)
- [Object Detection in 20 Years: A Survey](https://arxiv.org/abs/1905.05055)
[^1]: [(StackOverflow)Common causes of nans during training](https://stackoverflow.com/questions/33962226/common-causes-of-nans-during-training)
[^2]: [Dealing with NaNs](http://deeplearning.net/software/theano/tutorial/nan_tutorial.html#dealing-with-nans)