- 概要
- スタートアップガイド
- ユーザガイド
-
リファレンス
-
ABEJA Platform CLI
- CONFIG COMMAND
- DATALAKE COMMAND
- DATASET COMMAND
- TRAINING COMMAND
-
MODEL COMMAND
- check-endpoint-image
- check-endpoint-json
- create-deployment
- create-endpoint
- create-model
- create-service
- create-trigger
- create-version
- delete-deployment
- delete-endpoint
- delete-model
- delete-service
- delete-version
- describe-deployments
- describe-endpoints
- describe-models
- describe-service-logs
- describe-services
- describe-versions
- download-versions
- run-local
- run-local-server
- start-service
- stop-service
- submit-run
- update-endpoint
- startapp command
-
ABEJA Platform CLI
- FAQ
- Appendix
モデルハンドラー関数 FOR 19.04 +
本頁では、 19.04
以降のイメージで利用できるハンドラー関数について説明します。
18.10
のイメージで利用できるハンドラー関数については、こちらを参照ください。
モデルハンドラー関数はモデルを実行する際に呼び出されるコード内の関数です。ハンドラー関数は以下の構文構造を使用します。
def handler(request, context):
...
return response
- request : 入力データがこのパラメータに格納されます。このパラメータは
dict
であり、またfile-like object
のように振る舞います。 - context : 実行時のメタデータなどがこのパラメータに格納されます。
ハンドラーの引数
入力データ
ハンドラー関数の第一引数には入力データが渡されます。 入力データに含まれるデータの形式は Content-Type 毎に異なります。
multipart/form-data 以外
入力データからは以下の要素を取得できます。
キー | 内容 |
---|---|
http_method | Request HTTP Method |
content_type | Content-Type |
headers | Request Header。ヘッダー名及び値を含む dict のリストになっていて、ヘッダー名はすべて小文字に変換されています。 Authorization ヘッダーを含む一部のヘッダーは削除されます |
http_headers | dict形式でアクセスできるヘッダー情報です。含まれるヘッダー情報は headers と同じです。 |
read() |
リクエスト本文 |
以下のようにして、各値を取得できます。
>>> request['http_method']
'POST'
>>> request['content_type']
'application/json'
>>> pprint.pprint(request['headers'])
[{'key': 'content-type', 'values': ['application/json']},
{'key': 'accept-encoding': 'values': ['gzip, deflate, br']}, ...]
>>> pprint.pprint(request['http_headers']['accept-encoding'])
'gzip, deflate, br'
>>> request.read()
b'{"foo": "bar"}'
リクエスト本文( GET
ならリクエストパラメーター、POST
PUT
ならリクエストボディ)は、常にバイナリデータとして読み出されます。
multipart/form-data
入力データからは以下の要素を取得できます。
キー | 内容 |
---|---|
http_method | Request HTTP Method |
content_type | Content-Type |
headers | Request Header。ヘッダー名及び値を含む dict のリストになっていて、ヘッダー名はすべて小文字に変換されています。 Authorization ヘッダーを含む一部のヘッダーは削除されます |
http_headers | dict形式でアクセスできるヘッダー情報です。含まれるヘッダー情報は headers と同じです。 |
contents | Contents |
Contents
は 各 part 毎の Content
の配列です。
各 Content
からは以下の要素を取得できます。
キー | 内容 |
---|---|
content_type | Content-Type |
form_name | フォーム名 |
file_name | ファイル名 |
read() |
コンテンツ本文 |
以下のようにして、各値を取得できます。
>>> request['http_method']
'POST'
>>> request['content_type']
'multipart/form-data'
>>> contents = request['contents']
>>> contents[0]['content_type']
'application/json'
>>> contents[0]['form_name']
'file1'
>>> contents[0].read()
b'{"foo": "bar"}'
>>> contents[1]['content_type']
'image/jpeg'
>>> contents[1]['file_name']
'cat.jpg'
...
コンテキストデータ
ハンドラー関数の第二引数には実行時のメタデータなどが渡される予定です。
現在、この変数は空の dict
です。
ハンドラーの環境変数
ハンドラー関数では以下の環境変数を使用することが可能です。
環境変数名 | 説明 |
---|---|
ABEJA_ORGANIZATION_ID | オーガニゼーションのIDです |
ABEJA_MODEL_ID | モデルのIDです |
ABEJA_MODEL_VERSION_ID | モデルバージョンのIDです |
ABEJA_DEPLOYMENT_ID | デプロイメントのIDです |
ABEJA_SERVICE_ID | サービスのIDです |
HANDLER | ハンドラーのパスです |
TRAINING_JOB_DEFINITION_NAME | 学習ジョブ定義名です |
TRAINING_JOB_ID | 学習ジョブのIDです |
ABEJA_TRAINING_RESULT_DIR | 学習ジョブの結果が格納されるディレクトリへのパスです |
上記のほか、コードバージョン/サービス/トリガー/ Run 作成時に指定出来る環境変数も使用可能です。
ユーザーの指定可能な環境変数についての詳細は、こちらを参照ください。
学習結果のモデルハンドラーでの使用
ABEJA Platform では学習の結果を元にモデルバージョンを作成することができます。
学習時の出力をモデルハンドラー関数で使用するためには、学習時に ABEJA_TRAINING_RESULT_DIR
環境変数で指定されるディレクトリに格納する必要があります。
学習時に出力した結果は、モデルハンドラー関数実行時に ABEJA_TRAINING_RESULT_DIR
環境変数に格納されたディレクトリに展開されます。
ハンドラーの返り値
データを出力するためには、以下の要素を含む dict
を返却します。
キー | 内容 |
---|---|
status_code | HTTP Status Code |
content_type | Content-Type |
content | レスポンス本文 |
metadata | レスポンスヘッダーで返却したい要素の dict |
status_code
を指定しない場合は、デフォルト値として http.HTTPStatus.OK
が使用されます。
content_type
を指定しない場合は、デフォルト値として text/plain
が使用されます。
例えば以下のように返却します。
import http
def handler(request, context):
# some inference process...
content = {
'transaction_id': 1234567890,
'category_id': 10,
'predictions': predictions,
}
return {
'status_code': http.HTTPStatus.OK,
'content_type': 'application/json; charset=utf8',
'content': content
}
モデルハンドラー関数の実装例
以下はモデルハンドラー関数の簡単な実装例です。
JSON を入力し JSON を返却する
入力された JSON に対し簡単な計算を行い、JSON のレスポンスを返す例です。
入力データから入力となるデータを以下のように取得します。
import json
import http
def handler(request, context):
req_data = json.load(request)
total_without_tax = req_data['total_without_tax']
total_without_tax = int(total_without_tax)
total = total_without_tax * 1.08
return {
'status_code': http.HTTPStatus.OK,
'content_type': 'application/json; charset=utf8',
'content': {'total': total}
}
run-local
コマンドの実行結果です。 request.json
を入力とした出力結果が表示されます。
$ abeja model run-local --image abeja/all-cpu:19.04 --handler main:handler --input request.json --quiet
{
"total": 108.0
}
画像を入力とし画像を返却する
入力された画像を左右反転して返す例です。
JSON と同様に入力データから入力となるデータを以下のように取得します。
反転した入力画像を bytes
型として content
に含め返却しています。この例では 出力が JPEG 画像であるため content_type
に以下のように image/jpeg
を設定します。
import cv2
import http
import numpy as np
from io import BytesIO
from PIL import Image
def handler(request, context):
data = request.read()
imgfile = BytesIO(data)
img_pil = Image.open(imgfile)
img_numpy = np.asarray(img_pil)
flipped_array = cv2.flip(img_numpy, 1)
flipped_image = Image.fromarray(flipped_array)
retBytes = BytesIO()
flipped_image.save(retBytes, format='JPEG', quality=95)
retBytes.seek(0)
return {
'status_code': http.HTTPStatus.OK,
'content_type': 'image/jpeg',
'content': retBytes
}
また、画像処理ライブラリとして Pillow
を利用しているので、 requirements.txt
(または Pipfile
) で Pillow
のインストールを指定します。
requirements.txt
の場合
Pillow==6.0.0
Pipfile
の場合
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true
[dev-packages]
[packages]
Pillow = "==6.0.0"
[requires]
python_version = "3.6"
run-local
コマンドの実行結果です。入力である cat.jpg
を反転した画像が flipped_cat.jpg
として出力されます。
( --quiet
オプションを指定しないと、ログも出力されてしまうため JPEG ファイルにならないので、ご注意ください)
$ abeja model run-local -h main:handler -i abeja/all-cpu:19.04 --input cat.jpg --quiet > flipped_cat.jpg
学習結果を使用し画像から JSON を返却する
学習の結果として出力された HDF5 ファイルを用いたモデルの推論結果を JSON で返却します。
ABEJA_TRAINING_RESULT_DIR
に展開される学習結果から HDF5 ファイルを読み込み推論を行います。
この際、学習時に出力ディレクトリに保存した HDF5 ファイルと同じファイル名を指定する必要があります。
import os
import numpy as np
from io import BytesIO
from keras.models import load_model
from PIL import Image
model = load_model(os.path.join(os.environ.get('ABEJA_TRAINING_RESULT_DIR', '.'), 'model.h5'))
def handler(request, ctx):
data = request.read()
imgfile = BytesIO(data)
img_pil = Image.open(imgfile)
img_numpy = np.asarray(img_pil)
...
result = model.predict(img)
# CONVERT RESULT FOR RESPONSE
...
return {
'status_code': http.HTTPStatus.OK,
'content_type': 'application/json; charset=utf8',
'content': result
}
run-local
コマンドの実行結果です。入力である cat.jpg
を入力とした推論結果が返されます。
(環境変数 ABEJA_TRAINING_RESULT_DIR
には、初期値として abejainc_training_result
が設定されるので、指定しない場合は <カレントディレクトリ>/abejainc_training_result/
配下から HDF5 ファイルを検索することに注意してください)
$ abeja model run-local -h main:handler -i abeja/all-cpu:19.04 --input cat.jpg --environment ABEJA_TRAINING_RESULT_DIR:. --quiet
{
"result": [
{ "label":"cat", "probability":"0.85" },
{ "label":"dog", "probability":"0.15" }
]
}
不正な JSON 入力を通知する
status_code
を設定することで不正な入力であることをリクエスト実行者に通知します。
この場合、入力された JSON の total_without_tax
にデータが存在しない場合を不正な入力としています。
import json
import http
def handler(request, context):
req_data = json.load(request)
if 'total_without_tax' not in req_data:
return {
'status_code': http.HTTPStaus.BAD_REQUEST,
'content_type': 'application/json; charset=utf8',
'content': {'message': 'total_without_tax is need'}
}
...
run-local
コマンドの実行結果です。入力である request.json
を入力とした推論結果が返されます。
不正な入力に対しエラーが出力されていることが確認できます。
$ abeja model run-local --image abeja/all-cpu:19.04 --handler main:handler --input request.json --quiet
[error] failed to send request : 400 Client Error: Bad Request for url: http://localhost:62910/
------ Local Server Error ------
{"level":"info","msg":"proxy version: [0.4.1] start run.","time":"2019-05-23T09:29:06Z"}
{"level":"debug","msg":"call handleSignal","time":"2019-05-23T09:29:06Z"}
{"level":"debug","msg":"handleSignal: create channel for signal","time":"2019-05-23T09:29:06Z"}
{"level":"debug","msg":"runtime bootstrapping yet...","time":"2019-05-23T09:29:06Z"}
{"address":":5000","level":"debug","msg":"start listen.","time":"2019-05-23T09:29:06Z"}
{"level":"debug","msg":"handleSignal: channel for signal created","time":"2019-05-23T09:29:06Z"}
{"level":"debug","msg":"waiting signal...","time":"2019-05-23T09:29:06Z"}
{"log_id": "b94bba13-7046-4697-8417-8ab2f5020387", "log_level": "INFO", "timestamp": "2019-05-23T09:29:07.194826+00:00", "source": "runtime:run.main.26", "requester_id": "-", "message": "start executing model with abeja-runtime-python36 (version: 0.1.4)", "exc_info": null}
INFO: requirements.txt/Pipfile not found, skip installing dependencies process
INFO: Loading user model handler with main:handler...
{"level":"debug","msg":"runtime started","time":"2019-05-23T09:29:07Z"}
{"level":"debug","msg":"response body length = 157","time":"2019-05-23T09:29:08Z"}
{"level":"debug","msg":"response body = {\"content_type\": \"application/json; charset=utf8\", \"metadata\": {\"x-abeja-sys-meta-runtime-version\": \"0.1.4\"}, \"path\": \"/tmp/tmpgl8iizco\", \"status_code\": 400}","time":"2019-05-23T09:29:08Z"}