学習時のvalidation accuracy 97%だが、エッジ(m5stack S3)で推論を誤る

74 views
Skip to first unread message

sota1111

unread,
Mar 20, 2025, 11:37:20 PMMar 20
to Neural Network Console Users (JP)
Neural Network Consoleのコミュニティ御中

nnablaのhttps://github.com/sony/nnabla-examples/tree/master/image-classification/mnist-collectionを変更し、1D CNNの音声認識に変更しています。

以下の問題が発生しており、コミュニティのアドバイスを頂きたいです。

学習時にvalidation accuracyは97%ですが、生成したコードをエッジに読み込ませると、どの入力に対しても同じ確率を示し、推論を誤ります。以下のような状況です。
・学習時と推論時には同一のcsvファイルを読み込ませており、データを全て読み込んでいることは確認しています。
・エッジのm5stack S3ではmnistを動作させ、正しく推論されることは確認しています。
・入力値を正規化した値-1~1だと必ずclass0:0.72, class1:0.28となりますが、-1以下や1以上の値を入れると、class0:1, class1:0に変わります。

学習コード:
https://github.com/sota1111/nnabla-examples/blob/feature/qa_sony/image-classification/mnist-collection/classification.py

学習時のコマンド:
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

生成コマンド:
nnabla_cli convert -O CSRC -b 1 ./output/conv1d_result.nnp ./output_csrc

生成後のコードにconstを付ける:
https://github.com/sota1111/nnabla-examples/blob/feature/qa_sony/image-classification/mnist-collection/add_const.py


推論コード:
https://github.com/sota1111/DetectSoundS3/tree/feature/qa_sony
main.cpp
学習曲線.png
classification.py

G.T.

unread,
Mar 24, 2025, 11:41:51 AMMar 24
to Neural Network Console Users (JP)
sota1111さん、こんばんは。ソースを拝見しました。

m5stack S3を検索しましたが、ワンチップマイコン(?)なのですね?
つまりpythonのnnablaで学習させ、学習済みモデルのファイル(.nnp)をC++を使ってコンパイルし、m5stack S3上で動かすという認識で合っているでしょうか?
そして前提条件としてm5stack S3はloop関数がメインと言う事で宜しいでしょうか。

当方、NNCしか使った事が無く、nnablaもm5stack S3(のC++ライブラリ)も使った事はありませんが、C++や組み込みのプログラミング経験はあります。もしかすると的外れかも知れませんが、ちょっと気になった点を幾つか書かせて頂きます。

CSVファイルの情報が見当たらず、下記が意図通りなのか分からなかったので。
1.CSVファイルの1行目だけ読み込んでますが、読み込むのは1行目だけで良いのでしょうか?(CSVにヘッダ行はある?)
2.一つ推論したらNNコンテキストを解放していますが、CSVファイルは複数あるのでしょうか?
3.ローカル変数でchar csvBuffer[]を確保していますが、データサイズはスタックのサイズ内なのでしょうか?
4.正規化部分((atof(token)-2048.0)/2048.0)で2048を引いていますが、CSVファイル中の値の範囲は0~4096で良いのでしょうか?
5.atofはdoubleを返しますが、nn_input_buffer[index]への代入はdoubleで宜しいでしょうか?
6.コードを見るとCSVファイルの時だけcloseしてませんが、これは意図的でしょうか?

色々と書きましたが、怪しそうなのは1番でしょうか。
的外れだったら申し訳ありません。

以降はmain.cppを拝見して「私的にはこうした方が見やすいと思う」と感じた点を老婆心ながら書かせて頂きます。
私のコーディング上の好みなのでお気を悪くされたら申し訳ありません。

1.処理しない部分は先に除外する。
    // ディレクトリの場合は無視、
    if (file.isDirectory()) {
      file.close();  // ディレクトリの場合は閉じる
      continue;
    }

    // CSVファイルでない場合も同様に無視
    if (!filename.endsWith(".csv")) {
      file.close();  // .csv でないファイルは閉じる
      continue;
    }
