モデルデプロイのコードを読んでみよう

2017/09/14
執筆者:
· 推定読書時間 5  分

DataRobotの小川です。

DataRobotには様々なデプロイ手法があることを前回のブログで取り上げましたが、今回はその中のPrediction APIによる予測についてコードレベルで見ていきたいと思います。

DataRobotにおける一番オーソドックスな予測方法がPrediction APIを使ったものになります。DataRobotにおいて専有DataRobotリソースからインスタンスを選択していただきデプロイするとPythonのサンプルコードが確認できます。コメントを除くと20行程度のコードとなっていますので、スクリプトが書けるレベルであればDataRobotで簡単にPrediction APIでの予測ができるようになります。

共有DataRobotインスタンスではモデリングエンジンと同じエンジンを予測に利用するため、別のユーザーがオートパイロットなどを走らせていてリソースを枯渇させているとキューで待たされる形になりますが、専有DataRobotリソースを使用した予測では、予測専有のリソースとなっていますので、リアルタイムな予測結果を返すことができるようになっています。

# Usage: python datarobot-predict.py <input-file.csv>


# Usage: python datarobot-predict.py <input-file.csv>

 
# This example uses the requests library which you can install with:
# pip install requests
# We highly recommended that you update SSL certificates with:
# pip install -U urllib3[secure] certifi
import requests
import sys

# Note: Before running this, change USERNAME to your username and
# API_TOKEN to the value on your profile page
API_TOKEN = '<APIトークン>'
USERNAME = '<ユーザー名>'

PROJECT_ID = '<プロジェクトID>'
MODEL_ID = '<モデルI>'

# Set HTTP headers
# Note: The charset should match the contents of the file.
headers = {'Content-Type': 'text/plain; charset=UTF-8', 'datarobot-key': '<DataRobotキー>'}

# Make predictions on your data
# The URL has the following format:
# https://<専用予測サーバエンドポイント>/predApi/v1.0/<プロジェクトID>/<モデルID>/predict
# See docs for details:
# https://app.datarobot.com/docs/users-guide/basics/predictions/new-prediction-api.html
data = open(sys.argv[1], 'r').read()

predictions_response = requests.post('<専用予測サーバエンドポイント>/predApi/v1.0/%s/%s/predict' % (PROJECT_ID, MODEL_ID),
auth=(USERNAME, API_TOKEN), data=data, headers=headers)

if predictions_response.status_code != 200:
try:
message = predictions_response.json().get('message', predictions_response.text)
status_code = predictions_response.status_code
reason = predictions_response.reason

print(u'Status: {status_code} {reason}. Message: {message}.'.format(message=message, status_code=status_code, reason=reason)) except ValueError: print('Prediction failed: {}'.format(predictions_response.reason)) predictions_response.raise_for_status() else: print(predictions_response.json()) 

コメント箇所


# Usage: python datarobot-predict.py <input-file.csv>
# This example uses the requests library which you can install with:
# pip install requests
# We highly recommended that you update SSL certificates with:
# pip install -U urllib3[secure] certifi

まず始めに先頭のコメント文をみましょう。1行目はこのサンプルコードの使い方です。コピー&ペーストして、ファイル名をdatarobot_predict.pyとしてCSVファイルを引数として渡すPythonスクリプトとして実行することを示しています。もちろん例となっているだけでファイル名は自由です。

2行目、3行目はこのスクリプトで使用するPythonライブラリとpipによるインストール方法が記載されています。ここで必要とされているrequestsはPythonのHTTPライブラリであり、専用予測サーバへの予測はREST APIベースのため、特別なライブラリが必要ないことがわかります。

4行目、5行目はSSL認証鍵をアップデートすることを推奨し、そのためのコマンドを記載しています。DataRobotとの通信はSSLで行えます。

変数の設定


# Note: Before running this, change USERNAME to your username and
# API_TOKEN to the value on your profile page
API_TOKEN = '<APIトークン>'
USERNAME = '<ユーザー名>'
PROJECT_ID = '<プロジェクトID>'
MODEL_ID = '<モデルID>'

1行目、2行目はUSERNAMEとAPI_TOKENを自身のユーザー名とAPIトークンに変更しなさいと書かれています。この情報はユーザープロフィールの画面から取得できます。DataRobotではユーザーと作成したモデルが紐づいており、そのモデルを利用するためには、そのモデルを使用して予測するための権限を持ったユーザーのAPIトークンが必要となります。APIトークンはユーザープロフィール画面またはREST APIから取得可能です。またREST APIを使うことによって再生成することもできます。

