2020年12月31日木曜日

M5StickVでDonkeyCarのモデルは走らせられるのか(1)



年末進行感が全くないまま大みそかになっていた中の人です.今年はいろいろなことが変わった一年でしたが,いかがお過ごしでしょうか.
今回はM5StickVでDonkeyCarのモデルを走らせる話(1)です.個人的に気になっていたので,少しやってみようと思いました. 

今回の実装は,https://github.com/shtsno24/DonkeyCar_on_M5StickVに上げてあります.




1.DonkeyCarのモデル


https://www.donkeycar.com/より,DonkeyCarの一例

DonkeyCarはラジコンにRPiやカメラを組み合わせ,自動運転させようというプロジェクトです(車本体をさすこともあるとか).自動運転の心臓部はKerasによるDeepLearningのモデルで実装されており,使うデータの種類などでいくつかのパターンがあるようです.
2016年ごろにやっていたやつの結果を踏まえると,個人的にはcategoricalあたりがうまく動きそうです.今回はこれをM5StickV向けに変換します.

参考:
Donkey Car, "Keras - Donkey Car", http://docs.donkeycar.com/parts/keras/
腹筋開発,"Donkeycar3.1.0上の機械学習モデルを調べてみた", https://fight-tsk.blogspot.com/2019/09/donkeycar310.html

2.モデルの作成


本家の実装は汎用性が高い実装になっていますが,今回はcategoricalのみが動けばよいので,少し手を入れてミニマムな構成に直しました.


def Categorical(input_shape=(120, 160, 3), drop=0.2, l4_stride=1):
    """
    :param img_in:          input layer of network
    :param drop:            dropout rate
    :param l4_stride:       4-th layer stride, default 1, in Categorical, l4_stride=2
    """
    inputs = Input(shape=input_shape, name='img_in')
    x = conv2d_relu(inputs, 24, 5, 2, 1)
    x = Dropout(drop)(x)
    x = conv2d_relu(x, 32, 5, 2, 2)
    x = Dropout(drop)(x)
    x = conv2d_relu(x, 64, 5, 2, 3)
    x = Dropout(drop)(x)
    x = conv2d_relu(x, 64, 3, l4_stride, 4)
    x = Dropout(drop)(x)
    x = conv2d_relu(x, 64, 3, 1, 5)
    x = Dropout(drop)(x)

    x = Flatten(name='flattened')(x)

    x = Dense(100, name='dense_1')(x)
    x = ReLU()(x)
    x = Dropout(drop)(x)

    x = Dense(50, name='dense_2')(x)
    x = ReLU()(x)
    x = Dropout(drop)(x)

    outputs = []
    _x = Dense(15, name='throttle')(x)
    _x = Softmax()(_x)
    outputs.append(_x)
    _x = Dense(20, name='steer')(x)
    _x = Softmax()(_x)
    outputs.append(_x)

    model = Model(inputs=inputs, outputs=outputs)
    return model


def conv2d_relu(x, filters, kernel, strides, layer_num):
    """
    Helper function to create a standard valid-padded convolutional layer
    with square kernel and strides and unified naming convention
    :param filters:     channel dimension of the layer
    :param kernel:      creates (kernel, kernel) kernel matrix dimension
    :param strides:     creates (strides, strides) stride
    :param layer_num:   used in labelling the layer
    """
    x = Conv2D(filters=filters,
               kernel_size=(kernel, kernel),
               strides=(strides, strides),
               name='conv2d_' + str(layer_num))(x)
    x = ReLU()(x)

    return x



とりあえずこんな感じです(Conv2DのActivationとか使えばいいのですが,気にしない). Dropoutに関してはtfliteに変換する際に消えるので問題ないです.これをnncaseで変換し,PC上で実行時間のシミュレーションをしてみました(モデルの訓練はしていません).

参考:autorope, donkeycar/keras.py at dev · autorope/donkeycar, https://github.com/autorope/donkeycar/blob/dev/donkeycar/parts/keras.py

3.PC上でシミュレーション


入力画像

120x160x3のカラー画像(ランダムなピクセル値を使用)を使用し,実行時間を計測しました.


QuantizedConv2D: 16.6666ms
QuantizedConv2D: 35.7317ms
Dequantize: 0.0421ms
Conv2D: 21.1655ms
Quantize: 0.3078ms
KPUUpload: 0.0231ms
KPUConv2D: 15.5949ms
Pad: 0.0365ms
KPUUpload: 0.0038ms
KPUConv2D: 14.1453ms
Pad: 0.026ms
Transpose: 0.06ms
Dequantize: 0.0026ms
MatMul: 1.9926ms
Pad: 0.0108ms
Quantize: 0.0232ms
KPUUpload: 0.0039ms
KPUConv2D: 0.1828ms
Pad: 0.0011ms
Dequantize: 0.0006ms
MatMul: 0.0018ms
MatMul: 0.0012ms
Total: 106.024ms


PC上では100msで,全体としてはConv2Dの演算に時間がかかっていそうです.最初のQuantizedConv2DはKPU上で演算されていない様子なので,もう少し上手くやる方法がありそうです.意外とMatMulが高速みたいですが,実機だとどうなるかが気になります.

4.実機実装


実機で動かす様子

以前に実装していたコードを使ってパパっと実装.
(2021.1.1更新:時間のところが実際はFPSだったので時間表示に修正しました.)


KPU : fetch data from Camera
KPU : run kpu
KPU : fetch data from kpu
Time : 2.758 [s]


