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.

0 件のコメント:

コメントを投稿