Watson Machine LeaningにPython Functionを登録する

# はじめに
シリーズ第二部です。
　Python FunctionをWatson Machine Learningに登録し、更にWebサービス化します。
シリーズ第一部[Watson StudioのSPSS Modelerを使って回帰モデルをWebサービス化する](https://qiita.com/makaishi2/items/cd9382a8be9a1b5d6a51)と組み合わせると、SPSS Modeler Flowで作った回帰モデルをWatson ML上のPython Functionとすることができます。
ここまでの結果と、[シリーズ第三部の手順](https://qiita.com/makaishi2/items/de5630912fbbea7d879f)を組み合わせると、結果的に**SPSS Modeler Flowで作った回帰モデルをOpenScaleの説明性機能の対象とする**ことが可能となります。

# データ準備
以下のnotebookファイルを事前にダウンロードしておきます。
また、登録対象のサービスが第一部で説明したもの以外の場合は、notebookの中に埋め込まれているテスト用jsonファイルも別途用意しておく必要があります。

|ファイル名|リンク|目的|
|---|---|---|
|register-python-function-sample.ipynb|[register-python-function-sample.ipynb](https://raw.githubusercontent.com/makaishi2/sample-data/master/notebooks/register-python-function-sample.ipynb)|Python Function登録用notebook|


Notebookの全体をブラウザから見たい場合は、以下のリンクをクリックしてください。

https://github.com/makaishi2/sample-data/blob/master/notebooks/register-python-function-sample.ipynb

# 環境準備
Watson Studioでプロジェクトが作成済みで、かつWatson Machine Learningとの紐付けができていること。シリーズ第一部の準備をしていれば、条件を満たしています。

# セットアップ用情報の確認
Watson MLの資格情報と、対象となるWebサービスのscoring-urlを事前に入手しておく必要があります。

#### Watson ML資格情報
![スクリーンショット 2019-07-13 15.35.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206130/7608173d-cac9-3d6a-7b4c-863f230edefb.png)

#### scoring-url
![スクリーンショット 2019-07-13 15.34.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206130/1e35a753-6d0d-370d-4d14-b2a4200f4c15.png)


より具体的な手順については [Watson Studioでscikit-learn機械学習モデルをWebサービス化する](https://qiita.com/makaishi2/items/38371d272d752b6e7647)などを参考にしてください。

# Notebookファイルの読み込みと修正・実行

## Notebookファイルの読み込み
Watson Studio上のJupyter Notbookに事前ダウンロードしたNotebookファイルを読み込みます。
Jupyterの起動は**Add to project** -> **Notebook** から行ってください。

![スクリーンショット 2019-07-13 15.24.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206130/8a7b86d2-941e-9624-f317-1d14dac57d19.png)

## 環境固有情報の設定
Notebook上部のWatson ML credentails設定用セルと、scoring_url設定用セルに事前準備した情報を張り付けます

## セルの実行
第一部から継続して操作を行っている場合、あとはnotebookで shift + enterを繰り返し押せば、Python Functionの登録からテストまで実行できるはずです。

# コード解説
以下に、Jupyter Notebookコードの簡単な解説を記載します。

## Step 1 Webサービスの呼出し

シリーズ第一弾で事前に設定済みのWatson ML上のサービスを、Watson ML APIを使って呼び出すテストをしています。

```py3
# wml認証情報 (Watson Machine Learningのcredntailsをコピペします)

wml_credentials = {
}

# Machine LearningスコアリングURL (WebサービスエンドポイントのScoring End-pointをコピペします)
scoring_url = ''

# Webサービス呼出しテスト用データ
payload = {"fields": ["CRIM", "ZN", "INDUS", "CHAS", "NOX", "RM", 
  "AGE", "DIS", "RAD", "TAX", "PTRATIO", "B", "LSTAT"], 
"values": 
[[0.00632,18.0,2.31,0.0,0.538,6.575,65.2,4.09,1.0,296.0,15.3,396.9,4.98],
[0.02731,0.0,7.07,0.0,0.469,6.421,78.9,4.9671,2.0,242.0,17.8,396.9,9.14]]}

# Webサービス呼出しのテスト
# 普通にWebサービス呼出しができることを確認します。

from watson_machine_learning_client import WatsonMachineLearningAPIClient
client = WatsonMachineLearningAPIClient(wml_credentials)
client.deployments.score(scoring_url, payload)
```

第一弾もそのまま利用した場合、次のような結果が帰ってくるはずです。

```
{'fields': ['$E-PRICE'],
 'values': [[30.003843377020388], [25.025562379060613]]}
```

## Step 2 Python Functionの定義
上でテストしたWebサービス呼出しを、Python Function経由で行います。
ちょっとわかりにくいコードなのですが、関数内部で、実際のscoring機能を呼び出す関数を定義し、外側の関数の戻りとして内部関数そのものを返す作りになっています。

```py3
# Python関数呼び出し時に必要となる情報を ai_parmsという変数にまとめておきます
ai_parms = { "wml_credentials" : wml_credentials,
             "model_deployment_endpoint_url" : scoring_url }

# Python Functionの定義
# 上でテストしたWebサービス呼出しをPython Functionとして定義します。

def spss_function(parms=ai_parms):
    from watson_machine_learning_client import WatsonMachineLearningAPIClient
    client = WatsonMachineLearningAPIClient( parms["wml_credentials"] )
    def score(payload):
        return client.deployments.score(parms["model_deployment_endpoint_url"], payload)
    return score
```

このような関数を定義すると、この次のコードにより、スコアリングの呼出しが可能となります。

``` py3
# Python Functionのテスト
# 上で定義したPython Functionをテストします。
function_result = spss_function()(payload)
print(function_result)
```

うまくいくと次のような結果が帰ってくる形になります。

```
{'fields': ['$E-PRICE'], 'values': [[30.003843377020388], [25.025562379060613]]}
```

## Step 3 Python FunctionをWatson MLに登録
Step 2がややこしいのですが、ここまでできてしまえば、Python Functionの登録はとても簡単です。

### Functionの登録
テスト済みの``spss_function``を引数にWatson MLのAPIの一つである``store_function``を呼び出します。

```py3
# PythonFunctionをstore_function関数で登録します

meta_data = { client.repository.FunctionMetaNames.NAME : 'SPSS boston regression' }
function_details = client.repository.store_function( meta_props=meta_data, function=spss_function )
```

うまくいくと下のような結果が帰ってきます。

```
No matching default runtime found. Creating one...SUCCESS

Successfully created runtime with uid: da7caab5-d7d3-4c6c-b601-05daa0044eec
```

### Functionのデプロイ
次の登録したFunctionのデプロイを行います。

```py3
# Function IDを取得します
function_id = function_details["metadata"]["guid"]
print(function_id)

# Functionをdeployします
function_deployment_details = client.deployments.create( artifact_uid=function_id, name='SPSS boston deployment' )
```

次のような結果になれば、成功しています。

```
92cb9365-08fb-4d9a-827c-536fe51ad4ea

#######################################################################################

Synchronous deployment creation for uid: '92cb9365-08fb-4d9a-827c-536fe51ad4ea' started

#######################################################################################


INITIALIZING
DEPLOY_IN_PROGRESS.
DEPLOY_SUCCESS


------------------------------------------------------------------------------------------------
Successfully finished deployment creation, deployment_uid='4ace00d9-f5af-40d1-a960-65a82ebc7ec0'
------------------------------------------------------------------------------------------------
```

### Functionのテスト
最後に以下のコードで、デプロイしたFunctionのテストを行います。

```py3
# functionのdeployment URLを取得します
function_deployment_endpoint_url = client.deployments.get_scoring_url( function_deployment_details )
print(function_deployment_endpoint_url )

# deploy したPython Functionの呼出しテスト
client.deployments.score( function_deployment_endpoint_url, payload)
```

次のような結果が帰ってくれば、デプロイに成功しています。

```
https://us-south.ml.cloud.ibm.com/v3/wml_instances/fa53a255-4a53-4546-8208-9b92894ab3ba/deployments/4ace00d9-f5af-40d1-a960-65a82ebc7ec0/online

{'fields': ['$E-PRICE'],
 'values': [[30.003843377020388], [25.025562379060613]]}
```

# プロジェクト管理画面からの確認
一番最後にプロジェクト管理画面から、この結果を確認してみましょう。
まず、**Deployment**タブに、最後に登録したFunctionが含まれているはずです。そのリンクをクリックして詳細画面を表示すると、下のように**Asset type**が**function**となっていれば、FunctionのDeplymentに成功していることになります。

![スクリーンショット 2019-07-13 17.09.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206130/71617e43-f6c2-f823-bfd5-d84efb973a5f.png)

お疲れ様でした。ここまでの準備ができれば、いよいよ今登録したPython FunctionをOpen Scaleの管理対象とすることができます。その手順は第三部を参照してください。
また、この手順のキモとなっているStep 2のPython Functionの定義に際しては、その内部で呼び出しているscoring関数の参照先がWatson MLでなくても実装できることがわかると思います。
これが、**Python Functionを利用すればOpenScaleは任意のWebサービス化された機械学習モデルを管理対象にできる**といっている話の所以となります。