PCの結果の27倍実行に時間がかかっていることがわかりました.PCの結果やNNCaseのQAから察するに,おそらくフィルタサイズが5x5のConv2Dが部分的にしかアクセラレートされていないことが主な原因だと考えられます(1x1 or 3x3が完全な形でアクセラレートされる).

5.もう少し実行速度を上げる


AIが動くことが売りのM5StickVでDonkeyCarのモデルを実行してみました.結果として約400[ms]程度の実行時間がかかることがわかりました.実行できただけである程度は成功なのですが,実用上もう少し実行速度を上げる必要がありそうです.手法としては,

・5x5フィルタを複数の3x3フィルタに置き換える
・全結合の出力サイズを落とす
・SeparableConv2Dに置き換える

あたりが効果がありそうです.

6.最後に


今年も様々な方にこのブログを見ていただきました.来年は就活&修論に追われる予定なので,おそらく更新頻度は下がるかと思いますが何卒よろしくお願いします(UnitV2とかが出るので,それに関連した記事を書く予定です).

P.S. このブログを閲覧している方は,機会があったら中の人にこのブログを見たことを伝えていただけると幸いです.中の人が喜びます.

©2020 shts All Right Reserved.

2020年11月16日月曜日

M5StickVのIMUで詰まった話


M5StickVのIMUのドリフト補正したときのグラフ.
画像を作ってSDに書き出せるのは非常に便利.


研究をやり始めると,就活がおろそかになるシングルスレッド人間,中の人です.今回はM5StickVのIMUの話です.



1.M5StickVのIMUと回路について


M5StickVは生産された時期に応じて搭載しているIMUと,メインのマイコンであるK210との接続ピン・方法が異なっています.今回は2020/03以降に生産されたモデルで,MPU6886を搭載しているものを使用していきます.

参考:
SwitchScience, M5StickV--販売終了 - スイッチサイエンス, https://www.switch-science.com/catalog/5700/

2.MPU6886と会話する(I2C)


データの読出しに成功した.



MPU6886との通信は,SPIとI2Cの2つがあります.


どちらを使うかを決めるためには,G25をHigh or Lowにする必要があります(nnnさん教えてくださりありがとうございます).

手始めに,I2Cを使ってMPU6886との会話を試みます...


ほかの人だと読めたのに,なぜか読めない...
I2Cのコード自体は動いていたのでIMU側の問題と考え,データシートを眺めていましたが特に答えが得られなかったので,IMUにつながっているG24を操作しました.結果IMU側のアドレスをスキャンできました.
G24はIMUのアドレスを決定するピン(AD0)に接続されているのですが,G24を操作してHigh or Lowにしないと,回路的に浮いた状態になるようです(プルダウン/アップされていないみたい,SPIで読むときに,idle状態で不定にしないと値を吐き出すから?).
とりあえずnnnさんのサンプルコードを参考に,レジスタを設定したところ普通に動作しました.

参考:
m5stack, M5-Schematic/MPU-6886-000193+v1.1_GHIC.PDF.pdf at master · m5stack/M5-Schematic

anoken, purin_wo_motto_mimamoru_gijutsu/006_2_imu_new_MPU6886.py at master · , https://github.com/anoken/purin_wo_motto_mimamoru_gijutsu/blob/master/03_maixpy_example/006_2_imu_new_MPU6886.py

3.SPIで会話する


SPIは開発中...
なぜか読めないんだよね.





©2020 shts All Right Reserved.

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.


2020年9月11日金曜日

Matplotlibで散布図や折れ線グラフの30fps描画を実現したい




相変わらず引きこもり生活な中の人です。今回はMatplotlibで散布図や折れ線グラフを高速描画する話です。 



1.Matplotlibの描画の話


Pythonでグラフ描画を行う際によく使うのがMatplotlibです.Matlab-likeなインターフェースで扱いやすいのですが,リアルタイム描画を行おうとすると非常に遅いことがあります.
これに関してはいろいろな高速化のやり方があるのですが,この中でも手動更新のコードを自分で書くことで高速化を果たすやり方だと,データ数によっては100fps以上を狙うことができるらしいです.

参考:hukkumameo,【Python】matplotlibの手動で描画更新,俺言語。

2.手動更新について


描画の更新方法はいくつかあるのですが,今回は
  1. 描画領域を白く塗る
  2. データを描画する
  3. 描画領域のアップデート
  4. 画面の描画更新
でやってみました.(1.の参考に書いてあるcase4)

3.折れ線グラフ


折れ線グラフはhukkumameo氏がやった通りなのですが,少しいじってクラス化しました.まずは初期化から.

class Line:

    def __init__(self, fig, ax, plot_area=(1000, 1000), len_points=100):
        self.fig = fig
        self.plot_area = plot_area

        # axis setup
        self.line_ax = ax
        self.line_ax.set_xlim(0, plot_area[0])
        self.line_ax.set_ylim(-plot_area[1], plot_area[1])
        self.ydata = [0.0 for x in range(len_points)]
        self.line, = self.line_ax.plot(self.ydata)
        # show figure
        self.fig.canvas.draw()
        self.fig.show()

高速化のために自動での軸レンジの計算を手動に変更し,初期化部分で設定しています.描画するデータに関しては,専用のリスト(FIFO)を作成し,データ更新が走るたびに一番古いデータを破棄し,新しいデータを足していくようにしました.
次は更新周りです.

    def update_data(self, points):
        # draw background with white
        self.line_ax.draw_artist(self.line_ax.patch)

        # plot points
        self.ydata.append(points)
        self.ydata.pop(0)
        self.line.set_ydata(self.ydata)
        self.line_ax.draw_artist(self.line)

        # update this graph
        self.fig.canvas.blit(self.line_ax.bbox)

    def plot(self, ydata):
        self.update_data(ydata)
        self.fig.canvas.flush_events()

