😸

音識別エッジAI開発

に公開

1. 前提

目的

M5stackでCNNを動作させたことを説明しているサイトがいくつかあるが、サイトが消えてしまった時に困るので、備忘録として自身に必要な情報を集約しておく。

概要

以下を用いて、音を識別するDNNを作成する。

  • デバイス:M5stack S3, M5stack AWS
  • DNNフレームワーク:NNabla
  • 学習PCのGPU:Nvidia RTX3080Ti

手始めに声と拍手の識別を行った。

フレームワーク選定

以下のサイトを参考にエッジAIに用いるフレームワークを選定。
M5stackで実績のあるNNablaを選定した。
ONNXに興味があったが、NNablaで学習を行う。

アノテーション

学習時に読み込ませるcsvファイルを作成する。
エッジデバイスで音を検出した時に、マイクで取得したAD変換値を記録したcsvファイルを記録するアノテーション機能を開発した。
github link

2. 学習

取得したcsvファイルを学習させる。

ベースソフト

NNabla Mnistサンプルを1D CNNに変更して学習を実施した。

開発したソフト

https://github.com/sota1111/noise-detector/tree/main
nnabla runtimeが必要なので、recursiveでcloneしてください。

git clone --recursive https://github.com/sota1111/noise-detector.git

作成した1D CNN

    # 1層目: 畳み込み → ReLU → MaxPooling
    c1 = PF.convolution(x_in, 3, (3, 1), name='conv1d_1')
    c1 = F.relu(c1)
    c1 = F.max_pooling(c1, (2, 1))
    
    # 2層目: 畳み込み → ReLU → MaxPooling
    c2 = PF.convolution(c1, 6, (3, 1), name='conv1d_2')
    c2 = F.relu(c2)
    c2 = F.max_pooling(c2, (2, 1))
    
    # 3層目: 畳み込み → ReLU
    c3 = PF.convolution(c2, 12, (3, 1), name='conv1d_3')
    c3 = F.relu(c3)
    
    # 全結合層(24ユニット, ReLU活性化)
    c4 = PF.affine(c3, 24, name='fc1')
    c4 = F.relu(c4)
    
    # 出力層(2クラス分類: 全結合層)
    c5 = PF.affine(c4, 2, name='fc2')
    return c5

学習コマンド

以下のコマンドで学習させた。

python3 classification.py -c cudnn -n conv1d -o output --batch-size 32 --learning-rate 0.001 --max-iter 300 --val-interval 10 --val-iter 20

課題と対策

課題

  • 学習が進まない現象が発生。

対策

学習が進まない現象が発生。
正規化0~1

正規化の仕方を0~1ではなく、-1~1にすると学習が進んだ。
正規化-1~1

学習後の混合行列が以下。
学習後の混合行列

3. 学習PC➡エッジ

ソース生成

以下のコマンドでC言語のファイルを生成する。

cd traning
nnabla_cli convert -O CSRC -b 1 ./output/lenet_result.nnp ./output_csrc

以下のファイルが生成される。

.
└── output_csrc
    ├── GNUmakefile                  テスト用Makefile
    ├── Validation_example.c         テスト用コード
    ├── Validation_inference.c       モデル定義類
    ├── Validation_inference.h       ↑のヘッダ
    ├── Validation_parameters.c      重みパラメータ類
    └── Validation_parameters.h      ↑のヘッダ

ランタイムの格納&変更

ランタイム格納

M5stackのlibの下にnnablaのランタイムを格納する。

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

training/output_csrcをinference/src/Validationに格納する必要がある。
事前に一部、編集する必要がある。

ランタイム修正

■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_parameter**[] = {

学習PC➡エッジの度に上記ファイルを変更するのはめんどうなので、pythonでファイル修正&コピー置き換えを実行できるようにした。
以下のコマンドで上記の修正および、ファイルのコピー置き換えを実行する。

python3 add_const.py

4. 推論

SDカードからアノテーションにより作成したcsvファイルを読み出し、推論を実行した。
後述の課題を解決し、csvファイルによる推論は完成。
騒音検出をトリガに音を判別させ、200ms程度で推論が成功することを確認した。

課題と対策

課題

  • 学習時の正答率が97%以上にも関わらず、推論が誤る現象が発生。
    理由は不明だが、学習時は正答するが推論は誤る。

対策

  • 学習時のDNNからグローバルマックスプーリングを削除。

課題

  • スタックオーバーフローでリセット

対策

  • floatを10,000個読み込ませていたが、8,000個に減らした。
    スタックサイズを大きくする方法や動的に確保する方法もあるが、とりあえず動かしたかった。

5. 結果

音声識別AIをm5stackで200ms程度で実行し、95%以上の確率で正答できた。
今後は、高性能マイクを取り付け、複数の音を識別させたい。

参考サイト

https://mzmlab.hatenablog.com/entry/esp32-ai-platformio

確認したサイト

https://qiita.com/yoku_/items/547358444bd8b811c0e0
http://cedro3.com/ai/nnabla-original-dataset/
https://vengineer.hatenablog.com/entry/71626838

Discussion