PROJECT_IDとMODEL_IDは今回予測として使用するモデルを一意に定めるためのIDとなっています。モデルをデプロイしてサンプルコードを確認するとそのモデルに対応するプロジェクトIDとモデルIDが記入された状態になっています。また、プロジェクトIDとモデルIDはモデルを選択している状態のURLからも把握することができます。Picture1

HTTPヘッダーの設定


# Set HTTP headers
# Note: The charset should match the contents of the file.
headers = {'Content-Type': 'text/plain; charset=UTF-8', 'datarobot-key': '<DataRobotキー>'}

専用予測サーバへの通信はREST APIを使用しているため、HTTPヘッダーに必要な情報を埋め込む必要があります。送り込むデータを文字コードUTF-8のプレーンテキスト形式していることを宣言し、認証に必要なDataRobotキーを渡しています。DataRobotキーはモデルデプロイで専有DataRobotリソースを選択して出てくる今回のサンプルコードに対応しているものが記載されていますので、いつでもデプロイ画面から確認することができます。

予測用データの読み込みと予測の実行


    # Make predictions on your data
    # The URL has the following format:
    # https://<専用予測サーバエンドポイント>/predApi/v1.0/<プロジェクトID>/<モデルID>/predict
    # See docs for details:
    # https://app.datarobot.com/docs/users-guide/basics/predictions/new-prediction-api.html
    data = open(sys.argv[1], 'r').read()
    predictions_response = requests.post('<専用予測サーバエンドポイント>/predApi/v1.0/%s/%s/predict' % (PROJECT_ID, MODEL_ID),
    auth=(USERNAME, API_TOKEN), data=data, headers=headers)

コメントには専用予測サーバのエンドポイントのURLがどのように記述されているかの説明が書いてあります。この中で、プロジェクトIDとモデルIDをURLとして含むことによって、プロジェクトに対して一意なモデルを指定できています。

また詳細なドキュメント(日本語版)へのリンクもコメント内に記載がされています。

実際のコードが書かれている6行目は引数のファイルを読み込み変数dataに渡すという処理になっています。APIは、JSONとCSVの両方の形式の入力データをサポートし、リクエストボディまたはファイルアップロード(マルチパートフォーム)を介してポストすることができます。サンプルコードではファイルアップロードのコードが書かれています。sys.argv[1]はこのスクリプトの第一引数を意味します。’r’は読み取り専用で開くオプションを意味しています。文字コードや改行コードの違いからこの一文のパラメータを変更するなどして対応することが重要になります。

JSONファイルをリクエストボディとしてPOSTする場合には、正しいJSON形式になっていることが重要です。予測の前にjqなどのツールを使用して確認することを推奨します。そして、CSVと違って、HTTPヘッダーを

 headers = {'Content-Type': 'application/json', 'datarobot-key': '<DataRobotキー>'}

としておく必要があります。あとは引数としてJSONファイルを渡すだけです。

別のアプリケーションからファイルとしてでなく、JSON形式で出力されたデータに対しては、

 data=[{"a": 1, "b": 2, "c": 3},{"a": 7, "b": 8, "c": 9}]

で変数に格納し、

 predictions_response = requests.post('<専用予測サーバエンドポイント>/predApi/v1.0/%s/%s/predict' % (PROJECT_ID, MODEL_ID), auth=(USERNAME, API_TOKEN), json=data, headers=headers)

と、dataでなく、json引数に値を代入します。この時のheadersはJSONファイルを使用した時と同じ設定にしておく必要があります。

いくつかのパターンを紹介しましたが、変わらない点として7行目のrequests.postによってこれまで設定してきた情報をDataRobotの専用予測サーバに向かって投げています(POSTしています)。requests.post自体はPythonの標準的なライブラリを使用しているだけなので、第一引数にPOST先のurl、今回は専用予測サーバのエンドポイントを指定しています。authに認証に必要な情報ということで、ユーザー名とAPIトークンのセットを渡しています。data/jsonでデータを指定しています。headersでは、DataRobotキーなどのHTTPヘッダーを指定しています。この時のPOSTの結果をpredictions_responseに格納しています。

予測結果の出力とエラーハンドリング

 if predictions_response.status_code != 200:
 try:
 message = predictions_response.json().get('message', predictions_response.text)
 status_code = predictions_response.status_code
 reason = predictions_response.reason
print(u'Status: {status_code} {reason}. Message: {message}.'.format(message=message,
status_code=status_code,
reason=reason))
except ValueError:
print('Prediction failed: {}'.format(predictions_response.reason))
predictions_response.raise_for_status()
else:
print(predictions_response.json())

