Sipeed MaixPyで動くk210モデルを作る

# はじめに
Sipeed M1W dock suitを購入しました。
このキットはカメラがついていて、画像認識のモデルが動くみたいです。
[Train, Convert, Run MobileNet on Sipeed MaixPy and MaixDuino !](https://bbs.sipeed.com/t/topic/682?fbclid=IwAR0oWtaVtcY8fDWPKbn7xwefkw5ztIwOA1ecWk5BqE6Z9EdPpB6ouzbTDvU)
こちらのチュートリアルを見ながら画像認識のモデルを作ろうとしたのですが、色々障害にぶつかったため備忘録として残しておきます。
試した環境はMacOSXです。
#K210Modelとは

>MAIX’s chip K210 use different padding method with Keras’s default padding method, so we need adjust it.
K210 use the padding method that padding zeros all around (left, up, right, down), but Keras default pad right and down.
We use ZeroPadding2D function to set the padding method for Keras:
x = layers.ZeroPadding2D(padding=((1, 1), (1, 1)), name='conv1_pad')(inputs)
add this line before every conv layer which using stride.

翻訳
>MAIXのチップK210は、Kerasのデフォルトの埋め込み方法とは異なる埋め込み方法を使用するため、調整する必要があります。
K210は、パディング方式を使用して、ゼロ（左、上、右、下）全体にゼロをパディングしますが、Kerasのデフォルトでは右と下にパディングします。
ZeroPadding2D関数を使用して、Kerasのパディング方法を設定します。
x = layers.ZeroPadding2D（padding =（（1、1）、（1、1））、name = 'conv1_pad'）（inputs）
ストライドを使用するすべてのconvレイヤーの前にこの行を追加します。

パディング方式が違うらしいです。

# 環境構築
まずは環境構築からです。
チュートリアルでは
`tensorflow/tensorflow:1.13.1-gpu-py3-jupyter`
を使うよう指定されていましたが、
`tensorflow/tensorflow:latest-gpu-py3-jupyter`を使いました。

```bash
docker run -it  --name "tf" -v /Users/ユーザ名/work/maix/docker/:/tf/files  tensorflow/tensorflow:latest-gpu-py3-jupyter /bin/bash
```

# tfliteへの変換
変換にあたり以下２つのリポジトリが必要なので落としてきます

```bash
git clone git@github.com:sipeed/Maix-Keras-workspace.git
git clone git@github.com:sipeed/Maix_Toolbox.git
```

次に、データセットのDLをして学習済みのモデルのHDF5ファイルを作るところですが、
[Maix-Keras-workspace](https://github.com/sipeed/Maix-Keras-workspace)のリポジトリの中に[mbnet_keras.py](https://github.com/sipeed/Maix-Keras-workspace/blob/master/mbnet/mbnet_keras.py)というスクリプトがあり、
これを実行すると[mbnet75.h5](https://github.com/sipeed/Maix-Keras-workspace/blob/master/mbnet/mbnet75.h5)(学習済みのモデル)と
[mobilenet_7_5_128_tf_no_top.h5](https://github.com/sipeed/Maix-Keras-workspace/blob/master/mbnet/mobilenet_7_5_224_tf_no_top.h5)(構造データと重みあり)を読み込んで重みデータを更新するところまでやってくれるみたいです。

ですが！
うまく動かないので！

[mbnet75.h5](https://github.com/sipeed/Maix-Keras-workspace/blob/master/mbnet/mbnet75.h5)から直接tensorflow liteのファイルを作ります。
[h52tflite.py](https://github.com/kaeruko/Maix-Keras-workspace/blob/master/mbnet/h52tflite.py)という変換スクリプトを作ったので、これを実行するとmodel.tfliteというファイルができます。

# TensorFlowLiteをK210Modelに変換
次に[tflite2kmodel.sh](https://github.com/sipeed/Maix_Toolbox/blob/master/tflite2kmodel.sh)を実行してk210Modelに変換します。
このファイル、中を見るとimagesディレクトリの中にdatasetを入れ、nccコマンドを実行しています。
imagesの中に20ファイルほどのjpgファイルを配置しました。

```bash
# ./tflite2kmodel.sh ../Maix-Keras-workspace/mbnet/model.tflite
2019-09-09 08:13:01.937638: I tensorflow/core/platform/cpu_feature_guard.cc:141] Your CPU supports instructions that this TensorFlow binary was not compiled to use: SSE4.1 SSE4.2 AVX AVX2 FMA
0: InputLayer -> 1x3x224x224
1: K210Conv2d 1x3x224x224 -> 1x24x112x112
2: K210Conv2d 1x24x112x112 -> 1x24x112x112
3: K210Conv2d 1x24x112x112 -> 1x48x112x112
4: K210Conv2d 1x48x112x112 -> 1x48x112x112
5: K210Conv2d 1x48x112x112 -> 1x96x56x56
6: K210Conv2d 1x96x56x56 -> 1x96x56x56
7: K210Conv2d 1x96x56x56 -> 1x96x56x56
8: K210Conv2d 1x96x56x56 -> 1x96x56x56
9: K210Conv2d 1x96x56x56 -> 1x192x28x28
10: K210Conv2d 1x192x28x28 -> 1x192x28x28
11: K210Conv2d 1x192x28x28 -> 1x192x28x28
12: K210Conv2d 1x192x28x28 -> 1x192x28x28
13: K210Conv2d 1x192x28x28 -> 1x384x14x14
14: K210Conv2d 1x384x14x14 -> 1x384x14x14
15: K210Conv2d 1x384x14x14 -> 1x384x14x14
16: K210Conv2d 1x384x14x14 -> 1x384x14x14
17: K210Conv2d 1x384x14x14 -> 1x384x14x14
18: K210Conv2d 1x384x14x14 -> 1x384x14x14
19: K210Conv2d 1x384x14x14 -> 1x384x14x14
20: K210Conv2d 1x384x14x14 -> 1x384x14x14
21: K210Conv2d 1x384x14x14 -> 1x384x14x14
22: K210Conv2d 1x384x14x14 -> 1x384x14x14
23: K210Conv2d 1x384x14x14 -> 1x384x14x14
24: K210Conv2d 1x384x14x14 -> 1x384x14x14
25: K210Conv2d 1x384x14x14 -> 1x768x7x7
26: K210Conv2d 1x768x7x7 -> 1x768x7x7
27: K210Conv2d 1x768x7x7 -> 1x768x7x7
28: Dequantize 1x768x7x7 -> 1x768x7x7
29: GlobalAveragePool 1x768x7x7 -> 1x768x1x1
30: Reshape 1x768x1x1 -> 1x768
31: Quantize 1x768 -> 1x768
32: K210AddPadding 1x768 -> 1x768x4x4
33: K210Conv2d 1x768x4x4 -> 1x1000x4x4
34: K210RemovePadding 1x1000x4x4 -> 1x1000
35: Dequantize 1x1000 -> 1x1000
36: Softmax 1x1000 -> 1x1000
37: OutputLayer 1x1000
KPU memory usage: 2097152 B
Main memory usage: 188160 B

```
そのディレクトリの中に`model.kmodel`というファイルがぼこっと出来ています。
# NnCaseでエラーが出た場合

```bash
Couldn't find a valid ICU package installed on the system. Set the configuration flag System.Globalization.Invariant to true if you want to run with no globalization support.
```
こんなエラーが出たら、以下を試してみてください。

```bash
wget http://download.icu-project.org/files/icu4c/57.1/icu4c-57_1-RHEL6-x64.tgz
wget https://curl.haxx.se/download/curl-7.45.0.tar.gz
tar -xf curl-7.45.0.tar.gz 
cd curl-7.45.0
./configure     --disable-dict     --disable-file     --disable-ftp     --disable-gopher     --disable-imap     --disable-ldap     --disable-ldaps     --disable-libcurl-option     --disable-manual     --disable-pop3     --disable-rtsp     --disable-smb     --disable-smtp     --disable-telnet     --disable-tftp     --enable-ipv6     --enable-optimize     --enable-symbol-hiding     --with-ca-bundle=/etc/pki/tls/certs/ca-bundle.crt     --with-nghttp2     --with-gssapi     --with-ssl     --without-librtmp     --prefix=$PWD/install/usr/local
make install
tar -czf curl-7_45_0-RHEL6-x64.tgz *
mv curl-7_45_0-RHEL6-x64.tgz ../
cd ..
tar -xf curl-7_45_0-RHEL6-x64.tgz -C /
tar -xf icu4c-57_1-RHEL6-x64.tgz  -C /
export LD_LIBRARY_PATH=/usr/local/lib
pip install h5py==2.8.0rc1
```
なぜこれで動くかはわかりません！


# kmodelを基盤に焼く
### label.txtの転送
まずは`label.txt`というラベルファイルを基盤に置く必要があります。
https://bbs.sipeed.com/uploads/default/original/1X/d41ad9dfbe01f228abe726986fbf1baf4e288f2e.zip
こちらをDLしてampyやrshellで転送しましょう。

```
ampy --port=/dev/cu.usb(m1wがマウントされたディレクトリ) put label.txt
```
### バイナリの転送

参考リンク：http://blog.sipeed.com/p/390.html
次に[maixpy_mbnet](https://bbs.sipeed.com/uploads/default/original/1X/8c0575593642b838031ec2e1129c0fe075932ef2.zip)をDLし、中にあるmaixpy_mbnet.binを基盤に焼きます。

```
pipenv run kflash.py/kflash.py -p /dev/tty.wchusbserial1470 -b 2000000 -B dan maixpy_mbnet.bin
```

ほい！
slow modeうんちゃらといったエラーが出たらもう一回実行してみましょう

次にkmodelを焼きます。
上記maixpy_mbnetに入っている`mbnet.kfpkg`の拡張子をzipに変え、解凍すると、中に`flash-list.json`と`mbnet75.kmodel`が入っています。
このkmodelを先ほど作成したmodel.kmodelに差し替え、`flash-list.json`をエディタで開いてkmodelファイルの名前を書き換え、この`２つのファイルをzip化`して拡張子をkfpkgに戻します。

```
pipenv run kflash.py/kflash.py -p /dev/tty.wchusbserial1470 -b 2000000 -B dan maixpy_mbnet.kfpkg
```
どうでしたか？
うまく焼けたでしょうか？

# 実行
準備は整いました！
runしてみましょう。

ターミナルから以下を実行し、基盤に入ります。

```bash
screen /dev/cu.usb(環境による) 115200
```
Ctrl + Eでペーストモードになり、以下のスクリプトをべーっと張ります。
label.txtが正しいパスになってるか、`os.listdir('/flash/')`などで確認してください。

```python
import sensor, image, lcd, time
import KPU as kpu
lcd.init()
sensor.reset()
sensor.set_pixformat(sensor.RGB565)
sensor.set_framesize(sensor.QVGA)
sensor.set_windowing((224, 224))
sensor.set_vflip(1)
sensor.run(1)
lcd.clear()
lcd.draw_string(100,96,"MobileNet Demo")
lcd.draw_string(100,112,"Loading labels...")
f=open('labels.txt','r')
labels=f.readlines()
f.close()
task = kpu.load(0x200000) 
clock = time.clock()
while(True):
    img = sensor.snapshot()
    clock.tick()
    fmap = kpu.forward(task, img)
    fps=clock.fps()
    plist=fmap[:]
    pmax=max(plist)	
    max_index=plist.index(pmax)	
    a = lcd.display(img, oft=(0,0))
    lcd.draw_string(0, 224, "%.2f:%s                            "%(pmax, labels[max_index].strip()))
    print(fps)
a = kpu.deinit(task)
```

貼り付けたらCtrl + Dで実行です。
loading label...という文字が出た後、モニタにカメラの映像が出てきます。
ラベルの文字も出てきてると思います。
これで完了です！