Watson Machine Learningのバッチ処理をローカルから実行

CP4DaaS上のDeploymentSpaceにつくったバッチ・デプロイメントに、ローカルPCから入力ファイルを与えて、出力ファイルを取得するpythonのプログラムを作ってみます。
スクリプト化することで、日次で新しいデータをスコアリングすることを自動化するようなことが可能です。

バッチジョブは以下の@makaishi2 さんの記事で作ったものを使ってみます。以下の記事はSPSS Modeler Flowのバッチ処理ですが、今回作るプログラムはscikitlearnなどで作ったモデルのバッチ処理にも利用できます。

https://qiita.com/makaishi2/items/2fbb8d82894d5ac6013e

上の記事で作成したバッチ・デプロイメントを使って、
①ローカルPC上の入力ファイルをアップロードしてデータ資産として登録し
②バッチ・デプロイメントの入力として指定したジョブを実行し、
③その結果としてデータ資産に登録された出力ファイルをローカルPCにダウンロードします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/270b3ddc-2b86-c2b1-70fe-1b1f1cd5c17d.png)
■環境
Windows 10
Python 3.8.5 (Anaconda 202011)
ibm-watson-machine-learning 1.0.66

#1.事前準備
事前準備としてAPIキーの取得とWMLのライブラリを導入します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/0b110f94-821d-9e13-a1fe-9e7513bb54cd.png)

##1.1.APIキーの作成

まずWatson Machine Learningをプログラムから操作できるようにするためにAPIキーを作成します。

IBM Cloudの管理メニューから　アクセス（IAM)のメニューを選びます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/dee2d7f4-0128-b3c4-cb84-c623ba97e87e.png)

IBM Cloud APIキーの作成をクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/c50d9905-9b73-a4e1-d1ec-fb2d518b757d.png)

任意の名前で作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/f536c5a1-438f-a354-7eca-1a7d11273208.png)

作成されたAPIキーをダウンロードかコピーしておきます。後でプログラムから使います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/7164931b-d7ea-85e1-6a52-c9d5bf816a2e.png)

#ibm-watson-machine-learningの導入
Watson Machine Learningを操作するためのAPIライブラリのibm-watson-machine-learningをpipで導入します。コマンドプロンプトやPowershellで以下のコマンドで導入します。

```console:コマンド
python --version
pip install  ibm-watson-machine-learning
pip list|findstr /c:ibm-watson
```

