YoloV2をラズパイに突き刺したNCSで動かしたい

#目標
YoloV2をラズパイに突き刺したNCSで動かすことが目標。本当はyolov2を動かしたかったが、実験したところ、yolov2では内部でConvolution1×1を使用しており、NCSではConvolution1×1に対応していないっぽくてエラーになるのと、なんとか通過させてもNCSを動かすときにメモリを食い過ぎるのかタイムアウトエラーになって予測できなかった。結局このページではtiny-yolov2を動かす。ただ、それだけでは精度がイマイチなのでtiny-yolov2のパラメーターを少し増やして若干の精度向上を図る。

#前提
caffeのインストールが完了していること。
ncsdkのインストールは完了していること。
yolov2(darknet)のビルドが完了していること。
ubuntu16.04で実行。
ラズパイ2を使用。

#大筋の流れ
1. yolov2の本家サイトから、tiny-yoloのcfgファイルとweightファイルをダウンロードする
2. 1.でダウンロードしたファイルを変換ツールでcaffeモデルのファイルに変換する
3. caffeのファイルをNCS用ファイルに変換する
4. 3.のファイルをNCSに取り込む
5. NCSでの動作確認をする
6. tiny-yoloのパラメーターを変更して再学習、精度向上
7. 再学習したデータでNCSでの動作確認をする


#やり方
##1.変換用ツールのダウンロードとビルド
```
git clone https://github.com/duangenquan/YoloV2NCS.git
cd YoloV2NCS
※32bit環境でビルドするときはYoloV2NCS/src/PythonWrapper.cppのlong buflenをint buflenに編集する。
make
```

##2. Yoloの本家サイトからyolo-voc.weightとyolo-voc.cfgをダウンロードする
yolo-voc.cfgファイルとyolo-voc.weightsファイルをダウンロードする。
ダウンロードしたファイルはYoloV2NCS/models/yolomodelsの下に保存する。
https://github.com/pjreddie/darknet/blob/master/cfg/tiny-yolo-voc.cfg
https://pjreddie.com/media/files/tiny-yolo-voc.weights

##3. caffe用モデルファイルに変換する
```
cd YoloV2NCS/models
```
YoloV2NCS/models/convertyo.shを開いて
filename=tiny-yolo-vocをfilename=yolo-vocに変更する
convertyo.shは中で
../python/create_yolo_prototxt.pyと
../python/create_yolo_caffemodel.pyを呼び出している。
python3で動かす場合、
../python/create_yolo_caffemodel.pyはprint文を()付きに直すだけでOK。
../python/create_yolo_prototxt.pyは内部で
ConfigParserをimportしているが、これはpython2.7じゃないと何かエラーになる。
../python/create_yolo_prototxt.pyはcaffeがなくても動くスクリプトなので
自分の環境ではconvertyo.shを以下のように書き換え、
../python/create_yolo_prototxt.pyはpython2.7で、
../python/create_yolo_caffemodel.pyはpython3で動かしている。

```
python ../python/create_yolo_prototxt.py $yolocfg $yolocfgcaffe
python3 ../python/create_yolo_caffemodel.py -m $yolocfgcaffe -w $yoloweight -o 
$yoloweightcaffe
```
yoloのモデルファイルをcaffeモデルに変換する。

```
./convertyo.sh
```

実行時、Region layer is not supported"みたいなエラーが出ても無視してよい。
コマンド終了後、YoloV2NCS/models/caffemodels
にyolo-voc.prototxtとyolo-voc.caffemodelができていれば成功。

#4.NCS用ファイルを作成する
```
mvNCCompile ./caffemodels/yolo-voc.prototxt -w ./caffemodels/yolo-voc.caffemodel -s 12
```
カレントディレクトリにgraphファイルが吐かれます。

##5.NCSで動かす
graphファイルを../detectionExample/にコピーし、detectionExampleに移動してMain.pyを実行する。