自分で書いておいてなんですが,update_dataとplotの分割はあんまり意味は無さそうです...

4.散布図


折れ線ができたら散布図もやりたいということで,実際にやってみました.描画の基本方針は折れ線グラフとは変わらないのですが,やり方が少し変わってきます.このあたりの話に関しては,差分更新によるmatplotlibのアニメーションの高速化の記事によく書かれています.

class Scatter:

    def __init__(self, fig, ax, plot_area=(1000, 1000), len_points=100, show_icon=False, icon_radius=100):
        self.fig = fig
        self.plot_area = plot_area
        self.icon_radius = icon_radius if show_icon is True else None

        # axis setup
        self.pos_ax = ax
        self.pos_ax.set_xlim(-plot_area[0], plot_area[0])
        self.pos_ax.set_ylim(-plot_area[1], plot_area[1])
        self.pos_points = self.pos_ax.scatter([], [])
        self.xy = [[0.0, 0.0] for x in range(len_points)]
        if show_icon is True:
            self.agent_icon = mpatches.RegularPolygon(xy=(0, 0), numVertices=4, radius=self.icon_radius, orientation=0.0, ec="r", fill=False)
            self.pos_ax.add_patch(self.agent_icon)

        # show figure
        self.fig.canvas.draw()
        self.fig.show()


折れ線グラフと同じく,軸のレンジを決めFIFOの準備をしています.これに加え,最新のデータを示すためのポリゴン描画のコードが入っています.差分更新の記事ではmpatches.Circleの例が挙げられていますが,今回はポリゴンの方を使用し,描画に関してもdraw_artistを使用します.mpatchesに関しては公式が詳しいです.

    def update_data(self, points, orientation=0.0):
        # draw background with white
        self.pos_ax.draw_artist(self.pos_ax.patch)

        # plot points
        self.xy.append(points)
        self.xy.pop(0)
        self.pos_points.set_offsets(self.xy)
        self.pos_ax.draw_artist(self.pos_points)

        # plot the icon
        if self.icon_radius is not None:
            self.agent_icon.xy = points
            self.agent_icon.orientation = orientation
            self.pos_ax.draw_artist(self.agent_icon)

        # update this graph
        self.fig.canvas.blit(self.pos_ax.bbox)

    def plot(self, points, orientation=0.0):
        self.update_data(points, orientation)
        self.fig.canvas.flush_events()

基本的には折れ線グラフと一緒です.こっちもupdate_dataとplotはひとまとめにした方がよかったかもしれません.

   matplotlib公式,matplotlib.patches — Matplotlib 3.3.1 documentation,Matplotlib 3.3.1 documentation

5.動かす


とりあえず,[-1000, 1000]の範囲でランダムな点の組(X,Y)を生成し,グラフに打っていくことにしました.これを5000回繰り返して,平均フレームレートを計算します.実行コードはこちらから.


平均フレームレートは22.9[fps]で,目標の30[fps]には届きませんでした....
試しに一度に表示する点数を,折れ線グラフと散布図の両方で5000 -> 2500にしてみます.


平均フレームレートは31.0[fps]で,目標は達成したようです.一度の表示する点数を2500 -> 1250とさらに減らしてみます.


平均フレームレートは52.2[fps]とそこそこ速くなりました.どうやら表示するデータ数に応じてフレームレートが変動するようです.

6.結局どうなのよ


これは目標の30fps描画というべきかについてはいろいろ考える必要があると思いますは,とりあえず部分的には目標達成ということにしましょう.もやもやするけど.散布図のみだったり,グラフの更新を並列で走らせるとか,更新を隠蔽するとかすればもっとフレームレートは上がるはずなので,まだまだといったところでしょうか.

7.参考


matplotlib公式,matplotlib.patches — Matplotlib 3.3.1 documentation,Matplotlib 3.3.1 documentation




©2020 shts All Right Reserved.

2020年9月10日木曜日

このブログを参照するときに気を付けてほしいこと


インターン準備が大変すぎて,おめめぐるぐる中の人です.さて,今回はブログの利用に関しての話です.弊ブログも来月で7年目に突入し,だんだんとほかのブログなどから参照されること増えてきました. 今のところ目立ったトラブルもないのですが,もしかしたら今後何か起きるかもしれないので,その前に手を打っていくのがこの記事の趣旨です.



1.このブログの趣旨


このブログは,中の人が試してみてうまくいった・うまくいかなかった事例をまとめたものです.

2.情報の内容と免責


当然,開発環境・アップデートなどにより,このブログ内で紹介した方法も動かなくなることがあります.うまくいかない場合にはほかの方法を試してください.また,ブログ内で紹介した方法で何か起こっても(物損など)責任は負えないのでご了承ください.

3.ほかの媒体での利用


基本的に他の媒体(ブログ・Twitter・同人誌など)での使用に関して,商用・非商用問わず,基本的にNGはないのですが,元にしたブログ記事のリンクを貼ってほしいです.許可は取らなくてもいいです.
諸事情により元記事が消えることもあるのでご了承ください.


4.その他


この記事は状況に合わせてアップデートします.そのため途中で内容が変わるかもしれません.




