概要
Maix-EMC, Embedded Model Convertor Discuss topic
https://bbs.sipeed.com/t/topic/916
の翻訳です。
EMCについて
関連するドキュメントの説明は、githubのreadmeを参照してください。本書は主に、EMCの開発に関与するものについて説明しています。
https://github.com/sipeed/Maix-EMC
EMCの設計目的と作業
EMCは、NNモデルをより多くの組み込みプラットフォームで実行できるように設計されています。
JiaboのNNoMは良いスタートを切っていますが、kmodelバイナリモデルファイル+ kmodelインタープリターの形式は、誰にとっても使いやすく、より汎用性が高いと思います。
EMCはこの作業の左側であり、PC側のモデルファイルをバイナリフラットモデルファイルに変換します。
PC側のモデルファイルはTensorLayerを使用します。これは、tensorlayerのレイヤー定義がより適切であり、これは基本的にkmodelで定義されたレイヤーの高さと同等であるためです。 。
対照的に、tensorflowのprbファイルの演算子はAdd、mulと同じくらい低いため、これらの低レベルの演算子をレイヤー定義に手動で統合することは非常に不便です。
この作業の途中に、バイトコードまたはIRに似たkmodelファイルがあります。
ここでのモデルファイルは、一般的なprotobuf(pb)またはflatbuffer(tflite)を使用しません。これは、それらの解像度とメモリ消費量が組み込みプラットフォームにとって大きすぎるためです。
デモをすばやく実装し、組み込みの効率を検討するために、ここではk210 sdkのkmodel v3形式を使用します。
この形式はK210用に設計されていますが、一般的な組み込みプラットフォームにも適しています。共通のレイヤータイプの定義を追加するだけです。
この作業の右側には、組み込みハードウェアプラットフォーム上のkmodelインタープリターがあります。
k210の場合、SDK自体のkpu.cを借用し、わずかに変更するだけです。
通常のマイクロコントローラーの場合、kpu.cの畳み込み層計算関数を通常のcpu計算関数で置き換えるだけです。
ここでの計算バックエンドは、CMSIS-NNまたはNNoMのコンピューティングバックエンドを借用し、ラッパーをkmodelレイヤーパラメーターに配置できます。
EMCの現状
2019.6.28
設計の最初にK210ドライバーを借りたので、最初に動作していた非常にアルファ版のEMCの完成に1週間以上費やしました。
現在のバージョンは、TLの例のcifar10デモのみをデバッグします(モデルのサイズを小さくするために、完全に接続されたチャネルの数をわずかに変更しました)
一般的な畳み込み層、FC、upload、maxpool2d、flatten、GAP2D、quant、dequant、その他のレイヤータイプが実装されています。
ただし、量子化と逆量子化を処理するとき、kpuのアップロードとダウンロードの順序は慎重に処理されていないため、修正が必要なmobilenet変換エラーが発生します(約1日のワークロード)。
さらに、FCおよびconvアクセラレータを使用して最適化できるその他のレイヤーは現在最適化されていませんが、ローカルCPUによって直接使用され、効率は比較的低くなります。
現在、単純なスタックネットワーク構造のみがサポートされており、分岐構造はまだサポートされていないため、改善する必要があります。
仕事の優先順位:
- conv関連レイヤーのアップロード/ダウンロードの順序を修正し、mobilenetデモを完了します。
- TLがサポートしていないsoftmaxをサポートするために、レイヤーの加速最適化を実行します。
- 量子化アルゴリズムの最適化
- 通常のMCU共通サポート、統合されたNNoMバックエンド
- 分岐ネットワーク構造のサポートにより、RNNをより適切にサポートできます。
共同開発のためのクイックスタートティーチング
まず、EMCのK210プラットフォームのサポートを改善する必要があります。
EMC改善プログラムに参加している友人は、無料のK210 Deluxe開発ボードを受け取ることができます。詳細については、Zebianに連絡するか、support @ sipeed.comにメールしてください。
K210の概要
K210はRV64GCコアのAIOTチップで、内蔵KPUは畳み込み/プーリング/ BN /アクティブパケットアクセラレーション操作を実行できます。
詳細については、次を参照してください
MAIX KPU demystify: write your first layer by hand config registers
https://bbs.sipeed.com/t/topic/502
Sipeedはk210のmaixpy(micropython)環境を移植したので、Pythonを使用してK210ボードで独自のモデルを実行できますが、コミュニティはまだ多くの組み込み開発者、AI開発者の減少、およびモデルサポートの不足に苦しんでいます。 そこで、時間をかけてMaix-EMCプロジェクトを行い、より多くのAI開発者とモデルをインポートしたいと考えています。
Kmodel形式の概要
Kmodelは、カスタムのフラットモデルストレージ形式であり、EMCコードで行ったモデル形式のパッケージです。簡単に紹介します。
EMCでは、c構造を解析するpyhtonのライブラリであるdissect.cstructを呼び出しますが、kmodelのk210のkpu.c構造定義を使用すると非常に便利です。 EMCでは、定義のこの部分はk210_constant.pyに配置されます。
kmodel_def ="""
typedef struct
{
uint32 version;
uint32 flags;
uint32 arch;
uint32 layers_length;
uint32 max_start_address;
uint32 main_mem_usage;
uint32 output_count;
} kpu_model_header_t;
typedef struct
{
uint32 address;
uint32 size;
} kpu_model_output_t;
typedef struct
{
uint32 type;
uint32 body_size;
} kpu_model_layer_header_t;
kmodelヘッダーはkpu_model_header_tであり、バージョン、量子化ビット数、レイヤー数、最大メモリフットプリント(一度にモデルに適用するために必要な動的メモリ)、および出力ノードの数を記述します。
ヘッダーの後に、いくつかのkpu_model_output_tが配置されて、出力ノードの情報を記述します。
ノード情報が出力された後、すべてのレイヤーのヘッダー情報が配置されます:kpu_model_layer_header_tは、レイヤーのタイプとレイヤー本体のサイズを順番に記述します。
レイヤーヘッダー情報の後、レイヤー本体データはレイヤー情報ごとに順番に配置されますが、その一部には特定のバイトアラインメントが必要です。
レイヤータイプは、オリジナルのkpu.hの定義でわずかに変更されたedge_constant.pyで定義されます。これは、k210プライベートレイヤーを通常のレイヤーと区別します(これは、K210専用レイヤーを使用してK210ドライバーを高速移植します。理論的には、1つのみが定義されます。 共通層標準のセットが優れています)
K210のkmodelインタープリターの実装
kpu.cを参照してください。ドライバーはkmodelの各レイヤーのレイヤー情報を順番に読み取り、レイヤータイプに応じて対応する機能を実行します。
アップロード/ダウンロード操作に注意を払う必要があります。
K210メモリは、6M CPUメモリと2M KPUメモリに分かれています。
KPUによって計算されたレイヤーを使用するには、計算されるデータをKPUメモリにアップロードする必要があります。
KPUでは、結果をCPUメモリにダウンロードすることなく、CONV関連の計算の多くのレイヤーを継続的に計算できます。
ただし、次のレイヤーがCPU操作を必要とするレイヤーになったら、ダウンロードを実行して実行を続ける必要があります。
したがって、TL層の順序に注意を払い、KPU / CPUを切り替える必要がある層の前後にアップロードおよびダウンロードのダミー層を挿入する必要があります。
EMCでは、meta_info [‘is_inai’]フィールドを使用して、計算される現在のコンテンツがAIメモリ内にあるかどうかを確認します。
さらに、KPUは、使用される2Mメモリがピンポンの形式であると計算します。
つまり、入力データは先頭にあり、出力は末尾にあります。
次のレイヤーに入った後、前のレイヤーの出力が入力結果として使用され、最後に計算結果が先頭に置かれます。
この相互計算では、EMCのmeta_info ['conv_idx']は現在の畳み込み層番号を記録し、現在の出力が配置されているKPUメモリのオフセットを確認します。
その他の注意点、表示するにはkpu.cをダウンロードする必要があります
https://github.com/kendryte/kendryte-standalone-sdk/blob/develop/lib/drivers/kpu.c
TLレイヤーをkmodelに変換するEMCのプロセス
エントリファイルはedge_model.pyです
Gen_edge_layers_from_network TLレイヤーをEMCのレイヤー中間表現に変換します。
まず、platform_tableテーブルを使用して、現在のハードウェアプラットフォームで使用されるTLレイヤーからEMCレイヤーの機能テーブル、およびパッケージモデルの機能を選択します。
platform_table = {
platform tl layer convertor model generator
'k210' : [tl_to_k210_table, gen_kmodel]
'stm32' : gen_stm32_layer_func_table,
}
tl_to_k210_tableで、gen_edge_layer_from_networkは対応するTLレイヤータイプエントリを見つけてから、最も長いリストに一致し、そのリストをlayer_generatorに渡してEMC中間層のリストを生成します(追加/ダウンロードの前/後/ ダミー層の量子化/逆量子化)
tl_to_k210_table= {
TL layer class layer_generator merge
'Dense' :[gen_fc_layer, []] ,
'Flatten' :[gen_flatten_layer, []] ,
'Reshape' :[None, []] ,
'GlobalMaxPool2d' :[gen_gmaxpool2d_layer, []] ,
'GlobalMeanPool2d' :[gen_gavgpool2d_layer, []] ,
'MaxPool2d' :[gen_maxpool2d_layer, []] ,
'MeanPool2d' :[gen_avgpool2d_layer, []] ,
'Concat' :[gen_concat_layer, []] ,
'Conv2d' :[gen_k210_conv_layer, [['BatchNorm'],]] ,
'DepthwiseConv2d' :[gen_k210_conv_layer, [['BatchNorm'],]] ,
'DummyDequant' :[gen_dequant_layer, []] ,
}
これまで、最初にTLレイヤーを一連のレイヤーリストに変換してから、optimize_layersを使用してレイヤーリストを最適化し、オフセットレイヤー(隣接する量子化/逆量子化レイヤーなど)を削除します。
次に、gen_kmodelを使用して、レイヤーのリストをkmodelに変換します
レイヤーリストの各レイヤーにはto_kmodelメソッドがあり、これを使用して、kmodel形式に準拠する現在のレイヤーのレイヤー本体のbytearray結果を取得できます。
Gen_kmodelは、すべてのレイヤーのボディをスタックし、最大動的メモリ要件をカウントし、ヘッダーを追加してkmodelを取得します。
EMCレイヤーサポートの追加
最初に、edge_model.pyのtl_to_k210_tableに追加する必要があるTLレイヤーとEMCレイヤーの変換テーブルエントリを追加します。
次に、対応するxxx_layer.pyに対応する実装を追加します。
K210関連のアクセラレーションレイヤーはk210_layer.pyに実装されています(現在実装されていますが、いくつかのバグを修正する必要があります)
CPUによって計算された非加速層は、edge_layerに実装されています。
その中のレイヤーの実装を模倣するだけで、レイヤーのタイプごとに、次の関数の1つと1つのクラスを完了します。
Gen_xxx_layer:TLレイヤーリストを入力し、EMCレイヤーリストに変換します
クラスxxx_Layer:initメソッド(レイヤー情報を埋める)とto_kmodelメソッド(kmodel形式で情報を埋め、パッケージ化されたbytearrayを返す)を実装する必要がある
実際、カスタムto_xxxmodelメソッドを実装して、このフレームワークに独自のモデル形式を実装できます。
K210実機デバッグ
EMCのサンプルディレクトリにあるk210_projectプロジェクトから始めることをお勧めします開始したばかりのAI開発者については、WeChatグループで私に相談できます。
補足
本Qiitaは元の文書に機械翻訳をかけただけです。
おかしいところは、原文の記載を参照ください。