Watson Machine Learningをnoderedから利用する

## はじめに

Watson Machine LearningでAPI公開しているモデルをnoderedから利用する手順を記載したいと思います。

[IBM Watson Machine Learning API](http://watson-ml-api.mybluemix.net/#!/Deployments/post_v3_wml_instances_instance_id_published_models_published_model_id_deployments_deployment_id_online) のサイトを参考にしております。

手順は以下になります
1. curlコマンドを使用して、tokenを取得
2. noderedでtokenを利用して、WMLのAPIを呼び出し

モデル作成の材料としては、こちらの[チュートリアル：Watson machine learning within IBM Data Science Experience](https://developer.ibm.com/dwblog/2017/watson-machine-learning-within-ibm-data-science-experience/)のものを利用します。

シナリオとしては、アウトドア商品を販売している会社が、ロジスティック回帰のモデルを使って顧客がテントを買うか/買わないか、その期待度を予測するものです。

モデルの作成について、日本語の手順が以下のサイトで解説されておりますので、ご参照ください。
・[Watson Machine Learing チュートリアルを試してみる](https://qiita.com/makaishi2/items/dd9eafcbe824ef9482fd)
・[IBM Cloud上で機械学習モデルを簡単に作れる「Watson Machine Learning」を使ってみた](https://qiita.com/ishida330/items/0b60bc6f715df9da641c)

## WMLのサービスから token を取得
以下のcurlコマンドを実行して、tokenを取得

```shell:curl.sh

WML_SERVICE_CREDENTIALS_USERNAME=xxxxxxxxxxxxx
WML_SERVICE_CREDENTIALS_PASSWORD=xxxxxxxxxxxxx
curl --basic --user $WML_SERVICE_CREDENTIALS_USERNAME:$WML_SERVICE_CREDENTIALS_PASSWORD https://ibm-watson-ml.mybluemix.net/v3/identity/token

```

変数のusernameとpasswordは、IBM Cloud(PaaS)で作成した Watson Machine Learningの「サービス資格情報」より取得できます。

コマンド結果はこんな感じになります。
{"token":"XXXX"}のXXXXの部分がtokenになります。tokenは非常に長い文字列です。

```linux:Command結果
home$ curl --basic --user $WML_SERVICE_CREDENTIALS_USERNAME:$WML_SERVICE_CREDENTIALS_PASSWORD $WML_SERVICE_CREDENTIALS_URL/v3/identity/token
{"token":"eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJ0ZW5hbnRJZCI6ImI2OWVkM2UxLTVkN2EtNDY1Ny1hMjc2LWVjNmU2ZGQyNzdmMCIsImluc3RhbmNlSWQiOiJiNjllZDNlMS01ZDdhLTQ2NTctYTI3Ni1lYzZlNmRkMjc3ZjAiLCJwbGFuSWQiOiIzZjZhY2Y0My1lZGU4LTQxM2EtYWM2OS1mOGFmM2JiMGNiZmUiLCJyZWdpb24iOiJ1cy1zb3V0aCIsInVzZXJJZCI6IjZjOGU3OThkLWQ0NzQtNDcyMi1hMzU5LTYzZjY4OTA3Y2E1ZiIsImlzcyI6Imh0dHBzOi8vaWJtLXdhdHNvbi1tbC5teWJsdWVtaXgubmV0L3YzL2lkZW50aXR5IiwiaWF0IjoxNTE5NTM3NjcxLCJleHAiOjE1MTk1NjY0NzF9.qx5HG0q828WA_ht_R3CA_KXwEL4S5T0WlYOxeWjnnmVg1OLG9ESzUJbz_Vsi8CsplNjB-lIKer8DrdWJMSY1nlCI2bxoL3tPuUWgqCHBIVwWhTVDAhWQnayHcwQNcjB3kb1FPVFPWn-jqWgMEfMy2w2YsWJlfNh2RCnuecObvBETk81tWVcuTcYOkflOiKjyHs6koQ_Y-gNxeVooANMUcPiUS98t3P5fzT1Azfj3k8DXXXXehTvf1Oo1FcOIW3mKwaD-q85VvfsgmhkswZtciu5XfkFQ1p0NCuFZqsYjKXk3JvYyryAXXXXXXXXj3nKa0aqQD0OuH8zGG0B1EdxPX9L4tiQdA"}

```

## chromeの『Advanced REST client』 から呼び出してみる

noderedに行く前に、chromeの『Advanced REST client』 からPOSTしてみて呼び出せるか確認してみます。
『Advanced REST client』 を利用すると どうPOSTすればよいかイメージが湧きやすいです。

POSTするURLはWMLの管理画面から確認できる「Scoring Endpoint」のサイトになります。
最後に「/online」がつくURLになります。

以下のようなURLです。

```linux:API-Scoring-Endpoint
https://ibm-watson-ml.mybluemix.net/v3/wml_instances/XXXXXXX-XXXX-XXXX-XXX-XXXXX/published_models/XXXXXXXXX/deployments/XXXXXXXXX/online
```

POSTに必要なヘッダーは以下になります。

```linux:header
Accept: application/json
Content-Type: application/json
Authorization: Bearer  <token>
```

インプットするjsonの値は以下のとおりです。
1人の情報("M",27,"Single","Professional")をインプットしております。

```json:input
{"fields": ["GENDER", "AGE", "MARITAL_STATUS", "PROFESSION"],"values":[["M",27,"Single","Professional"]]}
```

上記を踏まえて、『Advanced REST client』 にインプットすると以下のような感じになります。

【インプット画面】
<img width="1268" alt="スクリーンショット 2018-02-25 22.05.09.png" src="https://qiita-image-store.s3.amazonaws.com/0/239309/af22438e-0894-8bf8-01ba-531c32255c4a.png">

【アウトプット画面】
<img width="1272" alt="スクリーンショット 2018-02-25 22.05.53.png" src="https://qiita-image-store.s3.amazonaws.com/0/239309/f3605c38-5598-49d8-5f18-467414e6c77c.png">

アウトプット画面で、JSONの結果が返ってきていることがわかります。


## noderedで実装してみる

ブラウザからnodered配下のサイト（/WML_if）に変数含めてPOSTして、WMLのモデルへコールし、その結果のTRUEの確率の結果を返すことを目指します。

【入力URL】

```linux:url
https://<noderedのサービス名>.mybluemix.net/WML_if?p1=F&p2=27&p3=Single&p4=Professional
```

【ブラウザの実行結果】
<img width="1065" alt="スクリーンショット 2018-02-25 22.14.23.png" src="https://qiita-image-store.s3.amazonaws.com/0/239309/97d17b45-0d9d-a08b-b32b-26ab938bc2de.png">

フローはこんな感じになります。

【noderedフロー】
<img width="754" alt="スクリーンショット 2018-02-25 22.09.58.png" src="https://qiita-image-store.s3.amazonaws.com/0/239309/93958985-cb8b-670f-e4e4-2ce97dacce23.png">

各コンポーネントの中身を説明します。

【Watson ML Interface】
POSTデータをもらうサイトを設定します
<img width="508" alt="スクリーンショット 2018-02-25 22.16.10.png" src="https://qiita-image-store.s3.amazonaws.com/0/239309/04b127f6-23b2-c0a2-c83e-181ea990b79d.png">

【Watson ML Response】
出力結果を返します
<img width="518" alt="スクリーンショット 2018-02-25 22.16.55.png" src="https://qiita-image-store.s3.amazonaws.com/0/239309/a74446ef-fae7-f330-588d-f2e705cc7312.png">

【Create Message】
WMLを利用するためのヘッダーの設定（msg.headers）、及び、また変数を入力されたURLから獲得し、WMLへの引数を設定(msg.payload)します。Bearerの箇所には、ご自身のtokenを入力してください。

<img width="505" alt="スクリーンショット 2018-02-25 22.18.41.png" src="https://qiita-image-store.s3.amazonaws.com/0/239309/906272e7-4bad-5b71-d0b8-3806f59baecd.png">

【Call Watson API】
Watson APIをコールします。API用のURL（API-Scoring-Endpoint）をコールします。
<img width="570" alt="スクリーンショット 2018-02-25 22.20.22.png" src="https://qiita-image-store.s3.amazonaws.com/0/239309/cc9036eb-42db-4c7e-607e-5e29f725d97b.png">

【DL結果のjson】
WatsonMLの結果をそのままdebugに出力するためのものです
ちなみにWMLの結果はこのような感じになります。

```json:WML結果
{ "fields": [ "GENDER", "AGE", "MARITAL_STATUS", "PROFESSION", "features", "rawPrediction", "probability", "prediction", "nodeADP_class", "nodeADP_classes" ], "values": [ [ "F", 27, "Single", "Professional", [ 2.0014299072911093, 2.668768261558189, 1.5677192932046256, 0.43422582176662905 ], [ 1064, 5 ], [ 0.9953227315247896, 0.004677268475210477 ], 0, "FALSE", [ "FALSE", "TRUE" ] ] ] }
```

【JSONからTrue確率値取得】
WatsonMLの戻り値は、複雑なJSON値なので、そこからTRUEの確率だけ取得する関数となります。
<img width="499" alt="スクリーンショット 2018-02-25 22.23.02.png" src="https://qiita-image-store.s3.amazonaws.com/0/239309/ff4f27c9-5cc9-78fe-e1c1-1080eb0072c0.png">

【結果（TRUEの確率）】
debugログでTRUEの確率を表示するためのものです

## ブラウザからの実行

以下のURLをブラウザで実行してみると、、、

【入力URL】

```linux:url
https://<noderedのサービス名>.mybluemix.net/WML_if?p1=F&p2=27&p3=Single&p4=Professional
```
以下のようなnoderedのdebugログの結果が得られます。

【nodered実行結果】
<img width="1093" alt="スクリーンショット 2018-02-25 22.25.20.png" src="https://qiita-image-store.s3.amazonaws.com/0/239309/d2fa749b-ea34-c175-7a3b-5e9a1e82c521.png">

画面はこんな感じです（再掲）

【ブラウザの実行結果】
<img width="1065" alt="スクリーンショット 2018-02-25 22.14.23.png" src="https://qiita-image-store.s3.amazonaws.com/0/239309/97d17b45-0d9d-a08b-b32b-26ab938bc2de.png">

以上で、noderedからWMLを実行することができました。

## nodered サンプルフロー

noderedのフローを記載しておきますので、インポートしてご利用ください。

```javascript:noderedフロー
[{"id":"44480ef2.06d6f","type":"http in","z":"47440251.52bb5c","name":"Watson ML Interface","url":"/WML_if","method":"get","swaggerDoc":"","x":102,"y":68,"wires":[["5e73f7fd.362788"]]},{"id":"5e73f7fd.362788","type":"function","z":"47440251.52bb5c","name":"Create Message","func":"var p1=msg.payload.p1;\nvar p2=msg.payload.p2;\nvar p3=msg.payload.p3;\nvar p4=msg.payload.p4;\nvar n2=Number(p2);\n\n//Header設定\nmsg.headers = { \n        \"Content-type\" : \"application/json\",\n        \"Accept\" : \"application/json\",\n        \"Authorization\" : \"Bearer <tokenを入力>\n};\n\n//POSTデータ作成\nmsg.payload = {\n    \"fields\": [\"GENDER\", \"AGE\", \"MARITAL_STATUS\", \"PROFESSION\"],\n    \"values\":[[p1,n2,p3,p4]]\n//    \"values\":[[\"M\",27,\"Single\",\"Professional\"]]\n};\nreturn msg;","outputs":1,"noerr":48,"x":217.5,"y":167.5,"wires":[["609ae5af.25f244"]]},{"id":"609ae5af.25f244","type":"http request","z":"47440251.52bb5c","name":"Call Watson API","method":"POST","ret":"obj","url":"https://ibm-watson-ml.mybluemix.net/v3/wml_instances/xxxxx-5d7a-4657-a276-ec6e6dd277f0/published_models/8xxxxx40-37aa-4df4-8d9a-f88a7a4fa378/deployments/xxxxx7aa-6f4f-4860-9047-6d06e889c7e9/online","tls":"","x":420.5,"y":166.5,"wires":[["eae695bc.e3ba68","db14b3f5.7b8a68"]]},{"id":"44507cb.6dd0004","type":"http response","z":"47440251.52bb5c","name":"Watson ML Response","x":562.5,"y":261.25,"wires":[]},{"id":"cb9c6b37.96d76","type":"debug","z":"47440251.52bb5c","name":"結果（TRUEの確率）","active":true,"console":"false","complete":"payload","x":560.5,"y":382.25,"wires":[]},{"id":"eae695bc.e3ba68","type":"function","z":"47440251.52bb5c","name":"JSONからTrue確率値取得","func":"// values結果取得\nvalues = msg.payload.values;\n//valuesの最初の結果取得\nTF_values = values[0];\n//確率取得（True,False）\nT_percent = TF_values[6]\n//Trueの確率\nmsg.payload = T_percent[0];\nreturn msg;","outputs":1,"noerr":0,"x":308.5,"y":327.5,"wires":[["cb9c6b37.96d76","44507cb.6dd0004"]]},{"id":"db14b3f5.7b8a68","type":"debug","z":"47440251.52bb5c","name":"DL結果のjson","active":true,"console":"false","complete":"payload","x":593,"y":104.75,"wires":[]}]
```
応用して色々と試してください。