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



<img width="600" alt="20190812145346.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81919/48af8e37-fe86-df61-7a7b-5e88f77fde8d.png">

# TensorFlow 2.xの対応
　TensorFlow 2.xの場合は以下のページを参照ください。

[「Object Detection API」で物体検出の自前データを学習する方法（TensorFlow 2.x版）](https://qiita.com/karaage0703/items/8567cc192e151bac3e50)

# 「Object Detection API」と「Object Detection Tools」に関して
　ディープラーニングで物体検出を行う際に、GoogleのTensorFlowの「Object Detection API」を使用して、自前データを学習する方法です。

　学習を簡単にするために、自作の「Object Detection Tools」というソフトを活用します。

<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>

　「Object Detection API」と「Object Detection Tools」に関しては、以下記事を参照下さい。

[TensorFlowの物体検出用ライブラリ「Object Detection API」を手軽に使えるソフト「Object Detection Tools」を作ってみた](https://karaage.hatenadiary.jp/entry/2019/05/27/073000)

　「Object Detection Tools」は簡単なスクリプトの詰め合わせなので「Object Detection API」だけで学習するときの参考にもなる記事とは思います。

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

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

　順に説明していきます。

　環境としてはMac環境を想定しています。Linux PCでも基本的にほぼ同じコマンドでOKです。Windows10でもできた人がいたようですが、パスの指定含め色々変えないとダメなようです。オススメはしません。

　また、環境によって`python`を`python3`に`pip`を`sudo pip3`に置き換える必要がありますので、適宜修正下さい。

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

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

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

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


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

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

https://github.com/Microsoft/VoTT/releases

　OSに対応したバイナリファイルをダウンロードしましょう。私はMacなので、現状の最新バージョンの`vott-2.1.0-darwin.dmg`をダウンロードしました。Windowsなら`vott-2.1.0-win32.exe`だと思います。

　起動してNew Projectをクリックします。以下の表示がでます。

![20190429163627.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81919/84d5b2ec-8216-305e-88cb-a266ce0d05c4.png)


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

<img width="600" alt="20190812145337.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81919/262b4264-4570-561e-c212-5f2901f70e0e.png">

　名前（Name）と内容（Description）は好きな内容でOKですが、教師データの場所と、アノテーションしたデータの場所の2種類用意する必要があります。今回は「DeepThereminSource」「DeepThereminTarget」にしてあります。

　「Provider」は「Local File System」を選択して「Folder Path」に、教師データの場所となるフォルダとアノテーションしたデータの場所を指定します。

　教師データの場所となるフォルダには、教師データ（画像 or 動画）を格納しておきましょう。

　ここで、最初の画面に戻り、以下の画面の様にSource ConnectionとTarget Connectionに先ほど設定した名前を選択します。

<img width="600" alt="20190812145402.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81919/8a1c532c-0b3e-3fd7-67e2-b5f06e92d04d.png">

　続いて、下の方のTagsに好きなタグを入力して、「SaveProject」をクリックしましょう。タグは後からでも追加できるので、ここでは空欄でもOKです。

<img width="600" alt="20190812145409.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81919/a4763fd9-6999-a2f0-aa0d-06fb521a6b6b.png">

### アノテーション作業
　ここから、いよいよアノテーション作業です。対象を囲ってひたすらタグ付けしていきます。タグは、右側の対応する数字を入力するとタグ付けできます。

<img width="600" alt="20190812145346.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81919/48af8e37-fe86-df61-7a7b-5e88f77fde8d.png">

　タグ付け終わったら、データをエクスポートします。事前にExport Settingsを以下の様に設定します。Asset Stateは「Only tagged Assets」（タグ付けしたデータのみを使用する）にしておきましょう。

<img width="600" alt="20190814105817.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81919/0f52cf7c-3fc9-c595-a872-a6ff5f11db3d.png">


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

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

　使用方法は、サイトのREADMEを確認下さい。

## 学習

### 学習環境構築
　Pyenv + Anacondaを使ってセットアップしていきます。以下記事を参考にPyenvのインストールをしておいて下さい。

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

　Pyenvインストール後は、以下コマンドを実行して物体検出（Object Detection）に必要な環境を用意します。

　最初にAnacondaをセットアッップして

```sh
$ pyenv install anaconda3-5.1.0 
$ pyenv global anaconda3-5.1.0
$ conda create -n od anaconda
$ pyenv global anaconda3-5.1.0/envs/od
```

　TensorFlowとOpenCVをセットアップします。

```sh
$ pip install tensorflow==1.14.0
$ pip install opencv-python
```

### Object Detection APIの設定とテスト
　Object Detection APIのリポジトリをクローンします。

```
$ cd && git clone https://github.com/tensorflow/models
$ cd ~/models/research
```

　以降の動作は、基本`models/research` 以下のディレクトリで行うことを前提として下さい。

　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-
$ 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
```

　参考にしていた以下サイトはリンク切れ
~~https://gist.github.com/sofyanhadia/37787e5ed098c97919b8c593f0ec44d8~~

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

```sh
$ cd ~/models/research
$ /usr/local/bin/protoc object_detection/protos/*.proto --python_out=.
```

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

```sh
$ cd ~/models/research
$ export PYTHONPATH=$PYTHONPATH:`pwd`:`pwd`/slim
$ python object_detection/builders/model_builder_test.py
```

　最後にOKと出ればOKです。

　ついでに、学習に必要なcocoapiライブラリをインストールしておきます。

```sh
$ pip install git+https://github.com/waleedka/coco.git#subdirectory=PythonAPI
```


### 「Object Detection Tools」を使った学習
　続いて、以下コマンドで「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_ssd_inception_v2_coco_model.sh
```

　次に、使用するconfigファイル`ssd_inception_v2_coco.config`を必要に応じて修正して下さい。特に`num_classes`はクラス数になるので、アノテーションしたラベルの数に合わせて修正して下さい。

　その他、data_augmentation_optionsでデータの水増しをする機能を使えます。詳細は、以下記事参照下さい。

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

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

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

```sh
$ cd ~/models/research
$ export PYTHONPATH=$PYTHONPATH:`pwd`:`pwd`/slim
$ python object_detection/model_main.py --pipeline_config_path="./object_detection_tools/config/ssd_inception_v2_coco.config" --model_dir="./saved_model_01" --num_train_steps=10000 --alsologtostderr
```

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

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

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

<img width="640" alt=" 2019-08-14 17.03.37.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81919/b22ab858-d7cd-4287-5e3c-8f0c6bcceb7e.png">

 以下の様に全く精度が上がらないようなら、一度学習データなどを見直してみましょう。

<img width="640" alt=" 2019-08-14 10.12.23.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81919/92bf090d-e7be-aa0a-6288-f52394399c7e.png">

　ちなみに、上記の場合は、アノテーションの際の
Export SettingsのAsset Stateが「Only visited Assets」（一度確認したデータのみを使用する）になっており、タグ付けされていないデータが大量に含まれていたのが原因でした。

### 学習したモデルファイルとラベルデータを変換
　以下コマンドで、ckptファイルをグラフファイル（pbファイル）へ変換します。configファイルは学習の際に使用したものを使って下さい。モデルファイルは、基本的には一番学習の進んだファイルを使用して下さい。以下は756世代進んだものを使用しているので`model.ckpt-756`を指定しています。

```sh
$ cd ~/models/research
$ export PYTHONPATH=$PYTHONPATH:`pwd`:`pwd`/slim
$ python object_detection/export_inference_graph.py --input_type image_tensor --pipeline_config_path object_detection_tools/config/ssd_inception_v2_coco.config --trained_checkpoint_prefix saved_model_01/model.ckpt-756 --output_directory exported_graphs
```

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

```
$ cp object_detection_tools/scripts/convert_pbtxt_label.py ./
$ export PYTHONPATH=$PYTHONPATH:`pwd`:`pwd`/slim
$ python convert_pbtxt_label.py -l='object_detection_tools/data/tf_label_map.pbtxt' > ./exported_graphs/labels.txt
```

　これで `exported_graphs` ディレクトリにラベルファイル`labels.txt` とモデルファイル `frozen_inference_graph.pb` が生成されました。

## テスト（学習したモデルを使って物体検出）
　以下実行すると学習したモデルを使って物体検出ができます。

```sh
$ cd ~/models/research
$ python object_detection_tools/scripts/object_detection.py
```

　以下は、公開されている学習済みモデルで実行した様子です

![20190525165632.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/81919/576d590c-bb82-9a07-0150-8b11f40180d5.gif)


　なお、Python、TensorFlowとOpenCVがセットアップされている環境だったら、先ほど生成したラベルファイル`labels.txt` とモデルファイル `frozen_inference_graph.pb`さえあれば、物体検出が実現できます。

　具体的には、以下で物体検出のプログラムをダウンロードして、同じ場所にラベルファイルとモデルファイルを格納します。

```sh
$ wget https://raw.githubusercontent.com/karaage0703/object_detection_tools/master/scripts/object_detection.py
```

　あとは、以下コマンドでラベルオプションと、学習済みモデルを指定して、プログラムを実行するだけでOKです。

```sh
$ python object_detection.py -l='labels.txt' -m='frozen_inference_graph.pb'
```

　学習モデルはpbファイルなので、ARMコアのJetsonでも動作します。Jetson Nanoでの環境構築から物体検出まで行う方法は、以下記事参照下さい。
[Jetson Nanoでリアルタイムに物体検出をする方法（TensorFlow Object Detection API/NVIDIA TensorRT）](https://qiita.com/karaage0703/items/67050f2418aa6bb3851a)

　またモデルを変えると、様々なものが認識できるようになります。以下は、手を検出するモデルの例です。
[ディープラーニングで高性能な手の検出器を簡単に作る方法](
https://qiita.com/karaage0703/items/962c550868b9383e78c7)


# まとめ
　「Object Detection API」と自作ツールの「Object Detection Tools」で物体検出の自前データを学習する方法について書きました。「Object Detection API」で学習する記事は多くありますが、TFRecordの変換やパスの指定でハマることが多かったので、極力ハマらない方法に仕上げました。Object Detection APIでハマっている人の一助になれば幸いです。

　ディープラーニングに関しては、ブログで以下の様に優良なチュートリアルをまとめたりしていますので、興味ある方はこちらもご覧下さい。

[手を動かしながら学べるディープラーニングの優良なチュートリアル](https://karaage.hatenadiary.jp/entry/2018/10/29/073000)


<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>

# 参考リンク
- https://ghlinkcard.com
- https://tensorflow-object-detection-api-tutorial.readthedocs.io/en/latest/index.html
- https://github.com/tensorflow/models/blob/master/research/object_detection/g3doc/installation.md
- https://qiita.com/sanoyo/items/1a5c4e8671203d190fca
- https://devtalk.nvidia.com/default/topic/1049646/jetson-nano/jupyter-notebook-in-jetson-nano/
- https://qiita.com/PINTO/items/c35535432a5a021ca1bb
- https://qiita.com/PINTO/items/8a91d79abe6e939ef01c
- https://qiita.com/PINTO/items/6eb6de95e3cda0e09c84
- https://qiita.com/tsutof/items/0328ede42d509eb247ad
- https://medium.com/nanonets/how-to-automate-surveillance-easily-with-deep-learning-4eb4fa0cd68d
- https://medium.com/nanonets/how-to-easily-detect-objects-with-deep-learning-on-raspberrypi-225f29635c74
- https://medium.com/coinmonks/build-a-diy-security-camera-with-neural-compute-stick-part-1-805a1b1596b0
- https://qiita.com/IchiLab/items/fd99bcd92670607f8f9b
- https://qiita.com/IchiLab/items/8ad0cdb9c2006f416c3b
- https://gist.github.com/NobuoTsukamoto/ba7028bd4e77c1431f4ff560ad61e8d7

# 変更履歴
- 2020/07/30 誤記訂正・追記
- 2020/07/20 TensorFlow 2.x対応に関して追記