・・・以下、CSVファイルの時の処理

こうした方がインデントが浅くて済みますし、「ここから先は全部CSVの時の処理」と切り分けできるので読みやすくなると思います。


2.CSV行を解析して各値を入力バッファに格納する処理の部分ですが、この場合はfor文の方がすっきりすると思います。
    // strtok を使うために、String を char 配列に変換
    char csvBuffer[csvLine.length() + 1];
    csvLine.toCharArray(csvBuffer, csvLine.length() + 1);
    char *token = strtok(csvBuffer, ",");

    // CSV行を解析し、各値を入力バッファに格納
    for( int index = 0; token != NULL && index < NNABLART_VALIDATION_INPUT0_SIZE; index++ ) {
      nn_input_buffer[index] = (atof(token)-2048.0)/2048.0;  // 文字列を float に変換して代入
      token = strtok(NULL, ",");
    }

2025年3月21日金曜日 12:37:20 UTC+9 sota1111:

G.T.

unread,
Mar 24, 2025, 5:27:06 PMMar 24
to Neural Network Console Users (JP)
おはようございます。

> 6.コードを見るとCSVファイルの時だけcloseしてませんが、これは意図的でしょうか?
失礼しました。m(_ _)m
CSVを1行読み込んだ後にcloseしてましたね。


2025年3月25日火曜日 0:41:51 UTC+9 G.T.:

sota1111

unread,
Mar 25, 2025, 10:56:16 PMMar 25
to Neural Network Console Users (JP)
回答有り難うございます。

1.CSVファイルの1行目だけ読み込んでますが、読み込むのは1行目だけで良いのでしょうか?(CSVにヘッダ行はある?)
ヘッダはなく、一行目にデータがあります。以下のデータを学習時にも推論時にも用いています。
https://github.com/sota1111/nnabla-examples/tree/feature/qa_sony/image-classification/mnist-collection/dataset
2.一つ推論したらNNコンテキストを解放していますが、CSVファイルは複数あるのでしょうか?
はい。

3.ローカル変数でchar csvBuffer[]を確保していますが、データサイズはスタックのサイズ内なのでしょうか?
はい。

4.正規化部分((atof(token)-2048.0)/2048.0)で2048を引いていますが、CSVファイル中の値の範囲は0~4096で良いのでしょうか?
はい。

5.atofはdoubleを返しますが、nn_input_buffer[index]への代入はdoubleで宜しいでしょうか?
floatを想定していました。キャストします。nn_input_bufferがfloatなので暗黙のキャストになっていると思われます。
キャストを付けましたが、動作に変わりはありませんでした。


2025年3月25日火曜日 0:41:51 UTC+9 G.T.:
sota1111さん、こんばんは。ソースを拝見しました。

G.T.

unread,
Mar 29, 2025, 6:19:29 AMMar 29
to Neural Network Console Users (JP)
sota1111さん、こんばんは。やっと余裕が出来たのでデータ拝見しました。
ヘッダなしの800個のデータなのですね。

> nn_input_bufferがfloatなので暗黙のキャストになっていると思われます。
実は前回、ちょっと気になったのがこの点(C++コンパイラの挙動)なのです。
と言うのも、下記のコードですが・・・C++だと配列を定義する際の添え字は定数じゃないとエラーになる筈なんです。
コンパイル時にサイズが分かって無いと、どの大きさの領域を確保すれば良いか分からないので。


    // strtok を使うために、String を char 配列に変換
    char csvBuffer[csvLine.length() + 1];

もっとも、ここでメモリ破壊を起こしてたら暴走するはずなのでもしかするとC+の内部で何か特別な事をやってて、その辺の挙動が違うのかと思い前回の質問に繋がりました。(double → float代入でワーニングがでない等)
もっともメモリ破壊を起こしてたら暴走してるだろうし、私が知ってるC++の挙動と違うんですよね。