©2020 shts All Right Reserved.

2020年7月30日木曜日

M5StickVでSemantic Segmentationやってみた(3)(失敗編)




最近忙しすぎて目がぐるぐるな中の人です.前回はモデルをNNCaseで変換したと思うのですが,今回はそれを実装していきます.



1.早速本体で動かす


kflash_guiでファームウェアとともに書き込む.


NNCaseでコンパイルすると".kmodel"が生成されるので,本体のフラッシュに焼いていきます.このバイナリファイルを書き込んだのち,MaixPy IDEから

2.あれ,画像が...


実行結果.虚無が広がる.

さて,実行してみました...が,虚無虚無な出力ですね.左にカメラからの画像,右には変換結果を表示しています.フレームレートは2fpsぐらいです.

 

3.敗因分析


validは動いたんだけど...

何となく予想できたのでやっぱりかって感じですね.実は,id:shintarofさんが既に動かしていたようで,その様子から考えると,

1. 背景とターゲットとなる物体は,色などで容易に分離できる
2. 背景にあまり多くの色が混ざっていない(単色に近い)
3. データセットとよく似た状況で運用する(<-超重要)

あたりを改善する必要があるようです.あとは,入力画像のサイズを大きくするとかですかね.


©2020 shts All Right Reserved.

2020年6月10日水曜日

M5StickVでSemantic Segmentationやってみた(2)





久しぶりに研究室に顔を出しに行ったら,次の日筋肉痛になった中の人です.前回に引き続き,M5StickV(K210)にSemantic Segmentationを入れる話です.



1.使えそうで使えないレイヤ



前回示したように,使えるレイヤを守ればKeras->TFLite->NNCaseの変換は可能です.しかし,Softmaxレイヤ(Activation("softmax")も同様)に関しては動作しませんでした.
公式がBBS等で配布しているデータだと,Softmaxを使っているモデルもあるのでそこから類推するに,画像のような2D(実際はチャンネル方向があるので3D)データは動かず,全結合層の出力のように1Dのデータのみ動くようになっていると考えられます.この問題はBeta3, Beta4で確認したのですが,そのうちだれかがIssue上げてくれないかなって思っています(そこまで手が回らない).

2.Softmaxを回避する


こうなるとSoftmaxを使わずにモデルを構築したいわけですが,トレーニング時にはこのレイヤがないと正しく収束しないことがほとんどだったので,結局必要になります.
回避方法としてはいくつか存在して,

  1. tensorflowの損失関数でSoftmaxが含まれているものを使う
  2. Softmaxありで学習したモデルからSoftmaxを取り除く

あたりが主な方法になりそうです.1のやり方は,Tensorflow 1.x系だと存在したのですが,2.x系で廃止されたようです(tf.compat.v1には残されているので,厳密には存在している).
2.xの書き方になるべくしたいと考えているので,今回は2のやり方で回避しました.

モデル分割のやり方はここを見てほしいです.元のモデルをロードして,分割したものを新しいモデルとして生成しなおす方式をとっています.

3.モデルのトレーニング  


今回はVOC2012をベースに,人,椅子+ソファー,テーブル,人,TVの5分類としました.画像サイズも入力が32x32x3,出力が32x32x5となるように変更し,データ生成時には左右を反転した画像も追加するようにしました(実際はじょうげ反転も入れたほうが良い).
誤差関数はCategorogicalCrossentropyの結果にピクセルの出現回数に合わせた係数を掛け合わせています.

4.トレーニング結果

input
Ground Truth

Output(Before Pruning)
Output(After Pruning)
こんな感じになりました(この入力画像はトレーニングに使用していない).
枝刈りをやってみたのですが,ファイルサイズなどはそこまで変わった気がしていません.クラス分類でArgmaxを取って結果を出すので,Softmaxは取り除いても影響は全くありません.
そこそこ動きそうだったのでNNCaseでコンパイルしました.NNCaseでのコンパイルの際はtfliteの量子化されていない版を使用しましょう.量子化はNNCaseで勝手にやります.

5.NNCase


NNCaseではここを参考にしてコンパイルをしました.オプション等が変わっているので,注意が必要です.


.\ncc.exe compile Model_V0_1.tflite Model_V0_1.kmodel -i tflite -o kmodel --dataset ./data/JPEGImages_Sample/ --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 128 --output-quantize-threshold 256


パラメータの量子化のためにデータセットのサンプルを準備することや,quantizeのためのオプションの値の設定があります.この辺はモデルと出力結果を眺めつつ変えていけばいいと思います.
このコンパイル作業は非常に長いので(3-40min, XPS 9360 i5モデル),気長に待ちます.この時の出力結果にモデルのサイズが出るので,KPUのメモリに載るかを確認しておきましょう.大きい場合は,元モデルの構造を変えることなどで修正しましょう.

6.次回予告


NNCaseでのコンパイルが終わったので,実機確認編です.





©2020 shts All Right Reserved.

2020年6月1日月曜日

M5StickVでSemantic Segmentationやってみた(1)



インターンの日程と授業日程がことごとく合わない中の人です.
今回はやるやる詐欺で延期していたM5StickV(K210)でSemantic Segmentationの話です.
メモリサイズとか、ネットワークが実行できないとか色々あったのでその辺の話もしていけたらと思います.



1.Semantic Segmenationとは


こんな感じのヤツ.https://mc.ai/semantic-segmentation-for-dummies/より

