SORACOM Beam + AWS IoT + Amazon Machine Learningで予防保守


この記事は、[SORACOM](https://soracom.jp/)の[Software Design誌巻頭特集記念リレーブログ](https://blog.soracom.jp/relay-blog/2016-04/) 5/11分の記事です。

先日、[AWS IoTがAmazon Machine Learningと連携](https://aws.amazon.com/jp/about-aws/whats-new/2016/04/aws-iot-now-supports-integration-with-amazon-machine-learning-and-aws-cloudtrail/)できるようになりました。

それを受けて、Mobile Blogで[AWS IoTとAmazon Machine Learningを使った予防保守](http://mobile.awsblog.com/post/Tx39VJ297BYW6K7/Predictive-Maintenance-with-AWS-IoT-and-Amazon-Machine-Learning)という記事が公開されましたが、今回はこの記事を元にして、Raspberry Piで取得したセンサーデータをSORACOM Beamを使ってAWS IoTにデータを送信し、Amazon Machine Learningでリアルタイムに故障を予測するまでの一連の流れを追ってみたいと思います。

## 概要

Raspberry PiからセンサーデータをSORACOM経由でAWS IoTにデータを送信します。
Amazon Machine Learningでは、あらかじめセンサーのデータと故障状態を示す教師データからモデルを作成しておき、リアルタイム予測APIが有効にします。
AWS IoTにセンサーデータが到着するとAWS IoTのルール上でAmazon Machine Learningのリアルタイム予測APIへ問い合わせを実行し、故障と判定されるとSNSでアラートを送信します。

Raspberry PiからAWS IoTへの通信はSORACOMをSIMを利用します。（今回のためにFS01BUを買いました) 
また、SORACOM Beamを利用してAWS IoTエンドポイントまでを暗号化することとします。

![diagram.png](https://qiita-image-store.s3.amazonaws.com/0/32775/1272844b-c10b-00bd-910b-c8bf54d40ba7.png "diagram.png")

なお、AWS IoTからAmazon Machine Learningを呼び出す場合は同じリージョン内である必要があるため、今回はus-east-1(北米リージョン)にAWSの環境を作成しました。

## サンプルデータ

今回は教師データおよびRaspberry Pi上のセンサーデータはブログ上で紹介されていたサンプルを利用しました。

以下がサンプルデータのフォーマットです。センサーデータ(sm1~7)と故障を示すフラグ(target)から構成されており、Amazon Machine Learningのリアルタイム予測APIでは各センサーの値をもとに故障(Target)を予測します。

```
cycle,sm1,sm2,sm3,sm4,sm5,sm6,sm7,target
1,3.51776147918,4.57820444211,0.00842414186895,-0.872470305634,5.40195714282,2.74974371824,0.0706883383205,0
2,3.55440088882,4.71832916822,0.00331972629602,0.965727762542,4.40008231689,2.74479525709,-0.0451647988781,0
3,3.43030467231,4.29812775724,0.0971679359269,-2.13483351595,4.73701926302,2.69477799618,0.00885534911496,0
```


## 教師データをS3にアップロード

では実際の手順を説明します。

Amazon Machine Learningのモデルを作成するために、こちらのサンプルデータ[sim_pred_maint_train.csv](https://s3.amazonaws.com/iot-aml-predmaint-demo/sim_pred_maint_train.csv)をダウンロードして自分のバケット(今回はpredictive-maintenance-demo)にアップロードします。（約130MBあります）

```
aws s3 mb s3://predictive-maintenance-demo
aws s3 cp sim_pred_maint_train.csv s3://predictive-maintenance-demo/sim_pred_maint_train.csv
```

## Amazon Machine Learningのデータソース作成
アップロードしたサンプルデータを元にAmazon Machine Learningのデータソースを作成します。

* マネージメントコンソールのトップからAmazon Machine Learningを選択します。
* 画面左上の"Amazon Machine Learning"メニューから **[Datasources]** を選択します。
* **[Create a new datasource]**をクリックします。
* 以下のようにサンプルデータが格納されているS3の情報を入力し、 **[Verify]** をクリックします。
![aml-1.png](https://qiita-image-store.s3.amazonaws.com/0/32775/b8ddce69-9cc4-0cf8-ffea-6dc120914996.png "aml-1.png")

* 'S3 permission...'の画面で **[Yes]**をクリックします。
* 'The validation is successful.'の表示がでたら、 **[Continue]** をクリックします。
* 'Schema'の画面で、'Does the first line in your CSV contain column name?'で **[Yes]** を選択し、9番目のTargetの行の'Data Type'を'Bynary'に変更、 **[Continue]** をクリックします。
![aml-2.png](https://qiita-image-store.s3.amazonaws.com/0/32775/e98678a3-d8c9-6855-f7c7-f07a47eb0582.png "aml-2.png")


* 'Target'の画面で、'Do you plan to use this dataset...'で **[Yes]** を選択し、データセット一覧のTargetのカラムで **'Target'** にチェックを入れ、 **[Continue]** をクリックします。

![aml-3.png](https://qiita-image-store.s3.amazonaws.com/0/32775/64fdf97b-8961-077e-9dc1-e0ede43c99e2.png "aml-3.png")


* 'Row identifier'の画面で **[Review]** をクリックします。
* 'Review'の画面で **[Finish]** をクリックします。（完了までしばらく時間がかかります）

## Amazon Machine Learningのモデル作成
次に、先ほどのデータソースを元に予測をするためのモデルを作成します。

* 画面左上の"Amazon Machine Learning"メニューから **[ML Models]** を選択します。
* 'Input data'から先ほど作成したデータソースを選択し、 **[Continue]** をクリックします。
* 'ML model settings'で **[Review]** をクリックします。
* 'Review'で **[Finish]** をクリックします。(完了まで20分くらいかかります)
* リアルタイム予測のAPIエンドポイントを作成します。モデルの作成が完了したら、作成したモデルの詳細画面の'Prediction'の'Enable real-time predictions'の **[Create Endpoint]** をクリックします。
* 'Create a real-time endpoint'の画面で **[Create]** をクリックします。'Real-time endpoint: ready'の状態になったらAmazon Machine Learningの準備が完了です。

## IAMの設定
AWS IoTに必要な権限の設定をIAM(Identity and Access Management)で行います。具体的にはAWS IoTからAmazon Machine Learningへの問い合わせを行なうためのロールを作成します。

ロールに必要な権限は **machinelearning:Predict** と **machinelearning:GetMLModel** です。また、AWS IoTから利用するためにAssumeRoleにIoTサービスを追加します。

以下のドキュメントでポリシーを作成します。

```
{
  "Version": "2012-10-17",
  "Statement": {
    "Effect": "Allow",
    "Action": ["machinelearning:Predict","machinelearning:GetMLModel"],
    "Resource": "*"
  }
}
```

* AWSマネージメントコンソールの'Identity and Access Management'を選択し、左のメニューの'Policy'をクリックします。
* 'Create Your Own Policy'の **[Select]** をクリックします。
* 'Policy Name'に **pm_policy** 、'Policy Document'に上記のJSONを入力し、 **Create Policy**　をクリックします。

次に、ロールを作成します。

* 左のメニューの'Role'をクリック、
* [Create New Role]をクリックします。
* 'Role Name'の画面で **pm_role** と入力し、 **[Next Step]** をクリックします。
* 'Role Type'の画面で'AWS Service Role'から'AWS IoT'を選択し、[Select]をクリックします。
* 'Attach Policy'の画面で何も選択せず、 **[Next Step]** をクリックします。
* 'Review'の画面で **[Create Role]** をクリックします。

作成したロールにポリシーをアタッチします。

* ロールの一覧から'pm_policy'を選択します。
* **[Attach Policy]** をクリックし、'Attach Policy'の画面でpm_policyを選択し、 **[Attach Policy]** をクリックします。

## AWS IoTの設定

AWS IoTで行う設定はこちらのとおりです。

* SORACOM Beamに設定するための証明書の作成
* MQTTトピックでデータを受信するためのAWS IoTのポリシーを作成
* Raspberry Piから送信されたデータを元に、Amazon Machine Learningのリアルタイム予測APIを実行するために、AWS IoTのルールを設定

では、それぞれの手順を見ていきましょう。

### 証明書の作成

まずはSORACOM Beamに登録するSSL証明書とキーを作成しましょう。

* AWSマネージメントコンソールからAWS IoTをクリックします。
* **[Create a resource]** をクリックし、画面下の **[Create a certificate]** をクリックします。
* 'Activate'にチェックを入れ、 **[1-Click certificate create]** をクリックします。

![awsiot-1.png](https://qiita-image-store.s3.amazonaws.com/0/32775/52f2aec5-ec72-9b92-9483-0750d751b813.png "awsiot-1.png")


* 'Download~'のリンクが表示されるので、public key, private key, certificateすべてローカルにダウンロードします。

### ポリシーの作成

次に、Raspberry PiがAWS IoTのMQTTトピックにPublishできるようにポリシーを作成します。

* **[Create a policy]** をクリックします。
* 'Name'にpm_policy、Actionにiot:*、Resourceに* を入力し、 **[Add statement]** をクリックします。
* **[Create]** をクリックします。

### ルールの作成

特定のトピック(今回はpm/topic)にデータが着信したら、Machine Learningのリアルタイム予測を行い、結果が1(=故障)であった場合にSNSに通知するためのルールを設定します。

設定の前にAmazon Machine Learningで設定したモデルのID(ml-XXXXXXXXXXX)と、IAMのロールのARNを準備してください。

* **[Create a rule]** をクリックします。
* 以下の設定項目を入力します。

項目|設定値
---|---
Name|pm_rule
Description|任意
SQL version|2016-03-23-beta
Attribute|*
Topic filter|pm/topic
Condition|machinelearning_predict('ml-XXXXXXXXXXX', 'arn:aws:iam::XXXXXXXXXXXX:role/pm-role', *).predictedLabel = 1

**Condition** に記載されている'machinelearning_predict'という関数が今回のキモとなります。この関数によってリアルタイム予測APIを実行し、結果のJSONがpredictedLabelとして返答されます。モデルを作成したときにTargetとして指定したカラムが予測値となり、こちらが'1'の場合に故障とみなし、SNSをアクションとして実行します。詳しい利用方法は[コチラ](http://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/aws-iot-sql-functions.html)をご参考ください。

* 'Choose an action'で **[Send message as a push notification (SNS)]** を選択します。
* 'SNS Target'で **[Create a new resource]** をクリックし、新しいSNSのトピックを作成します。Subscriptionには自分のE-mailを指定してください。
* 'Role name'で **[Create a new role]** をクリックして、SNSに送信できるロールを作成します。
* **[Add Action]**をクリックし、SNSがアクションとして設定されたら **[Create]** をクリックします。

## SORACOM Beamの設定

Raspberry Piからのデータ転送はSORACOM Beamsを利用します。SORACOMのMQTTエンドポイントからUSリージョンのAWS IoTエンドポイントまでの暗号化をオフロードします。東京リージョンだけでなくUSリージョンも指定できるのはすばらしいですね！

* SORACOMコンソールのメニューから[グループ]をクリックし、新しいグループを作成します。
* リストの中から新しく作成したグループをクリックし、[基本設定]タブの[SORACOM Beam設定]をクリックします。
* MQTTエンドポイントを以下のように追加します。
	* プロトコル：MQTT
	* ホスト名：data.iot.us-east-1.amazonaws.com
	* ポート番号：8883
	* 証明書：ON
* 認証情報はAWS IoTの設定で作成した証明書の情報を貼り付けます。CA証明局は[こちら](https://www.symantec.com/content/en/us/enterprise/verisign/roots/VeriSign-Class%203-Public-Primary-Certification-Authority-G5.pem)からルート証明書ダウンロードして、同じように中身を貼り付けます。

## データ送信

Raspberry Piからデータを送信します。今回はCSV形式のダミーデータを読み込み、そのデータの１行づつをMQTTでpublishするpythonスクリプトを作成しました。

MQTTを利用するため、pahoライブラリをインストールします。

```
pip install paho-mqtt
```

以下をemit.pyとして保存します。

```emit.py
#!/usr/bin/python
# -*- coding: utf-8 -*-

import paho.mqtt.client as paho
import json
import time
import csv
import argparse

topic='pm/topic'

parser = argparse.ArgumentParser()
parser.add_argument("datafile", help="file to send to AWS")
args = parser.parse_args()

mqttc = paho.Client()
mqttc.connect('beam.soracom.io', 1883, 10)

with open(args.datafile) as f:
    for line in csv.DictReader(f):
        line['timestamp']=int(time.time()*1000)
        line_json = json.dumps(line)
        print line_json
        mqttc.publish(topic, line_json)

mqttc.disconnect()
```

また、こちらのダミーデータをdummy.csvとして保存します。

```dummy.csv
cycle,part-no,sm1,sm2,sm3,sm4,sm5,sm6,sm7,target
547,2500,4.5487304366,5.19001645556,-0.536969719622,0.581571229692,6.40265172319,2.04556903472,-0.297991533449,1
548,2500,4.43676983825,4.9265168248,-0.556375591298,0.390902414635,6.59540631868,2.09438294035,-0.247576579403,1
549,2500,4.46589980823,4.96322508396,-0.517978037289,0.66434511567,6.95218898953,2.11452097034,-0.349720906894,1
550,2500,4.65648479541,5.05420889359,-0.559600993485,0.657170877818,6.37402971638,2.11627946418,-0.425819068408,1
551,2500,4.38586232185,5.05376823935,-0.495169889372,0.574089524538,6.45009665122,2.07466280926,-0.363336278406,1
```

スクリプトを実行します。

```
python emit.py dummy.csv
```

SNSのsubscriptionに設定したメールアドレスにメールが届いていればOKです。もしメールが届かない場合はAWS IoTの設定で[CloudWatch Logsを有効に](http://docs.aws.amazon.com/ja_jp/iot/latest/developerguide/cloud-watch-logs.html)してデバッグしてみてください。


## さいごに
今回はアップロードされたデータが故障と予測されたらすぐにアラートを送信される＝故障の間ずっとアラートが送信されるため、本番の環境ではアラート送信済フラグを使ったり、アラートのしきい値を設けるなど少し工夫が必要だと思います。

また、今回はサンプルの教師データを利用しましたが、そもそも教師データを作るのが一番大変な気がしています。。。


## 免責事項

本記事の内容はあくまでも個人の意見であり、所属する企業や団体は関係ございません。