ネットでMinstデータのクラス分類のコードを調べましたが、やはり変えた部分(CSVデータの取得)が怪しそうなんですよね・・・
もしかして、

    char *token = strtok(csvBuffer, ",");
この部分でいきなりNULLが返って来てる可能性も否定できません。

そこで急がば回れで少々面倒ですが、試しにデバッグとして
1.まず学習済みのnnpを使い、Pythonのnnablaで推論結果が想定通りか確認する。(基本的には正しく動くはず)

2.次にm5stack S3側でCSVファイルの読み込みを詳細に確認します。
・読み込むCSVファイルを一つにする。(SDカードに1つだけCSVを置く)
・正規化後のデータの最初の幾つかと最後の幾つかを表示し、元データの読み込みが正しく行われている事を確認する。(LCDに表示可能な行数によっては表示後にキー入力で続行させる等の工夫が必要)
※:正規化後(Token分割後)と言うのがポイントです。
・別のCSVファイルをSDカードに一つだけ置いて、再度2.の最初から行う。

上記の状態で幾つかのCSVファイルでクラス分類を行ってみて、それでも状況に変化が無いか確認するという案は如何でしょうか?

2025年3月26日水曜日 11:56:16 UTC+9 sota1111:

sota1111

unread,
Mar 29, 2025, 1:00:28 PMMar 29
to Neural Network Console Users (JP)
返答ありがとうございます。

> 1.まず学習済みのnnpを使い、Pythonのnnablaで推論結果が想定通りか確認する。(基本的には正しく動くはず)
何度か20サンプルを確認して全て正しいことを確認しました。(最後にデータを示す。)

> 2.次にm5stack S3側でCSVファイルの読み込みを詳細に確認します。
全てのデータは正しく配置されています。
(SDカードの読み取りが怪しいのは同感したので、問題の切り分けのために、SDカードの処理は削除し、配列データを.cppに直書きしました。)

MNISTは正しく推論できているので、1D CNNは生成できないのか、1D CNNの使い方が誤っているか。
エッジでの2D CNNは一次元配列を読み込ませるので、1D CNNと同じ使い方のはずです。生成されたコードが怪しいのですかね。

1の結果
=== Label 0 のサンプルの検証 ===

File: log_2025-03-29_12-40-34.csv
Label 0の確率: 0.9349
Label 1の確率: 0.0651
予測: Label 0

File: log_2024-12-31_18-14-18.csv
Label 0の確率: 0.8214
Label 1の確率: 0.1786
予測: Label 0

File: log_2025-03-29_12-37-17.csv
Label 0の確率: 0.9093
Label 1の確率: 0.0907
予測: Label 0

File: log_2025-03-29_12-40-14.csv
Label 0の確率: 0.9879
Label 1の確率: 0.0121
予測: Label 0

File: log_2025-03-29_12-36-26.csv
Label 0の確率: 0.9763
Label 1の確率: 0.0237
予測: Label 0

File: log_2025-03-29_12-35-19.csv
Label 0の確率: 0.9892
Label 1の確率: 0.0108
予測: Label 0

File: log_2024-12-31_18-14-01.csv
Label 0の確率: 0.7964
Label 1の確率: 0.2036
予測: Label 0

File: log_2025-03-29_12-40-26.csv
Label 0の確率: 0.9886
Label 1の確率: 0.0114
予測: Label 0

File: log_2025-03-29_12-35-50.csv
Label 0の確率: 0.9270
Label 1の確率: 0.0730
予測: Label 0

File: log_2025-03-29_12-36-20.csv
Label 0の確率: 0.9810
Label 1の確率: 0.0190
予測: Label 0

=== Label 1 のサンプルの検証 ===

File: log_2024-12-31_18-05-33.csv
Label 0の確率: 0.1007
Label 1の確率: 0.8993
予測: Label 1

File: log_2024-12-31_18-03-36.csv
Label 0の確率: 0.1313
Label 1の確率: 0.8687
予測: Label 1