```
cp ./graph ../detectionExample/
cd ../detectionExample/
python3 Main.py --image ../data/dog.jpg
```
以下の画像が表示されれば成功。
![test.jpg](https://qiita-image-store.s3.amazonaws.com/0/228050/76a7e67e-9f5c-99a9-0541-c4e612791c13.jpeg)
車の矩形が複数あるのはtiny-yolo-v2の精度の問題。これはパラメーターを改善することで以下のように改善できる。tiny-yoloのパラメーターの変更と再学習については後述する。
![test.jpg](https://qiita-image-store.s3.amazonaws.com/0/228050/6e9e3059-07a0-a784-e218-a727dd3bfa7a.jpeg)


※同じ学習データをを本家のdarknetで実行すると以下のように表示され、微妙に矩形の位置が異なる（自転車のハンドルの高さあたり、車の上下あたりなど）。ObjectWrapper.pyのどこかに不良があるみたいだけど、原因がわかりません。誰かわかったら教えてください。
![predictions.jpg](https://qiita-image-store.s3.amazonaws.com/0/228050/5018aa58-212a-0ec3-d587-90378d35a0a6.jpeg)

##6.tiny-yolov2のパラメーター変更と再学習
https://pjreddie.com/darknet/yolo/
に記載されているが、手順をまとめておく。
以下を実行して、学習用の画像データをダウンロードする。

```
wget https://pjreddie.com/media/files/VOCtrainval_11-May-2012.tar
wget https://pjreddie.com/media/files/VOCtrainval_06-Nov-2007.tar
wget https://pjreddie.com/media/files/VOCtest_06-Nov-2007.tar
tar xf VOCtrainval_11-May-2012.tar
tar xf VOCtrainval_06-Nov-2007.tar
tar xf VOCtest_06-Nov-2007.tar
```
解凍したデータはdarknet/scriptsの下に全部置く。
ラベル生成用のツールをダウンロードする

```
wget https://pjreddie.com/media/files/voc_label.py
```
ダウンロードしたvoc_label.pyもscriptsの下に置く。
以下を実行して、ラベルを生成する。

```
python voc_label.py
```
以下を実行して各ラベルを一つのマージする。

```
cat 2007_train.txt 2007_val.txt 2012_*.txt > train.txt
```
cfg/voc.dataを編集する。

```
classes= 20(分類するオブジェクトの種類の数。今回は変更なし)
train  = <path-to-voc>/train.txt(学習用データ)
valid  = <path-to-voc>2007_test.txt(検証用データ)
names = data/voc.names(ラベルの名前)
backup = backup(学習済みデータの保存先。darknetコマンドの下にbackupができる)
```
tiny-yolo-voc.cfgを編集し、フィルター数を変更する。変更箇所には→を記述している。

```
[net]
batch=32
subdivisions=8
width=416
height=416
channels=3
momentum=0.9
decay=0.0005
angle=0
saturation = 1.5
exposure = 1.5
hue=.1

learning_rate=0.001
max_batches = 160200→学習回数変更
policy=steps
steps=-1,100,20000,30000
scales=.1,10,.1,.1

[convolutional]
batch_normalize=1
filters=32→フィルター数を16→から32に変更
size=3
stride=1
pad=1
activation=leaky

[maxpool]
size=2
stride=2

[convolutional]
batch_normalize=1
filters=64→フィルター数を32→から64に変更
size=3
stride=1
pad=1
activation=leaky

[maxpool]
size=2
stride=2

[convolutional]
batch_normalize=1
filters=128→フィルター数を64→から128に変更
size=3
stride=1
pad=1
activation=leaky

[maxpool]
size=2
stride=2

[convolutional]
batch_normalize=1
filters=256→フィルター数を128→から256に変更
size=3
stride=1
pad=1
activation=leaky

[maxpool]
size=2
stride=2

[convolutional]
batch_normalize=1
filters=512→フィルター数を256→から512に変更
size=3
stride=1
pad=1
activation=leaky

[maxpool]
size=2
stride=2

[convolutional]
batch_normalize=1
filters=1024→フィルター数を512→から1024に変更
size=3
stride=1
pad=1
activation=leaky

[maxpool]
size=2
stride=1

[convolutional]
batch_normalize=1
filters=1024
size=3
stride=1
pad=1
activation=leaky

###########

[convolutional]
batch_normalize=1
size=3
stride=1
pad=1
filters=1024
activation=leaky

[convolutional]
size=1
stride=1
pad=1
filters=125
activation=linear

[region]
anchors = 1.08,1.19,  3.42,4.41,  6.63,11.38,  9.42,5.11,  16.62,10.52
bias_match=1
classes=20
coords=4
num=5
softmax=1
jitter=.2
rescore=1

object_scale=5
noobject_scale=1
class_scale=1
coord_scale=1

absolute=1
thresh = .6
random=1
```

※あまりパラメーター数を増やすと、NCS実行時にタイムアウトを起こす。上に記したパラメーターでNCSを実行すると推測に450ms秒くらいかかる。変更しない場合は250msくらいかかる。

変更後、以下を実行すると学習が始まる。GTX1060で２日くらいかかる。

```
./darknet detector train cfg/voc.data cfg/tiny-yolo-voc.cfg ./tiny-yolo-voc-final.weights
```
学習データは随時backupディレクトリに吐かれる。

学習が完了したら、
tiny-yolo-voc-final.weightsをtiny-yolo-voc.weightsにリネームしたものとtiny-yolo-voc.cfgを
YoloV2NCS/models/yolomodelsに上書きして、3.〜5.を再度実行する。
実行したら、以下のようにtiny-yoloでも何とか分類できる。
![test.jpg](https://qiita-image-store.s3.amazonaws.com/0/228050/ac124421-aef9-f73f-c598-85829296b64c.jpeg)