本篇主要介绍使用docker
和tensorflow serving
搭建一个模型预测服务,通过RESTful API和gRPC请求预测(python)。在下一篇将介绍使用Python web框架Django + tfserving,搭建一个图像分类预测web服务。
Docker CE的安装可以参考docker官方参考文档:install Docker CE
安装完成后,可以运行 $sudo docker run hello-world
测试是否安装成功,成功安装的情况下应该看到如下输出:
docker安装完成后,在使用docker命令的时候需要使用sudo,这是因为docker守护进程绑定的是Unix socket,默认情况下Unix socket为root用户所有,其他用户想要访问需要使用sudo。如果希望不使用sudo,可以新建一个名为docker的用户组,并将当前用户添加到该用户组,这样docker守护进程启动的时候,会自动为docker用户组用户获取Unix socket的读写权限。
sudo groupadd docker # 创建docker用户组
sudo usermod -aG docker ${USER} # 将当前用户添加到docker组
sudo systemctl restart docker # 重启docker
su root
su ${USER} # 切换或退出当前用户再登入生效
完成后可以运行$docker ps -a 查看效果。
保存模型是将训练得到的tensorflow模型保存为可以用于tensorflow serving的模型文件,主要是通过GraphDef对象保存模型文件。相关详细说明和完整示例代码可以参考tensorflow模型保存与恢复(2) 使用SavedModel,可以直接使用simple_save方法保存,更简洁方便。保存模型的时候注意设置好input和output,使用tfserving预测的输入即是从input tensor输入到网络,一次前向传播完成后,得到output tensor,返回预测结果。
这一步是从docker仓库获取tensorflow serving的镜像,用于后面的预测。
docker pull tensorflow/serving
下载完成后,可以使用docker images命令查看镜像:
上述准备工作完成后可以运行容器:
$ docker run -p 8501:8501 --name tfserving_testnet --mount type=bind,source=/tmp/testnet,target=/models/testnet -e MODEL_NAME=testnet -t tensorflow/serving &
上面的命令中:
(1)-p 8501:8501
是端口映射,是将容器的8501端口映射到宿主机的8501端口,后面预测的时候使用该端口;
(2)-e MODEL_NAME=testnet
设置模型名称;
(3)--mount type=bind,source=/tmp/testnet,target=/models/testnet
是将宿主机的路径/tmp/testnet挂载到容器的/models/testnet下。/tmp/testnet是存放的是上述准备工作中保存的模型文件,‘testnet’是模型名称,包含一个.pb文件和一个variables文件夹,在/tmp/testnet下新建一个以数字命名的文件夹,如100001,并将模型文件放到该文件夹中。容器内部会根据绑定的路径读取模型文件;
(4)-t tensorflow/serving
根据名称“tensorflow/serving”运行容器;
(1) 通过model status API 查看模型状态
curl http://localhost:8501/v1/models/testnet
结果为:
{
"model_version_status": [
{
"version": "1",
"state": "AVAILABLE",
"status": {
"error_code": "OK",
"error_message": ""
}
}
]
}
(2) 通过model metadata API 查看模型的元数据
curl http://localhost:8501/v1/models/testnet/metadata
结果为:
{
"model_spec":{
"name": "testnet",
"signature_name": "",
"version": "1"
}
,
"metadata": {"signature_def": {
"signature_def": {
"serving_default": {
"inputs": {
"input0": {
"dtype": "DT_FLOAT",
"tensor_shape": {
"dim": [
{
"size": "-1",
"name": ""
},
{
"size": "1",
"name": ""
}
],
"unknown_rank": false
},
"name": "Placeholder:0"
}
},
"outputs": {
"output0": {
"dtype": "DT_FLOAT",
"tensor_shape": {
"dim": [
{
"size": "-1",
"name": ""
},
{
"size": "1",
"name": ""
}
],
"unknown_rank": false
},
"name": "Add:0"
}
},
"method_name": "tensorflow/serving/predict"
}
}
}
}
}
使用Predict API请求预测,格式为:
POST http://host:port/v1/models/${MODEL_NAME}[/versions/${MODEL_VERSION}]:predict
其中模型版本MODEL_VERSION可以省略,默认使用最新版本的模型。
curl -d '{"instances": $input_data}' -X POST http://localhost:8501/v1/models/testnet:predict
input_data
为模型的输入。如果是使用tensorflow模型保存与恢复(2) 使用SavedModel中的代码生成的模型,可以使用如下命令访问:
curl -d '{"instances": [[[10,10,10,10,10,10,10],[10,10,10,10,10,10,10],[10,10,10,10,10,10,10],[10,10,10,10,10,10,10],[10,10,10,10,10,10,10],[10,10,10,10,10,10,10],[10,10,10,10,10,10,10]]]}' -X POST http://localhost:8501/v1/models/testnet:predict
并将得到如下返回结果:
如果机器有公网IP(如将tfsevring部署在阿里云服务器上),则可以在其他机器上通过IP地址访问进行预测,将上面的地址中的localhost改为运行容器的机器的公网IP地址即可。
也可以使用python代码请求预测
import requests
import numpy as np
SERVER_URL = 'http://localhost:8501/v1/models/testnet:predict'
def prediction():
predict_request='{"instances":%s}' % str([[[10]*7]*7])
#print(predict_request)
response = requests.post(SERVER_URL, data=predict_request)
prediction = response.json()['predictions'][0]
print(prediction)
if __name__ == "__main__":
prediction()
需要安装tensorflow-serving-api,这里将gRPC的端口映射到宿主机的8500端口。
请求预测python代码:
import grpc
import numpy as np
from tensorflow_serving.apis import model_service_pb2_grpc, model_management_pb2, get_model_status_pb2, predict_pb2, prediction_service_pb2_grpc
from tensorflow_serving.config import model_server_config_pb2
from tensorflow.contrib.util import make_tensor_proto
from tensorflow.core.framework import types_pb2
def predict_test(batch_size, serving_config):
channel = grpc.insecure_channel(serving_config['hostport'], options=[('grpc.max_send_message_length', serving_config['max_message_length']), (
'grpc.max_receive_message_length', serving_config['max_message_length'])])
stub = prediction_service_pb2_grpc.PredictionServiceStub(channel)
# Creating random images for given batch size
input_data=[5.0,2.0]
request = predict_pb2.PredictRequest()
request.model_spec.name = serving_config['model_name']
request.model_spec.signature_name = serving_config['signature_name']
request.inputs['input0'].CopyFrom(make_tensor_proto(
input_data, shape=[1,2], dtype=types_pb2.DT_FLOAT))
result = stub.Predict(request, serving_config['timeout'])
channel.close()
return result
if __name__ == "__main__":
serving_config = {
"hostport": "127.0.0.1:8500",
"max_message_length": 10 * 1024 * 1024,
"timeout": 300,
"signature_name": "serving_default",
"model_name": "testnet"
}
predict_result = predict_test(1, serving_config)
print(predict_result)
print(predict_result.outputs['output0'].float_val)
返回的结果如下(这里使用的是另外一个模型,模型对输入数据进行加1操作):
outputs {
key: "output0"
value {
dtype: DT_FLOAT
tensor_shape {
dim {
size: 1
}
dim {
size: 2
}
}
float_val: 5.099999904632568
float_val: 2.0999999046325684
}
}
model_spec {
name: "testnet"
version {
value: 1
}
signature_name: "serving_default"
}
Done!
上述使用如Python代码处理完成后再请求预测的方法,有时使用起来会显得不方便。有些情况下也可能有将预测的过程作为一个web服务,实现在网页上填写图片的地址信息,然后返回预测结果的需求。在下一篇将介绍使用开源的Python web框架Django结合tfserving,搭建一个图像分类预测web服务。