はじめに
Sipeed M1W dock suitを購入しました。
このキットはカメラがついていて、画像認識のモデルが動くみたいです。
Train, Convert, Run MobileNet on Sipeed MaixPy and MaixDuino !
こちらのチュートリアルを見ながら画像認識のモデルを作ろうとしたのですが、色々障害にぶつかったため備忘録として残しておきます。
試した環境は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
を使いました。
docker run -it --name "tf" -v /Users/ユーザ名/work/maix/docker/:/tf/files tensorflow/tensorflow:latest-gpu-py3-jupyter /bin/bash
tfliteへの変換
変換にあたり以下2つのリポジトリが必要なので落としてきます
git clone git@github.com:sipeed/Maix-Keras-workspace.git
git clone git@github.com:sipeed/Maix_Toolbox.git
次に、データセットのDLをして学習済みのモデルのHDF5ファイルを作るところですが、
Maix-Keras-workspaceのリポジトリの中にmbnet_keras.pyというスクリプトがあり、
これを実行するとmbnet75.h5(学習済みのモデル)と
mobilenet_7_5_128_tf_no_top.h5(構造データと重みあり)を読み込んで重みデータを更新するところまでやってくれるみたいです。
ですが!
うまく動かないので!
mbnet75.h5から直接tensorflow liteのファイルを作ります。
h52tflite.pyという変換スクリプトを作ったので、これを実行するとmodel.tfliteというファイルができます。
TensorFlowLiteをK210Modelに変換
次にtflite2kmodel.shを実行してk210Modelに変換します。
このファイル、中を見るとimagesディレクトリの中にdatasetを入れ、nccコマンドを実行しています。
imagesの中に20ファイルほどのjpgファイルを配置しました。
# ./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でエラーが出た場合
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.
こんなエラーが出たら、以下を試してみてください。
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を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ファイルの名前を書き換え、この2つのファイルをzip化
して拡張子をkfpkgに戻します。
pipenv run kflash.py/kflash.py -p /dev/tty.wchusbserial1470 -b 2000000 -B dan maixpy_mbnet.kfpkg
どうでしたか?
うまく焼けたでしょうか?
実行
準備は整いました!
runしてみましょう。
ターミナルから以下を実行し、基盤に入ります。
screen /dev/cu.usb(環境による) 115200
Ctrl + Eでペーストモードになり、以下のスクリプトをべーっと張ります。
label.txtが正しいパスになってるか、os.listdir('/flash/')
などで確認してください。
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...という文字が出た後、モニタにカメラの映像が出てきます。
ラベルの文字も出てきてると思います。
これで完了です!