Semantic Segmentationはピクセル単位でクラス分類を行うタスクで,自動運転などの例でよく出てくるあれです.普通のクラス分類と比べ出力のサイズが大きくなりやすい傾向があります.Instance Segmentationはこれの親戚にあたる.

2.モデルの構築


Semantic Segmentationのモデルはいろいろ提案されています.速度重視のものだとENet,ICNet,ESPNetなどがあります。とはいえ論文中だと速度計測用のデバイスがRTX2080やTitanといった普通のGPUになっていることが多く,そのままでは到底K210では使えません。
そこで今回は泣く泣く独自にモデルを設計することから始めます。

3.KPUのメモリとモデルの制約


K210のKPUを使う場合,メモリの制約を気にする必要があります.

参考:kmodel v4 tips

特に入出力用のメモリは2MBなので、画像サイズを気にしないとサイズオーバーで動かなくなります。(NNCaseでのコンパイルはサイズオーバーでも通ってしまい、実機に突っ込んで初めて気がつくなんてことがありました)
今回は32x32x3の入力で32x32x5の出力にしました。もう少し大きくても動きそうな
印象です。
パラメータの方はまだ余裕がありそうなので、もう少し深いモデルでも動きそうです.(今回の場合では50%程度しか使っていない)

4.NNCaseが対応するレイヤ


NNCaseで使えるTFLiteのレイヤには制限があり,kerasで作ったモデルが使えないなんてこともあります.

参考:tflite_ops.md

netronなどで,TFLiteに変換したモデルを確認するといいでしょう.
実はTFLite以外にもONNXとCaffeにも対応しています.こちらも対応表があるので,使う場合には参考にしましょう.

5.自作したモデル


モデル全体図.サイズ違いの同じ画像の入力を渡すようにしている.

今回作成したモデルはサイズ違いの入力を持つ構成にしました.各ブロックの中身はこんな感じ.

ブロック部分.フィルタサイズを変えている.

MixConvもどきです.高速化の観点からだとKPUがダイレクトに走らせられるようにカーネルサイズを3x3にしたいのですが,無理やり3x3を2回走らせるよりはマシってことで5x5カーネルを使用しています.
元の入力が32x32の時点でここまで小さくする必要はないのですが,何となくやってます.実は,KPUの仕様的にはあと1回ぐらいはサイズを落とせる(4x4)ようなのですが,そこまで落としても意味が無さそうなのでやっていません.

6.次回予告


次回もモデルの解説をうだうだやりつつ,NNCase v0.2.0 Beta4を使った実装の方もやっていきたいと思います.



©2020 shts All Right Reserved.

2020年5月30日土曜日

iPad Pro買ってみた




在宅作業が始まる前よりも、忙しくなっている中の人です。在宅作業であったら便利かなと思いタブレットを買ったのですが、使い始めてから1週間ぐらい使ってみたのでそのレビューをしてみようと思います。



1.購入までの経緯


こんな感じの奴を見たかった

そもそもなんで買ったのかって話ですが,
  • 在宅でバイト・授業を受けるので,資料がPDFなどの電子データで来る
  • 家のPCにつないでる画面は1枚だけなので,PDF+ビデオ会議見たいのなことはしにくい
  • PDFに書き込みたい
ってあたりを前々から考えており,実験TAのレポート確認がPDFになることが確定したことがダメ押しになった感じです.

2.機種選定


お値段約12万円(学割適応済み)

今回はiPad Pro 11inch 128GB Wi-fiのみを選びました.いわゆる最安の奴なんですが,
  • iPhone XRの運用経験から,容量は64GBあれば足りそう
  • 出先ではiPhoneのテザリングを使うので,cellularはいらない
  • 画面サイズはPDF読む程度なら11inchで足りる
  • ぬるぬる動いてほしい
  • ビデオ通話もやりたい
ってあたりを判断した結果,iPad Proの最安モデルになりました.

3.使ってみた感想  


汚い画面だ.

買って2週間ぐらいたったわけなんですが,そこそこ好印象でした.
画面が大きくなった恩恵が想像以上に大きく,
  • PCでサイトを読むよりも一度に見れる情報量が多い上,スマホより文字が大きく出来る.
  • ペンでかける範囲が大きいのでメモとかが取りやすい.
ってあたりが良かったです.縦置きディスプレーの印象に近い感じです.

4.その他


扉にくっつく.

元々iPhone XRを使っていたのですが,初期設定の際にアカウントデータとかをbluetoothで勝手に引き継いでくれる機能が良かったです.
バッテリーもそこそこ持ち,Zoom8h耐久とかやっても100%->20%と耐えてくれました.
ケースは純正の奴を買ったのですが,磁力があまりにも強いので鉄の扉とかにiPadごと貼り付けられます.結構便利です.





©2020 shts All Right Reserved.

2020年5月12日火曜日

VSCodeでDraw.ioが使えるExtensionが出たので試してみた


 就活が始まり、慌ててRustやり始めた中の人です。さて今回はVSCode向けにDraw.ioのExtensionが出たのでを使ってみた話です。中の人は結構Draw.ioとVSCodeを使うこともあり、非常にありがたい話です。

開発者(@hediet_dev)の元ツイート https://twitter.com/hediet_dev/status/1259214515683590144?s=20


2020/5/17追記:v0.4.0におけるExport機能についてを追記


1.インストール

"draw"まで打ち込んだら、出てきた

ExtensionsでDraw.ioって打つと"Draw.io Integration"が一番上に出てくるので、それをインストールするだけ。超絶簡単。

