motoh's blog

主に趣味の電子工作やプログラミングについて書いていきます

M5Stack CoreS3でディープラーニング推論を実装してみた

以前、M5StackシリーズのTimer Camera X(ESP32マイコンを搭載)でディープラーニングの推論を試してみた記事を投稿しましたが、新しく発売されたM5Stack CoreS3にカメラが搭載されたため、移植して記事も刷新しました。また、以前は開発環境としてArduinoIDEを使用していたため、ライブラリを取り込むところでやや余計な手順が発生していましたが、今回はPlatformIOを使用して開発効率も上がりました。

本記事ではM5Stack CoreS3のカメラに映った手の指がさしている方向(中央、上、下、右、左)を、デバイス側のプログラムだけで推論できるようにするまでの手順を解説します。学習はGoogle Colaboratory上で行います。自分で用意した画像を用いて学習させることができます。

次の動画は、M5Stack CoreS3で実行した様子です。動画では完璧に指差した方向を認識していますが、背景の色などが変わると認識率がとても悪くなります。実用的なものを作るにはもっと勉強が必要のようです^^;

youtube.com

GitHubにPlatformIOの最終的なプロジェクトも入っていますので、ビルドしてM5Stack CoreS3に書き込めばすぐに試すことができます。

github.com

開発環境

項目 名称 バージョン
M5Stack実機 M5Stack CoreS3
開発用PC Windows 11 Pro 21H2
IDE
(実機プログラム開発用)
PlatformIO Core 6.1.7 Home 3.4.4
AI開発環境
(学習モデル生成用)
Google Colaboratory
ライブラリ : Neural Network Library (nnabla)

1.33.1

PlatformIOについて

PlatformIOはVisual Studio Code拡張機能としてインストールします。インストール手順の説明は割愛しますが、こちらの記事などが参考になります。

qiita.com

Google Colaboratoryについて

学習は自前のPC上のPython環境で行ってもよいのですが、今回はGoogle Colaboratory (以下Colab)を利用しました。Colabなら環境構築に手間をかけることなく、GPUも簡単に使うことができます(ここではColabの使い方は解説しませんが、Googleのアカウントさえ持っていれば簡単に使い始めることができます)。

機械学習フレームワーク Neural Network Library (nnabla) について

マイコンディープラーニングの推論を動かす方法はいくつかあるようですが、今回はSonyフレームワーク Neural Network Library (nnabla)を使う方法を選びました。nnablaは学習済みモデルをマイコンで実行できるCソースコードに変換する機能を備えています。

nnablaのライブラリやサンプルプログラムはGithubで公開されています。

github.com

github.com

開発手順の概要

大まかな手順は次の通りです。以降、詳しく解説していきます。

No. 作業内容 作業環境
CSVファイルでデータセットのリストを作成する(nnablaでデータセットを読み込むときに必要)。 Python (Colab)
nnablaのMNISTサンプルプログラムを、MNISTデータセットではなく自分のデータセットを読み込めるように改造する。 -
改造したサンプルプログラムを実行し学習済みモデルのファイル(.nnp)を出力する。 Python (Colab)
nnabla_cliコマンドでnnpファイルをCソースコードに変換する。 Python (Colab)
変換結果ファイルとnnablaランタイムをPlatformIOのプロジェクトに取り込む。 PlatformIO
カメラ画像から推論するコードを作成する。 PlatformIO

なお、①~④のColabでの作業については、解説のコメントを付けたColabノートブックをGitHubで公開しますので、このノートブックのコードを順番に実行すれば④までの作業が完了します。

github.com

[補足]
ColabでGPUを有効にしていないと、上記ノートブックを実行できません。もし有効になっていなければ、Colabのメニューの「ランタイムのタイプを変更」から設定してください。

手順① データセットのリストを作成する

nnablaにデータセットを読み込ませるためには、データセットのリストをCSVファイルで渡す必要があります。 CSVファイルは次のようなフォーマットで、学習用とテスト用に分けて作成します。

x:image,y
画像データのファイルパス, 分類(0,1,...,n)
画像データのファイルパス, 分類(0,1,...,n)
画像データのファイルパス, 分類(0,1,...,n)
・
・
・

今回、分類は次のようにしました。

0: 何も写っていない画像
1: 中央を指している画像
2: 上を指している画像
3: 下を指している画像
4: 右を指している画像
5: 左を指している画像

データセットGitHubのmy_datasetフォルダに、このような感じで分類毎のフォルダに分けて入っています。

今回、このようにフォルダ分けしたデータセットから、自動的にCSVファイルを作成するPythonスクリプトも作ってGitHubに入れました(make_dataset_csv.py)。データセットを8:2の割合で分けて、train.csvとtest.csvを作成してくれます。さらに、データセットの画像を28x28のグレースケールに変換してくれます(変換後の画像データはconverted_datasetsフォルダに保存されます)。

Colabノートブックでは、このデータセットスクリプトを利用してCSVファイルを作成しています。

手順② MNISTサンプルプログラムを改造する

nnablaのMNISTサンプルプログラム(classification.py)を、MNISTデータセットの代わりにCSVファイル(train.csv,、test.csv)を読み込むように改造します。