```console:導入例
(anaconda202011) PS C:\Users\HAJIMEKAWADA> python --version
Python 3.8.5
(anaconda202011) PS C:\Users\HAJIMEKAWADA> pip install  ibm-watson-machine-learning
Collecting ibm-watson-machine-learning
  Downloading ibm_watson_machine_learning-1.0.66-py3-none-any.whl (1.7 MB)
     |████████████████████████████████| 1.7 MB 2.2 MB/s
Requirement already satisfied: urllib3 in c:\users\hajimekawada\miniconda3\envs\anaconda202011\lib\site-packages (from ibm-watson-machine-learning) (1.25.11)
Requirement already satisfied: ibm-cos-sdk==2.7.* in c:\users\hajimekawada\miniconda3\envs\anaconda202011\lib\site-packages (from ibm-watson-machine-learning) (2.7.0)
Requirement already satisfied: tabulate in c:\users\hajimekawada\miniconda3\envs\anaconda202011\lib\site-packages (from ibm-watson-machine-learning) (0.8.9)
Requirement already satisfied: certifi in c:\users\hajimekawada\miniconda3\envs\anaconda202011\lib\site-packages (from ibm-watson-machine-learning) (2020.6.20)
Requirement already satisfied: lomond in c:\users\hajimekawada\miniconda3\envs\anaconda202011\lib\site-packages (from ibm-watson-machine-learning) (0.3.3)
Requirement already satisfied: packaging in c:\users\hajimekawada\miniconda3\envs\anaconda202011\lib\site-packages (from ibm-watson-machine-learning) (20.4)
Requirement already satisfied: requests in c:\users\hajimekawada\miniconda3\envs\anaconda202011\lib\site-packages (from ibm-watson-machine-learning) (2.24.0)
Requirement already satisfied: pandas<1.3.0,>=0.24.2 in c:\users\hajimekawada\miniconda3\envs\anaconda202011\lib\site-packages (from ibm-watson-machine-learning) (1.0.5)
Requirement already satisfied: ibm-cos-sdk-core==2.7.0 in c:\users\hajimekawada\miniconda3\envs\anaconda202011\lib\site-packages (from ibm-cos-sdk==2.7.*->ibm-watson-machine-learning) (2.7.0)
Requirement already satisfied: ibm-cos-sdk-s3transfer==2.7.0 in c:\users\hajimekawada\miniconda3\envs\anaconda202011\lib\site-packages (from ibm-cos-sdk==2.7.*->ibm-watson-machine-learning) (2.7.0)
Requirement already satisfied: jmespath<1.0.0,>=0.7.1 in c:\users\hajimekawada\miniconda3\envs\anaconda202011\lib\site-packages (from ibm-cos-sdk==2.7.*->ibm-watson-machine-learning) (0.10.0)
Requirement already satisfied: six>=1.10.0 in c:\users\hajimekawada\miniconda3\envs\anaconda202011\lib\site-packages (from lomond->ibm-watson-machine-learning) (1.15.0)
Requirement already satisfied: pyparsing>=2.0.2 in c:\users\hajimekawada\miniconda3\envs\anaconda202011\lib\site-packages (from packaging->ibm-watson-machine-learning) (2.4.7)
Requirement already satisfied: chardet<4,>=3.0.2 in c:\users\hajimekawada\miniconda3\envs\anaconda202011\lib\site-packages (from requests->ibm-watson-machine-learning) (3.0.4)
Requirement already satisfied: idna<3,>=2.5 in c:\users\hajimekawada\miniconda3\envs\anaconda202011\lib\site-packages (from requests->ibm-watson-machine-learning) (2.10)
Requirement already satisfied: python-dateutil>=2.6.1 in c:\users\hajimekawada\miniconda3\envs\anaconda202011\lib\site-packages (from pandas<1.3.0,>=0.24.2->ibm-watson-machine-learning) (2.8.1)
Requirement already satisfied: pytz>=2017.2 in c:\users\hajimekawada\miniconda3\envs\anaconda202011\lib\site-packages (from pandas<1.3.0,>=0.24.2->ibm-watson-machine-learning) (2020.1)
Requirement already satisfied: numpy>=1.13.3 in c:\users\hajimekawada\miniconda3\envs\anaconda202011\lib\site-packages (from pandas<1.3.0,>=0.24.2->ibm-watson-machine-learning) (1.19.2)
Requirement already satisfied: docutils<0.16,>=0.10 in c:\users\hajimekawada\miniconda3\envs\anaconda202011\lib\site-packages (from ibm-cos-sdk-core==2.7.0->ibm-cos-sdk==2.7.*->ibm-watson-machine-learning) (0.15.2)
Installing collected packages: ibm-watson-machine-learning
Successfully installed ibm-watson-machine-learning-1.0.66
(anaconda202011) PS C:\Users\HAJIMEKAWADA> pip list|findstr /c:ibm-watson
ibm-watson-machine-learning        1.0.66
```

#2.オブジェクトのIDの確認
Pythonのプログラムから操作するデプロイメントスペースとバッチ・デプロイメントのIDを調べます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/f5054d0c-498a-37a3-4a5e-872d570ebd05.png)

##2.1.デプロイメントスペースのID
Manageのタブの中のSpace GUIDをコピーします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/3cbe5266-e819-6de6-196e-63d5d2c97265.png)

また、このデプロイメントスペースに関連付けられたMachine Learning Serviceも確認して、dallasやtokyoなどロケーションがどこで動作しているかも確認しておきます。リソース・リストで確認が可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/6dc8af96-8f12-46d3-943b-dc020aa755cb.png)

##2.2.バッチ・デプロイメントのID
デプロイメントのタブの中でPythonプログラムから実行したいバッチ・デプロイメントを選びます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/b3da64a3-b5f2-6030-f2d8-d6e5ee0dc3a2.png)