File: log_2025-03-29_13-02-11.csv
Label 0の確率: 0.0000
Label 1の確率: 1.0000
予測: Label 1

File: log_2024-12-31_18-04-33.csv
Label 0の確率: 0.1453
Label 1の確率: 0.8547
予測: Label 1

File: log_2024-12-31_18-03-14.csv
Label 0の確率: 0.1256
Label 1の確率: 0.8744
予測: Label 1

File: log_2024-12-31_18-03-34.csv
Label 0の確率: 0.3080
Label 1の確率: 0.6920
予測: Label 1

File: log_2024-12-31_18-03-55.csv
Label 0の確率: 0.4742
Label 1の確率: 0.5258
予測: Label 1

File: log_2024-12-31_18-05-36.csv
Label 0の確率: 0.4953
Label 1の確率: 0.5047
予測: Label 1

File: log_2024-12-31_18-04-46.csv
Label 0の確率: 0.4428
Label 1の確率: 0.5572
予測: Label 1

File: log_2024-12-31_18-05-04.csv
Label 0の確率: 0.2696
Label 1の確率: 0.7304
予測: Label 1

2025年3月29日土曜日 19:19:29 UTC+9 G.T.:

G.T.

unread,
Mar 31, 2025, 12:42:46 AMMar 31
to Neural Network Console Users (JP)
sota1111さん、こんにちは。


> MNISTは正しく推論できているので、1D CNNは生成できないのか、1D CNNの使い方が誤っているか。
いえ、Python側では正しく動作しているようなので、Python側での1D CNNの使用方法も生成も問題ないはずです。(それを確認する為に敢えてPythonで動作確認したので)

となると残るはC++の方のコードかC言語のnnablaのライブラリ(或いは使い方)と言う事になるのですが・・・
データを直書きしたという事は、例の「char csvBuffer[csvLine.length() + 1];」の部分も変更されたのでしょうか?

下記のような感じで
    // 800個分のCSVデータを入力バッファに格納
    float csvData[] = {
        -0.103027344,-0.082519531,-0.072265625,-0.069335938,-0.069824219,-0.074707031,-0.092285156,-0.109863281,
        ・・・0.699707031,0.999511719,0.995605469,-0.112792969
    };

    for( int index = 0; index < NNABLART_VALIDATION_INPUT0_SIZE; index++ ) {
      nn_input_buffer[index] = csvData[index];
    }

正規化の計算と気になっていた配列サイズの動的確保も排除し、直接正規化後のデータを「nn_input_buffer」に代入。
これでもダメなら、いよいよライブラリ(の1D CNNの挙動)が怪しい感じになりますね・・・

2025年3月30日日曜日 2:00:28 UTC+9 sota1111:

sota1111

unread,
Mar 31, 2025, 10:45:32 PMMar 31
to Neural Network Console Users (JP)
返答ありがとうございます。
グローバル平均プーリングを無くしたら推論が上手く行くようになりました。
推論側のコードは変更なしで問題ありませんでした。
2025年3月31日月曜日 13:42:46 UTC+9 G.T.:

G.T.

unread,
Apr 3, 2025, 12:41:51 AMApr 3
to Neural Network Console Users (JP)
sota1111さん、こんにちは。

> グローバル平均プーリングを無くしたら推論が上手く行くようになりました。
おお、それは良かった!
問題解決のお役に立てず、残念。

使ってる人が少ないライブラリ(フレームワーク)でありがちではありますが、nnablaのPythonとCライブラリで挙動の違いがあったのでしょうね。 (^^;

ところで公開されたclassification.pyを見ると、確かに「h = F.global_average_pooling(h)」がありますね。
私が聞きかじった知識だと、Global Average Poolingは終盤の全結合の代わりに使う(パラメータ削減の効果)と言う認識でしたが、その意味では確かに無くても問題ないかもですね。

2025年4月1日火曜日 11:45:32 UTC+9 sota1111:
Reply all
Reply to author
Forward
0 new messages