「Object Detection API」で物体検出の自前データを学習する方法（TensorFlow 2.x版）

# 「Object Detection API」に関して
　ディープラーニングで物体検出を行う際に、GoogleのTensorFlowの「Object Detection API」を使用して、自前データを学習する方法です。以下の記事のTensorFlow 2.x版となります。
[TensorFlowの「Object Detection API」で物体検出の自前データを学習する方法](https://qiita.com/karaage0703/items/76ef6b774e3cd69028bb)

　基本的な流れはTensorFlow 1.xと同じです。公式のマニュアルはかなりあっさりしていて上級者向きなので、この記事では、仮想環境と拙作のObject Detection API用のソフト「Object Detection Tools」を使って比較的安全かつ簡単に学習を実現する方法を解説していきます。

　手っ取り早く物体検出のデモを動かしたいという人は、以下記事を参照ください。
[TensorFlowでの物体検出が超手軽にできる「Object Detection Tools」をTensorFlow 2.xに対応しました](https://qiita.com/karaage0703/items/8c3197d11f61812546a9)

# 物体検出の自前データの学習方法
　自前データの学習は、以下の4つのステップとなります。

- 教師データの準備
- アノテーション
- 学習
- テスト

　順に説明していきます。

　環境としてはローカルのMac/Linux PCを想定しています。

　Google Colabで手軽に試してみたいという方は以下リポジトリが参考になると思います（カメラを用いたリアルタイムな物体検出はできません）。

[Kazuhito00/Tensorflow2-ObjectDetectionAPI-Colab-Hands-On](
https://github.com/Kazuhito00/Tensorflow2-ObjectDetectionAPI-Colab-Hands-On)

## 教師データ準備
　まずは教師データの準備をします。静止画か動画を準備しましょう。以下では動画を例にして説明しますが、基本的に静止画でもやることは同じです。

　適当な動画がない方は、以下にフリー素材へのリンクを貼りますので、ご使用下さい。

[フリー素材](https://github.com/karaage0703/ssd_keras/raw/master/free_data/lonestartx_free.mp4)

　提供元は「[変デジ研究所](https://lab.hendigi.com)」という素晴らしいサイト様です。カメラ好きの方は是非訪問しましょう。


## アノテーション
### アノテーション準備
　アノテーションは、教師データにラベル付けしていく作業です。多くのソフトがありますが、ここでは、「Object Detection API」で使用されるTFRecordsという形式のファイルを直接エクスポートできることから、Microsoft社のVoTTという無料のソフトを使用します。

　VoTTは、以下リンクからダウンロードできます。

[microsoft/VoTT(Releases)](https://github.com/Microsoft/VoTT/releases)

　OSに対応したバイナリファイルをダウンロードしましょう。私はMacなので、現状の最新バージョンの`vott-2.2.0-darwin.dmg`をダウンロードしました。以下は、Macの画面で説明していきますが、他のOSでも流れは同じと思います。

　起動画面は以下です。

![vott_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81919/e8c7baf3-e30d-61e4-007e-34f4fc9f80e3.png)

　「新規プロジェクト」をクリックします。以下の表示がでます。


![vott_03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81919/6d8e2069-787a-ce84-ac4d-55bbf06c4540.png)


　最初に教師データの場所の設定と、アノテーションしたデータの場所の設定を行います。上記画面真ん中の右端の「Add Connection」というボタンをクリックすると新たな画面が出ます。

　設定は以下の通りにします。

-  「表示名」に好きな名前を入力
-  「プロバイダー」は、「ローカルファイルシステム」を選択
-  「フォルダーパス」に、アノテーションしたい教師データ（先ほどの動画）の入ったフォルダを設定

　以下の図は設定例です。

![vott_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81919/2ecee5fd-2fc1-ae98-e22d-5274eaaadbc4.png)

　ここで、最初の画面に戻り、以下の画面の様に「ソース接続」と「ターゲット接続」に先ほど設定した名前を選択します。

![vott_04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81919/7167f182-0ed1-95fe-7868-c1912468a036.png)


　下の方に行き、「プロジェクト作成」をクリックします。タグは後から追加できるので、ここは空欄でOKです。

![vott_05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81919/be5db6f5-7939-4a53-c347-ccc29c4fcdcf.png)


### アノテーション作業
　ここから、いよいよアノテーション作業です。タグは、右の「TAGS」という表示の右の「+」をクリックすると追加できます。

![vott_06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81919/7581b0c4-2445-8eef-53bc-a95d29aaa6d7.png)


　対象を囲ってひたすらタグ付けしていきます。タグは、右側の対応する数字を入力するとタグ付けできます。
![vott_07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81919/e5e03980-24e7-7894-b75f-b321f2cc2b68.png)

　タグ付け終わったら、データをエクスポートします。エクスポート前に「エクスポート設定」をしましょう。左のアイコンの右斜め上方向の矢印のアイコンをクリックします。プロバイダーは「TensorFlow レコード」、アセットの状態は「タグ付きアセットのみ」にしておきましょう。以下は設定した図です。

![vott_08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81919/96843251-471e-8a24-c308-d7564f553afb.png)

　最後にエクスポートします。エクスポートは、タグ付け作業をする画面の、右上の斜め矢印のアイコンをクリックして下さい（Command or Control + EでもOK）。最初に設定したフォルダにデータがエクスポートされます。

### アノテーションしたデータの確認
　tfrecordは、データがシリアライズされているので、中身の確認がそのままでは難しいのですが、[tfrecord-viewer](https://github.com/sulc/tfrecord-viewer)を使うと、ブラウザでアノテーションした内容が手軽に確認できます。

　使うためには、まず必要なライブラリをインストールします。

```sh
$ pip install flask 
$ pip install pillow
```

　GitHubからリポジトリをcloneして使います。オリジナルのものはTensorFlow2.xに対応していないので、私がforkして修正したものを使用します（といっても1行変えただけです）。

```sh
$ git clone -b tf2 https://github.com/karaage0703/tfrecord-viewer
$ cd tfrecord-viewer
$ python tfviewer.py ../data/train/*.tfrecord
```

　コマンド実行すると、サーバが立つので、ブラウザで`localhost:5000`にアクセスすると以下のようにアノテーション情報が表示されます。

<img width="836" alt="tfrecord.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81919/9438fdf5-8b31-312d-30d9-74407b12c184.png">


## 学習
### 学習環境構築
　pyenv/pyenv-virtualenvを使ってPython環境をセットアップします。以下記事を参考にpyenv/pyenv-virtualenvのセットアップを実施下さい。

[pyenv/pyenv-virtualenv/Anacondaを使ってクリーンなPython環境をセットアップ](https://karaage.hatenadiary.jp/entry/2016/04/04/073000)

　pyenv/pyenv-virtualenvは必須ではありませんが、Pythonのバージョンやライブラリの関係でハマる可能性が高いので（私はよくハマります）、何らかの仮想環境を使うことをおすすめします。

　pyenv/pyenv-virtualenvをセットアップしたら、以下コマンドを実行して物体検出（Object Detection）に必要な環境を用意します。

```sh
$ pyenv install 3.7.3
$ pyenv virtualenv 3.7.3 od
$ pyenv global 3.7.3/envs/od
```

　Protocol Buffersをインストールします。以下はMacの例です。

```sh
$ curl -OL https://github.com/protocolbuffers/protobuf/releases/download/v3.8.0-rc1/protoc-3.8.0-rc-1-osx-x86_64.zip
$ sudo unzip -o protoc-3.8.0-rc-1-osx-x86_64.zip -d /usr/local bin/protoc
$ rm -f protoc-3.8.0-rc-1-osx-x86_64.zip
```

　Linux(Ubuntu)の場合は、以下コマンドでセットアップできると思います。

```sh
$ curl -OL https://github.com/google/protobuf/releases/download/v3.2.0/protoc-3.2.0-linux-x86_64.zip
$ unzip protoc-3.2.0-linux-x86_64.zip -d protoc3
$ sudo mv protoc3/bin/* /usr/local/bin/
$ sudo mv protoc3/include/* /usr/local/include/
$ rm -rf protoc3 protoc-3.2.0-linux-x86_64.zip
```

　Protocol Buffersをセットアップしたら、以下を実行しておいて下さい。

```sh
$ cd && git clone --depth 1 https://github.com/tensorflow/models
$ cd ~/models/research
$ /usr/local/bin/protoc object_detection/protos/*.proto --python_out=.
```

　続いて必要なライブラリのセットアップをします。

```sh
$ cd ~/models/research
$ cp object_detection/packages/tf2/setup.py .
$ python -m pip install .
```

　自分の環境では、TensorBoardのインストールに失敗したので、もう一度以下コマンドでインストールし直しました。

```sh
$ pip install tensorboard
```

　ここで、Object Detection APIに必要な環境が正しくセットアップできているかテストを行います。以下コマンドを実行して下さい。

```sh
$ cd ~/models/research
$ python object_detection/builders/model_builder_tf2_test.py
```

　以下のようにOKが出たら確認は完了です。

```
OK (skipped=1)
```

### 「Object Detection Tools」を使った学習
　拙作のソフト「Object Detection Tools」（Object Detection APIでの学習や推論を楽にするツール群です）を使って学習をしていきます。公式のマニュアルは設定ファイルの作成など、かなり煩雑なので、本記事の通りに実施すると、比較的楽に学習できます。

　最初に、以下コマンドで「Object Detection Tools」をダウンロードします。

```sh
$ cd ~/models/research
$ git clone https://github.com/karaage0703/object_detection_tools
```

　次に、教師データをコピーします。以下のような構成になるように、`data`ディレクトリ直下にラベルファイル(`tf_label_map.pbtxt`)、`train`、`val`ディレクトリ以下にtfrecordファイルを格納して下さい。valは検証用のファイルです。`train`と`val`の割合はとりあえず 7:3くらいにしておいて下さい。

```
object_detection_tools/
└── data
    ├── change_tfrecord_filename.sh
    ├── tf_label_map.pbtxt
    ├── train
    │   ├── xxx.tfrecord
    │   ├── xxx.tfrecord
    │   ├── ...
    └── val
        ├── xxx.tfrecord
        ├── xxx.tfrecord
        ├── ...
```

　tfrecordファイル名を、スクリプトで連番になるように変換します。

```sh
$ cd ~/models/research/object_detection_tools/data
$ ./change_tfrecord_filename.sh
```

　学習済みのモデルをダウンロードします。学習済みモデルを使用するのは、今回、学習時間を短縮するため、転移学習（Fine Tuning）を行うためです。ゼロから学習する場合は、学習済みモデルのダウンロードは不要です。

```sh
$ cd ~/models/research/object_detection_tools/models
$ ./get_efficientdet_d0_coco17_tpu-32.sh
```

　次に、使用するconfigファイル`~/models/research/object_detection_tools/config/efficientdet_d0_coco17_tpu-32.config`を必要に応じて修正して下さい。特に`num_classes`はクラス数になるので、アノテーションしたラベルの数に合わせて修正して下さい。最低限試すだけなら、`num_classes`のみの修正でもOKです。

　その他は様々な設定項目がありますが、data_augmentation_optionsでデータの水増しをする機能を使えます。詳細は、以下記事参照下さい。

[フリー素材で遊びながら理解するディープラーニング精度向上のための画像データ水増し（Data Augmentation）手法](https://karaage.hatenadiary.jp/entry/2019/07/22/073000)

　また、転移学習が不要な場合は `fine_tune_checkpoint`の行を削除しておいて下さい。

　設定が完了したので以下コマンドで学習させます。学習前に、学習したモデルを保存するディレクトリ（下記コマンドだと`saved_model_01`）は、空にしておくか削除しておいて下さい。

```sh
$ cd ~/models/research
$ python object_detection/model_main_tf2.py --pipeline_config_path="./object_detection_tools/config/efficientdet_d0_coco17_tpu-32.config" --model_dir="./saved_model_01" --num_train_steps=10000 --alsologtostderr
```

　しばらく学習が進んだら、TensorBoardで学習した結果を可視化します。--logdirで先ほど指定したモデルを保存するディレクトリを選択します。

```sh
$ tensorboard --logdir='saved_model_01'
```

　ブラウザで`http://localhost:6006/`にアクセスします。以下の様に、lossが下がって入れば、学習が進んでいることになります。

<img width="640" alt="tfboard.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81919/af139d1f-248c-df31-3cd6-33c3d546218a.png">

 lossが下がらない場合は、一度学習データなどを見直してみましょう。

 lossが下がらない理由は以下などです。

- データが全く足りない
- Export SettingsのAsset Stateが「Only visited Assets」（一度確認したデータのみを使用する）になっており、タグ付けされていないデータが大量に含まれている

### 学習したモデルファイルとラベルデータを変換
　学習済みモデルの情報が保存されているckptファイルを、推論に使用するsaved model形式の学習モデルに変換します（TensorFlow1.xで使われていたFreezeGraph形式は、TensorFlow 2.xでは非推奨です）。以下コマンドを実行します。configファイルは学習の際に使用したものを使います。

```sh
$ cd ~/models/research
$ python object_detection/exporter_main_v2.py --input_type image_tensor --pipeline_config_path ./object_detection_tools/config/efficientdet_d0_coco17_tpu-32.config --trained_checkpoint_dir ./saved_model_01 --output_directory ./exported_graphs
```

　続いて、以下コマンドでラベルデータをpbtxtファイルから、シンプルなラベルファイルに変換します。

```sh
$ cp object_detection_tools/scripts/convert_pbtxt_label.py ./
$ python convert_pbtxt_label.py -l='object_detection_tools/data/tf_label_map.pbtxt' > ./exported_graphs/labels.txt
```

参考：[Exporting a Trained Model(TensorFlow 2 Object Detection API Tutorial)](https://tensorflow-object-detection-api-tutorial.readthedocs.io/en/latest/training.html#exporting-a-trained-model)

## テスト（学習したモデルを使って物体検出）
　saved model形式の学習モデルとラベルファイルが作成できたので、後はこのモデルを使えば推論ができます。推論に関しては、以下記事を参照ください。

[TensorFlowでの物体検出が超手軽にできる「Object Detection Tools」をTensorFlow 2.xに対応しました](https://qiita.com/karaage0703/items/8c3197d11f61812546a9)

　学習の流れで、手っ取り早く推論まで試したい方は、続けて以下のコマンドを実行してください。

　推論のサンプルに必要なので、OpenCVをインストールします。

```sh
$ pip install opencv-python
```

　推論プログラムを実行します。

```sh
$ cd ~/models/research
$ python object_detection_tools/scripts/object_detection_tf2.py -l='./exported_graphs/saved_model/labels.txt' -m='./exported_graphs/saved_model/'
```

　カメラが起動して、認識プログラムが動作します。以下が実行画面例です。

<img width="1072" alt="odt_02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81919/b1dccfc9-aa8f-75ef-04e9-195f2710705a.png">

# 疑問点

## 学習中のエラー
　以下のようなエラーが出てはまっていたのですが、いつの間にか解消していました。再現できないため、原因不明…（心当たりある人は教えてください）。

```
tensorflow.python.framework.errors_impl.InvalidArgumentError: 2 root error(s) found.
  (0) Invalid argument:  assertion failed: [[0.602057695][0.501923203][0]...] [[0.696463704][0.603723407][0.0920351818]...]
```

　追記：1度、極端に学習データを減らしたらうまくいきました。

## TensorBoardに精度を出したい
　lossだけじゃなくて、Precisionを表示させて、IoUとか確認したいのですが、やり方がまだわからないです。調査中。


# まとめ
　Object Detection APIをTensorFlow 2.xで学習する方法をまとめました。TensorFlow 1.xとの後方互換も意識しているため、慣れてないと分かりづらいところもあるように感じました。

　この記事の手順だと、比較的簡単にTensorFlow 2.xでも学習できると思いますので、参考にしてみてください。

<a href="https://github.com/karaage0703/object_detection_tools"><img src="https://github-link-card.s3.ap-northeast-1.amazonaws.com/karaage0703/object_detection_tools.png" width="460px"></a>


# 関連ページ
[TensorFlowでの物体検出が超手軽にできる「Object Detection Tools」をTensorFlow 2.xに対応しました](https://qiita.com/karaage0703/items/8c3197d11f61812546a9)

# 参考リンク
## 公式
- [Object Detection API with TensorFlow 2](https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/tf2.md)
- [Training and Evaluation with TensorFlow 2](https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/tf2_training_and_evaluation.md)

## その他
- https://tensorflow-object-detection-api-tutorial.readthedocs.io/en/latest/index.html
- https://tensorflow-object-detection-api-tutorial.readthedocs.io/en/latest/training.html
- https://towardsdatascience.com/how-to-train-a-tensorflow-2-object-detection-model-25d4da64b817

# 変更履歴
- 2020/08/03 ckptファイルのsaved model形式への変換とテストに関して追記