改造の仕方は次の記事がとても参考になりますのでここでの解説は割愛しますが、改造済みのプログラム(classification_mydata.py)をGitHubに置いています。Colabノートブックでもこのプログラムをダウンロードして使用しています。

cedro3.com

手順③~④ Colab上で学習し、モデルファイルを生成する

Python環境でnnablaのMNISTサンプルプログラムを実行し、学習済みモデルのファイル(.nnp)を出力します。その後、nnabla_cliコマンドでnnpファイルをCソースコードに変換します。ここまではColabノートブックで実行できます。

変換結果として以下のCソースコードが得られ、以降の手順で使用します。

  • Validation_inference.c
  • Validation_inference.h
  • Validation_parameters.c
  • Validation_parameters.h

手順⑤ 変換結果ファイルとnnablaランタイムをPlatformIOのプロジェクトに取り込む

ここからはPlatformIOでの作業になります。変換で得られたCソースコード、及びnnablaのランタイムを、下図のようにPlatformIOのプロジェクトフォルダに配置します。

nnablaのランタイムはgit cloneで入手します。

> git clone https://github.com/sony/nnabla-c-runtime.git

図中のstb_image_resize.hは、カメラ画像を前処理としてリサイズするときに使うライブラリで、こちらGitHubリポジトリで公開されているものを使用させていただきました。

また、Validation_inference.cとValidation_parameters.cは次のように修正が必要です。

■Validation_inference.c 修正内容
130行目付近の関数宣言をコメントアウト(削除)します(そうしないと、ビルド時に二重定義だと言われる)。

//void *(*rt_variable_malloc_func)(size_t size) = malloc;
//void (*rt_variable_free_func)(void *ptr) = free;

//void *(*rt_malloc_func)(size_t size) = malloc;
//void (*rt_free_func)(void *ptr) = free;

■Validation_parameters.c 修正内容
全てのfloat型配列にconstを付けます。ビルド時にRAMがオーバーフローしたというエラーが出たため、配置先をFlashに変更するためにconstを付けました。

const float Validation_parameter11[] = {      /* その他全てのfloat型配列にconstを付ける */

手順⑥ カメラ画像から推論するコードを作成する

カメラ画像から推論するコードを作成します。この部分に関しては、ほとんどこちらの記事を参考にさせていただきました。

qiita.com

ソースコードのプロジェクト一式はGitHubのPlatformioフォルダに置いてあります。手順①~⑤を実施済みのプロジェクトなので、ビルドしてM5Stack CoreS3に書き込めば動かすことができます。

■カメラ画像から推論するコード(抜粋)

    uint8_t resized_img[NNABLART_VALIDATION_INPUT0_SIZE];

    _context = nnablart_validation_allocate_context(Validation_parameters);
    float *nn_input_buffer = nnablart_validation_input_buffer(_context, 0);
    
    while(true){    

        fb = esp_camera_fb_get();
        if (!fb) {
            Serial.println("Camera capture failed");
            res = ESP_FAIL;
        } else {

              // 28x28にリサイズ
              stbir_resize_uint8(fb->buf, 160, 120, 0, resized_img, 28, 28, 0, 1);

              // 推論
              int64_t infer_time = esp_timer_get_time();
              nnablart_validation_inference(_context);

              // 推論結果をフェッチ
              float *probs = nnablart_validation_output_buffer(_context, 0);

              int top_class = 0;
              float top_probability = 0.0f;
              for (int classNo = 0; classNo < NNABLART_VALIDATION_OUTPUT0_SIZE; classNo++) {
                  if (top_probability < probs[classNo]) {
                      top_probability = probs[classNo];
                      top_class = classNo;
                  }
              }
                
              Serial.printf("Result %d ", top_class);  
        } 
    }

まとめ

新しく発売されたM5Stack CoreS3はカメラが搭載されたため、以前Timer Camera Xでやっていたディープラーニング推論を移植してみました。また、以前はArduinoIDEで開発していましたが、今回はPlatformIOで開発することで開発効率も上がっています。冒頭の動画を見るとかなり精度が高いように見えますが、実際は背景に映り込むものによってかなり精度が悪くなります。そこらへんはこれから勉強していきたいと思います。ちなみに、指差した方向を推論させた理由は、スタックチャンとあっち向いてほいをしてみたいと思ったからですw(スタックチャンって何?という方はぜひTwitter等で検索してみてください)

参考書籍・サイト

文中で紹介したものもありますが、参考書籍・サイトを記載します。

人気ブロガーからあげ先生のとにかく楽しいAI自作教室 (Amazon)

M5Stackは扱われていませんが、ディープラーニングを学び直したり今回の挑戦(エッジAI)をしたりするきっかけになった本です。Google Colaboratoryを使っているのもこの本の影響を強く受けています。

マイコンでディープラーニングした話 on ESP32 - Qiita

M5StackでNeural Network Library (nnabla)のMNISTのサンプルを動かす方法について参考にさせていただきました。

SONY Neural Network Libraries データセットの読ませ方 | cedro-blog

nnablaに独自のデータセットを読み込ませる方法について参考にさせていただきました。