2020年10月20日火曜日

Interface10月号のNNCaseネタをやってみる(途中経過)



インターン地獄に巻き込まれた中の人です(これ年明けも続くのつらいな).今回は雑誌記事の検証的なネタです.
Interface10月号のP.55-58に"AIマイコンK210の汎用AIチップとしての可能性を探る"という記事がありまして,BodyPixを変換し実装までやるという内容でした.記事内の結果では,ファームウェアがkmodelv4に対応していないため動かなかったという結論でした.
記事を読んだ感じでは動かせる気がしたので,とりあえずやってみます(ソースコードの公開は,もう少し先になりそうです).



1.モデルの変換


記事内では,PINTOさんのPINTO_model_zooにある.tfliteを使用していたので,同じようにモデルを準備します(量子化などは行わずfloatのままtfliteにする).入力サイズは320x240,出力はいじりません.
tfliteが生成出来たら,NNCaseを使い,kmodelへの変換を行います.NNCaseでの変換では,量子化のためのデータセット(データの分布を見たいから?)を指定する必要があるのですが,BodyPixのデータセットが見当たらなかったので,各ピクセルの値がランダムな320x240の画像を10枚生成し,データセットの代わりにしました(Imgフォルダに突っ込みました).もしかしたらCocoDatasetを使っているかもしれないですね.

.\ncc.exe compile .\035_BodyPix_tflite\bodypix_025_320x240_weight_quant.tflite .\035_BodyPix_tflite\bodypix_025_320x240_weight_quant.kmodel -i tflite -o kmodel --dataset .\Img\ --dataset-format image --inference-type uint8 --input-mean 0 --input-std 1 --dump-ir --input-type uint8 --max-allocator-solve-secs 120 --calibrate-method l2 --dump-weights-range --weights-quantize-threshold 1024 --output-quantize-threshold 4096

参考
中村仁昭,"AIマイコンK210の汎用AIチップとしての可能性を探る",Interface10月号,CQ出版,PP.55-58
Kaz Sato,Google Developers Japan: BodyPix の概要: ブラウザと TensorFlow.js によるリアルタイム人セグメンテーション,https://developers-jp.googleblog.com/2019/04/bodypix-tensorflowjs.html

2.ファームウェアの準備

kmodelv4を使う場合,ファームウェアのバージョン違いではじかれることがあります(SipeedやM5Stackが配布しているファームが古い時がある).今はアップデートされて行けるかも).そこで,手元のLinux環境でM5StickV向けにファームウェアをビルドしました.(MaixHubのサービスで,カスタムファームウェアのビルドをやっているみたいだけど,いろいろあって手元の環境でやった).
ファームウェアのビルドは紅樹 タカオさんの記事を参考にUbuntu20.04マシンで行いました.オプションで,support v4 kmodelというオプションがあるので,そこを有効化しておく必要があります(配布されているファームウェアだと,これが有効になってないかも).

参考
紅樹 タカオ,"M5StickVのファームウェアビルド手順",https://raspberrypi.mongonta.com/howto-build-firmware-of-m5stickv/

3.MaixPyIDE


BodyPixは4つの出力があるモデルなので,KPUを使う際にkpu.set_outputs()を4つ書いておきます.kpu.forward()を実行し推論をしたのち,kpu.get_output()で目的の出力を得ます.取り出したデータをいい感じに整形すれば完成です.

4.出力データを正しく表示したい


出力データが出たところで,4つの出力を確認していきます.まずは形状とデータサイズから.

{"fmap": "data"=0x80330b70, "size"=20400, "index": 41, "w": 17, "h": 20, "ch": 15, "typecode": f}
{"fmap": "data"=0x803382f8, "size"=40800, "index": 41, "w": 34, "h": 20, "ch": 15, "typecode": f}
{"fmap": "data"=0x80342258, "size"=28800, "index": 41, "w": 24, "h": 20, "ch": 15, "typecode": f}
{"fmap": "data"=0x8032f8b0, "size"=1200, "index": 41, "w": 1, "h": 20, "ch": 15, "typecode": f}

"size"のところと"w","h","ch"の計算がを合わせることで,データ型がわかります.

20400 / (17 x 20 x 15) = 4
40800 / (34 x 20 x 15) = 4
28800 / (24 x 20 x 15) = 4
1200 / (1 x 20 x 15) = 4

全部一致したので,4Byte,かつ出力データからFP32と分かります.形状はw*h*chのように見えますが,実際は,ch * w * hの順番らしいです(NNCaseの方の出力がこっち:MaixPyでのKPUのプロパティが違う).

NNCaseの出力.ch,w,hの順番になっている.

ここまではほぼ確定なのですが,ここからがよくわからないところで,この4つの出力モデルの解釈で,ch=24は体の部位ごとのセグメンテーション,ch=1は体とそれ以外のセグメンテーションと読めるのですが,ほかの2つがよくわかりませんでした.とりあえずch=1の奴を使ってみます.





赤い部分が濃いほど,人がそこにいる確率が高いことを表しているのですが,あまりうまくいっていなさそうです.

5.今後やりたいこと


データの解釈があっているかの確認や精度向上,高速化あたりをやっていきたいです.もしかしたらch,w,hの解釈もあっていないかもしれないですね...



©2020 shts All Right Reserved.