マーケットプレイス
https://marketplace.visualstudio.com/items?itemName=hediet.vscode-drawio

2.使ってみる


使ってみた様子。数式も使えれば、矢印もオートでつながる。

使い勝手は、Draw.ioのオフラインエディタに近い感じです。PNGなどの画像出力とか、数式組版とかもそのまま使えます。(数式組版使ったやつをPNGとかJPGとかで吐き出すと、組版の表示が元に戻るみたい)

先ほどのやつをPNGで吐き出した。数式組版が戻る。

今のところ(2020/05/12現在)、PNGなどできれいな数式を入れたい場合は、VSCodeで編集した.drawioファイルをオンラインのDraw.ioに上げてエクスポートしてあげる必要があるみたいです。

2020/5/17追記:

v0.4.0のChange Log.

バージョン0.4.0がリリースされ,Export機能が削除されたようです.
(Removes export options as they did not work.)
もしExportを使いたい場合は,0.3.0にダウングレードして使うか,Draw.ioに編集した.drawioファイルを上げて,Exportしましょう.

3.その他


GitHubにリポジトリが転がってたよ。PDF吐き出せないとかがissueに上がってるみたいです。(2020/05/12現在)



©2020 shts All Right Reserved.

2020年5月10日日曜日

素人でもわかる!"回 路 設 計"におけるリスケジューリング



今まで作った基板たち。圧倒的な青率

 家籠り生活1か月目、ついに手が滑ってiPad Pro 11inchを買ってしまった中の人です。
約1か月振りの投稿です。
 回路設計におけるリスケジューリングって題名ですが、予定通りに進まず、締め切り駆動開発になった話です。




1.回路設計の始まり


作った回路。ふぇぇぇぇ

メカの開発に四苦八苦していたのは前回話しましたが、実はこの裏で回路設計を始めていました。使ってみたいセンサやマイコンのテストを2019/12-2020/01初頭にかけてやりつつ卒論を書きつつ卒研発表準備みたいなことをしていたので、夜の研究室で一人発狂していました(メタなことを話せば、この時期のブログ投稿はすべてこのための準備)。
 なんだかんだコノ辺までは計画通りで、旧正月前には基板が届くようなスケジュールで動くことができていました。

2.発注期限は旧正月前


別基板にして配線するので、未配線の部分が残っている

 とりあえず仕様が決まったので、回路設計を始めました。いろいろな機能を載せようとた結果、再設計を何回もする羽目に...。想像以上にESP32のアンテナ周りの制約がきつく、メカ側の設計にも影響が出ました。この一件で進捗が大幅に遅れました。
 こんな感じで設計を進めていたところ、TLにコロナの話題がちらほらと流れてきました。旧正月は織り込み済みだったのですが、想定外だったのがコロナです。しかも深センあたりも封鎖するみたいな話が流れてきたので、急ピッチで設計を進めました。
 結果、メールを受け取ってくれる最終日に何とか発注を終え一安心...と思ったのですが、結局業者の方が旧正月後も動けず(当たり前ですが)、基板発注から1か月後に到着しました。当初の予定から1か月遅れです。

3.遅れた原因を振り返る


当時のツイート。

 今振り返ってみると、"同時に走らせるタスクが多すぎてキャパオーバーした"ことと、"どこかで詰まることは予想していたが、バッファの見積もりも甘く、時間が足りなかった"のが原因だったと思います。まあ趣味でやってるものなので学業優先が基本なのですが。



©2020 shts All Right Reserved.

2020年4月14日火曜日

0からごみを生み出す!素人機械設計 入門




 サークルの新歓っていつなのかわからない中の人です。今回はメカ設計未経験のド素人が、トイラジコンの設計をする話です。



1.サイズを決めよう!


作りたいサイズは道路が基準。
Jet何某が2車線使っているので、対抗して1車線分で作成。

 何を作るかは決めたので、最初はメカの設計から始めました。手元にあったレゴの道路プレート(廃盤、代替品が出てる)の1車線分を基準にサイズを決めました(50mmx95mm、車軸間62.5mm)。このサイズが後々悲劇を生むわけなのですが。

2.大まかに設計しよう!


設計の参考にしたMINI-Z AWD MA-010。
4輪駆動、ギヤデフ、4輪独立サスなどロマンもりもり。
これの最新版がMA-030で、FF版のMA-03Fがベース。

盛り込みたい機能は決まっているので、実際のラジコンを参考に設計をはじめました。参考にしたラジコンは目標サイズより一回り大きいので、各部分をスケールダウンしながら、違う設計に変えていく必要がありました(丸パクリはできないからね)。
 うだうだ設計を進めつつ(実は基板と並行して設計)、2月中頃には最初の設計が終了しました。

3.条件をよく見よう...


DMMのAgiristaは初めて使った。
最小厚み等で3Dプリント担当者には相当ご迷惑をおかけしました。

 今回はAgiristaやMJFの精度を見極めたかったので、アクリルUltraの使用は最小限にして、AgiristaやMJFを多用することにしました。(Agiristaは単価の安いアクリルだと思っていた)
 実際にDMMの3Dプリントサービスに自分で設計したデータを投げつけたところ、Agiristaで作ろうとしていたデータに厚みが足らない部分があるために作成できないとの連絡が届き、結局設計変更を行い事なきを得ました。

4.足らない強度と精度


