TensorFlow Object Detection APIを使ってFaster R-CNN、R-FCN、SSDを比較してみた

## 5行まとめ

* TensorFlow Object Detection APIには各種モデルが準備されており、簡単に試すことができた。
* SSDは推論がとても早いが学習に時間がかかる。
* R-FCNは推論時間でSSDに劣るが、検出精度がSSDより高め。学習時間と精度のバランスも良い。
* ラベル付けの補助としてR-FCNを使い、推論時間が重要な場面での最終的な検出器としてSSDを使うのが良さそう。
* ラベル付け（アノテーション）は苦行。

## 概要

　[TensorFlow Object Detection API](https://github.com/tensorflow/models/tree/master/research/object_detection)を使い、独自のデータセットで物体検出（Object Detection）を行ってみました。
使用したモデルは、Faster R-CNN、R-FCN、SSDの3つです。本記事では同一のデータセットに対して3つのモデルを適用し、その精度、速度などを比較しています。

　当然ながらデータセットとモデルの組み合わせによって特性は異なるので、「あるデータセットではこうだった」という参考程度にどうぞ。

## 用語

* R-CNN: Region CNN
* R-FCN: Region-based Fully Convolutional Networks
* CNN: Convolutional Neural Network
* SSD: Single Shot multibox Detector
* mAP: mean Average Precision
* IoU: Intersection over Union

## 環境

　学習、推論に用いた環境は以下の通りです。

* ハードウェア:
    * CPU: Intel Core i5 7600
    * メモリ: 16GB
    * GPU: GeForce GTX1070 8GB
* ソフトウェア:
    * OS: Ubuntu 18.04.3 LTS
    * Docker: 18.09.7
    * docker-compose: 1.24.1
    * nvidia-docker: 2.2.0
    * NVIDIAドライバ: 410.104
    * Python: 3.6.8
    * TensorFlow: 1.14.0
    * TensorFlow Object Detection API: コミットID `62184a96e740a3ecd65018acda42a11062b6d43d`
    * COCO API: コミットID: `636becdc73d54283b3aac6d4ec363cffbb6f9b20`
    * Pythonパッケージ:
        * `Click`: 7.0
        * `contextlib2`: 0.5.5
        * `Cython`: 0.29.13
        * `opencv-python`: 4.1.0.25
        * `Pillow`: 6.1.0

## データセット

　今回使用したデータセットは独自のもので、残念ながら詳細はお伝えできません。規模は以下の通りです。

* ラベル: 2種類
* ラベル数（正解バウンディングボックス数）:
    * ラベルA: 2,224
    * ラベルB: 47
    * 合計: 2,271
* ラベル付けされた画像数（バウンディングボックスなしも含む）: 2,274枚
    * うち、ラベルを含む画像数: 1,212枚
* 学習データ/テストデータの割合: 9:1

　「ラベルB」のラベルが不足しているため十分な学習が行えておらず、平均精度に悪影響を及ぼしているかもしれません。ただ、今回は相対的な精度だけを見ており、絶対的な精度は見ていないのであまり影響ないものと思われます。
また、ラベルを含まない画像（ネガティブサンプル）は使用していません。

　蛇足ながら、ラベル付け（アノテーション）によって腱鞘炎（っぽい状態）になりました。みなさんもラベル付けに使うツール（ソフトウェア、ハードウェア）には十分に気を配り、ラベル付けを行いましょう。健康第一です。

## ベースモデル

　今回はTensorFlow Object Detection APIの[Model Zoo](https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/detection_model_zoo.md)で提供されている、COCOにより事前学習済みのモデルをベースとして使用しました。
ベースとしたモデルデータと設定ファイルは以下の通りです。

| No | モデル | 学習済みモデル | 設定ファイル |
|---:|:---|:---|:---|
| 1 | Faster R-CNN | [faster_rcnn_resnet101_coco](http://download.tensorflow.org/models/object_detection/faster_rcnn_resnet101_coco_2018_01_28.tar.gz) | [`faster_rcnn_resnet101_coco.config`](https://github.com/tensorflow/models/blob/62184a96e740a3ecd65018acda42a11062b6d43d/research/object_detection/samples/configs/faster_rcnn_resnet101_coco.config) |
| 2 | R-FCN        | [rfcn_resnet101_coco](http://download.tensorflow.org/models/object_detection/rfcn_resnet101_coco_2018_01_28.tar.gz) | [`rfcn_resnet101_coco.config`](https://github.com/tensorflow/models/blob/62184a96e740a3ecd65018acda42a11062b6d43d/research/object_detection/samples/configs/rfcn_resnet101_coco.config) |
| 3 | SSD          | [ssd_mobilenet_v2_coco](http://download.tensorflow.org/models/object_detection/ssd_mobilenet_v2_coco_2018_03_29.tar.gz) | [`ssd_mobilenet_v2_coco.config`](https://github.com/tensorflow/models/blob/62184a96e740a3ecd65018acda42a11062b6d43d/research/object_detection/samples/configs/ssd_mobilenet_v2_coco.config) |

## 学習時間/推論時間

　今回はすべてのモデルにおいて30,000ステップの学習を行いました。また、データセットの1割、122枚をテストデータとして推論を行いました。
1回あたり10枚ずつ推論を行い、初回の推論時にはモデルのロードなどの初期化が行われています。そのため、初回を除く推論時間も併記しています。

* SSDの推論時間は、Faster R-CNN、R-FCNの10分の1以下。
* SSDの学習時間は、Faster R-CNN、R-FCNの2倍程度。

| No | モデル | 学習時間 | 推論全体 | 初回を除く110枚の推論 | 左記1枚あたり | 初回を含む122枚の推論 | 左記1枚あたり |
|---:|:---|---:|---:|---:|---:|---:|---:|
| 1 | Faster R-CNN | 155分 | 45.816500秒 | 34.694372秒 | 0.315403秒 | 43.957657秒 | 0.360308秒 |
| 2 | R-FCN        | 146分 | 39.321194秒 | 28.740958秒 | 0.261281秒 | 37.311630秒 | 0.305833秒 |
| 3 | SSD          | 295分 |  3.885033秒 |  1.333236秒 | 0.012120秒 |  2.947371秒 | 0.024158秒 |

## 定量的な精度

　30,000ステップ時点での評価結果は以下の通りです。TensorFlow Object Detection APIの評価方法`coco_detection_metrics`を使用しています。

* Precision:
    * すべての項目においてSSD、R-FCN、Faster R-CNNの順番で精度が良い。
* Recall:
    * SSDの方が基本的に良いが、R-FCNの方が良いこともある。

| No | 項目 | Faster R-CNN | R-FCN | SSD |
|---:|:---|---:|---:|---:|
|  1 | Average Precision  (AP) @[ IoU=0.50:0.95, area=   all, maxDets=100 ] | 0.218 | 0.230 | 0.327 |
|  2 | Average Precision  (AP) @[ IoU=0.50     , area=   all, maxDets=100 ] | 0.419 | 0.439 | 0.717 |
|  3 | Average Precision  (AP) @[ IoU=0.75     , area=   all, maxDets=100 ] | 0.206 | 0.209 | 0.259 |
|  4 | Average Precision  (AP) @[ IoU=0.50:0.95, area= small, maxDets=100 ] | 0.165 | 0.189 | 0.209 |
|  5 | Average Precision  (AP) @[ IoU=0.50:0.95, area=medium, maxDets=100 ] | 0.245 | 0.247 | 0.486 |
|  6 | Average Precision  (AP) @[ IoU=0.50:0.95, area= large, maxDets=100 ] | 0.505 | 0.545 | 0.534 |
|  7 | Average Recall     (AR) @[ IoU=0.50:0.95, area=   all, maxDets=  1 ] | 0.160 | 0.179 | 0.289 |
|  8 | Average Recall     (AR) @[ IoU=0.50:0.95, area=   all, maxDets= 10 ] | 0.271 | 0.396 | 0.375 |
|  9 | Average Recall     (AR) @[ IoU=0.50:0.95, area=   all, maxDets=100 ] | 0.299 | 0.405 | 0.378 |
| 10 | Average Recall     (AR) @[ IoU=0.50:0.95, area= small, maxDets=100 ] | 0.267 | 0.434 | 0.254 |
| 11 | Average Recall     (AR) @[ IoU=0.50:0.95, area=medium, maxDets=100 ] | 0.290 | 0.291 | 0.535 |
| 12 | Average Recall     (AR) @[ IoU=0.50:0.95, area= large, maxDets=100 ] | 0.558 | 0.583 | 0.600 |

## 定性的な精度

　正解データとモデル3つの推論結果の合計4枚を並べ、定性的な精度を確認してみました。数値では示せないので、参考程度にどうぞ。

* 全体:
    * 全体的に検出精度は良好で今回の用途では実用的。
        * 特に対象物が正面から写っている場合は、ほぼ検出できている。
    * 対象物の側面しか写っていない場合は、検出できたりできなかったり。
* Faster R-CNN:
    * もっとも精度が高い印象。
* R-FCN:
    * 擬陽性（誤検出）が他のモデルに比べて多いように感じる。
* SSD:
    * バウンディングボックスが大きめに出力されがち。
    * 偽陰性（検出漏れ）が他のモデルに比べて多いように感じる。
    * 小さい物体の検出精度が低め。

## 付録: Dockerfile

　今回、学習と推論はDocker（`nvidia`ランタイム）上で行いました。`model_builder_test.py`より後のパッケージは推論処理で使っているだけで、TensorFlow Object Detection APIとしては不要です。

```Dockerfile
FROM tensorflow/tensorflow:1.14.0-gpu-py3-jupyter

RUN apt-get update \
  && DEBIAN_FRONTEND=noninteractive apt-get install --yes --no-install-recommends \
    git-core \
    protobuf-compiler \
  && rm --recursive --force /var/lib/apt/lists/*

RUN git clone https://github.com/tensorflow/models.git /opt/tensorflow/models \
  && cd /opt/tensorflow/models/ \
  && git checkout 62184a96e740a3ecd65018acda42a11062b6d43d \
  && cd /opt/tensorflow/models/research/ \
  && protoc --python_out . object_detection/protos/*.proto
ENV PYTHONPATH ${PYTHONPATH}:/opt/tensorflow/models/research
ENV PYTHONPATH ${PYTHONPATH}:/opt/tensorflow/models/research/slim

COPY requirements.txt /root/
RUN pip3 install --requirement /root/requirements.txt

RUN git clone https://github.com/cocodataset/cocoapi.git /opt/cocoapi \
  && cd /opt/cocoapi/ \
  && git checkout 636becdc73d54283b3aac6d4ec363cffbb6f9b20 \
  && cd /opt/cocoapi/PythonAPI/ \
  && make \
  && cp --recursive --verbose pycocotools /opt/tensorflow/models/research/

RUN cd /opt/tensorflow/models/research/ \
  && python3 object_detection/builders/model_builder_test.py

RUN apt-get update \
  && DEBIAN_FRONTEND=noninteractive apt-get install --yes --no-install-recommends \
    libsm6 \
    libxext6 \
    libxrender-dev \
  && rm --recursive --force /var/lib/apt/lists/*
RUN pip3 install opencv-python==4.1.0.25
```

```requirements.txt
Click==7.0
contextlib2==0.5.5
Cython==0.29.13
Pillow==6.1.0
```