Edge Impulseを試してみる

先日行った、[mbed祭り2020@春の新横浜](https://mbed.doorkeeper.jp/events/102927)（[レポート](https://os.mbed.com/users/MACRUM/notebook/mbed-fest-2020-spring-report/)）でマイコン向けの機械学習のソリューションを提供しているサービスである[Edge Impulse](https://www.edgeimpulse.com/)が紹介されていたので、気になって試してみました。ちなみにサンプルはMbed OSを使って動かしている様です。

# マイコンで動かす機械学習について
　IoTの世界でセンサーの値をクラウドにアップロードするエッジデバイスのCPUとして、マイコンが使用されることが多いです。特にバッテリー駆動する必要がある場合や大量のデバイスを必要とする場合は、必然的に消費電力の少ないマイコンが選ばれると思います。
　IoTのシステムとして、エッジデバイスに接続されたセンサーの値を常にクラウドにアップロードし、クラウド側でセンサーの値をグラフで表示したり、センサーの値を解析して、イベントを検出したりすることはよくあると思います。
　しかし、常にセンサーの値をクラウドにアップロードしていると通信量多くなったりやクラウド側のストレージ容量を圧迫したりと、バッテリー駆動のエッジデバイスでは持続時間の問題となりますし、運用コストの問題になる場合があります。また、連続したデータが必要な場合は通信エラーによるデータの欠損も気にしなくてはいけません。
　そのため、最近ではエッジデバイス側でセンサーのデータを処理してから必要なデータのみをクラウドにアップロードする方法が考えられてきています。その中の一つの方法として、エッジデバイス側に機械学習の機能を入れ、センサーデータを分析してからイベントリーな情報だけをクラウドにアップロードという方法があります。

# Edge Impulse
Edge Impulseは簡単に言うとマイコンで機械学習を動かす手順をとっても簡単にしてくれるサービスです。
デバイスとクラウドを連携させた学習データの生成と学習データを含めたライブラリやファームウェア生成などを行ってくれます。

* トップページ：https://edgeimpulse.com/
* ドキュメント：https://docs.edgeimpulse.com/
* Github：https://github.com/edgeimpulse/

## チュートリアル
Edge ImpulseはMbedプラットフォームの[DISCO-L475VG-IOT01A](https://os.mbed.com/platforms/ST-Discovery-L475E-IOT01A/)を使った[チュートリアル](https://docs.edgeimpulse.com/docs/unboxing-the-st-b-l475e-iot01a)が用意されているので、それを手順にしたがって試してみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109992/41019c15-0d70-8192-3c9a-0c249dcc0875.png)

## アカウントを作る
[Edge Impulseのトップページ](https://www.edgeimpulse.com/)からLoginをクリックして、Create new accountからアカウントを作ります。
入力する項目は、名前、ユーザーネーム、メールアドレス、パスワードぐらいです。
アカウントを作成するとメールが来るので、メールのリンクから認証します。

## デバイスの準備
### 1. 設定ツールの準備
設定ツールは[nodejs 12以上](https://nodejs.org/en/)と[ST-link](https://cdn.edgeimpulse.com/drivers/st-link.zip)をインストールして、npmインストールします。

```
> npm install -g edge-impulse-cli @serialport/terminal
```
### 2. ファームウェアの書き込み
DISCO-L475VG-IOT01A用のファームウェアはコンパイル済みのものが用意されているので、[ここ](https://cdn.edgeimpulse.com/firmware/DISCO-L475VG-IOT01A.bin)からダウンロードして書き込みます。
Mbedプラットフォームなので、繋げればUSBストレージとして認識されるので、ダウンロードしたファイルを置くだけです。

### 3. ボードの設定
以下のコマンドで設定ツールを起動し、Edge Impulseのログイン情報やボードのWi-Fiの設定を行います。
対話型なので、聞かれた情報を入力したり、選択したりするだけです。

```
> edge-impulse-daemon
```

ボードに対してAPIキー等もセットしてくれるので、ボードとEdge Impulseのプロジェクトの紐づけは自動的にやってくれます。
設定が終わるとボードとEdge Impulseが接続された状態になります。

Edge Impulseのプロジェクト画面から[Devices]を開くと接続されたデバイスが表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109992/3b1704f3-5ca9-5f72-5630-c5108ccbc2ca.png)

## 学習データを作る
チュートリアルには、[加速度センサを使ったモーション](https://docs.edgeimpulse.com/docs/continuous-motion-recognition)と[マイクロフォンを使った音声](https://docs.edgeimpulse.com/docs/audio-classification)のパターン認識の例がありますが、加速度センサの方を試してみました。

### 1.センサーデータを収集する
Edge Impulseのプロジェクト画面から[Data acquisition]を開き、Recode new dataからデバイスとセンサー、サンプリング時間、サンプリング周期を設定して、識別するラベルを入力します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109992/b6234ee0-4c05-6240-1841-55425e68507c.png)

Start samplingをクリック後、デバイスを一定のパターンで動かして、センサーデータを収集します。

↓こんな感じでデータが取れました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109992/63015787-d189-08cd-2223-c4240866caf8.png)

センサーデータのサンプルはある程度多い方が良いということで、チュートリアルと同じように3分ぶんのデータを10秒づつ記録しました。

センサーデータの取得が面倒であれば、サンプルのデータが[ここ](https://docs.edgeimpulse.com/docs/continuous-gestures)にあります。データファイルをダウンロードして、設定ツールからEdge Impulseの自分のプロジェクトにアップロードできます。

### 2.インパルス設計(Impulse design)
センサーデータのサンプルを取ったら、まず、データを分類するための特徴を抽出させる設計を行います。

#### インパルスの作成(Create impulse)
Edge Impulseのプロジェクト画面から[Impulse design] - [Create impulse]を開き、以下の様に設定していきます。

##### Raw data
センサーデータからWindow sizeとWindow increaseを入力し、データを分割・抽出する幅を設定します。
（パラメータの決め方はセンサーのサンプリングや検出するモーションによると思いますが、ここではチュートリアルのままにしてあります。）

##### 信号処理ブロック（processing block）
信号処理ブロックはスペクトル分析（Spectral Analysis）を選び、箱を作ります。（名前はデフォルトであるSpectral featuresのままにしています。）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109992/51352dcc-c3cc-3bf6-4f97-ded0adc88c8e.png)

##### 学習ブロック（learning block）
学習ブロックはニューラルネットワーク（Neural Network）を選び、箱を作ります。（名前はデフォルトであるNIN Classifierのままにしています。）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109992/c01046c6-9019-6ed0-2e95-4118f3e0d8ed.png)

作成が終わったら[Save Impulse]をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109992/955a8743-d92b-a8e4-e50c-08d3a0c76aee.png)


次にインパルスの作成を保存するとEdge Impulseのプロジェクト画面のインパルス設計(Impulse design)に作成したスペクトル分析の構成(Spectral features)とニューラルネットワークの構成(NIN Classifier)のメニューが追加されますので、これらを実施しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109992/da6b31c4-153b-c2ba-cea1-bdd8c0520bb0.png)

