PythonでのCloud Pak for Dataのオブジェクト操作例 (WML client, project_lib)

更新履歴：
(2020/9/8) WML client初期化でのversion番号を自動取得するように変更

---

# はじめに
Cloud Pak for Data (以下CP4D)の分析プロジェクトでNotebook (Jupyter Notebook)でモデルを作る場合、データの取り込みやモデルの格納、作ったモデルのデプロイなどを行うためのライブラリとしてwatson-machine-learning-client-V4 (以下WML client)[^1]とproject_lib[^2]があります。どちらもCP4DのNotebookの標準Python環境にデフォルトで入ってます。
この記事では、これらのライブラリの具体的な使い方を紹介します。

[^1]: 詳細は、WML client リファレンスガイド [watson-machine-learning-client(V4)](https://wml-api-pyclient-dev-v4.mybluemix.net/#) および、CP4D v2.5 製品マニュアル [Python クライアントを使用したデプロイ](https://www.ibm.com/support/knowledgecenter/ja/SSQNUZ_2.5.0/wsj/wmls/wmls-deploy-python.html)を参照。WML clientリファレンスガイドは随時更新される可能性があるので注意。

[^2]: 詳細は、CP4D v2.5 製品マニュアル [Python 用の project-lib の使用](https://www.ibm.com/support/knowledgecenter/ja/SSQNUZ_2.5.0/wsj/analyze-data/project-lib-python.html)を参照

なお、WML clientはURLを指定して認証も行うため、CP4D外のPython環境でも動作します。外部のバッチプログラムなどからCP4D内のモデルやデプロイメントなどのオブジェクト操作手段としても使えます。

（動作確認済バージョン）

* Cloud Pak for Data v2.5 / v3.0LA / v3.0.1
* WML client v1.0.64 / v1.0.103
* project_lib v1.7.1

<details><summary>Notebookでのpipコマンドでの確認方法</summary><div>

```python:
!pip show watson-machine-learning-client-V4
```

```:output
Name: watson-machine-learning-client-V4
Version: 1.0.64
Summary: Watson Machine Learning API Client
Home-page: http://wml-api-pyclient-v4.mybluemix.net
Author: IBM
Author-email: svagaral@in.ibm.com, nagiredd@in.ibm.com, kamigupt@in.ibm.com
License: BSD
Location: /opt/conda/envs/Python-3.6-WMLCE/lib/python3.6/site-packages
Requires: urllib3, pandas, tabulate, requests, lomond, tqdm, ibm-cos-sdk, certifi
Required-by: 
```

```python
!pip show project_lib
```

```:output
Name: project-lib
Version: 1.7.1
Summary: programmatic interface for accessing project assets in IBM Watson Studio
Home-page: https://github.ibm.com/ax/project-lib-python
Author: IBM Watson Studio - Notebooks Team
Author-email: None
License: UNKNOWN
Location: /opt/conda/envs/Python-3.6-WMLCE/lib/python3.6/site-packages
Requires: requests
Required-by: 
```
</div></details>



# CP4D上での主な操作と使用するライブラリ一覧
データ資産(Data Asset)、モデル(Models)、関数(Functions)、デプロイメント(Deployments)などを生成したり、保存したりできます。また、作成したデプロイメントを実行することも可能です。

## 分析プロジェクトに対する操作
データ資産は主にproject_libを使用し、モデル系はWML clientを使用します。

| 主な操作 | 使用するライブラリ |
| :--- | :--- |
| データ資産からのデータの読み込み[^3] | project_lib または、<br>pandas.read_csvで'/project_data/data_asset/ファイル名'を直接読む |
| データ資産へのファイルデータの出力[^4] | project_lib |
| データ資産の一覧表示 | WML client |
| モデルの保存 | WML client|
| モデルの一覧表示 | WML client |
| 関数の保存 | WML client |
| 関数の一覧表示 | WML client |

[^3]: データの読み込みは、Notebook画面の右上にあるデータボタン(0100と書いてある)をクリックし、該当のデータ資産名 > コードに挿入 > pandas DataFrame をクリックすることで、Notebook内のセルにコードが自動挿入されます。標準では、ファイルの場合はpandas.read_csvのコードが、DBテーブルの場合はproject_libのコードが挿入されるようです。

[^4]: WML clientでも可能ですが、格納されたファイルが本来のデータ資産とは異なる領域にファイルが保存され、ダウンロードするとファイル名が不正になるなど問題が確認されているため、WML clientでのデータ資産への保管はオススメしません。その方法もこの記事には書きません。</span>

## デプロイメントスペース(分析デプロイメント)に対する操作
全てWML clientを使用します。

| 主な操作 | 使用するライブラリ |
| :--- | :--- |
| データ資産へのファイルデータの出力 | WML client |
| データ資産の一覧表示 | WML client |
| モデルの保存 | WML client|
| モデルの一覧表示 | WML client |
| 機能(関数)の保存[^5] | WML client |
| 機能(関数)の一覧表示[^5] | WML client |
| デプロイメントの作成 | WML client |
| デプロイメントの一覧表示 | WML client |
| デプロイメントの実行 | WML client |

[^5]: デプロイメントスペースの画面上は、関数が「機能」と表現されています。日本語訳が統一されておらずイマイチな気がしますが。



# WML clientのインポートと初期化
## WML clientのインポート

```python
from watson_machine_learning_client import WatsonMachineLearningAPIClient
```

## WML clientの初期化（認証）
接続先や認証情報を付与して、WML clientを初期化します。認証情報の取得方法は2種類あります。

1. OS環境変数 USER_ACCESS_TOKEN の値を使う
1. CP4Dユーザー名とパスワードを使う

1はCP4D上のNotebookで使える方法。もし、CP4D外の環境でWML clientを使うときは2です。
注意事項としては、

* instance_id は "wml_local" または "openshift" とします。マニュアルには前者が記載されてますが、後者を使うようガイドされたことがあります。実質的にはどちらでも動くようです。
* urlは、使用しているCP4D環境のURL（https://ホスト名 or IPアドレス(:ポート番号))とします。***最後にスラッシュを付けないことがポイントです。付けたら後続の処理でエラーになります(ハマりポイント）。***
* versionは、2.5の場合は"2.5.0"、3.0LAなら"3.0.0"、3.0GAなら"3.0.1" -> (2020/9/8更新) 方法1はrequestsを使ってバージョン番号を自動で取得する方法に変更しました。

```python:方法1の場合

import requests, os
token = os.environ['USER_ACCESS_TOKEN']
url = "https://cp4d.host.name.com"
version = requests.get(os.environ['RUNTIME_ENV_APSX_URL'] + "/diag").text[0:5]
wml_credentials = {
    "token" : token,
    "instance_id" : "openshift",
    "url": url,
    "version": version
}
client = WatsonMachineLearningAPIClient(wml_credentials)
```

```python:方法2の場合
# usernameとpasswordは、実際に認証に使うCP4Dユーザーのものを指定する
url = "https://cp4d.host.name.com"
wml_credentials = {
   "username":"xxxxxxxx",
   "password": "xxxxxxxx",
   "instance_id": "openshift",
   "url" : url,
   "version": "3.0.0"
}
client = WatsonMachineLearningAPIClient(wml_credentials)
```



# 分析プロジェクトとデプロイメントスペースの切り替え
後続の処理の操作対象を、分析プロジェクト(default_project)かデプロイメントスペース(default_space)のどちらにするかをセットします。初期状態は分析プロジェクトにセットされています。
***操作する対象を変更する場合は、この切替え操作を必ず実施するのを忘れないこと（ハマりポイント)。***


## 各IDの調べ方

分析プロジェクトのIDは、OS環境変数 PROJECT_ID に入っているものを使います。

```python:分析プロジェクトのIDをセット
project_id = os.environ['PROJECT_ID']
```

デプロイメントスペースのIDは、CP4D画面のデプロイメントスペースの「設定」にある「スペースGUID」で予め調べて置くか、以下の方法でclient.repository.list_spaces() で表示されるGUIDを使います。

```python:デプロイメントスペースのIDを調べる
client.repository.list_spaces()
```

```:output
------------------------------------  --------------------  ------------------------
GUID                                  NAME                  CREATED
xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx  DepSpaceName          2020-05-25T09:13:04.919Z
------------------------------------  --------------------  ------------------------
```

```python:デプロイメントスペースのIDをセット
space_id = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"
```

## 操作対象の切り替え

```python:操作対象を分析プロジェクトに切り替える
client.set.default_project(project_id)
```

```python:操作対象をデプロイメントスペースに切り替える
client.set.default_space(space_id)
```



# データ資産の操作
## データ資産の一覧表示 (分析プロジェクト)
WML clientを使用します。

```python
# 分析プロジェクトに切り替え（切り替える必要がある場合のみ実施）
client.set.default_project(project_id)

# データ資産の一覧を表示
client.data_assets.list()
```

## データ資産の一覧表示 (デプロイメントスペース) 
WML clientを使用します。

```python
# デプロイメントスペースに切り替え（切り替える必要がある場合のみ実施）
client.set.default_space(space_id)

# データ資産の一覧を表示
client.data_assets.list()
```

## データ資産からのデータの読み込み (分析プロジェクト)
Notebook画面の右上にあるデータボタン(0100と書いてある)をクリックし、該当のデータ資産名 > コードに挿入 > pandas DataFrame をクリックすることで、Notebook内のセルにコードが自動挿入されます。これを使うのが楽です。

CSVなどファイルの場合は、pandas.read_csvでデータを読むコードが自動挿入されます。df_data_XのXの部分は、挿入操作を繰り返すと自動的に増えていきます。

```python:挿入コード(ファイルの場合)
import pandas as pd
df_data_1 = pd.read_csv('/project_data/data_asset/filename.csv')
df_data_1.head()
```

project_libを使ったファイルデータを読むコード例は製品マニュアル内に例がありますが、こんなコードです。

```python:project_libを使ったファイルの読み込み
from project_lib import Project
project = Project.access()

my_file = project.get_file("filename.csv")

my_file.seek(0)
import pandas as pd
df = pd.read_csv(my_file)
```

DBテーブルの場合は、前述の「コードに挿入」でproject_libを使ったコードが自動挿入されます。頭に```# @hidden_cell```と入っているので、Notebookを共有する時にこのセルを含めない選択が可能です。[^6]

[^6]: CP4D v2.5 製品マニュアル [ノートブックでの機密コード・セルの非表示](https://www.ibm.com/support/knowledgecenter/ja/SSQNUZ_2.5.0/wsj/analyze-data/hide_code.html)を参照

```python:挿入コード(Db2テーブルSCHEMANAME.TBL1の例）
# @hidden_cell
# This connection object is used to access your data and contains your credentials.
# You might want to remove those credentials before you share your notebook.

from project_lib import Project
project = Project.access()
TBL1_credentials = project.get_connected_data(name="TBL1")

import jaydebeapi, pandas as pd
TBL1_connection = jaydebeapi.connect('com.ibm.db2.jcc.DB2Driver',
    '{}://{}:{}/{}:user={};password={};'.format('jdbc:db2',
    TBL1_credentials['host'],
    TBL1_credentials.get('port', '50000'),
    TBL1_credentials['database'],
    TBL1_credentials['username'],
    TBL1_credentials['password']))

query = 'SELECT * FROM SCHEMANAME.TBL1'
data_df_1 = pd.read_sql(query, con=TBL1_connection)
data_df_1.head()

# You can close the database connection with the following code.
# TBL1_connection.close()
# To learn more about the jaydebeapi package, please read the documentation: https://pypi.org/project/JayDeBeApi/
```

## データ資産へのデータの保存 (分析プロジェクト)
pandasのデータフレームをCSVファイルとして保存する方法です。project_libを使います。

```python
from project_lib import Project
project = Project.access()

project.save_data("filename.csv", df_data_1.to_csv(),overwrite=True)
```

## データ資産へのデータの保存 (デプロイメントスペース)
同様に、CSVファイルをデプロイメントスペースへ保存する方法です。デプロイメントスペースのデータ資産は、デプロイメントのバッチ実行時の入力データとして使用されます。
WML clientを使います。

```python
# pandasデータフレームをCSVファイルとして一旦出力。デフォルトでは/home/wsuser/work配下に格納される
df_data_1.to_csv("filename.csv")

# デプロイメントスペースに切り替え（切り替える必要がある場合のみ実施）
client.set.default_space(space_id)

# データ資産として保存
asset_details = client.data_assets.create(name="filename.csv",file_path="/home/wsuser/work/filename.csv")
```

保存したデータ資産のIDやhrefは、createの戻り値asset_details内に含まれています。IDやhrefはデプロイメントスペースでデプロイメントをバッチ実行する際に使用します。

```python
#  createの戻り値(メタ情報)の確認
asset_details
```

```:output
{'metadata': {'space_id': 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
  'guid': 'yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy',
  'href': '/v2/assets/zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz?space_id=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
  'asset_type': 'data_asset',
  'created_at': '2020-05-25T09:23:06Z',
  'last_updated_at': '2020-05-25T09:23:06Z'},
 'entity': {'data_asset': {'mime_type': 'text/csv'}}}
```

以下のように取り出します。

```python:戻り値asset_detailsからのメタ情報の取得
asset_id = client.data_assets.get_uid(asset_details)
asset_href = client.data_assets.get_href(asset_details)
```

```python:戻り値asset_detailsからのメタ情報の取得（もう1つの方法)
asset_id = asset_details['metadata']['guid']
asset_href = asset_details['metadata']['href']
```


# モデルの操作

## 準備としてモデルを作成しておく
例として、Irisのサンプルデータを使ったsckikt-learnのランダムフォレストモデルを作成します。

```python
# Irisサンプルデータをロード
from sklearn.datasets import load_iris
iris = load_iris()
df = pd.DataFrame(iris.data, columns=iris.feature_names)
df['iris_type'] = iris.target_names[iris.target]

# ランダムフォレストでモデルを作成
from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestClassifier

X = df.drop('iris_type', axis=1)
y = df['iris_type']
X_train, X_test, y_train, y_test = train_test_split(X,y,random_state=0)

clf = RandomForestClassifier(max_depth=2, random_state=0, n_estimators=10)
model = clf.fit(X_train, y_train)
    
# モデルの精度を確認
from sklearn.metrics import confusion_matrix, accuracy_score
y_test_predicted = model.predict(X_test)    
print("confusion_matrix:")
print(confusion_matrix(y_test,y_test_predicted))
print("accuracy:", accuracy_score(y_test,y_test_predicted))
```

上記の```model```が学習済モデルです。


## モデルの保存 (分析プロジェクト)
分析プロジェクトへのモデルの保存は、デプロイのための必須操作ではないですが可能です。
WML clientを使用します。

```python
# 分析プロジェクトに切り替え（切り替える必要がある場合のみ実施）
client.set.default_project(project_id)

# モデルのメタ情報を記述
model_name = "sample_iris_model"
meta_props={
    client.repository.ModelMetaNames.NAME: model_name,
    client.repository.ModelMetaNames.RUNTIME_UID: "scikit-learn_0.22-py3.6",
    client.repository.ModelMetaNames.TYPE: "scikit-learn_0.22",
    client.repository.ModelMetaNames.INPUT_DATA_SCHEMA:{
        "id":"iris model",
        "fields":[
            {'name': 'sepal length (cm)', 'type': 'double'},
            {'name': 'sepal width (cm)', 'type': 'double'},
            {'name': 'petal length (cm)', 'type': 'double'},
            {'name': 'petal width (cm)', 'type': 'double'}
        ]
    },
    client.repository.ModelMetaNames.OUTPUT_DATA_SCHEMA: {
        "id":"iris model",
        "fields": [
            {'name': 'iris_type', 'type': 'string','metadata': {'modeling_role': 'prediction'}}
        ]
    }
}

# モデルを保存。戻り値に作成したモデルのメタデータが含まれる
model_artifact = client.repository.store_model(model, meta_props=meta_props, training_data=X, training_target=y)
```

###### 補足: モデルに含めるメタ情報

モデルに含めるメタ情報meta_propsで、INPUT_DATA_SCHEMAとOUTPUT_DATA_SCHEMAの指定は必須ではありませんが、***デプロイ後のデプロイメント詳細画面でテスト実行をフォーム形式で指定したい場合は必須です。ここで指定した形式がフォーム入力の形式となります（ハマりポイント)***。

<details><summary>指定可能なRUNTIME_UIDの調べ方</summary><div>
GUIDに表示された部分を、meta_props内のRUNTIME_UIDとして指定できます。

```python:
# https://wml-api-pyclient-dev-v4.mybluemix.net/#runtimes
client.runtimes.list(limit=200)
```

```:output(CP4Dv2.5の場合)
    --------------------------  --------------------------  ------------------------  --------
    GUID                        NAME                        CREATED                   PLATFORM
    do_12.10                    do_12.10                    2020-05-03T08:35:16.679Z  do
    do_12.9                     do_12.9                     2020-05-03T08:35:16.648Z  do
    pmml_4.3                    pmml_4.3                    2020-05-03T08:35:16.618Z  pmml
    pmml_4.2.1                  pmml_4.2.1                  2020-05-03T08:35:16.590Z  pmml
    pmml_4.2                    pmml_4.2                    2020-05-03T08:35:16.565Z  pmml
    pmml_4.1                    pmml_4.1                    2020-05-03T08:35:16.537Z  pmml
    pmml_4.0                    pmml_4.0                    2020-05-03T08:35:16.510Z  pmml
    pmml_3.2                    pmml_3.2                    2020-05-03T08:35:16.478Z  pmml
    pmml_3.1                    pmml_3.1                    2020-05-03T08:35:16.450Z  pmml
    pmml_3.0                    pmml_3.0                    2020-05-03T08:35:16.422Z  pmml
    ai-function_0.1-py3.6       ai-function_0.1-py3.6       2020-05-03T08:35:16.378Z  python
    ai-function_0.1-py3         ai-function_0.1-py3         2020-05-03T08:35:16.350Z  python
    hybrid_0.2                  hybrid_0.2                  2020-05-03T08:35:16.322Z  hybrid
    hybrid_0.1                  hybrid_0.1                  2020-05-03T08:35:16.291Z  hybrid
    xgboost_0.90-py3.6          xgboost_0.90-py3.6          2020-05-03T08:35:16.261Z  python
    xgboost_0.82-py3.6          xgboost_0.82-py3.6          2020-05-03T08:35:16.235Z  python
    xgboost_0.82-py3            xgboost_0.82-py3            2020-05-03T08:35:16.204Z  python
    xgboost_0.80-py3.6          xgboost_0.80-py3.6          2020-05-03T08:35:16.173Z  python
    xgboost_0.80-py3            xgboost_0.80-py3            2020-05-03T08:35:16.140Z  python
    xgboost_0.6-py3             xgboost_0.6-py3             2020-05-03T08:35:16.111Z  python
    spss-modeler_18.2           spss-modeler_18.2           2020-05-03T08:35:16.083Z  spss
    spss-modeler_18.1           spss-modeler_18.1           2020-05-03T08:35:16.057Z  spss
    spss-modeler_17.1           spss-modeler_17.1           2020-05-03T08:35:16.029Z  spss
    scikit-learn_0.22-py3.6     scikit-learn_0.22-py3.6     2020-05-03T08:35:16.002Z  python
    scikit-learn_0.20-py3.6     scikit-learn_0.20-py3.6     2020-05-03T08:35:15.965Z  python
    scikit-learn_0.20-py3       scikit-learn_0.20-py3       2020-05-03T08:35:15.939Z  python
    scikit-learn_0.19-py3.6     scikit-learn_0.19-py3.6     2020-05-03T08:35:15.912Z  python
    scikit-learn_0.19-py3       scikit-learn_0.19-py3       2020-05-03T08:35:15.876Z  python
    scikit-learn_0.17-py3       scikit-learn_0.17-py3       2020-05-03T08:35:15.846Z  python
    spark-mllib_2.4             spark-mllib_2.4             2020-05-03T08:35:15.816Z  spark
    spark-mllib_2.3             spark-mllib_2.3             2020-05-03T08:35:15.788Z  spark
    spark-mllib_2.2             spark-mllib_2.2             2020-05-03T08:35:15.759Z  spark
    tensorflow_1.15-py3.6       tensorflow_1.15-py3.6       2020-05-03T08:35:15.731Z  python
    tensorflow_1.14-py3.6       tensorflow_1.14-py3.6       2020-05-03T08:35:15.705Z  python
    tensorflow_1.13-py3.6       tensorflow_1.13-py3.6       2020-05-03T08:35:15.678Z  python
    tensorflow_1.11-py3.6       tensorflow_1.11-py3.6       2020-05-03T08:35:15.646Z  python
    tensorflow_1.13-py3         tensorflow_1.13-py3         2020-05-03T08:35:15.619Z  python
    tensorflow_1.13-py2         tensorflow_1.13-py2         2020-05-03T08:35:15.591Z  python
    tensorflow_0.11-horovod     tensorflow_0.11-horovod     2020-05-03T08:35:15.562Z  native
    tensorflow_1.11-py3         tensorflow_1.11-py3         2020-05-03T08:35:15.533Z  python
    tensorflow_1.10-py3         tensorflow_1.10-py3         2020-05-03T08:35:15.494Z  python
    tensorflow_1.10-py2         tensorflow_1.10-py2         2020-05-03T08:35:15.467Z  python
    tensorflow_1.9-py3          tensorflow_1.9-py3          2020-05-03T08:35:15.435Z  python
    tensorflow_1.9-py2          tensorflow_1.9-py2          2020-05-03T08:35:15.409Z  python
    tensorflow_1.8-py3          tensorflow_1.8-py3          2020-05-03T08:35:15.383Z  python
    tensorflow_1.8-py2          tensorflow_1.8-py2          2020-05-03T08:35:15.356Z  python
    tensorflow_1.7-py3          tensorflow_1.7-py3          2020-05-03T08:35:15.326Z  python
    tensorflow_1.7-py2          tensorflow_1.7-py2          2020-05-03T08:35:15.297Z  python
    tensorflow_1.6-py3          tensorflow_1.6-py3          2020-05-03T08:35:15.270Z  python
    tensorflow_1.6-py2          tensorflow_1.6-py2          2020-05-03T08:35:15.243Z  python
    tensorflow_1.5-py2-ddl      tensorflow_1.5-py2-ddl      2020-05-03T08:35:15.209Z  python
    tensorflow_1.5-py3-horovod  tensorflow_1.5-py3-horovod  2020-05-03T08:35:15.181Z  python
    tensorflow_1.5-py3.6        tensorflow_1.5-py3.6        2020-05-03T08:35:15.142Z  python
    tensorflow_1.5-py3          tensorflow_1.5-py3          2020-05-03T08:35:15.109Z  python
    tensorflow_1.5-py2          tensorflow_1.5-py2          2020-05-03T08:35:15.079Z  python
    tensorflow_1.4-py2-ddl      tensorflow_1.4-py2-ddl      2020-05-03T08:35:15.048Z  python
    tensorflow_1.4-py3-horovod  tensorflow_1.4-py3-horovod  2020-05-03T08:35:15.019Z  python
    tensorflow_1.4-py3          tensorflow_1.4-py3          2020-05-03T08:35:14.987Z  python
    tensorflow_1.4-py2          tensorflow_1.4-py2          2020-05-03T08:35:14.945Z  python
    tensorflow_1.3-py2-ddl      tensorflow_1.3-py2-ddl      2020-05-03T08:35:14.886Z  python
    tensorflow_1.3-py3          tensorflow_1.3-py3          2020-05-03T08:35:14.856Z  python
    tensorflow_1.3-py2          tensorflow_1.3-py2          2020-05-03T08:35:14.829Z  python
    tensorflow_1.2-py3          tensorflow_1.2-py3          2020-05-03T08:35:14.799Z  python
    tensorflow_1.2-py2          tensorflow_1.2-py2          2020-05-03T08:35:14.771Z  python
    pytorch-onnx_1.2-py3.6      pytorch-onnx_1.2-py3.6      2020-05-03T08:35:14.742Z  python
    pytorch-onnx_1.1-py3.6      pytorch-onnx_1.1-py3.6      2020-05-03T08:35:14.712Z  python
    pytorch-onnx_1.0-py3        pytorch-onnx_1.0-py3        2020-05-03T08:35:14.682Z  python
    pytorch-onnx_1.2-py3.6-edt  pytorch-onnx_1.2-py3.6-edt  2020-05-03T08:35:14.650Z  python
    pytorch-onnx_1.1-py3.6-edt  pytorch-onnx_1.1-py3.6-edt  2020-05-03T08:35:14.619Z  python
    pytorch_1.1-py3.6           pytorch_1.1-py3.6           2020-05-03T08:35:14.590Z  python
    pytorch_1.1-py3             pytorch_1.1-py3             2020-05-03T08:35:14.556Z  python
    pytorch_1.0-py3             pytorch_1.0-py3             2020-05-03T08:35:14.525Z  python
    pytorch_1.0-py2             pytorch_1.0-py2             2020-05-03T08:35:14.495Z  python
    pytorch_0.4-py3-horovod     pytorch_0.4-py3-horovod     2020-05-03T08:35:14.470Z  python
    pytorch_0.4-py3             pytorch_0.4-py3             2020-05-03T08:35:14.434Z  python
    pytorch_0.4-py2             pytorch_0.4-py2             2020-05-03T08:35:14.405Z  python
    pytorch_0.3-py3             pytorch_0.3-py3             2020-05-03T08:35:14.375Z  python
    pytorch_0.3-py2             pytorch_0.3-py2             2020-05-03T08:35:14.349Z  python
    torch_lua52                 torch_lua52                 2020-05-03T08:35:14.322Z  lua
    torch_luajit                torch_luajit                2020-05-03T08:35:14.295Z  lua
    caffe-ibm_1.0-py3           caffe-ibm_1.0-py3           2020-05-03T08:35:14.265Z  python
    caffe-ibm_1.0-py2           caffe-ibm_1.0-py2           2020-05-03T08:35:14.235Z  python
    caffe_1.0-py3               caffe_1.0-py3               2020-05-03T08:35:14.210Z  python
    caffe_1.0-py2               caffe_1.0-py2               2020-05-03T08:35:14.180Z  python
    caffe_frcnn                 caffe_frcnn                 2020-05-03T08:35:14.147Z  Python
    caffe_1.0-ddl               caffe_1.0-ddl               2020-05-03T08:35:14.117Z  native
    caffe2_0.8                  caffe2_0.8                  2020-05-03T08:35:14.088Z  Python
    darknet_0                   darknet_0                   2020-05-03T08:35:14.059Z  native
    theano_1.0                  theano_1.0                  2020-05-03T08:35:14.032Z  Python
    mxnet_1.2-py2               mxnet_1.2-py2               2020-05-03T08:35:14.002Z  python
    mxnet_1.1-py2               mxnet_1.1-py2               2020-05-03T08:35:13.960Z  python
    --------------------------  --------------------------  ------------------------  --------
```
</div></details>

他にもmeta_propsに含められるメタ情報はあり、作成したモデルがどのような条件下で作成されたのかを記録しておけるので、できるだけ付与しておくことが一般的にはおすすめです。

<details><summary>meta_propsに記述できるメタ情報</summary><div>

```python
client.repository.ModelMetaNames.get()
```

```:output
['CUSTOM',
 'DESCRIPTION',
 'DOMAIN',
 'HYPER_PARAMETERS',
 'IMPORT',
 'INPUT_DATA_SCHEMA',
 'LABEL_FIELD',
 'METRICS',
 'MODEL_DEFINITION_UID',
 'NAME',
 'OUTPUT_DATA_SCHEMA',
 'PIPELINE_UID',
 'RUNTIME_UID',
 'SIZE',
 'SOFTWARE_SPEC_UID',
 'SPACE_UID',
 'TAGS',
 'TRAINING_DATA_REFERENCES',
 'TRAINING_LIB_UID',
 'TRANSFORMED_LABEL_FIELD',
 'TYPE']
```
</div></details>

## モデルの保存 (デプロイメントスペース)
WML clientを使用して、デプロイメントスペースにモデルを保存します。
その他の方法として、モデルを上記の操作で分析プロジェクトに保存した後、CP4D画面上で該当モデルの"プロモート"をクリックすることで、分析プロジェクトのモデルをデプロイメントスペースへコピー保存することでも同じ結果になります。

```python
# デプロイメントスペースに切り替え（切り替える必要がある場合のみ実施）
client.set.default_space(space_id)

# モデルのメタ情報を記述
model_name = "sample_iris_model"
meta_props={
    client.repository.ModelMetaNames.NAME: model_name,
    client.repository.ModelMetaNames.RUNTIME_UID: "scikit-learn_0.22-py3.6",
    client.repository.ModelMetaNames.TYPE: "scikit-learn_0.22",
    client.repository.ModelMetaNames.INPUT_DATA_SCHEMA:{
        "id":"iris model",
        "fields":[
            {'name': 'sepal length (cm)', 'type': 'double'},
            {'name': 'sepal width (cm)', 'type': 'double'},
            {'name': 'petal length (cm)', 'type': 'double'},
            {'name': 'petal width (cm)', 'type': 'double'}
        ]
    },
    client.repository.ModelMetaNames.OUTPUT_DATA_SCHEMA: {
        "id":"iris model",
        "fields": [
            {'name': 'iris_type', 'type': 'string','metadata': {'modeling_role': 'prediction'}}
        ]
    }
}

# モデルを保存。戻り値に作成したモデルのメタデータが含まれる
model_artifact = client.repository.store_model(model, meta_props=meta_props, training_data=X, training_target=y)
```

補足として、meta_props内に含めるメタ情報については、上にかいた[「補足:モデルに含めるメタ情報」](#補足-モデルに含めるメタ情報)と同じなのでそちらを参照ください。



保存したモデルのIDは、戻り値model_artifact内に含まれています。IDはデプロイを作成する際に必要となります。以下のようにIDを取り出します。

```python:戻り値からのIDの取得
model_id = client.repository.get_model_uid(model_artifact)
```


```python:戻り値からのIDの取得（もう1つの方法）
model_id = model_artifact['metadata']['guid']
```

## モデルの一覧表示 (分析プロジェクト)
```python
# 分析プロジェクトに切り替え（切り替える必要がある場合のみ実施）
client.set.default_project(project_id)

# モデルの一覧を表示
client.repository.list_models()
```

## モデルの一覧表示 (デプロイメントスペース)
WML clientを使用します。

```python
# デプロイメントスペースに切り替え（切り替える必要がある場合のみ実施）
client.set.default_space(space_id)

# モデルの一覧を表示
client.repository.list_models()
```

# デプロイメントの操作 （デプロイメントスペースのみ)
WML clientを使用します。デプロイメントはBatch型とOnline型があります。作成するcreateにはデプロイするモデルのIDを与えます。

## デプロイメントの作成(Online型)

```python:Online型のデプロイ
dep_name = "sample_iris_online"
meta_props = {
    client.deployments.ConfigurationMetaNames.NAME: dep_name,
    client.deployments.ConfigurationMetaNames.ONLINE: {}
}

deployment_details = client.deployments.create(model_id, meta_props=meta_props)
```

デプロイは1分弱かかりますが、以下のような出力が出ればデプロイ成功です。
    
```:output
#######################################################################################

Synchronous deployment creation for uid: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx' started

#######################################################################################
    
initializing
ready
        
------------------------------------------------------------------------------------------------
Successfully finished deployment creation, deployment_uid='yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy'
------------------------------------------------------------------------------------------------
```

作成したデプロイメントのIDは、戻り値から以下のように取り出せます。

```python
# ONLINE型デプロイメントのID
dep_id_online = deployment_details['metadata']['guid']
```

## デプロイメントの作成(Batch型)

```python:Batch型のデプロイ
dep_name = "sample_iris_batch"
meta_props = {
    client.deployments.ConfigurationMetaNames.NAME: dep_name,
    client.deployments.ConfigurationMetaNames.BATCH: {},
    client.deployments.ConfigurationMetaNames.COMPUTE: {
        "name": "S",
        "nodes": 1
    }
}

deployment_details = client.deployments.create(model_id, meta_props=meta_props)
```

Successfullyと表示されたらデプロイ成功です。
作成したデプロイメントのIDは、戻り値から以下のように取り出せます。

```python
# BATCH型デプロイメントのID
dep_id_batch = deployment_details['metadata']['guid']
```

## デプロイメントの一覧表示
こちらもWML clientを使用します。

```
# デプロイメントの一覧を表示
client.deployments.list()
```

## デプロイメントの実行(Online型)
Online型のデプロイメントの実行では、スコアリング用の入力データ（JSON形式）を作成して、それをデプロイメントにRESTで投げ、予測結果を受け取ります。まず、サンプル入力データを作成します。

```python:スコアリング実行用のサンプル入力データを生成
# sample data for scoring (setosa)
scoring_x = pd.DataFrame(
    data = [[5.1,3.5,1.4,0.2]],
    columns=['sepal length (cm)','sepal width (cm)','petal length (cm)','petal width (cm)']
)

values = scoring_x.values.tolist()
fields = scoring_x.columns.values.tolist()
scoring_payload = {client.deployments.ScoringMetaNames.INPUT_DATA: [{'fields': fields, 'values': values}]}
scoring_payload
```

```:output
{'input_data': [{'fields': ['sepal length (cm)',
    'sepal width (cm)',
    'petal length (cm)',
    'petal width (cm)'],
   'values': [[5.1, 3.5, 1.4, 0.2]]}]}
```

Onlineデプロイメントの実行は、WML clientとrequestsの2つの方法が可能です。

```python:WMLclientによるOnlineスコアリング実行
prediction = client.deployments.score(dep_id_online, scoring_payload)
prediction
```

```:output
{'predictions': [{'fields': ['prediction', 'probability'],
   'values': [[0, [0.8131726303900102, 0.18682736960998966]]]}]}
```

requestsの実行例は、CP4D画面のデプロイメント詳細画面にあるコードスニペットからコピペで使えます。
mltokenはAPIの認証トークンで、この記事の最初の方にある[WML clientの初期化(認証)](# WML-clientの初期化認証)でOS環境変数 USER_ACCESS_TOKEN から得た```token```がそのまま使用できます。
CP4D外の環境から実行する場合は、[CP4D製品マニュアルの「ベアラー・トークンの取得」](https://www.ibm.com/support/knowledgecenter/ja/SSQNUZ_2.5.0/wsj/analyze-data/ml-authentication-local.html)を実施して予め入手しておきます。


```python
import urllib3, requests, json

# token = "XXXXXXXXXXXXXXXXXX"
# url = "https://cp4d.host.name.com"
header = {'Content-Type': 'application/json', 'Authorization': 'Bearer ' + token}
dep_url = url + "/v4/deployments/" + dep_id_online + "/predictions"

response = requests.post(dep_url, json=scoring_payload, headers=header)
prediction = json.loads(response.text)
prediction
```

```:output
{'predictions': [{'fields': ['prediction', 'probability'],
   'values': [['setosa', [0.9939393939393939, 0.006060606060606061, 0.0]]]}]}
```

もしCP4Dのドメインが自己署名証明書を使っていてrequests.postが証明書チェックで失敗する場合は、requests.postのオプション```verify=False```を使うと暫定的に回避できます。ご利用は自己責任で。

## デプロイメントの実行(Batch型)
Batch型のデプロイメントの実行では、入力データとなるCSVファイルを予めデプロイメントスペースのデータ資産に登録しておき、そのデータ資産のhrefを指定します。

```python:入力データの準備
# サンプルとしてIris学習データXの先頭5行をCSV化
X.head(5).to_csv("iris_test.csv")

# デプロイメントスペースに切り替え（切り替える必要がある場合のみ実施）
client.set.default_space(space_id)

# データ資産への登録
asset_details = client.data_assets.create(name="iris_test.csv",file_path="/home/wsuser/work/iris_test.csv")
asset_href = client.data_assets.get_href(asset_details)
```

```python:Batchスコアリング実行
# 実行ジョブのメタ情報を作成
job_payload_ref = {
    client.deployments.ScoringMetaNames.INPUT_DATA_REFERENCES: [{
        "location": {
            "href": asset_href
        },
        "type": "data_asset",
        "connection": {}
    }],
    client.deployments.ScoringMetaNames.OUTPUT_DATA_REFERENCE: {
        "location": {
            "name": "iris_test_out_{}.csv".format(dep_id_batch),
            "description": "testing csv file"
        },
        "type": "data_asset",
        "connection": {}
    }
}

# バッチ実行(create_jobすると実行される)
job = client.deployments.create_job(deployment_id=dep_id_batch, meta_props=job_payload_ref)
job_id = client.deployments.get_job_uid(job)
```

実行結果のステータスは、以下のコードで確認できます。プログラムに組み込む場合は、stateがcompletedになるまでループを回すと良いと思います。

```python
# バッチ実行ジョブのステータス確認
client.deployments.get_job_status(job_id)
```

```:output
# 実行中の場合
{'state': 'queued', 'running_at': '', 'completed_at': ''}

# 実行完了の場合
{'state': 'completed',
 'running_at': '2020-05-28T05:43:22.287357Z',
 'completed_at': '2020-05-28T05:43:22.315966Z'}
```

以上です。
他にも、Python関数を保存したりデプロイしたりできますが、機会があれば追記 or 別の記事で書きます。

---

（2020/6/1追記) 以下のGitリポジトリにCP4D v3.0で使えるモデルやデプロイ系のサンプルNotebookがありました。https://github.ibm.com/GREGORM/CPDv3DeployML