【物体検出】AI開発者になりたいけど餅つきをしたくない

# きっかけ
「[AI開発チームが「餅つき」をする理由](http://www.nhk.or.jp/ohayou/digest/2018/04/0405.html)」を紹介されたので、[将棋AIで学んだ](/kfjt/items/8553cfe6a61dd04b2d0b)次の一歩を物体検出に決めました。
テレビ放送は見てないのでニュアンスは知りませんが記事から「正解を***たくさん教え込まなければならない***ってエキスパートシステム作ってた人の発想か、GAN以前からDLの流れを追い損ねてね？」と印象を受けた。

# 技術調査＠Qiita記事
- [CNNを用いた物体検出アルゴリズムの性能比較](/tokkuman/items/3fabd04a1a524843bea5)
- [「教師なし」でお米](/samacoba/items/f04ed6a3a170fd97cef5)
- [石原さとみ](/harupy/items/5d0a33bb0bf90d043e9c)
- [☆](/samacoba/items/c4dd820648f4271381a0)
- [SSD](/de0ta/items/1ae60878c0e177fc7a3a)
- [転移学習](/icoxfog417/items/48cbf087dd22f1f8c6f4)

# 道具と対象
- Chainer
 - [将棋AIのとき](/kfjt/items/8553cfe6a61dd04b2d0b)に環境整えたし、いまのところ評判良いからChainer
 - [使用フレームワークに制約がないお勉強なら日本製を応援する価値があると思う](http://segafreder.hatenablog.com/entry/2017/08/15/154326)からChainer
- SSD
 - [chainer+BoundingBoxでググった](https://www.google.com/search?q=chainer+BoundingBox)ら一番めYOLO、二番目SSDだった
 - 学習が速そうだからSSD
 - [論文を和訳してみたありがたい方がいらっさる](/de0ta/items/1ae60878c0e177fc7a3a)からSSD
- ChainerCV 
 - 以上を踏まえて[chainer+SSDでググった](https://www.google.com/search?q=chainer+SSD)ら[chainercv](https://github.com/chainer/chainercv)にsampleがありました
- 学習データは手書きの記号にしてみる
 - Pascal VOCの学習済モデルはchainercvのexampleに公開されてるので
 - ひとまずお米と☆を参考に手作りデータでやってみたい

# やること
1. chainercvインストール
1. デモを動かしてみる
1. 学習データをつくる
 1. アノテーションつける←ここを沢山はやりたくない
 1. 学習する

# 環境
```console:再掲
ハード
    CPU:i7-4770S @ 3.10GHz
    GPU:GeForce GTX 750 Ti ドライバーのバージョン391.24
ソフト
    Microsoft Windows 10 Pro (x64)
    Visual Studio Community 2017
    CUDA9.0
    cuDNN7.1.2
    python3.6.4(Anaconda3 5.1.0)
    chainer3.5.0
    cupy2.5.0
```

# さきに結果
- ネットワーク構造の見直しなしです。
- 今回覚えさせた３図形に反応します。
 - [転移学習](https://qiita.com/icoxfog417/items/48cbf087dd22f1f8c6f4#%E3%83%A9%E3%83%99%E3%83%AB%E3%81%8C%E7%95%B0%E3%81%AA%E3%82%8B)がやり方をちゃんと知らないまま出来ちゃったという感じです
- [ソースはこちらです](https://github.com/kfjt/chainer-bbox/)
- [google colab向けnotebookはこちらです](https://github.com/kfjt/chainer-bbox/blob/master/chainercv_bbox.ipynb)

# 試行錯誤と手順
# chainercvインストール
- [参考：本家（Linux版のみ）](http://chainercv.readthedocs.io/en/stable/install.html#anaconda)
- ChainerCVをダウンロード(git|zip)してルートディレクトリへ移動`cd chainercv`しておく

```console:chainercvインストール
>conda env create -f environment.yml
>conda activate chainercv
>pip install -e .
```

# デモを動かしてみる
- 適当な画像を用意しておく（仮にa.jpgとしてコマンド実行する）
 - [pixabay](https://pixabay.com/)でとんかつとかプチトマトとか検索しとけばよいかと
 - 残念ながらダイニングテーブル、鉢植えなどざっくりした分類しか学習していない

```console:a.jpgに対してデモ実行
>python examples\ssd\demo.py --gpu 0 a.jpg
```

# 【参考】トレーニング実行

```console:愛機ではOutOfMemory
>python examples\ssd\train.py --model ssd300 --batchsize 32 --gpu 0
```
```sh:代わりにgoogleにやらせる
!apt -y install libcusparse8.0 libnvrtc8.0 libnvtoolsext1
!ln -snf /usr/lib/x86_64-linux-gnu/libnvrtc-builtins.so.8.0 /usr/lib/x86_64-linux-gnu/libnvrtc-builtins.so
!pip install 'cupy-cuda80==4.0.0rc1' 'chainer==4.0.0rc1'
!pip install -U numpy
!pip install chainercv
!git clone https://github.com/chainer/chainercv.git
!python chainercv/examples/ssd/train.py --model ssd300 --batchsize 32 --gpu 0
```
# 学習データをつくる
- ペイントで丸、三角、バツを書く
- バウンディングボックスにアノテーションをつける
- [VGG Image Annotator](http://www.robots.ox.ac.uk/~vgg/software/via/)ツール
 - 導入が一番簡単でHTMLファイル１つで構成されています
 - Region Shapeがいろいろ選べるのですが、PASCAL VOCに寄せてRectangleにしておく
 - 出力がCSV|JSON、できたらXML（[PASCAL VOC format](http://host.robots.ox.ac.uk/pascal/VOC/)）がよかった
 - ![viaでアノテーションをつける](https://qiita-image-store.s3.amazonaws.com/0/244948/aaae1a4a-28b0-4488-8919-f44c4da5e94b.png)

### 学習する
- 【コード改変】trainとdatasetを変更する
 1. アノテーションの記録、読み取り方法：XML→JSON（ツールの都合）
 1. ラベル：車とか人とか12種類のなんか→丸、三角、バツの3種
 1. PASCAL VOCからダウンロードする→用意した画像を読む
 1. 画像がJPG→PNG
 1. アノテーションのファイル数：画像ファイルと同じ→ひとファイル（ツールの都合）
- 学習しようとするとOutOfMemoryになる
- バッチサイズを小さくしてみる

```console:バッチサイズ指定
>python train.py --gpu 0 --batchsize 6
```

- やっぱり[google](https://colab.research.google.com/)にやらせる
- ソースと画像はzipにしてgoogle driveにuploadしておく

```sh:driveからソースと画像を取得する
# さっきの続き
!pip install -U -q PyDrive
from google.colab import auth
from pydrive.auth import GoogleAuth
from pydrive.drive import GoogleDrive
from oauth2client.client import GoogleCredentials
auth.authenticate_user()
gauth = GoogleAuth()
gauth.credentials = GoogleCredentials.get_application_default()
drive = GoogleDrive(gauth)
id = 'ファイルの共有をしてIDを確認する、ここは内緒'
downloaded = drive.CreateFile({'id':id})
downloaded.GetContentFile('chainer-bbox.zip')
!mkdir chainer-bbox
!unzip chainer-bbox/chainer-bbox.zip -d chainer-bbox
!ls chainer-bbox
```

```python:学習実行
import os
os.chdir('chainer-bbox')
!python train.py --gpu 0
```
- モデル保存が随時のsnapshotでもいいけど回数ついてるし扱い面倒いので最終的なモデルをnpzシリアライズ追加
- あとgoogleでも長いこと終わらないのでイテレート回数減
- で改めて学習開始

### 検証する
- 【コード改変】demo.pyでつかうラベルの名前を丸、三角、バツにする
- いったん、学習データを丸だけに絞ってみる。かつひとファイルにひと丸だけにしてみる。
- で検出。![検出できた、もどき](https://qiita-image-store.s3.amazonaws.com/0/244948/6f56cb56-955f-a950-8e0b-3826baaa8a15.png)
- これ失敗してます。原因は思い当たってます。やはり、学習データのつくりの問題。アノテーションで与えたバウンディングボックス領域が大きすぎました。
- で再度学習やりなおし。
 - ![丸をひとつ検出できた](https://qiita-image-store.s3.amazonaws.com/0/244948/3b06eda3-3a0c-cd11-2d5d-e413262a961d.png)
 - ![丸をふたつ検出できた](https://qiita-image-store.s3.amazonaws.com/0/244948/7e673a61-20e9-31c0-e936-01e319ee8745.png)
- よし、できた。。。丸だけ。
- 丸の学習データ12枚、検証用4枚の計16枚。ここまで餅つきはしてない。


# 学習データを餅つきする（準備のみ）
- 使わなくなったけど学習の待ちの間に餅つきのスクリプト書いたので公開します。
 - 使い方：バッチファイルにPNGファイルをドロップする。
 - または`for /f %%a in ('dir /b *.png') do call _rotate %%a`で一括処理できる。とおもう。
 - 結果：元画像を90度、180度、270度、および水平反転で8倍に増やしてくれます。

```sh:_rotate.bat
if "x"=="x%1" goto usage

call :write_ps %1
call :run_ps
call :del_ps

goto :eof

:write_ps
>%~dpn0.ps1 echo ^
[void][Reflection.Assembly]::LoadWithPartialName("System.Drawing"^);^
$image = New-Object System.Drawing.Bitmap("%1"^);^
$image.RotateFlip("Rotate90FlipNone"^);^
$image.Save("%~dpn1_r90%~x1", [System.Drawing.Imaging.ImageFormat]::Png^);^
$image.RotateFlip("Rotate90FlipNone"^) ;^
$image.Save("%~dpn1_r180%~x1", [System.Drawing.Imaging.ImageFormat]::Png^);^
$image.RotateFlip("Rotate90FlipNone"^);^
$image.Save("%~dpn1_r270%~x1", [System.Drawing.Imaging.ImageFormat]::Png^);^
$image.RotateFlip("Rotate90FlipX"^);^
$image.Save("%~dpn1_x0%~x1", [System.Drawing.Imaging.ImageFormat]::Png^);^
$image.RotateFlip("Rotate90FlipNone"^);^
$image.Save("%~dpn1_x90%~x1", [System.Drawing.Imaging.ImageFormat]::Png^);^
$image.RotateFlip("Rotate90FlipNone"^);^
$image.Save("%~dpn1_x180%~x1", [System.Drawing.Imaging.ImageFormat]::Png^);^
$image.RotateFlip("Rotate90FlipNone"^);^
$image.Save("%~dpn1_x270%~x1", [System.Drawing.Imaging.ImageFormat]::Png^);^
$image.Dispose(^);^

exit /b

:run_ps
powershell -NoProfile -ExecutionPolicy Unrestricted %~dpn0.ps1
exit /b

:del_ps
del %~dpn0.ps1
exit /b

:usage
echo usage: %~nx0 filepath
>nul pause
goto :eof
```

### 検証する、つづき
- よし、引き続き三角とバツもいってみます。それぞれtrain10枚、test5枚ずつ。
- 複合画像、丸を検出、三角は誤検出、バツを見逃し、重なった図形はだめ。
- 三角もバツも単独の画像ならいけますね。
 - ![丸は正解、三角は誤認、バツが無視](https://qiita-image-store.s3.amazonaws.com/0/244948/c991f7bb-1d0a-610b-aaab-541015221df5.png)
 - ![三角も単独ならでる](https://qiita-image-store.s3.amazonaws.com/0/244948/38c7cf28-6ff0-be80-1797-0ed43260af49.png)
 - ![バツも単独ならでる](https://qiita-image-store.s3.amazonaws.com/0/244948/378321c2-3951-db46-e858-5bb0e797e3ab.png)
- なお元のモデルと追加学習したモデルで人と馬の写真を検出させると。これはヒドイ。
 - 事前![事前](https://qiita-image-store.s3.amazonaws.com/0/244948/b8960312-12a2-e3a2-483d-68ffe7c7cf82.png)
 - 事後![事後というか事故ですね](https://qiita-image-store.s3.amazonaws.com/0/244948/177d143f-ec65-1578-220f-68fcb55d46f1.png)
- 学習データにノイズを載せました。
 - ![ノイズ例１](https://qiita-image-store.s3.amazonaws.com/0/244948/6673de1c-630d-596e-02ce-63dd55b2bf68.png)
 - ![ノイズ例２](https://qiita-image-store.s3.amazonaws.com/0/244948/65ec6218-1da9-6faa-5117-e8e9848e3bdc.png)
- バツも検出できました。![バツも検出](https://qiita-image-store.s3.amazonaws.com/0/244948/5302388f-230f-c40d-fdae-2bb67dc10d15.png)
- 写真に図形描画して検出できるようになりました。![写真のなかに図形](https://qiita-image-store.s3.amazonaws.com/0/244948/9158926a-24fc-edcf-9357-20464e6f0368.png)
- 手書きでもう一度![手書き、バツの認識が弱い](https://qiita-image-store.s3.amazonaws.com/0/244948/4ab7867f-ba86-a279-4588-cb7e54146b97.png)

# つぎの一歩
- 気になるキーワード***GAN***

# 技術調査
- [GANでお米カウント](/samacoba/items/f04ed6a3a170fd97cef5)
 - [DiscoGAN_counter](https://github.com/samacoba/DiscoGAN_counter)
- [DiscoGANのchainer実装※お米の記事で参照してるやつ](http://www.monthly-hack.com/entry/2017/03/20/111614)
- [少ないデータで学習](https://qiita.com/icoxfog417/items/48cbf087dd22f1f8c6f4#%E5%B0%91%E3%81%AA%E3%81%84%E3%83%87%E3%83%BC%E3%82%BF%E3%81%A7%E3%81%AE%E5%AD%A6%E7%BF%92)
- [chainercvを用いたMask R-CNNの実装](https://engineer.dena.jp/2017/12/chainercvmask-r-cnn.html)
 - [Chainer_Mask_R-CNN](https://github.com/DeNA/Chainer_Mask_R-CNN)
- [Mask R-CNNのRoI AlignとFast(er) R-CNNのRoI Poolingの違い](/yu4u/items/5cbe9db166a5d72f9eb8)
- [簡単にMask R-CNNを試す方法](https://ai-coordinator.jp/mask-r-cnn)

# どっちにしよう***DiscoGAN*** or ***MASK R-CNN***
- どちらもChainer実装があるので愛機環境ですぐ動きそう
- githubの最終更新はDiscoGANは2017年、MASK R-CNNは5日前
- クラス分けまであるしMASK R-CNNをためします

## それはまた別のお話

おわり