ペンギン分類器をつくってみた（前編）

# はじめに
この記事は[SRA Advent Calendar 2018](https://qiita.com/advent-calendar/2018/sra) の22日目の記事です。
産業第2事業部の廣瀬です。SRAの本社界隈で機械学習勉強会をやってます。

## ペンギン分類器
ペンギン画像をCNNで学習させ、画像からペンギンの種類を推定する分類器を作りました。
といってもペンギンは6属18種[^1]もあるので、ここは代表的な[^2]、画像を集めやすい下記6種類のペンギンのみに絞りました。

![penguins.PNG](https://qiita-image-store.s3.amazonaws.com/0/143174/50395279-49e7-1be5-4ce7-18a50d6302e6.png)

なので、正確にはペンギン6種分類器です。
本記事はペンギン分類器を作る過程の前半、データ準備についてまとめています。

[^1]: 17種とも19種とも言われる。諸説ある。
[^2]: 独断です。エンペラー属とアデリー属は全種入っているのに、マカロニペンギン属がひとつも入っていないとか、かなり偏ってます。

# おおまかな流れ
このような手順で分類器を作成しました。
 1. 画像の収集
 2. 収集画像を物体検出でピン画像にわける
 3. ピン画像をラベルづけ
 4. 画像のリサイズ
 5. CNNで学習させる

本記事では4まで行います。

# 画像の収集
別記事になっていますのでこちらをご覧ください。
[Bing画像検索API v7で画像を収集する](https://qiita.com/hirosemi/items/086f8375fd0f3fa5237b)

やっていることは下記の通り。

1. Bing 画像検索APIでキーワードから画像のURLを取得する。
 + 一度のAPIで取得できる結果数に限りがあるので、要求する数を満たすまで複数回APIを投げて取得
2. 画像URLに順次アクセスして画像をダウンロードする。
 + 同じ画像が別URLで入っている場合があるので画像のhash値をファイル名にして保存

# 収集画像を物体検出でピン画像にわける
いざ大量のペンギン画像を集めてきましたが、画像にはペンギン以外もいろいろ写っているので、なるべくペンギン単体を切り抜きたいと思いました。
例えば、猫の画像とかだったら、OpenCVで猫の顔を検出することもできるのですが[^3]、ペンギンの検出はありません。あたりまえです。
なので、YOLOv2アルゴリズムをつかった[YAD2K:Yet Another Darknet 2 Keras](https://github.com/allanzelener/YAD2K)の物体検出を使い、検出された画像を切り抜くことにしました。

[^3]: https://github.com/opencv/opencv/blob/master/data/haarcascades/haarcascade_frontalcatface.xml

YAD2Kのモデル作成まではこちらの記事を参考にさせていただきました。
[YOLOv2(Keras / TensorFlow)でディープラーニングによる画像の物体検出を行う](https://qiita.com/yampy/items/7a705acf4c6899bc11c7)

モデル model_data/yolo.h5  まで作ったら、YAD2Kの test_yolo.py をちょっといじったコードで画像を切り抜きを行います。
切り抜きに使ったコードは[こちら](https://github.com/hirosemi/penguins_classfications/blob/master/cut_yolo.py)にあげておきます。

使い方は

```bash
$ python3 cut_yolo.py model_data/yolo.h5 -t ターゲット画像の入ったディレクトリ -o 切り抜き画像出力ディレクトリ | tee -a ログファイル名
```

この切り抜きしょり、たまに特定のファイルでエラーが起きて落ちることがありました。なので、切り抜き対象の画像をファイル名でソートして1ファイルずつ作業し、処理が終わった画像ファイル名はログファイルに吐き出すようにしました。
落っこちた場合は、ログから終了したファイルのリストを作り、それを-skp オプションにわたしてスキップするようにしています。

```bash
$ grep filename adelie.log | sed 's/filename=//g'  > adelie_end.txt
$ python3 cut_yolo.py model_data/yolo.h5 -t ../bing/test/images2/adelie_penguin_20180127023044/ -o ../images/penguins/YOLO/public/adelie_penguin -skp adelie_end.txt| tee -a adelie.log
```

スマートなやり方ではありませんが、そこは前処理なので、運用でカバー。

# ピン画像をラベルづけ
切り抜いた画像を見るとけっこう混乱してました。

- YAD2Kでペンギン以外の物体が切り抜かれている
 + たまたま写っていた人など
 + 元の写真がペンギンのぬいぐるみや置物など
- ペンギンの種類が間違っている
 + エンペラーペンギンのキーワードで引っ掛けたのに、写っているのはキングペンギン
 + ペンギンの親とひなは外見が大きく違うので、別ラベルにしたい
 + 特にエンペラーペンギンはひなと親の差が大きいので、親のみの画像に絞った。
 + 親とひなが一緒に切り抜かれている画像も弾いた。

エンペラーペンギンという名前になっているキングペンギンが多かったです。世の中のひとはエンペラーとキングの見分けをつけていないらしい。

このラベルづけ作業は正直大変でした。キーワードごとに1300枚から2000枚くらいの画像を切り抜いていたので、9000枚くらいの画像をひたすらわける。つらみ。

# 画像のリサイズ
切り抜いた画像はサイズも形もまちまちなので128x128にリサイズしました。
縦横比率を変えたくないので、あらかじめ正方形になるように足りない部分を黒でpaddingしてからリサイズしています。
リサイズに使ったコードも一応[こちら](https://github.com/hirosemi/penguins_classfications/blob/master/resize.py)に。

```bash
$ python3 resize.py リサイズ対象ディレクトリ
```

resizeというディレクトリに対象ディレクトリ名で書き出します。

# 用意できた画像枚数
これまでの苦労（主にペンギン手動分類）を通して得られた画像の枚数は以下のようになりました。

| 種類 | Bingで取得した枚数 | 最終的な枚数 |
|:-:|:-:|:-:|
| エンペラーペンギン  | 782  | 562  |
| キングペンギン  | 859  | 1123  |
| アデリーペンギン  | 833  | 854  |
| ジェンツーペンギン  | 806  | 769  |
| ヒゲペンギン  | 868  | 854  |
| イワトビペンギン  | 929  | 830  |

エンペラーが特に少ないのは、ヒナが多かったからです。ヒナかわいいけど、親と姿が違いすぎる。
各ペンギン500枚ずつの画像を使って分類器を作ることにします。

分類器実装については後編に続く。

# 参考資料

- [YOLOv2(Keras / TensorFlow)でディープラーニングによる画像の物体検出を行う](https://qiita.com/yampy/items/7a705acf4c6899bc11c7)
- [TensorFlowによるももクロメンバー顔認識（前編）](https://qiita.com/kenmaz/items/4b60ea00b159b3e00100)