できた部品。
届いた部品を嬉々として組み立てたが...

 数日後、部品が届いたので試しに組んでみることにしたのですが、届いた部品を見て絶句しました。あまりにも小さすぎたのです。
 これは決してDMMのせいではなく(むしろほぼ設計通り)、完全に設計段階で本当のサイズを確認していなかった私のせいでした。
 実際に組んでみたのですが、当然思ったとおりには動作しませんでした(小さく作りすぎたため、強度も精度も足らない)。

数万円のお小遣いがゴミに変わった瞬間でした。

5.再設計


再設計は辛いよ...

 ガラクタを作った事実に直面した次の日から、再設計を始めました。この時には、いろいろ妥協して既製品を使うことにしたのですが、またいろいろありました(その辺は再設計編で)。

6.次回予告

 URLのまとめは初回投稿にあるので、よかったらどうぞ。また、テクニカルな面の解説は別にしようと思っています。こちらは少々お待ちください...





©2020 shts All Right Reserved.

2020年4月13日月曜日

卒業制作(改め入学制作)(現在進行形)




 大学院に進学したものの授業開始が5月になり、就活がリアルに終活になりそうな中の人です。今回はかねてからやってはいたものの、あまり表に出してこなかった、卒業制作(改め入学制作)の話です。



1.卒業(入学)制作までの経緯


現状の"名称未設定"君。いい名前を考えてほしい。

 大学に入ってから、あまり"自分の作りたいものを作る"ことができていなかったのと、卒業研究からの逃避場所を設ける観点から、何となくCADをいじり始めたのが最初です。作るものに関しても完全に何となくで自動運転の車に決めました。

2.仕様について


ユニットごとにばらしたところ。

 仕様を固め始めたあたりにM5StickVを塩漬けにしていたことを思い出し、これを使うことに決めました。せっかくなのでいくつか試すことにしました。
  • サスペンション、デフ、ステアリングを盛り込む
  • 昇圧IC、モータの電流検知、LiPo充放電回路の搭載
  • テレメトリシステム、DeepLearningによる画像処理(Semantic Segmentation)
 ざっと書くとこの辺でしょうか。(本当はFPGAの小さいヤツを積んで、デジタルセンサのハブを作るとかも考えていました。)
 すべてにおいて専門外の中の人がやるにはとてつもなく酷野心的ではありますが、とりあえずやってみました。

3.ページまとめと次回予告


  1. 作り始めた経緯(今回)
  2. 0からごみを生み出す!素人機械設計 入門
  3. 素人でもわかる!"回 路 設 計"におけるリスケジューリング
  4. 0からごみを生み出す!素人機械設計 あげいん
  5. 基 板 が 届 か な い
  6. M5StickV触ってみた
  7. (M5StickVでセマンティックセグメンテーション)
  8. (基 板 実 装)
  9. (回路設計あげいんにならないといいな)
  10. (ESP32でテレメトリ)
  11. (動け、"名称未設定"君)
()の中身は現在進行中or予定です。おそらくうまくいかないので、あてにしないでください。

次回は、"0からごみを生み出す!素人機械設計 入門"です。次の更新はいつになるのか...


©2020 shts All Right Reserved.

2020年3月26日木曜日

kmodel v4 tips




 ここ最近はFPGAに全く触れていない中の人です。kmodel v4の実装で詰まったところがあったので、まとめておきます。


1.KPUのメモリとモデル圧縮


推定されるKPUのメモリ空間の割り当て(中の人の想像)。
コンパイル時にWeightsとDataの境界を決定しているらしい。

NNCaseでは、KPUのメモリ空間を6MBのメインメモリと2MBの入出力データ用メモリに分けており、重みやバイアス、中間生成データはメインメモリに載せているようです。
 このため、大きなモデルを載せるためにはメインメモリ側と入出力専用メモリ側の両方でモデルを圧縮する必要があります。今回作成したモデルの場合、入出力専用メモリは足りるのですが、メインメモリ側でサイズ超過が発生しました。

1-1.カーネルサイズ、チャンネル数


 CNN系のレイヤでは、カーネルサイズと入出力のチャンネル数が重みパラメータ数を決めるのですが、大半のモデルでは チャンネル数 >> カーネルサイズなので、チャンネル数削減の方がよりモデルの圧縮に効いてくるようです。

1-2.レイヤ数


レイヤ数の削減は、パラメータ数の削減以外にもネットワーク全体の高速化に効いてきます。ですが精度を落ちてしまうのでそこのバランスはしっかり見ておく必要があります(チャンネル数の削減も同じ)。

1-3.入力データサイズ


 中間生成データの削減には、入力データのサイズを落とすことも有効です。入力のサイズを落とすことで、レイヤ通過後の中間生成データも小さくしようというわけです。


 NNCaseの仕様的にパラメータを載せる領域と中間生成データを載せる領域も分けているようです。中間生成データ領域の量は、モデル内で最も中間データが大きくなった時の値で固定なので、ここを小さくするのが最も効果的でした。

2.アクセラレーション


 完全な形でKPUのアクセラレーションをしようとした場合、いくつかの制限があります。
  • 各レイヤの入力フィーチャーマップが320x240以下かつ出力フィーチャーマップが4x4
  • どの辺も同じパディング幅であること
  • Conv2DやDepthwiseConv2Dのカーネルサイズは1x1または3x3、かつストライド幅が1または2
  • MaxpoolとAveragepoolは2x2または4x4
  • 要素ごとの活性化関数がReLU, ReLU6, LeakyRelu, Sigmoidで、PReLUは未対応(FAQ上だと使える関数に...ってなってたけど、実際はよくわからん)
 部分的なアクセラレーションになるのは

  • 畳み込み系のレイヤで非対称なパディング、パディングされていない場合
  • ストライドが1、2以外(KPUConv2D+StrideSliceに置き換え)
  • MatMul(Pad(to 4x4)+KPUConv2D(1x1kernel)+Crop(to 1x1)に置き換え)->Beta 3でKPU matmulが追加されたので、もしかしたら専用の命令に置き換わってるかも。
  • TransposeConv2D(SpaceToBatch+KPUConv2D+BatchToSpaceに置き換え)
