yolo+画像分類で料理物体検出

#今回のデータ
[git](https://github.com/watanabehrts/ProjectX-FoodsDetection)
[学習検証用データ](https://drive.google.com/open?id=1F8hbp5OGM5syGDQLpFPxbP1n9yFz804K)

#はじめに
　定食の料理などを物体検出して個々の料理名を摘出してみたいと考えたので料理の物体検出に取り組んでみました。
　今回は料理の細かな種類を分けるのに物体検出だけだと難しいと考えたので、物体検出+画像分類と二つのCNNを利用することで料理の細かい検出ができるのではないか？と考えたので「料理の検出」と「料理画像の分類」を分けて行ってみることにしました。今回はその独自のデータセットでの物体検出について説明していこうと思います。

　使用したネットワークは、

- **yolo(darknet)**
- **VGG-16**

　の二つです。

#Google Colaboratory
　今回学習に使用した主な環境としてGoogle Colaboratory(以下colab)を利用させていただきました。colabとはPythonの実行環境で無料でGPUを使わせてくれます。ローカルに学習できるGPUの環境がない場合に学習に時間がかかってしまうので、環境がない場合にとても便利です。機械学習で必要な環境も整っているので環境構築に時間も取られません。
　しかし連続稼働時間が12時間でランタイムが切断されるとColaboratory上でのファイル等もリセットされてしまいます。Colaboratoryについて詳しくは[環境構築不要でPython入門！Google Colaboratoryの使い方を分かりやすく説明](https://cpp-learning.com/python_colaboratory/)などをご覧ください。

#環境
　上述の通り今回は主にColaboratoryを利用しましたが、学習に使用するための前処理はcolab上で行うと時間がかかってしまうので、データの前処理だけローカルの環境で行い、学習時にcolabにアップロードして学習するという方法で行いました。実際に今回使用した実行環境は

- **Ubuntu 14.04**
- **Colaboratory**

となっています。

#YOLO
　まず初めにYOLOでの物体検出のための学習を行い実際に物体検出を行っていきます。

###データセット作成
　YOLOで学習するためには学習する画像とその画像の座標データがラベルとなっているデータセットが必要となっています。今回は独自のデータを学習するため自らラベルを付けていかないといけません。手動でラベルを付けてもいいのですが、それだととても時間がかかってしまう為、今回はYOLO用の学習データセット作成ツールの[labelImg](https://github.com/tzutalin/labelImg)を使わせていただきました。

[labelImg](https://github.com/tzutalin/labelImg)の使い方は[Yolo学習用データセットの作成ツール：labelImg](http://demura.net/misc/14350.html)を参考にすると効率よく進めることができます。

ちなみに今回料理物体検出をするために学習に必要な作成したデータセットは、

- ご飯
- 味噌汁
- 焼き魚
- サラダ
- ラーメン

の5つとなっています。1つのクラスにつきおよそ700枚ほどラベル付けを行いました。精度を高めたい場合は1クラスにつき1000枚程はラベル付けを行い、用意した方がいいと思われます。

###学習するための準備
　今回はdarknetのyoloを利用して学習を行っていくので、学習を行う為に前処理を行っていきます。

**1. darknetのインストール**
  　学習に必要なdarknetをダウンロードしていきます。darknetの公式サイトは[こちら](https://pjreddie.com/darknet/yolo/)になります。ダウンロードは公式サイトに従い、

```
$ git clone https://github.com/pjreddie/darknet.git
$ cd darknet
```
としてダウンロードを行い、darknetフォルダに移動します。

**2. 作成したデータセットを格納**
 　 [labelImg](https://github.com/tzutalin/labelImg)で使用した画像と作成したラベルデータの入っているtxtデータの二つがあると思います。この二つを学習のためdarknetに移動させてきます。画像とtxtデータの階層は、

>darknet
>>data
>>> images
>>>>images001.jpg
>>>>images002.jpg
>>>>....

>>> labels
>>>>images001.txt
>>>>images002.txt
>>>>....

となります。複数のクラスをlabelImgで作成した場合でも他のクラスと同じimagesとlaelsのフォルダに格納します。

**3. trainとtestに分ける**
　先程dataに格納したデータセットを訓練データとテストデータに分けなければいけません。darknetで訓練データとテストデータに分けるのも、手動でやるととても時間がかかってしまうので[process.py](https://github.com/wakuphas/wakuphas/blob/master/AI/Scripts/process.py)を使わせていただきます。このソースコードを使うと

```
percentage_test = 20;
```
に従って画像をランダムにtest20%,train80%でデータが分けられ、それらのパスが「test.txt」「train.txt」というファイル名で出力されていきます。この「process.py」をdarknet/dataの中に入れます。その後、

```
$ python process.py
```

と実行すると「train.txt」「test.txt」が作成されます。もし訓練とテストデータの割合を変えたい場合は先ほどの「percentage_test = 20;」を好きな値に変更してみてください。

**4. 学習の前処理**
　学習を行う為にdarknet内の様々なパラメータを変更していきます。

**・クラス名の設定**
　学習を行うクラスの名前をdarknet上で設定していきます。obj.namesとnames.listという名前のファイルに学習するクラス名を1行ずつ記載していきます。フォルダの構造は

>darknet
>>data
>>> images
>>>>obj.names
>>>>images001.jpg
>>>>images002.jpg
>>>>....

>>> labels
>>> names.list

となります。今回使用した料理検出の場合、

```obj.names/names.list
rice
miso soup
griled fish
salad
noodle
```

と記述していきます。

**・クラスの数とデータのパスの設定**
　yoloの前処理の一つとしてクラスの数やデータのパスについて設定する必要があります。cfg/obj.dataの中身を変更していきます。obj.dataの中身は

```obj.data
classes=5 #クラスの数
train = data/images/train.txt 
valid = data/images/test.txt 
labels = data/images/obj.names 
backup = backup/
```

とそれぞれ先ほど作ったファイルのパスを入力していきます。

　次に使いたいversion毎にパラメータを変更していきます。yoloのv2を使いたい方はdarknet/cfg/yolov2-voc.cfgを、v3を使いたい方はdarknet/cfg/yolov3-voc.cfgをcfg内に複製してください。そのごyolo-obj.dataを開き

```
3行目 batch=64
4行目 subdivision=8
　　　classes=5(クラスの数)
      filters=35(yolov2の場合(classes + coords + 1) * 5)
                (yolov3の場合(classes + +5) * 3)
```

と変更してください。

**・バウンディングボックスの検出について**
　今回yoloで一品毎の料理を検出し、その後別のネットワークを用いて細かく料理を検出していきたかったので、個々の料理を検出していくために出力のバウンディングボックスを検出する必要があります。バウンディングボックスの検出についてはdarknet/src/image.cを[こちら](https://drive.google.com/open?id=1-w1cuRGQF1cOBnv0WqFwLN1L-njqG8hN)に差し替えてください。.txtにて座標データが出力されるようになっています。

**・初期の重み**
初めての学習の際は適当な重みをダウンロードしようすることで学習結果が収束しやすいとのことです。学習の初期の重みについては
yolov2の初期の重みファイル
[https://pjreddie.com/media/files/darknet19_448.conv.23](https://pjreddie.com/media/files/darknet19_448.conv.23)
yolov3の初期の重みファイル
[https://pjreddie.com/media/files/darknet53.conv.74](https://pjreddie.com/media/files/darknet53.conv.74)
こちらからダウンロードし、darknetフォルダ内に入れてください。

###colaboratory上に移行して学習と検出
　ここまで来たらようやく前処理が終了し、いよいよcolaboratory上にて学習等を行っていきます。

**1.フォルダのアップロード**
　初めに今回前処理を行ってラベル付けや設定を行ったdarknetフォルダをアップロードしていきます。  colaboratoryは12時間制限がありcolaboratory上のファイルなどは全てリセットされてしまいます。なのでcolab上に直接アップロードするのではなく、Google Driveにアップロードし、colab上でdriveにマウントすることで、毎回アップロードする手間を省きます(1回のアップロードに相当の時間がかかると思うので...)。  
　なのでgoogleアカウントのログインをして使用する**drive上にdarknetフォルダ毎アップロード**してください。圧縮してからアップロードを行ってcolab上で解凍することもできるのですが、今回使用したcolab用のコードには解凍のコードを載せていないので、そのままソースを利用する場合は圧縮せずにアップロードをお願いいたします。

**2.colab上にて学習、検出**
　学習と検出は[こちら](https://github.com/watanabehrts/ProjectX-FoodsDetection)の**ソースコードを.ipyndとしてダウンロードをし、driveにアップロードして起動、上から実行**をすることで学習と物体検出を行うことができるはずです。検出したい画像はdarknetフォルダの中にいれてソースコード上の「物体検出の実行」という部分でファイルのパスと名前を変更してください。  
(colab上でダウンロードしているcuda等のバージョンの関係でできなくなっているかもしれません。その場合は最新版に変更してpip等を行ってください)   
driveにマウントする関係上driveの配置などで人それぞれファイルのパスは変わってきますので、**ファイルのパスについては十分注意してください！**  

また学習時に出力される学習の重みは自動的にdarknet/backupに保存されています。一度学習した重みを再度学習したい場合は「yoloにて学習」の部分の重みのパスを自分の**buckup/yolo-XXX.weights**に変更してください。

###検出された画像をトリミング
　先ほどのipyndのソースコードの続きを実行していくとトリミングされた画像が保存されていきます。実行すると物体検出時に出力したバウンディングボックスの座標を用いて、物体検出に使用した画像からその座標の場所がトリミングされimageフォルダ内に画像が保存される構造となっています。

#個々の料理の学習
　今回はUEC-FOOD100というデータセットを用いて転移学習を行ったVGG-16を利用して学習を行って画像分類を行いました。基本的にはソースコード通りで上から実行していけば使用できます。ただ今回は料理の検証用で転移学習をしてしまったので、0から学習する方は、それ用のデータセット作成などが必要になってくるのでご了承ください。また、今回自分たちの作成した重みで検証したい場合は、ファイルパスに要ご注意ください。

#実際の検出結果
　自分たちの料理の物体領域検出となります。初めにloss(損失関数)です。
<img width="748" alt="キャプチャ.JPG" src="https://qiita-image-store.s3.amazonaws.com/0/329424/d7cbdf46-c697-ad2e-5ee5-b6369c509243.jpeg">
　こんな感じとなりました。学習回数は4073回でlossの値は「0.09」となりました。0.06くらいまでは下がるとのことだったのでもう少し学習を回しても良かったのかなといった感じです。

次に実際に画像を入力して物体検出を行ったのがこちらです。

![図1.jpg](https://qiita-image-store.s3.amazonaws.com/0/329424/63fdf34d-c555-a87d-a974-fa896f4f46b5.jpeg)

悪くはないのかな？といった感じの例です。riceとmisosoupとgrilled fishと今回学習したラベルがしっかり検出できたのは良かった例だと思います。これらをVgg-netの転移学習を用いてさらに細かく検出した結果が次になります。

![図2.png](https://qiita-image-store.s3.amazonaws.com/0/329424/857a1a0a-6a96-db28-2a89-9ec816629bc6.png)

![図3.png](https://qiita-image-store.s3.amazonaws.com/0/329424/38f81e82-41e8-e801-a6fc-275cbde4ac77.png)

![図4.png](https://qiita-image-store.s3.amazonaws.com/0/329424/695423a2-db8d-2540-0801-9ac1754e42e2.png)

このような感じとなりました。riceと表現した物体領域検出から白米として認識され直しているので、今回目的の大まかな領域検出から細かい種類の分類は成功していると思われます。ただ最後のgrilled fishはオムライスといった全く違う検出結果となってしまっています。


#学習済みの重みを使って検証だけしてみたい場合
上記の料理物体検出結果の重みを利用して検出だけ行ってみたいといった場合は
[https://drive.google.com/open?id=1F8hbp5OGM5syGDQLpFPxbP1n9yFz804K](https://drive.google.com/open?id=1F8hbp5OGM5syGDQLpFPxbP1n9yFz804K)
このデータを解凍しdriveにアップロードして物体検出.ipyndを上から実行してください。自分たちが学習を行った料理の物体検出が行えると思います。またこれも**ファイルパスは自分で変更しないといけないと思うのでご注意ください！**

#参考文献
猫と宇宙と音楽と　Google Colab上でdarknet（YOLO）を使って物体を数える【画像認識】
[https://wakuphas.hatenablog.com/entry/2018/09/19/025941#Google-Colaboratory%E3%81%A8%E3%81%AF](https://wakuphas.hatenablog.com/entry/2018/09/19/025941#Google-Colaboratory%E3%81%A8%E3%81%AF)
darknet公式サイト
[https://pjreddie.com/darknet/yolo/](https://pjreddie.com/darknet/yolo/)
YOLO V3：オリジナルデータの学習
[http://demura.net/misc/14458.html](http://demura.net/misc/14458.html)
#