#### スペクトル分析の構成(Spectral features)
Edge Impulseのプロジェクト画面から[Impulse design] - [Spectral features]を開くと解析結果がグラフで表示されるので、パラメータを確認し、問題無ければ[Save parameters]をクリックします。（とりあえずパラメータをいじらずにSaveしました）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109992/01ef71fb-4b47-f47a-0be0-71b696761204.png)

設定に問題が無ければ、[Generate features]をクリックし、処理を開始します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109992/4d38b384-ab4a-1491-ddd2-63c1553bd4d5.png)

処理が終われば、Spectral featuresの左の丸がグレーからグリーンに変わります。

#### ニューラルネットワークの構成(NIN Classifier)
Edge Impulseのプロジェクト画面から[Impulse design] - [NIN Classifier]を開きます。
パラメータを確認して、[Start training]をクリックして、処理を開始します。（とりあえずパラメータをいじらずにStartしました）
しばらくすると結果が表示されます。こちらはマトリクスの該当する同項目の数値のみがそれぞれ高ければ精度が高いということになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109992/11076f7e-c2df-9f67-0f4f-f93ffa47b8e7.png)

処理が終われば、NIN Classifierの左の丸がグレーからグリーンに変わります。

以上で、インパルスの設計は完了です。
他のEdge Impulseのプロジェクト画面のメニューからトレーニングやデバイスと繋げて検証が行えますが、とりあえずモデルを作って、デバイスで動かしてみます。

## モデルの実行
Edge Impulseのプロジェクト画面から[Deployment]を開き、以下の様に設定してBuildをクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/109992/7de16246-94f2-f443-012e-1b2c4c3b17eb.png)

Buildが完了するとファームウェアのダウンロードが開始しますので、ダウンロードしたファームウェアをデバイス（DISCO-L475VG-IOT01A）に書き込みます。

