【失敗談】KPUでオリジナルのモデルを楽に動かしたかった

#はじめに
失敗談です。解決策を求めてきた方はそっとタブを閉じてください。
同じことをやろうとした人がいたら情報交換したいと思って書きました。
※このまえ熊本のイベントで話した内容です。

#発端
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261327/58dca349-e6b3-b1f6-ef68-83d77b038e2a.png)


ちょっと前に中国の深圳のスタートアップ企業であるSipeedから激安なAI向けのボードが発売されました。

* RISC-V x2
* KPU(ニューラルネットワーク処理
* カメラ
* ディスプレイ
* マイク
* オーディオアンプ

など色々ついてて2800円です。（スイッチサイエンスはもう少し高い）

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr"><a href="https://twitter.com/hashtag/Sipeed?src=hash&amp;ref_src=twsrc%5Etfw">#Sipeed</a> MAixシリーズはShigezone開店当初からの人気製品。特に人気なのはESP32やDAC、3Wオーディオアンプを搭載した「MAixduino」です。<br><br>液晶とカメラまでセットで税込3,500円です。<br><br>店頭販売のほか、多数まとめてのご購入や製品組込のコンサルティング・製造など、お気軽にご相談ください。 <a href="https://t.co/NVeBFrEtqm">pic.twitter.com/NVeBFrEtqm</a></p>&mdash; Shigezone〜深圳直送便〜電子部品＆機器のお店：秋葉原ラジオデパート3F (@ShigezoneAkiba) <a href="https://twitter.com/ShigezoneAkiba/status/1148243873199607809?ref_src=twsrc%5Etfw">July 8, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

5月末にマイクロソフトのde:codeの帰りに、秋葉原のShigezoneで購入しました。ここは深圳から仕入れた面白怪しいものを破格の値段で買えるのでお勧めです。
私が買ったのは[Sipeed Maix M1 Dock](https://www.switch-science.com/catalog/5701/)です。Wifiのモジュールもありますが技適ががが。
技適付きがいいなら[Sipeed Maixduino](https://www.switch-science.com/catalog/5707/)を買うといいです。

細かい説明は他の方が書いてるので省きますが、これだけ安いボードでAIを動かせるとなると自分でいろいろと動かしたくなるわけです。
参考：[Sipeed M1 dockを動かしてみた](https://qiita.com/kitata9/items/37f39b91e2b007ff8cd5)

#【理想】楽にAIやりたい
AIに関してはTensorFlowを一通りやったくらいです。あとはAzureのCognitive Servicesをよく使ってます。最近だとCustomVisionを使って[アナログメータ(湿度計)の読み取り](https://qiita.com/t-sho/items/5e6cbb3fbb547d946bed)とかやってました。

##CustomVisionのモデルをSipeedに持っていきたい
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261327/7bd7c6e8-f956-d77e-17ce-8888cf45c2e6.png)
ざっくりしたイメージはこんな感じです。
CustomVisionで作成したモデルをエクスポートして、kmodelへ変換してsipeedに持っていくというものです。

##先に結論を言うと
TensorFlow Liteからkmodelへ変換する際にエラーとなり詰みました。エラーは下記のような感じ。
```
/content/Maix_Toolbox
Fatal: Layer tflite.Operator is not supported: Only scalar multiply is supported
NnCase.Converter.Converters.LayerNotSupportedException: Layer tflite.Operator is not supported: Only scalar multiply is supported
   at NnCase.Converter.Converters.TfLiteToGraphConverter.ConvertMul(Operator op) in D:\Work\Repository\nncase\src\NnCase.Converter\Converters\TfLiteToGraphConverter.cs:line 243
   at NnCase.Converter.Converters.TfLiteToGraphConverter.ConvertOperator(Operator op) in D:\Work\Repository\nncase\src\NnCase.Converter\Converters\TfLiteToGraphConverter.cs:line 79
   at System.Linq.Enumerable.SelectEnumerableIterator`2.ToList()
   at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
   at NnCase.Converter.Converters.TfLiteToGraphConverter.Convert() in D:\Work\Repository\nncase\src\NnCase.Converter\Converters\TfLiteToGraphConverter.cs:line 34
   at NnCase.Cli.Program.Main(String[] args) in D:\Work\Repository\nncase\src\NnCase.Cli\Program.cs:line 113
   at NnCase.Cli.Program.<Main>(String[] args)
```
では、やったことを書いておこうと思います。
##CustomVisionからモデルを引っこ抜く
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261327/29ee361b-b857-7b96-0458-4d8048bf1691.png)
CustomVisionでドメインをcompactにして学習させた後、TensorFlowとしてエクスポートします。
今回は簡単に5種類の食べ物を分類するモデルを作りました。

* model.pb
* labels.txt

エクスポートするとTensorFlowのPBファイルとラベルのテキストファイルがダウンロードできます。

##TensorFlowからTensorFlow Liteへ変換
[kmodelへの変換ツール](https://github.com/sipeed/Maix_Toolbox)はGitで公開されています。
さらに[kmodelへ変換するための参考となる情報](https://bbs.sipeed.com/t/topic/682)も公開されてます。

* [Maix_Toolbox](https://github.com/sipeed/Maix_Toolbox)
* [Train, Convert, Run MobileNet on Sipeed MaixPy and MaixDuino !](https://bbs.sipeed.com/t/topic/682)

###CustomVisionのPBファイルの中を確認
変換するために必要なパラメータをtensorboardで確認します。
pbファイルを置いた場所で下記を実行(なぜかColaboratoryがうまく動かなかったのでここはAnacondaのjupyterでやりました)

```
import tensorflow as tf
from tensorflow.python.platform import gfile

with tf.Session() as sess:
    model_filename ='./tensorflow_inception_graph.pb'
    with gfile.FastGFile(model_filename, 'rb') as f:
        graph_def = tf.GraphDef()
        graph_def.ParseFromString(f.read())
        g_in = tf.import_graph_def(graph_def)

    LOGDIR='LOGDIR'
    train_writer = tf.summary.FileWriter(LOGDIR)
    train_writer.add_graph(sess.graph)

```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261327/6cb640c8-34f0-e923-639b-d1686971f10f.png)


上のpythonのコードを実行するとLOGDIRというフォルダができると思うので、この階層で下記のコマンドを実行します。

```
tensorboard --logdir=LOGDIR
```

そうすると上の画像の一番下にあるようにURLが発行されるのでアクセスする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261327/60b7bf57-a2a1-d6ea-1627-d3225a3715bc.png)
Main Graphのimportの中の出力層と入力層を調べます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261327/0bc1b3d4-f694-96ec-59f3-f3407ee3881d.png)
赤枠で囲ったところ３つをメモしておきます。

###PBファイルからTFLITEへ
ここからはGoogleのColaboratoryで作業してます。
参考：[Maixpy GO Mobilenet Transfer learning for Image Classfication](https://iotdiary.blogspot.com/2019/07/maixpy-go-mobilenet-transfer-learning.html)

```
#変換用のtoolをclone(先頭の「!」はColaboratoryでのお作法) 
!git clone https://github.com/sipeed/Maix_Toolbox.git
```
cloneしたら、

```
#変換用のフォルダ作成と解凍作業
%%bash
cd Maix_Toolbox 
mkdir -p ncc
mkdir -p workspace
mkdir -p images
mkdir -p log
cd ncc
wget https://github.com/kendryte/nncase/releases/download/v0.1.0-rc5/ncc-linux-x86_64.tar.xz
tar -Jxf ncc-linux-x86_64.tar.xz
rm ncc-linux-x86_64.tar.xz
echo "download nncase ok!"
```
モデルをアップロード(ドライブにフォルダ作ってマウントしてもどっちでもよいです)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261327/b7c8f18e-a000-8adc-3c88-bd97bd03e447.png)

toolboxのworkspaceにコピーして変換

```
!cp model.pb Maix_Toolbox/workspace/model.pb
!cp labels.txt Maix_Toolbox/workspace/labels.txt
%cd Maix_Toolbox
#pb -> tflite
!toco --graph_def_file=workspace/model.pb --input_format=TENSORFLOW_GRAPHDEF --output_format=TFLITE --output_file=workspace/model.tflite --inference_type=FLOAT --input_type=FLOAT --input_arrays=Placeholder --output_arrays=model_outputs --input_shapes=1,224,224,3
```

* input_arrays：Placeholder
* output_arrays：model_outputs 
* input_shapes：1,224,224,3

この3つのパラメータはtensorboardでメモした値です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261327/700824b9-8c45-ffc9-3a0e-ada28afb8657.png)
これでtfliteに変換完了です。

##TensorFlow Liteからkmodelへ変換
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261327/8a2a09b3-fe60-b71b-1077-602290edbd5e.png)

```
%%bash
cd /content

mkdir /content/test_photos/cake
mkdir /content/test_photos/curry
mkdir /content/test_photos/pizza
mkdir /content/test_photos/sandwich
mkdir /content/test_photos/sushi
```
フォルダを作成する。
テスト用の画像をアップロードして、このフォルダ移動していく

```
#foodのテスト画像(ファイル名はご自身のものに適宜変えてください)
!mv /136149.jpg /content/test_photos/cake
!mv /176394.jpg /content/test_photos/cake
!mv /42503.jpg /content/test_photos/cake
!mv /908125.jpg /content/test_photos/cake
!mv /91387.jpg /content/test_photos/cake
!mv /1225331.jpg /content/test_photos/curry
!mv /1251235.jpg /content/test_photos/curry
!mv /1285185.jpg /content/test_photos/curry
!mv /834077.jpg /content/test_photos/curry
!mv /949975.jpg /content/test_photos/curry
!mv /289265.jpg /content/test_photos/sandwich
!mv /326580.jpg /content/test_photos/sandwich
!mv /335343.jpg /content/test_photos/sandwich
!mv /360537.jpg /content/test_photos/sandwich
!mv /366787.jpg /content/test_photos/sandwich
!mv /1025041.jpg /content/test_photos/sushi
!mv /1098082.jpg /content/test_photos/sushi
!mv /377047.jpg /content/test_photos/sushi
!mv /638929.jpg /content/test_photos/sushi
!mv /723084.jpg /content/test_photos/sushi
```
最後にkmodelへの変換を実行し・・・エラーとなるorz

```
%cd /content/Maix_Toolbox
!./ncc/ncc -i tflite -o k210model --dataset /content/test_photos /content/Maix_Toolbox/workspace/model.tflite /content/Maix_Toolbox/workspace/model.kmodel
```

Fatal: Layer tflite.Operator is not supported: Only scalar multiply is supported
というエラーなわけですが、調べてみたらどうやらKmodelへの変換に際し、レイヤーの制限があるらい
参考：[nncase](https://github.com/kendryte/nncase/blob/master/README.md)

問題はここからどうやってトレースすればいいのかTensorflowをちょっとかじった程度の人間にはわからず、そもそもモデルはAzure側で作成してる以上PBファイルの修正なんてできないのでここでgame overとなりました。

初めての試みなので、そもそもどこか途中で間違ってて実はうまくいくという可能性もゼロではないですが、もし同じようなことをして成功したという方がいればぜひ情報交換したいです。

ちなみに最近発売された[M5StickV](https://www.switch-science.com/catalog/5700/)もおなじKPUを搭載していて、Sipeedとほぼ同じ開発手順で開発できるので基本的にはSipeedで出来ることはM5StickVにも応用できると思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/261327/f202a920-f839-18b0-bdb6-4ed1b50535d1.png)