- Overview
- Getting Started Guide
- UserGuide
-
References
-
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
Model handler function FOR 19.04 +
This page explaines the handler functions that can be used in images after 19.04
.
See here for the handler functions available in the 18.10
image.
Model handler is a function in the code that are called when the model is executed. The handler function uses the following syntax structure.
def handler(request, context):
...
return response
- request : Input data is stored in this parameter. This parameter is
dict
. It behaves likefile-like object
. - context : Runtime metadata etc. are stored in this parameter.
Handler arguments
Input Data
Input data is passed to the first argument of the handler function. The format of the data included in the input data differs for each Content-Type.
Other than multipart/form-data
The following items can be acquired from the input data.
Key | Description |
---|---|
http_method | Request HTTP Method |
content_type | Content-Type |
headers | Request Header. It is a list of dicts containing header names and values, all header names are converted to lower case. Some headers will be removed, including Authorization header |
http_headers | Header information which you can access by dict format.Included header information is same as headers |
read() |
Request body |
You can get each value as follows.
>>> request['http_method']
'POST'
>>> request['content_type']
'application/json'
>>> pprint.pprint(request['http_headers']['accept-encoding'])
'gzip, deflate, br'
>>> request.read()
b'{"foo": "bar"}'
The request body is always read as binary data. GET
is Request parameter,POST
PUT
are request body.
multipart/form-data
The following items can be acquired from the input data.
Key | Description |
---|---|
http_method | Request HTTP Method |
content_type | Content-Type |
headers | Request Header. It is a list of dicts containing header names and values, all header names are converted to lower case. Some headers will be removed, including Authorization header |
http_headers | Header information which you can access by dict format.Included header information is same as headers |
contents | Contents |
Contents
is an array ofContent
for each part.
The following elements can be obtained from each Content
.
Key | Description |
---|---|
content_type | Content-Type |
form_name | Form Name |
file_name | File Name |
read() |
Contents body |
You can get each value as follows.
>>> 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'
...
Context data
The metadata of the runtime will be passed to the second argument of the handler function.
This variable is an empty dict
.
Handler environment variables
The handler environment can use the following environment variables.
Environment variable name | Description |
---|---|
ABEJA_ORGANIZATION_ID | ID of the organization |
ABEJA_MODEL_ID | ID of the model |
ABEJA_MODEL_VERSION_ID | ID of the model version |
ABEJA_DEPLOYMENT_ID | ID of the deployment |
ABEJA_SERVICE_ID | Service ID |
HANDLER | Handler path |
TRAINING_JOB_DEFINITION_NAME | Training job definition name |
TRAINING_JOB_ID | ID of the Training job |
ABEJA_TRAINING_RESULT_DIR | Path to the directory where learning job results are stored |
In addition to the above, environment variables that can be specified when creating code version/service/trigger/run can also be used.
For more information on user-specifiable environment variables, see here.
Using learning results in a model handler
ABEJA Platform can create model versions based on learning results.
When using the output of learning in the model handler function, it must be stored in the directory specified by the environment variable ABEJA_TRAINING_RESULT_DIR
during learning.
The results output during learning are expanded to the directory stored in the environment variable ABEJA_TRAINING_RESULT_DIR
when the model handler function is executed.
Handler return values
To output data, return a dict
containing the following elements.
Key | Description |
---|---|
status_code | HTTP Status Code |
content_type | Content-Type |
content | Response body |
metadata | Dict of the element you want to return in the response header |
If you do not specify status_code
,http.HTTPStatus.OK
is used as the default value.
If content_type
is not specified,text / plain
is used as the default value.
For example, the value is returned as follows.
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
}
Implementation example of model handler function
The following is a simple implementation example of a model handler function.
Input JSON data and return JSON data
This is an example of performing a simple calculation on the input JSON data and returning a JSON data response.
Get the input data from the input data as follows.
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}
}
Execution result of run-local
command. The output result with request.json
as input is displayed.
$ abeja model run-local --image abeja/all-cpu:19.04 --handler main:handler --input request.json --quiet
{
"total": 108.0
}
Return an image as an input
This is an example of returning the input image flipped horizontally.
As with JSON, the input data is obtained from the input data as follows.
The inverted input image is returned as a bytes
type in thecontent
. In this example, since the output is a JPEG image, set content_type
toimage/jpeg
as follows.
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
}
Also, since Pillow
is used as the image processing library,Pillow
installation is specified in requirements.txt
(orPipfile
).
For requirements.txt
Pillow==6.0.0
For Pipfile
[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true
[dev-packages]
[packages]
Pillow = "==6.0.0"
[requires]
python_version = "3.6"
Execution result of run-local
command. An image obtained by inverting the input cat.jpg
is output as flipped_cat.jpg
.
Please note that if you do not specify the --quiet
option, the log will also be output and a JPEG file will not be generated.
$ abeja model run-local -h main:handler -i abeja/all-cpu:19.04 --input cat.jpg --quiet > flipped_cat.jpg
Return JSON from images using learning results
The inference result of the model using the HDF5 file output as the learning result is returned in JSON.
Read and infer HDF5 file from the learning result expanded to ABEJA_TRAINING_RESULT_DIR
.
In this case, it is necessary to specify the same file name as the HDF5 file saved in the output directory during learning.
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
}
Execution result of run-local
command. An inference result with the input cat.jpg
as an input is returned.
The environment variable ABEJA_TRAINING_RESULT_DIR
is set toabejainc_training_result
as an initial value.
Note that if not specified, HDF5 files will be searched under <current directory>/abejainc_training_result/
$ 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" }
]
}
Notify invalid JSON input
Set status_code
to notify requester that the input is invalid.
In this case, invalid input is when there is no data in the total_without_tax
of the input JSON data.
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'}
}
...
Execution result of run-local
command. An inference result with the input request.json
as input is returned.
It can be confirmed that an error is output for invalid input.
$ 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"}