手作業から解放！　－オートアノテーションツールの開発ー

※こちらは[Pythonデータ分析勉強会#07](https://pythondata.connpass.com/event/121688/)の発表資料です。

オートアノテーションツールを作ってみました。
このツールを使えば、人間は**画像を見ることなく**、アノテーション作業ができちゃいます。

![ダウンロード.png](https://qiita-image-store.s3.amazonaws.com/0/264781/4602cc2a-73a2-1412-c03a-779e4421ea24.png)

#アノテーション作業とは
ここでいうアノテーション作業とは、物体検出で行われる作業のことです。

物体検出の学習データでは、物体の位置を示す必要があり、一般的にマウスで
ドラッグさせる作業が発生します。

![vxjwy-qkgb4.gif](https://qiita-image-store.s3.amazonaws.com/0/264781/2b08c513-8133-476c-b3f6-5b58e357cc5f.gif)


上の動画のように、ひたすら、ただひたすら、ドラッグし続けます。

この作業、多くの問題があります。

+ 手作業の時間が長い
過去に[こちらの記事](https://qiita.com/shinmura0/items/f818bcbb92d5fff5279d)でアノテーション作業を行いましたが、画像400枚に対し
手作業で4時間かかっています。これが1000枚、5000枚になると何十時間も
かかってしまいます。

+ 精神的ダメージが大きい
作業中の精神状態は良くありません。「このデータ量で足りるだろうか？」
「そもそも物体検出なんて無理じゃないか」などとマイナスのことを考えがちです。
作業後の疲労感が半端ないです。

そこで、本稿ではアノテーション作業を自動化する「オートアノテーションツール」を
作ってみました。（もう誰かがやっているかもしれませんが。。。）

#オートアノテーションツールを使った作業の流れ

+ ラベル付きの画像を集める。
+ 集めた画像を分類できる畳み込みニューラルネットワーク（CNN）を用意する（学習させる）。
+ オートアノテーションツールで画像にアノテーションを付ける。
+ アノテーション結果が正しいかどうか画像を見ながらチェックし、的外れなアノテーションした場合は削除する。
+ 最後に、アノテーションに関するデータを保存する。

#使い方
全体は[github](https://github.com/shinmura0/Auto-Annotation)に置きました。

前段にcifar-10で動かしてみた結果を示しています。
興味ある方は実行してみてください。15分くらいかかります。

後段は[YOLOv3-keras](https://github.com/qqwweee/keras-yolo3)用にアノテーションデータを出力する
コードが書かれています。

ダウンロードしていただき、Colaboratoryに持って行ってポチポチ実行して
いけば、使い方は分かると思います。一部変更すればJupyter Notebookでも
動くと思います。

#アルゴリズム
基本的にGrad-CAMをベースに作っています。

![ダウンロード (1).png](https://qiita-image-store.s3.amazonaws.com/0/264781/c6206b46-4ad8-4784-bad0-54ea0fd65823.png)

+ ステップ0
あらかじめCNNを学習させておきます。

+ ステップ1
画像を用意し、Grad-CAMでヒートマップを取得します。

+ ステップ2
取得したヒートマップを二値化します(Binary map)。

+ ステップ3
Binary mapにベイズ最適化を適用し、イイ感じにボックスを決めさせます。

##ステップ0
自作でも転移学習でも良いですが、認識させたい画像をある程度正確に分類できるCNNを
用意します。ポイントは**Grad-CAMで可視化したときに物体全体に反応する**CNNを用意することです。

##ステップ1とステップ2
Grad-CAMはCNNがどこを見て判断したのかを示すヒートマップを出してくれます。
コードは以下の記事を参考にしています。
https://qiita.com/haru1977/items/45269d790a0ad62604b3

そのヒートマップに閾値（デフォルトで0.3）を設け、二値化しています。

##ステップ3
ボックスの最適な形状は、ベイズ最適化で見つけています。
ベイズ最適化は以下の記事を見れば、イメージが掴めるかと思います。
https://qiita.com/shinmura0/items/2b54ab0117727ce007fd

本当は総当たりでボックスの探索できれば良いのですが、あまりにも
時間がかかるので、ベイズ最適化で効率的に探索させています。

中ではこんな形で色々なボックスを試しています。
![k_means (1).gif](https://qiita-image-store.s3.amazonaws.com/0/264781/858c6056-bfdd-11dd-6786-2dbaea76e54a.gif)

ベイズ最適化では、評価関数を用意しないといけません。
今回は、IOUを評価関数にしています。IOUは以下の記事を参考にしてください。
https://meideru.com/archives/3538#i-2

要は、IOUを評価関数にすることで、過不足なくBinary mapの赤い部分を囲わせることができます。

##学習用画像の制約
このツールでは二種類の画像が必要です。

+ あらかじめ学習させておくCNNの学習用画像
+ アノテーション用の画像

どちらも同じデータの使い回しで良いのですが、画像の構図について以下の制約があります。

+ 一枚の画像に**物体検出させたい物が一個だけ**の構図
+ 写真の構図をコントロールできる工業製品などに向いている

もちろん、YOLOv3などの物体検出に組み込んでしまえば、一枚の画像から複数個の物体検出が可能となります。

逆に以下の条件のものは適していません。

+ 一枚の画像に**物体検出させたい物が複数個**含まれる構図
+ 景色の写真など色々なものが写っている画像は不得意

色々なものが写っていても、「物体検出させたい物が一個だけの構図」であれば適用できます。

#動作速度
このツールは画像一枚に対し、20秒ほど時間がかかります。
内訳はGrad-CAMで5秒、ベイズ最適化で15秒ほどです。

ベイズ最適化の速度は以下の``` max_iter```の値を小さくすれば早くなりますが、精度と速度の
トレードオフの関係になっているので、お気を付けください。

```py
    myBopt.run_optimization(max_iter=10)
```

#ツールの出力結果
このツールでは、以下のファイルが出力されます。

+ xmlファイル
+ jpgファイル
+ trainval.txtファイル

これらはYOLOv3-keras用のファイルとなっていますが、最新のM2Det用に
改造できると思うので、興味ある方は試してみてください。

xmlファイルの取り扱いは、以下の記事を参考にさせていただきました。
https://qiita.com/icchi_h/items/44abb5a8147b1ef7475f


#実験
##自前の画像で実験
今回、用意した画像の内訳は以下のとおりです。

||ナットの画像|ボルトの画像|何も映っていない画像|
|---|---|---|---|---|
|数量|30|30|10|

![ダウンロード.png](https://qiita-image-store.s3.amazonaws.com/0/264781/ca5175de-6443-8352-c605-9e310f3ae6a6.png)

これらを分類するCNNでは、DataAugmentationを行い7000枚近くの画像に
水増しして学習させています。

そして、せっかくアノテーションデータを用意したので、YOLOv3(tiny)で学習させてみます。
学習のさせ方は[こちら](https://qiita.com/shinmura0/items/dc0d02dc1e716b912932)の記事を参考にしてください。

###アノテーション結果
最初にアノテーション結果を示します。
![ダウンロード.png](https://qiita-image-store.s3.amazonaws.com/0/264781/fe864030-fe5a-94ec-12e7-0897fa961083.png)
![ダウンロード (1).png](https://qiita-image-store.s3.amazonaws.com/0/264781/6931def9-d329-59db-ea9f-e0521b7f8929.png)
おおむね良好な結果ですが、ボルトのボックスが小さい傾向にあります。

###YOLOv3(tiny)の物体検出結果
個人的にやってみたい実験があります。
それは、「学習データの数と検出精度」の関係を見てみるということです。

ディープラーニングなので、当然、**データ数が多いほど正確な検出をしてくれる**
とは思いますが、何事も実験してみたいもので、今回やってみました。

この実験は、手作業のアノテーションでは膨大な時間がかかって無理でしたが、
オートアノテーションツールを使えば比較的簡単にできます。

オートアノテーションツールでは、DataAugmentationしながらデータを増やすので、
元の画像をある程度用意できれば、良質なデータを無限に作ることができます。

早速、結果を見ていきましょう。

+ データ数が100の場合

![out.jpg](https://qiita-image-store.s3.amazonaws.com/0/264781/1bc8fb92-6324-bba6-59b6-009aa27bd975.jpeg)

ナットは検出できましたが、ボルトは全く検出できませんでした。
ナットのバウンディングボックスも大きめな印象を受けます。

+ データ数が500の場合

![out.jpg](https://qiita-image-store.s3.amazonaws.com/0/264781/58912f61-9ec2-9348-d7bc-8b762b8919b4.jpeg)

ナットのバウンディングボックスは絞られてきた印象です。
ボルトは検出できるようになりましたが、検出漏れがあります。

+ データ数が1000の場合

![out.jpg](https://qiita-image-store.s3.amazonaws.com/0/264781/21a6935d-6618-9781-ff2d-1d92ab49c9fa.jpeg)

データ数が500の場合とほぼ同じ結果です。

+ データ数が2500の場合

![out.jpg](https://qiita-image-store.s3.amazonaws.com/0/264781/5684e362-03f4-751d-6bd4-122a405eea48.jpeg)

**精度が劇的に向上しました！**

ナットのバウンディングボックスは丁度良いサイズになり、そのスコアも上がっています。
さらに、ボルトも5本中4本は検出できました。

ちなみに、val lossは以下のとおりになりました。

|データ数|100|500|1000|2500|
|---|---|---|---|---|
|val loss|7.847|7.041|6.608|6.716|

データ数が1000と2500の間に大きな壁があると思うのですが、数値的には
大きな差は見られませんでした。

##cifar-10で実験
最後にcifar-10で実験した結果を示します。
###アノテーション結果
![ダウンロード (1).png](https://qiita-image-store.s3.amazonaws.com/0/264781/b6f4fc95-be28-2c28-6289-db81bd76dbc9.png)
![ダウンロード (2).png](https://qiita-image-store.s3.amazonaws.com/0/264781/2749930b-3f71-84cf-302d-248b77d76fd5.png)
![ダウンロード.png](https://qiita-image-store.s3.amazonaws.com/0/264781/524022ac-b83e-8ac1-4170-0e8c2340c889.png)
アノテーションされる箇所として、鳥は頭、車はボンネット、船は船橋になる傾向です。

#まとめ
+ オートアノテーションツールは**全自動で**アノテーション作業をしてくれる。
+ ただし、学習用画像に制約があり、ある程度誤差もある。
+ 主題からは逸れるが、「学習データの数と検出精度の関係」を確認する実験を行った。その結果、**学習画像が多いほど正確な検出**をしてくれることが分かった。