らしいです。これ以外にも置き換えはあるかもしれないのですが、公開はされていないです(NNCaseのオプションに--dump-irをつければレイヤの変換過程が見られる)。
意外とFakeKPUConv2DとかIgnoreが多かったです。

参考:https://github.com/kendryte/nncase/blob/master/docs/FAQ_EN.md

3.入出力データの扱い


この辺は情報が特に出回っていないので、苦労しました。

3-1.データの正規化


 トレーニング時に入力データの正規化を行いますが、M5StickV上で動かすことを考えると[0, 1]に正規化したほうが扱いやすかったです。どうやらNNCaseが勝手に、画像データの入力の場合は[0, 1]に正規化しているようです。[0, 255]とかでもやったのですが、うまくいかなかったです。

3-2.出力データフォーマット


 入力が画像の場合、image.Image()にすればいいので問題ないのですが、出力は画像出力でも1次元のtupleなので変換する必要があります。いろいろ探した結果、NCHWだったことが判明し、実際に変換してみても同じだったので安心しました。
 ただ、tuple->Imageの変換の際には、画素ごとに色を割り当てたいのでNCHWよりもNHWCの方がより便利です。NNCaseのオプションで変えられればいいのですが。

4.その他

4-1.Maix_Toolbox


 Maix_Toolboxにはtflite2kmodel.shという変換用のshellスクリプトが用意されています。中身はNNCaseでコンパイルを実行しているだけなのですが、コンパイルオプションから推定するとNNCase V0.1.0向けであることがわかります。
 なので、NNCase V0.2.0系を入れてしまうと動かなくなります。また、V0.1.0は対応するレイヤが少ないので、個人的にはV0.2.0をお勧めします。

参考:

4-2.Beta2とBeta3


 いつの間にやらNNCase v0.2.0 Beta3がリリースされたようなので、そちらのテストをしました。
 コンパイルのオプションは、Beta 2で使えたものはすべて使えました。Beta 3で追加されたオプションはあるかどうかがわかりませんでした。
 コンパイル結果を見ると、同じモデルでもBeta3の方がメインメモリの使用量が増えているようです。また、"Optimize Pass 3"という工程が増えていました。実行速度はあまり変わっていませんでした。もう少しコンパイラの気持ちになってモデルを作れば、速くなるかもしれません。

4-2(2020/5/21追記).Beta4


 Beta3が出たと思ったら,NNCase v0.2.0 Beta4がリリースされたようなので、そちらのテストをしました。
 リリースの内容を見ると,オプションとして,--weights-quantize-threshold,--output-quantize-threshold,--no-quantized-binaryが追加されているようです.(実は,--dump-weights-rangeなんていうオプションも追加されてる)
量子化周りの設定のようで,しきい値の設定をできるようです.(後でいろいろ書きます.)
 出力されるモデルサイズはBeta3とBeta4で違いはないようです.オプション次第で変わるかもしれないです.

4-3.ファームウェア


カスタムファームウェアの_boot.pyをいじった。
中央右の得体のしれないものは猫。

 フラッシュに焼く際に、モデルが大きすぎると載せられない問題が発生します。その対策として、自分でファームウェアをビルドする方法があります。


 やり方は、上のURLを参考にしてもらうとして、私の場合は_threadとulab、MaixPy IDEのみを有効化し、あとは無効化しました。


©2020 shts All Right Reserved.

2020年3月21日土曜日

NNCase on Windows 10



 NNCaseのV0.2.0がWindows 10に対応しているとのことなので、使ってみました。
"tf.kerasの自作モデルをNNCase  v0.2.0 (kmodel V4)に対応させ、M5StickV(K210)上で動かす(2)" のWindows 10版だと思ってください。





2-1.NNCaseのダウンロード


 今回使用する kmodel V4に対応するNNCaseはV0.2.0以降なので、最新版(2020/3/16現在)であるNNCase v0.2.0 から各OS版の圧縮ファイルをダウンロードします。Windows 10の場合は、ncc_win_x86_64.zip を選択しましょう、

2-2.NNCaseのインストール(?)

 落としてきたファイルをどこでもいいので解凍します。解凍した中にncc.exeが存在するはずです。
 このexeファイルをトレーニング済みの.tfliteがあるフォルダに投げ込みましょう。これでインストール(?)は終わりです。

2-3.コンパイルオプション

2-4.コンパイルの例

 コンパイル例です。


.\ncc.exe compile Model.tflite Model.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

 中の人は、PowerShellで動作確認しました。

2-5.その他

 一応動いたので、よし。
 (Macを持っていないので、そちらの動作確認はほかの方に投げたい所存です。)


©2020 shts All Right Reserved.

NNCase 記事まとめ




 NNCaseの記事のまとめです。


tf.kerasの自作モデルをNNCase v0.2.0 (kmodel V4)に対応させ、M5StickV(K210)上で動かす


kmodelを作るときのtips


セマンティックセグメンテーションやってみた


NNCase V1.7を試す




©2020 shts All Right Reserved.