Watson MLのWebサービスをNode-REDから呼び出す(v2対応済み)

# はじめに
Node-REDから、Watson ML上にdeployしたWebサービスを呼び出す方法としては

https://flows.nodered.org/node/node-red-contrib-watson-machine-learning

に部品があるのですが、コードを調べてみたところ古いインターフェースのままで、バージョンアップも止まっていました。
(というか、API仕様が根本的に変わったので 、ML v2対応しようとすると全部作り直しになりそう)

ということで、昨年9月にリリースした新しいWatson ML API仕様v2に対応するにはどうしたらいいか、考えてみました。
頑張って全部JavaScriptで実装して、新しいNode-REDの部品も作れなくないはずですが、しばらくJavaScriptから離れているため、コールバック先の関数から値を戻すのをどうしたらいいか思い出せず。。。
あまりきれいでないのですが、ひな形のフローファイルを作ってお茶を濁すことにしました。

# 完成イメージ
下の図が、完成イメージです。

![screen-01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206130/dc488b27-d72a-cdf3-648e-35255fd7c1df.png)

呼び出し先のAPIは、別記事[Watson Studioでscikit-learn機械学習モデルをWebサービス化する](https://qiita.com/makaishi2/items/38371d272d752b6e7647)で作ったものです。
(Iris datasetを学習データにした分類モデル)
こちらに関しては、Webサービスのデプロイまで済んでいることが前提になりますので、その点はご注意下さい。

フローファイルは、以下のGithubサイトにアップしてあります。

https://github.com/makaishi2/sample-data/blob/master/node-red/wml-flows-share.json

このフローをNode-REDに取り込んで、2箇所ノードの設定(apikeyとscroring_url)を変更すると、Watson MLのAPIを呼び出せるようになります。

# サンプルの動かし方
## 設定値の取得
API_KEYの値
[1.3 API Keyの取得](https://qiita.com/makaishi2/items/38371d272d752b6e7647#13-api-key%E3%81%AE%E5%8F%96%E5%BE%97)を参照して下さい。

scoring_urlの値
[6.3 URL取得](https://qiita.com/makaishi2/items/38371d272d752b6e7647#63-url%E5%8F%96%E5%BE%97)を参照して下さい。Node-REDから呼び出す場合は、バージョンIDも付加した``scoring_url2``の方を使う必要があるので、その点を注意して下さい。

## Node-RED上の設定
取り込んだフローの中で修正が必要なのは
①　「token取得準備」
②　「API呼び出し」
の二つのノードです。

![screen-02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206130/5a82a144-e97b-d2d2-5158-48ba128f3183.png)

### 「token取得準備」ノードの設定
下記が、該当functionノードのコードです。

```js
// header設定
msg.headers = {};
msg.headers['Accept'] = "application/json";
msg.headers['Content-Type'] = "application/x-www-form-urlencoded";

// apikey設定
var apikey = "xxxx";
msg.payload = "apikey=" + apikey + "&grant_type=urn:ibm:params:oauth:grant-type:apikey";
return msg;
```

この中で、

```js
var apikey = "xxxx";
```

の行に調べたAPI_KEYの値を設定します。

### 「API呼び出し」ノードの設定

下記が「API呼び出し」の設定ノードです。

![screen-03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206130/564eaa0a-7cef-dfd7-9e36-d092102523fa.png)

この中で**URL**の欄に、事前に調べたscring_urlの値を貼り付けます。

## テスト
これで準備はすべて整いました。正しく設定ができていれば、一番左のinjectノードのクリックで、Watson MLのAPIが呼び出せるはずです。

# フロー解説
最後に、フローの各ノードで何をやっているか解説します。

![screen-02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206130/a1e04b3b-2865-9bf6-bc1f-7b04d23c95dc.png)


基本的には[6. 予測(Watsonライブラリを使わない方法](https://qiita.com/makaishi2/items/38371d272d752b6e7647#6-%E4%BA%88%E6%B8%ACwatson%E3%83%A9%E3%82%A4%E3%83%96%E3%83%A9%E3%83%AA%E3%82%92%E4%BD%BF%E3%82%8F%E3%81%AA%E3%81%84%E6%96%B9%E6%B3%95)とまったく同じことを、Node-RED上でやっているだけです。
元記事も合わせて読んでいただけると、より理解が深まると思います。

## Token取得準備
コードは1章で一度載せていますが、再掲します。

```js
// header設定
msg.headers = {};
msg.headers['Accept'] = "application/json";
msg.headers['Content-Type'] = "application/x-www-form-urlencoded";

// apikey設定
var apikey = "xxxx";
msg.payload = "apikey=" + apikey + "&grant_type=urn:ibm:params:oauth:grant-type:apikey";
return msg;
```

この後のHTTPリクエストノードでの準備をしています。
リクエストヘッダは``msg.headers``に、データは``msg.payload``にそれぞれ設定しています。

## Token取得
http requestノードで、メソッドを``POST``に、URLを``https://iam.cloud.ibm.com/identity/token``にしています。

![screen-04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206130/4177d857-9eb6-b1a4-0619-e7f7e2e409b7.png)

それ以外のパラメータはその前のノードで設定済みなので、これだけでTokenが取得できます。

## API呼び出し準備
下記が、API呼び出し準備用のfunctionノードの内容です。

```js
// token取得
value = JSON.parse(msg.payload);
console.log(value);
console.log(value["access_token"]);
var token = value.access_token;

// header設定
msg.headers = {};
msg.headers['Content-Type'] = "application/json";
msg.headers['Authorization'] = 'Bearer ' + token;

// data設定
msg.payload = '{"input_data": [{"values": [[6.9, 3.1, 5.4, 2.1], [4.4, 2.9, 1.4, 0.2]]}]}'

return msg;
```

まず、token取得APIの戻り値をパースした後で、キー値``access_token``に対応するvalueを取得します。
この値が、次のAPI呼び出しで必要なtokenになります。

header設定で、``Bearer ``の後に、今取得したtokenを追加し、これを``Authorization``の値にします。

最後の``msg.payload``は、APIの引数になる入力データ部分です。
今は、iris datasetの入力値２行分をベタうちにしてしまっていますが、実際のアプリでは、他からのデータを使ってこの部分を組み立てることになります。

## 戻り値の読み取り方

最後にWatson MLの戻り値の解説をします。
リクエストの戻りをパースすると、次のような結果になります。

![screen-05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206130/4cbf1628-33ea-696f-55f2-55d51cb6d6a6.png)

戻り値は、赤枠で示した**予測値**(prediction)と青枠で示した**確率値**(probability)に分けられます。
このモデルの目的は、アヤメの花の種類(0か1か2)を区別することなのでが、モデルの予測結果としての種類が``prediction``の値です。
もう一つの``probability``とは、それぞれの判断結果に対する確信度だと思って下さい。
今回の2件の予測結果に関して言うと、「1件目の予測結果が2である」ことは100%自信があるが、「2件目の予測結果が0である」ことに対しては、確信度が99%で、多少自信がないということになります。