今回は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
腹筋開発,"Donkeycar3.1.0上の機械学習モデルを調べてみた", https://fight-tsk.blogspot.com/2019/09/donkeycar310.html
2.モデルの作成
本家の実装は汎用性が高い実装になっていますが,今回はcategoricalのみが動けばよいので,少し手を入れてミニマムな構成に直しました.
とりあえずこんな感じです(Conv2DのActivationとか使えばいいのですが,気にしない). Dropoutに関してはtfliteに変換する際に消えるので問題ないです.これをnncaseで変換し,PC上で実行時間のシミュレーションをしてみました(モデルの訓練はしていません).
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のカラー画像(ランダムなピクセル値を使用)を使用し,実行時間を計測しました.
PC上では100msで,全体としてはConv2Dの演算に時間がかかっていそうです.最初のQuantizedConv2DはKPU上で演算されていない様子なので,もう少し上手くやる方法がありそうです.意外とMatMulが高速みたいですが,実機だとどうなるかが気になります.
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だったので時間表示に修正しました.)
PCの結果の27倍実行に時間がかかっていることがわかりました.PCの結果やNNCaseのQAから察するに,おそらくフィルタサイズが5x5のConv2Dが部分的にしかアクセラレートされていないことが主な原因だと考えられます(1x1 or 3x3が完全な形でアクセラレートされる).
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.