情報からデプロイメントIDをコピーします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/459e26ed-b827-06a7-49f6-8360f3b158b2.png)


#3.pythonからの実行

準備ができましたのでpythonから実行します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/60da0cc5-c83f-ec8d-c713-8c92fe153833.png)

以下のプログラムを、APIキーなどを環境に合わせて設定して実行します。


```python:batchjobwml.py
#APIKEY、Location,デプロイメントスペースのID、バッチ・デプロイメントのIDを指定する
api_key = 'XXXXXXXXXXXXXXXXXXXX'   #enter your key here – keep the single quotes
location = 'https://us-south.ml.cloud.ibm.com'
space_id = '8929f245xxxxxxxxxxxxxxxx'
deployment_uid="b17c279xxxxxxxxxxxxxxxx"

#入出力ファイル名の定義
input_filename="bank-test-jp-autoai-01.csv"
input_filepath=""+input_filename

import datetime
ts= datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
output_filename="spss-batch-output_"+ ts +".csv"
output_filepath=""+output_filename

#wmlへ接続
# Import the APIClient class
from ibm_watson_machine_learning import APIClient


wml_credentials = {
    "apikey": api_key,
    "url": location
}

client = APIClient(wml_credentials)

client.set.default_space(space_id)
deployment_details = client.deployments.get_details(deployment_uid)


#入力ファイルのアップロード
input_assest_detail=client.data_assets.create(name=input_filename,file_path=input_filepath)

#ジョブ作成と実行
job_payload_ref = {
    client.deployments.ScoringMetaNames.INPUT_DATA_REFERENCES: [{
        "name": input_filename,
        "type": "data_asset",
        "connection": {},
        "location": {
                      "href":  input_assest_detail['metadata']['href']
                    }
    }],
 client.deployments.ScoringMetaNames.OUTPUT_DATA_REFERENCE: {
         "type": "data_asset",
         "connection": {},
         "location": {
             "name": output_filename,
             "description": "spss-batch-output"
         }
     }}
job = client.deployments.create_job(deployment_uid, meta_props=job_payload_ref, platform_job_immediate=True)


job_id = client.deployments.get_job_uid(job)
job_details=client.deployments.get_job_details(job_id)


#非同期実行のためJobの終了を確認
import time
retry_times=12
retry_sleep=3

for i in range(retry_times):
    job_details=client.deployments.get_job_details(job_id)
    jobstatus = job_details['entity']['scoring']['status']['state']
    print(jobstatus)
    if jobstatus in ['completed', 'failed']: 
        break
    time.sleep(retry_sleep)
    if i==retry_times-1:
        print("Time up")


#入出力ファイルのIDを取得
import re
input_asset_href=job_details['entity']['scoring']['input_data_references'][0]['location']['href']
input_asset_uid=re.match('/v2/assets/(.*?)\?space_id=', input_asset_href)[1]
output_asset_href=job_details['entity']['scoring']['output_data_reference']['location']['href']
output_asset_uid=re.match('/v2/assets/(.*?)\?space_id=', output_asset_href)[1]


#出力ファイルのダウンロード
client.data_assets.download(output_asset_uid,output_filepath)

#入出ファイルのDeployment Spaceからの削除
client.data_assets.delete(output_asset_uid)
client.data_assets.delete(input_asset_uid)
```

サンプルプログラムはgithubにも置きました。

https://github.com/hkwd/210402batchjobwml/blob/master/batchjobwmlpub.py

bank-test-jp-autoai-01.csvが入力ファイルです。

```console
(anaconda202011) PS C:\Users\HAJIMEKAWADA\anacondaprogram> dir


    ディレクトリ: C:\Users\HAJIMEKAWADA\anacondaprogram


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----        2021/03/31      8:54            684 bank-test-jp-autoai-01.csv
-a----        2021/04/01     16:58           2862 batchjobwmlpub.py
````

pythonスクリプトを実行します。
spss-batch-output_20210401_165557.csvがスコアリング結果の出力ファイルです。

```
(anaconda202011) PS C:\Users\HAJIMEKAWADA\anacondaprogram> python .\batchjobwml.py
Creating data asset...
SUCCESS
queued
queued
queued
running
running
running
completed
Successfully saved data asset content to file: 'spss-batch-output_20210401_165557.csv'
(anaconda202011) PS C:\Users\HAJIMEKAWADA\anacondaprogram> dir


    ディレクトリ: C:\Users\HAJIMEKAWADA\anacondaprogram


Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
-a----        2021/03/31      8:54            684 bank-test-jp-autoai-01.csv
-a----        2021/04/01     16:57           2911 batchjobwml.py
-a----        2021/04/01     16:56            160 spss-batch-output_20210401_165557.csv
````


