忘れそうな内容をメモしています

忘れっぽいのでメモ帳がわりに色々書いてます

音分類モデルを作ってSipeed Maix Bitで動かそうとした話①

前回はmaix bitでマイクとFFTでスペクトログラム的なものを作って遊んでみました。 shintarof.hatenablog.com

今回の内容は、簡単な音分類モデルを作ってmaix bitで動かそうとしたものの、モデルをmaixpyで動かす方法が分からなかった(無かった?)というお話です。

忘れないようにやったことをメモしておきます。

1.モデル作成

まずは簡単な問題から試してみようと、キーボードを叩く音と、マウスをクリックする音を分類するモデルを作ってみました。
前回作成したスペクトログラムでキーボードを叩く音とマウスをクリックする音を表示して、その時の各周波数成分(写真の横軸の白黒の輝度)をcsv形式で保存しました。

f:id:shintarof:20200621235122j:plainf:id:shintarof:20200621235117j:plain
左:キーボードカチャカチャ音、右:マウスカチカチ音のスペクトログラム
(横方向が周波数、縦方向が時間)

次に作成したcsvファイルをもとに以下のコードでデータセットを作成しました。
今回もgoogle colab上で実行しました。

#データセットのアップロード
from google.colab import files
uploaded = files.upload()

import csv
import numpy as np
from keras.utils import np_utils

def create_dataset(path,label):
   data =[]
   labels =[]
   with open(path) as f:
       reader = csv.reader(f)    
       for row in reader:
           data.append(row)
           labels.append(label)
   return data,labels

key_data,key_labels = create_dataset("keybord_fft.csv",0)
mouse_data,mouse_labels = create_dataset("mouse_fft.csv",1)

dataset = key_data + mouse_data
dataset = np.asarray(dataset)

labels = key_labels + mouse_labels
labels = np.asarray(labels)

dataset = dataset.reshape(dataset.shape[0],dataset.shape[1],1)
# One-hot encoding
labels = np_utils.to_categorical(labels)

作成する分類モデルですが、以下記事の「CNN(畳み込みニューラルネットワーク)」のコードを参考にさせていただきました。ありがとうございます。 qiita.com

from keras.models import Sequential, load_model, Model
from keras.layers import Dense, Activation, Dropout, Input
from keras.layers import Embedding
from keras.layers import Conv1D, GlobalAveragePooling1D, MaxPooling1D
from keras.optimizers import Adam
from keras.utils import Sequence

class CNN():
    def build_model(self):
        inputs = Input(shape=(128, 1))
        x = Conv1D(64, 2, activation='relu')(inputs)
        x = Conv1D(64, 2, activation='relu')(x)
        x = MaxPooling1D(2)(x)
        x = Conv1D(128, 2, activation='relu')(x)
        x = Conv1D(128, 2, activation='relu')(x)
        x = GlobalAveragePooling1D()(x)
        x = Dropout(0.5)(x)
        x = Dense(2, activation='softmax')(x)
        #model = Model(input=inputs, output=x)
        model = Model(inputs=inputs, outputs=x)
        optimizer = Adam()
        model.compile(loss="categorical_crossentropy",
                    optimizer=optimizer, metrics=['accuracy'])
        model.summary()

        return model

if __name__ == '__main__':
    save_model_name = 'cnn_mouse_keyboard.h5'
    dnn = CNN()
    model = dnn.build_model()  
    model.fit(dataset, labels, batch_size=5, epochs=100,verbose=1)
    model.save(save_model_name)

作成したh5形式のモデルをtflite形式に変換します。

!tflite_convert  --output_file=cnn_mouse_keyboard.tflite --keras_model_file=cnn_mouse_keyboard.h5

最後にtflite形式からkmodel形式に変換をしますが、ここで少しハマりました。
これまで画像データを扱うモデルを作成した際にはnncaseのコマンドラインオプションの--dataset-formatimageとしていましたが、今回のような1次元データの場合はrawを選択する必要があります。またそれに伴ってモデル変換時に--datasetで参照するデータセットとしてfloatのバイナリ形式で作成する必要があるようです。

以下nncaseのドキュメントから抜粋

--dataset-format is to set the format of the calibration dataset. Default is image, nncase will use opencv to read your images and autoscale to the desired input size of your model. If the input has 3 channels, ncc will convert images to RGB float tensors [0,1] in NCHW layout. If the input has only 1 channel, ncc will grayscale your images. Set to raw if your dataset is not image dataset for example, audio or matrices. In this scenario you should convert your dataset to raw binaries which contains float tensors. You only need this option when you set --inference-type to uint8

以下の手順で無事kmodel形式に変換できました。

  1. nncaseのインストール
!mkdir ncc_v2
%cd ncc_v2
!wget https://github.com/kendryte/nncase/releases/download/v0.2.0-beta4/ncc_linux_x86_64.tar.xz
!tar Jxf ncc_linux_x86_64.tar.xz
%cd ../

2.データセットの作成

import struct

with open("data/data.bin", "wb") as fout:
    for x in dataset[0]:
        fout.write(struct.pack("f", x))

3.変換

!./ncc_v2/ncc compile dnn_keybord_mouse.tflite dnn_keybord_mouse.kmodel -i tflite -o kmodel --dataset data --dataset-format raw 

これでモデルができました。モデルのサイズは62Kでした。

2. maix bitで動かしてみる

これまで作ったモデルと同様に、作成したモデルをmaix bitに転送しtask = kpu.loadでロードして、 fmap=kpu.forwardを動かしてみましたが、「TypeError: Can't convert bytes to type」というエラーで止まってしまいました。

モデルを作成した際にnncaseの--dataset-formatをrawに選択したので、モデル実行時も何か指定が必要なのでは?と思いmaixpyのドキュメントを見てみたところ以下の記載がありました。

以下maixpyのkpu.forwardのドキュメントから抜粋

Parameters
kpu_net: kpu_net object
image_t: image captured from sensor
int: specifies the number of layers to calculate to the network

どうやらkpu.forwardは入力データとして画像データしか受け付けないようです。
モデルの実行方法についてもう少し詳しく調べてみようと思います。

追記

似たような質問を発見

https://en.bbs.sipeed.com/t/topic/1809/11en.bbs.sipeed.com github.com