すでに予測結果のオブジェクト自体はpredictions_responseに格納しているため、そこまででエラーが出ていないかなどを確認しながら最終的な結果を出力しています。初めのif文としてpredictions_response.status_codeによって、HTTPリクエストのステータスコードを取得しています。200番なら以外ならこの次の処理に進み、200番なら最後の処理に進むように書かれています。通常HTTPリクエストにおいては200は正常に完了したことを示し、200なら最後のelseの後のprint(predictions_response.json())によって、JSON形式で予測値を画面上に出力する動きとなります。サンプルコードでは、画面上に表示する動きとなっていますが、実際にシステムに組み込む際には、システム画面にデータを飛ばして表示させたり、データベースにつないで予測値をinsert文などで挿入するという書き換えを行う形になります。200番以外がステータスコードとして返ってきている場合には、その際のメッセージ、具体的なステータスコードの値を取得して出力するように例として書いています。

例としてJSON形式でなく、その後にEXCELなどのソフトで見て行くためにCSVファイルで出力する方法を紹介します。print(predictions_response.json())を以下に書き換えていただくだけで可能です。

 df = pandas.DataFrame(predictions_response.json()['data'])
 df[['rowId','prediction']].to_csv('sample.csv',index = False)

注意点として、pandasのDataFrameを利用するため、pandasのimportが事前に必要になります(import pandas)。pandasではディクショナリをDataFrameにすることができるので、JSONのdata部分を抜き出してdf変数にDataFrame型で代入しています。DataFrameはto_csvで簡単にCSV変換することが可能なので、df変数の中からrowIdとprediction列の順で、indexを除いてファイル名sample.csvとして出力しています。

pandasはPythonにおけるデータ整形にとても便利で、例えば元のアップロードしたデータセットの特徴量の値と予測値の結果を横に並べて出力することも下のコード例で簡単に行うことができます。

 basedata = pandas.read_csv(sys.argv[1])
 result = pandas.concat([df[['rowId','prediction']], basedata], axis=1)
 result.to_csv('sample.csv', index = False)

basedataとして元のファイルもdataframeに読み込み、concatを使い結合を行なった結果をresultに格納し、先ほどと同じto_csvでCSV変換しています。

リーズンコード付き予測

DataRobotには予測値を導き出した理由をだすリーズンコードという機能がありますが、Prediction APIからもリーズンコードを使うことができます。事前準備としてリーズンコードを初期化したモデルである必要がありますが、

 predictions_response = requests.post('<専用予測サーバエンドポイント>/predApi/v1.0/%s/%s/reasonCodesPredictions' % (PROJECT_ID, MODEL_ID), auth=(USERNAME, API_TOKEN), json=data, headers=headers)

POST先のURLの末尾がpredictだったのをreasonCodesPredictionsに変更するだけになっています。URLクエリーを使用することによって、リーズンコードのリーズンの個数やしきい値を変更することもできます。

 predictions_response = requests.post('<専用予測サーバエンドポイント>/predApi/v1.0/%s/%s/reasonCodesPredictions?maxCodes=2&thresholdLow=0.2&thresholdHigh=0.5' % (PROJECT_ID, MODEL_ID), auth=(USERNAME, API_TOKEN), json=data, headers=headers)

まとめ

コメントとライブラリのインポートを除くと全体で各ステップ数行の4ステップに分解できる形となっています。予測データセットをファイルではなく、データベースからPythonで抜いて来て直接渡したり、何らかのアプリケーションから生成されるデータをそのまま活用するなど応用も簡単にできます。また予測結果もサンプルでは標準出力に返すだけですが、ファイルに書き込むことやデータベースに書き込む形に変更するなどAPIとして操作できるがために様々な応用が可能となっています。サンプルコードはPythonで書かれていますが使用したライブラリは標準的なHTTPライブラリだけなので、curlで実装することも可能です。

 curl -H 'datarobot-key:<DataRobotキー>' -i -X POST <専用予測サーバのアドレス>/predApi/v1.0/<PID>/<MID>/predict' -u <ユーザー名>:<APIトークン> -F file=@<予測用csvファイルの絶対パス>
執筆者について
小川 幹雄
小川 幹雄

DataRobot Japan
VP, Japan Applied AI Experts

DataRobot Japan 3番目のメンバーとして参加。現在は、金融業界を担当するディレクター兼リードデータサイエンティストとして、金融機関のお客様での AI 導入支援から CoE 構築の支援を行いながら、イベント、大学機関、金融庁、経産省などでの講演を多数実施。初期はインフラからプロダクトマネジメント業、パートナリング業まで DataRobot のあらゆる業務を担当。前職はデータマネジメント系の外資ベンダーで分析ソリューション・ビッグデータ全般を担当。

小川 幹雄 についてもっとくわしく