#4.プログラム解説

以下でご自身の環境のAPIKEY、Location,デプロイメントスペースのID、バッチ・デプロイメントのIDを指定してください。


```python:batchjobwml.py-ID指定など
#APIKEY、Location,デプロイメントスペースのID、バッチ・デプロイメントのIDを指定する
api_key = 'XXXXXXXXXXXXXXXXXXXX'   #enter your key here – keep the single quotes
location = 'https://us-south.ml.cloud.ibm.com'
space_id = '8929f245xxxxxxxxxxxxxxxx'
deployment_uid="b17c279xxxxxxxxxxxxxxxx"
```

api_key: 1.1.APIキーの作成で作成したapiキーを設定してください。
space_id: 2.1.デプロイメントスペースのIDで確認したIDを設定してください。
deployment_uid: 2.2.バッチ・デプロイメントのIDで確認したIDを設定してください。
location ：2.1.デプロイメントスペースで確認したロケーションに合わせて以下のマニュアルからURLを設定します。

https://dataplatform.cloud.ibm.com/docs/content/wsj/analyze-data/ml-overview.html?context=analytics

    Dallas: https://us-south.ml.cloud.ibm.com
    London - https://eu-gb.ml.cloud.ibm.com
    Frankfurt - https://eu-de.ml.cloud.ibm.com
    Tokyo - https://jp-tok.ml.cloud.ibm.com


以下で入出力ファイル名の定義をしてください。

```python:batchjobwml.py-入出力ファイル名の定義
#入出力ファイル名の定義
input_filename="bank-test-jp-autoai-01.csv"
input_filepath=""+input_filename

import datetime
ts= datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
output_filename="spss-batch-output_"+ ts +".csv"
output_filepath=""+output_filename
```

以下でプログラムからAPIキーをつかってWatson Machine Learningのサービスに接続して、デプロイメントスペースに入っています。

```python:batchjobwml.py-wmlへ接続
#wmlへ接続
# Import the APIClient class
from ibm_watson_machine_learning import APIClient

wml_credentials = {
    "apikey": api_key,
    "url": location
}

client = APIClient(wml_credentials)
#デプロイメントスペースへ接続
client.set.default_space(space_id)
deployment_details = client.deployments.get_details(deployment_uid)
```

以下でローカルPC上にあるファイルをデプロイメントスペース内のデータ資産として登録しています。
file_pathはローカルPCのパスを示しています。nameはデプロイメントスペース上に登録される名前です。ただややこしくなるので、元のファイル名と同じにしておくのが良いと思います。


```python:batchjobwml.py-入力ファイルのアップロード
#入力ファイルのアップロード
input_assest_detail=client.data_assets.create(name=input_filename,file_path=input_filepath)
```
イメージで示すと以下の部分です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/f12508bd-be94-82d2-9ebd-cdd92facf548.png)

ローカルPCのファイルが、デプロイメントスペースのデータ資産としてアップロードされ、登録されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/e032a3ec-b62a-9329-b4bf-599f38044216.png)


以下でジョブを作成しています。
あらかじめデプロイ済みのバッチデプロイメントIDのdeployment_uidと入出力の定義job_payload_refを設定してジョブを作成し、実行しています。create_jobを実行するとジョブ実行までが行われます。

client.deployments.create_job(deployment_uid, meta_props=job_payload_ref, platform_job_immediate=True)

