CPLEXサンプル(warehouse)をWatson MLのWebサービス化する

# はじめに
別記事 [CPLEXサンプル(warehouse)をWatson StudioのDecision Optimization上で動かす](https://qiita.com/makaishi2/items/4c4935f8ce31ac4d2efe) の続編です。

上記の記事で動作が確認されたCPLEXのコード(OPL)は、Watson Machine Learningにデプロイして、Webサービスとして呼び出すことが可能です。その手順を以下で説明します。
以下で説明するコード一式は、次のURLにアップしてあります。

https://github.com/makaishi2/cplex-samples/tree/master/warehouse-python-batch

# 対象アプリケーション
上記のものとまったく同じです。
モデルファイル: ``warehouse_ml.mod``
CSVファイル: ``warehouses.csv``と``supplyCosts.csv``となります。


# 認証情報の取得
最初にWatson MLの認証情報(APIKEYとINSTANCE_ID)を取得する必要があります。手順は、下記リンク先の上の方にあるので、そちらを参照して下さい。

https://github.com/makaishi2/sample-data/blob/master/notebooks/bank-marketing-autoai-test.ipynb

# gzファイルの生成

モデル登録前に、対象のモデルファイルをgzまたはzip形式で圧縮する必要があります。
最初のEXPORT文はMACで圧縮をする場合に必要になります。
この環境変数設定をしないと、変なファイルもtarに入ってしまい、その後の処理でエラーになります。

```sh
export COPYFILE_DISABLE=1
tar czvf warehouse-model.tar.gz warehouse_ml.mod
```

# モデルの登録

以下のpythonファイルで、``apikey``と``instance_id``を事前に調べた値に設定して下さい。

```py3
# -*- coding: utf-8 -*-

# コマンドによる事前準備
# $ pip install watson-machine-learning-client-V4
# $ MACでは次が重要
# $ export COPYFILE_DISABLE=1
# $ tar warehouse-model.tar.gz warehouse_ml.mod

import sys

# Watson ML credentails
apikey = 'xxxx'
instance_id = 'xxxx'

tarfile = 'warehouse-model.tar.gz'

# --------------------------------------------------------
# メインルーチン
# --------------------------------------------------------
if __name__ == '__main__':

    # 引数の受け取り
    argv = sys.argv
    argc = len(argv)

    wml_credentials = {
        "apikey": apikey,
        "instance_id": instance_id,
        "url": 'https://us-south.ml.cloud.ibm.com'
    }

    from watson_machine_learning_client import WatsonMachineLearningAPIClient
    client = WatsonMachineLearningAPIClient(wml_credentials)

    print('List Models')
    client.repository.list_models()

    print('List Deployments')
    client.deployments.list()
    
    # 登録に必要な情報の設定
    mdl_07_metadata = {
        client.repository.ModelMetaNames.NAME: "WAREHOUSE OPL",
        client.repository.ModelMetaNames.DESCRIPTION: "WAREHOUSE OPL",
        #client.repository.ModelMetaNames.TYPE: "do-docplex_12.10",
        client.repository.ModelMetaNames.TYPE: "do-opl_12.10",
        client.repository.ModelMetaNames.RUNTIME_UID: "do_12.10"
    }

    # モデルの登録
    model_details = client.repository.store_model(model=tarfile, meta_props=mdl_07_metadata)

    # モデルUIDの取得
    model_uid = client.repository.get_model_uid(model_details)
    print( model_uid )

    # Webサービス化に必要な情報
    meta_props = {
        client.deployments.ConfigurationMetaNames.NAME: "WAREHOUSE OPL Deployment",
        client.deployments.ConfigurationMetaNames.DESCRIPTION: "WAREHOUSE OPL Deployment",
        client.deployments.ConfigurationMetaNames.BATCH: {},
        client.deployments.ConfigurationMetaNames.COMPUTE: {'name': 'S', 'nodes': 1}
    }

    # Webサービス化
    deployment_details = client.deployments.create(model_uid, meta_props=meta_props)

    deployment_uid = client.deployments.get_uid(deployment_details)
    print( deployment_uid )
    
    client.deployments.list()
 ```

あとは、

```sh
$ python ml-deploy.py
```

と実行すると、以下のような結果がかえってくるはずです。

```txt
List Models
------------------------------------  -------------------------------------  ------------------------  ----------------
GUID                                  NAME                                   CREATED                   TYPE
b862c975-dc26-4b0a-94d5-06f099fb5a51  Auto generated DO opl model            2020-07-19T06:59:38.305Z  do-opl_12.10
------------------------------------  -------------------------------------  ------------------------  ----------------
List Deployments
------------------------------------  ------------------------------------------  -----  ------------------------  -------------
GUID                                  NAME                                        STATE  CREATED                   ARTIFACT_TYPE
00616f70-64b0-46fb-97f1-3284bc8a6640  Auto generated DO opl deployment            ready  2020-07-19T06:59:38.400Z  model
------------------------------------  ------------------------------------------  -----  ------------------------  -------------
211f73af-6a7c-445c-a5b8-ffa526fd703f


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

Synchronous deployment creation for uid: '211f73af-6a7c-445c-a5b8-ffa526fd703f' started

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


ready.


------------------------------------------------------------------------------------------------
Successfully finished deployment creation, deployment_uid='cf184900-bce6-4737-bfdb-ec8902057f35'
------------------------------------------------------------------------------------------------


cf184900-bce6-4737-bfdb-ec8902057f35
------------------------------------  ------------------------------------------  -----  ------------------------  -------------
GUID                                  NAME                                        STATE  CREATED                   ARTIFACT_TYPE
cf184900-bce6-4737-bfdb-ec8902057f35  WAREHOUSE OPL Deployment                    ready  2020-07-21T04:06:42.233Z  model
00616f70-64b0-46fb-97f1-3284bc8a6640  Auto generated DO opl deployment            ready  2020-07-19T06:59:38.400Z  model
------------------------------------  ------------------------------------------  -----  ------------------------  -------------
```

# モデルの呼び出し
登録ができたら、REST APIによりモデルを呼び出すことが可能です。
呼び出しコードのサンプルと結果例を示します。
deployment_uidには、先ほど登録時に返ってきたIDを設定して下さい。

```py3
# -*- coding: utf-8 -*-

# コマンドによる事前準備
# pip install watson-machine-learning-client-V4

import sys
import pandas as pd

# Watson ML credentails
apikey = 'xxxx'
instance_id = 'xxxx'

# DO Deployment ID
deployment_uid = 'xxxx'

# Input CSV File
input_data1 = 'warehouses.csv'
input_data2 = 'supplyCosts.csv'

# --------------------------------------------------------
# メインルーチン
# --------------------------------------------------------
if __name__ == '__main__':

    # 引数の受け取り
    argv = sys.argv
    argc = len(argv)

    wml_credentials = {
        "apikey": apikey,
        "instance_id": instance_id,
        "url": 'https://us-south.ml.cloud.ibm.com'
    }

    from watson_machine_learning_client import WatsonMachineLearningAPIClient
    client = WatsonMachineLearningAPIClient(wml_credentials)

    input_df1 = pd.read_csv(input_data1)
    input_df2 = pd.read_csv(input_data2)
    
    solve_payload = {
        client.deployments.DecisionOptimizationMetaNames.INPUT_DATA: [
            {
                "id": input_data1,
                "values" : input_df1
            },
            {
                "id": input_data2,
                "values" : input_df2
            }
        ],
        client.deployments.DecisionOptimizationMetaNames.OUTPUT_DATA: [
            {
                "id":".*\.csv"
            }
        ]
    }

    # DO Job 投入
    job_details = client.deployments.create_job(deployment_uid, solve_payload)
    job_uid = client.deployments.get_job_uid(job_details)
    print( job_uid )


    #  status確認
    from time import sleep
    while job_details['entity']['decision_optimization']['status']['state'] not in ['completed', 'failed', 'canceled']:
        print(job_details['entity']['decision_optimization']['status']['state'] + '...')
        sleep(5)
        job_details=client.deployments.get_job_details(job_uid)

    detail =  job_details['entity']['decision_optimization']['output_data']

    # 結果確認
    import json
    detail2 =  job_details['entity']['decision_optimization']
    
    # 最終ステータス表示
    print(json.dumps(detail2['status'], indent=2))
    
    # 結果表示
    for item in detail:
        id = item['id']
        fields = item['fields']
        values = item['values']
        df_work = pd.DataFrame(values, columns=fields)
        print(df_work.head(10))
```

呼び出しコマンド

```sh
$ python ml-submit.py
```

結果サンプル

```txt
401c79b4-c4b5-4ad9-b1b3-9f6b013fbeee
queued...
queued...
queued...
queued...
queued...
{
  "completed_at": "2020-07-21T04:12:41.537Z",
  "running_at": "2020-07-21T04:12:40.449Z",
  "state": "completed"
}
  warehouseName  storeId
0          Bonn        4
1      Bordeaux        2
2      Bordeaux        6
3      Bordeaux        7
4      Bordeaux        9
5        London        8
6        London       10
7          Rome        1
8          Rome        3
9          Rome        5
```