書き込みが終わったら接続しているPCでシリアルコンソールを開き、デバイスのシリアルポートを指定します。ちなみにボーレートは115200bpsです。

ATコマンドでコマンドを打てるようになっているので、以下の様に入力し、実行します。

```
> AT+RUNIMPULSE
```

すると2秒おきに加速度センサーのモーションを解析した結果が出力されます。デバイスを置いたままであればIDLEの確率がほぼ1.0になります。

学習させたモーションと同じような動きをすれば、ちゃんとそれが一番確率が高くなります。

↓私が試した、シリアルコンソールのログです。デバイスを上下に動かしたときはちゃんとupdownがほぼ1.0となります。

```


Hello from the Edge Impulse Device SDK.
Mounting file system: OK
Loaded configuration
Connecting to 'jksoft-wifi'
Connected to WiFi, will now automatically upload samples
Getting NTP info...
Current time is Thu Feb 13 11:21:16 2020
ws connect
ws connect ok
Connecting to WS Client returned 0
Type AT+HELP to see a list of commands.
> AT+RUNIMPULSE
Inferencing settings:
        Interval: 16.00 ms.
        Frame size: 375
        Sample length: 2000 ms.
        No. of classes: 4
Starting inferencing, press 'b' to break
Sampling... Storing in file name: /fs/device-classification.0
Predictions (DSP: 21 ms., Classification: 14 ms., Anomaly: 0 ms.):
    idle: 0.99783
    snake: 0.00039
    updown: 0.00159
    wave: 0.00019
Finished inferencing, raw data is stored in '/fs/device-classification.0'. Use AT+UPLOADFILE to send back to Edge Impulse.
Starting inferencing in 2 seconds...
Sampling... Storing in file name: /fs/device-classification.1
Predictions (DSP: 16 ms., Classification: 14 ms., Anomaly: 0 ms.):
    idle: 0.99783
    snake: 0.00039
    updown: 0.00159
    wave: 0.00019
Finished inferencing, raw data is stored in '/fs/device-classification.1'. Use AT+UPLOADFILE to send back to Edge Impulse.
Starting inferencing in 2 seconds...
Sampling... Storing in file name: /fs/device-classification.2
Predictions (DSP: 16 ms., Classification: 14 ms., Anomaly: 0 ms.):
    idle: 0.00315
    snake: 0.04552
    updown: 0.95020
    wave: 0.00113
Finished inferencing, raw data is stored in '/fs/device-classification.2'. Use AT+UPLOADFILE to send back to Edge Impulse.
Starting inferencing in 2 seconds...
Sampling... Storing in file name: /fs/device-classification.3
Predictions (DSP: 16 ms., Classification: 18 ms., Anomaly: 0 ms.):
    idle: 0.99783
    snake: 0.00039
    updown: 0.00159
    wave: 0.00019
Finished inferencing, raw data is stored in '/fs/device-classification.3'. Use AT+UPLOADFILE to send back to Edge Impulse.
Starting inferencing in 2 seconds...
Sampling... Storing in file name: /fs/device-classification.4
Predictions (DSP: 20 ms., Classification: 14 ms., Anomaly: 0 ms.):
    idle: 0.99783
    snake: 0.00039
    updown: 0.00159
    wave: 0.00019
Finished inferencing, raw data is stored in '/fs/device-classification.4'. Use AT+UPLOADFILE to send back to Edge Impulse.
Starting inferencing in 2 seconds...
Sampling... Storing in file name: /fs/device-classification.5
Predictions (DSP: 16 ms., Classification: 14 ms., Anomaly: 0 ms.):
    idle: 0.99779
    snake: 0.00040
    updown: 0.00161
    wave: 0.00019
Finished inferencing, raw data is stored in '/fs/device-classification.5'. Use AT+UPLOADFILE to send back to Edge Impulse.
Starting inferencing in 2 seconds...

```

こんな感じで、チュートリアルを動かすことができました。
ちなみにしばらく動かしているとHeapメモリ不足で落ちるので、あくまでチュートリアル用って感じです。

# あとがき
Edge Impulseのチュートリアルで動かしたソースコードはgithub(下記URL)に公開されていて、[ポーティングガイド](https://docs.edgeimpulse.com/docs/porting-guide)もあるので、他のボードやセンサーでも動作させてみたいと思います。

https://github.com/edgeimpulse/firmware-st-b-l475e-iot01a