```python:batchjobwml.py-ジョブ作成と実行
#ジョブ作成と実行
job_payload_ref = {
    client.deployments.ScoringMetaNames.INPUT_DATA_REFERENCES: [{
        "name": input_filename,
        "type": "data_asset",
        "connection": {},
        "location": {
                      "href":  input_assest_detail['metadata']['href']
                    }
    }],
 client.deployments.ScoringMetaNames.OUTPUT_DATA_REFERENCE: {
         "type": "data_asset",
         "connection": {},
         "location": {
             "name": output_filename,
             "description": "spss-batch-output"
         }
     }}
job = client.deployments.create_job(deployment_uid, meta_props=job_payload_ref, platform_job_immediate=True)

job_id = client.deployments.get_job_uid(job)
job_details=client.deployments.get_job_details(job_id)
```
イメージで示すと以下の部分です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/0cf5f336-3372-6588-0e62-8043d5802ba7.png)

ジョブは非同期実行されますので、終了をループして確認します。

```python:batchjobwml.py-非同期実行のためJobの終了を確認
#非同期実行のためJobの終了を確認
import time
retry_times=12
retry_sleep=3

for i in range(retry_times):
    job_details=client.deployments.get_job_details(job_id)
    jobstatus = job_details['entity']['scoring']['status']['state']
    print(jobstatus)
    if jobstatus in ['completed', 'failed']: 
        break
    time.sleep(retry_sleep)
    if i==retry_times-1:
        print("Time up")
```

以下でjob_detailsから入出力ファイルのIDを取得し、出力ファイルをローカルPCにダウンロードしています。
入力ファイルのjob_details['entity']['scoring']['input_data_references'][0]['location']['href']
の[0]は入力ファイルの1つ目という意味です。入力ファイルは複数のファイルから入力する可能性があります。

```python:batchjobwml.py-出力ファイルのダウンロード
#入出力ファイルのIDを取得
import re
input_asset_href=job_details['entity']['scoring']['input_data_references'][0]['location']['href']
input_asset_uid=re.match('/v2/assets/(.*?)\?space_id=', input_asset_href)[1]
output_asset_href=job_details['entity']['scoring']['output_data_reference']['location']['href']
output_asset_uid=re.match('/v2/assets/(.*?)\?space_id=', output_asset_href)[1]

#出力ファイルのダウンロード
client.data_assets.download(output_asset_uid,output_filepath)
```

ジョブによって作られた出力ファイルがPCにダウンロードされました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/f98ac0aa-64bb-4280-cd3d-2cf242379574.png)


最後にいらなくなった入出ファイルのデプロイメント・スペースから削除しています。


```python:batchjobwml.py-入出ファイルのDeploymentSpaceからの削除
#入出ファイルのDeployment Spaceからの削除
client.data_assets.delete(output_asset_uid)
client.data_assets.delete(input_asset_uid)
```

pythonスクリプトから指定した入出ファイルが削除されました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/14f883b2-61b0-6784-ee08-36b830b4d09a.png)

バッチジョブの実行履歴はジョブタブの中のdefault job - bank maketing spss batchにあります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/bad37b59-c320-bc2a-4248-5b1fecad903f.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/47bd931b-2d28-8c7b-e626-e45d82bf4bb5.png)

client.deployments.get_details(deployment_uid)でとった内容と同じものです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/223763/e5138291-ab88-5621-1abd-6d9228384837.png)

#5.最後に
サンプルの入出力ファイルやModelerのストリームは以下に置きました。

https://github.com/hkwd/210402batchjobwml

例外処理などは作っていないので、実環境で利用する場合には付け加えてもらえればと思います。

#6.参考

http://ibm-wml-api-pyclient.mybluemix.net/#id34

Python クライアントを使用したデプロイ (バージョン 2.0)
https://www.ibm.com/support/knowledgecenter/ja/SSBFT6_1.1.0/wsj/wmls/wmls-deploy-python.html#deploy-batch

https://qiita.com/makaishi2/items/2fbb8d82894d5ac6013e

https://qiita.com/ttsuzuku/items/eac3e4bedc020da93bc1#%E3%83%87%E3%83%97%E3%83%AD%E3%82%A4%E3%83%A1%E3%83%B3%E3%83%88%E3%81%AE%E5%AE%9F%E8%A1%8Cbatch%E5%9E%8B