TensorFlow Serving是一个适用于深度学习模型的推理服务引擎,支持将TensorFlow标准的SavedModel格式的模型部署为在线服务,并支持模型热更新与模型版本管理等功能。本文为您介绍如何使用镜像部署的方式部署TensorFlow Serving模型服务。
前提准备
模型文件
使用TensorFlow Serving镜像部署要求模型文件存储在OSS,且模型文件的存储目录需满足以下要求:
模型版本目录:每个模型至少包含一个模型版本目录,且必须以数字命名,作为模型版本号,数字越大版本号越新。
模型文件:模型版本目录下存放导出的SavedModel格式的模型文件,服务会自动加载最大模型版本号目录下的模型文件。
操作步骤如下:
在OSS存储空间中创建模型存储目录(例:
oss://examplebucket/models/tf_serving/
),详情请参见管理目录。将模型文件上传到步骤1创建的目录(您可以下载本文案例TensorFlowServing模型文件使用),得到模型存储目录格式如下:
tf_serving ├── modelA │ └── 1 │ ├── saved_model.pb │ └── variables │ ├── variables.data-00000-of-00001 │ └── variables.index │ ├── modelB │ ├── 1 │ │ └── ... │ └── 2 │ └── ... │ └── modelC ├── 1 │ └── ... ├── 2 │ └── ... └── 3 └── ...
模型配置文件
通过配置文件的方式可以实现让一个服务同时运行多个模型。如果您只需要部署单模型服务,可跳过本小节。
请根据如下说明创建配置文件并上传到OSS(模型文件准备小节中给出的案例文件里已有一个模型配置文件model_config.pbtxt,您可直接使用或按需修改)。本案例中将模型配置文件上传到目录oss://examplebucket/models/tf_serving/
。
模型配置文件model_config.pbtxt内容示例如下:
model_config_list {
config {
name: 'modelA'
base_path: '/models/modelA/'
model_platform: 'tensorflow'
model_version_policy{
all: {}
}
}
config {
name: 'modelB'
base_path: '/models/modelB/'
model_platform: 'tensorflow'
model_version_policy{
specific {
versions: 1
versions: 2
}
}
version_labels {
key: 'stable'
value: 1
}
version_labels {
key: 'canary'
value: 2
}
}
config {
name: 'modelC'
base_path: '/models/modelC/'
model_platform: 'tensorflow'
model_version_policy{
latest {
num_versions: 2
}
}
}
}
其中关键配置说明如下:
参数 | 是否必选 | 描述 |
name | 否 | 自定义配置模型名称。建议配置该参数,如果不配置模型名称,则model_name为空,后续无法调用该模型服务。 |
base_path | 是 | 配置模型存储目录在实例中的路径,后续部署服务时用于读取模型文件。例如:挂载目录为 |
model_version_policy | 否 | 表示模型版本加载策略。
|
version_labels | 否 | 为模型版本配置自定义标签。如果没有配置version_labels,那么就只能通过版本号来区分模型版本,请求路径为: 如果设置了version_labels,那么就可以请求version label来指向指定的版本号 说明 标签默认只能分配给已成功加载并启动为服务的模型版本。若想要预先为尚未加载的模型版本分配标签,需要在运行命令中设置启动参数 |
部署服务
可以通过以下两种方法来进行TensorFlow Serving的镜像部署。
场景化模型部署:适用于基本场景部署。您只需配置几个参数,即可一键部署TensorFlow Serving模型服务。
自定义模型部署:适用于在特定环境下运行的服务。您可以根据自身业务的具体需求来调整更多配置选项,从而实现更灵活的服务配置。
TensorFlow Serving模型服务支持配置8501和8500两种端口。
8501:支持HTTP请求,在8501端口启动HTTP或REST服务。
8500:支持gRPC请求,在8500端口启动gRPC服务。
场景化模型部署默认使用8501端口,无法修改。如果您需要配置8500端口来使用gRPC服务请选择自定义模型部署。
以下示例中单模型部署以部署modelA为例进行说明。
场景化模型部署
具体操作步骤如下:
登录PAI控制台,在页面上方选择目标地域,并在右侧选择目标工作空间,然后单击进入EAS。
在模型在线服务(EAS)页面,单击部署服务。然后在场景化模型部署区域,单击TFServing部署。
在TFServing部署页面配置参数。关键参数说明如下,其他参数配置说明,请参见服务部署:控制台。
参数
描述
部署方式
支持以下两种部署方式:
标准模型部署:用于部署单模型服务。
配置文件部署:用于部署多模型服务。
模型配置
部署方式选择标准模型部署时,您需要配置模型文件所在的OSS路径。
部署方式选择配置文件部署时,您需要配置以下参数:
OSS:选择模型文件所在的OSS路径。
挂载路径:挂载服务实例中的目标路径,用来读取模型文件。
配置文件:选择模型配置文件所在的OSS路径。
参数配置示例:
参数
单模型示例(部署modelA)
多模型示例
服务名称
modela_scene
multi_scene
部署方式
选择标准模型部署。
选择配置文件部署。
模型配置
OSS:
oss://examplebucket/models/tf_serving/modelA/
。OSS:
oss://examplebucket/models/tf_serving/
。挂载路径:/models
配置文件:
oss://examplebucket/models/tf_serving/model_config.pbtxt
参数配置完成后,单击部署。
自定义模型部署
具体操作步骤如下:
登录PAI控制台,在页面上方选择目标地域,并在右侧选择目标工作空间,然后单击进入EAS。
在模型在线服务(EAS)页面,单击部署服务。然后在自定义模型部署区域,单击自定义部署。
在新建服务页面配置参数,关键参数说明如下,其他参数配置说明,请参见服务部署:控制台。
参数
描述
镜像选择
在PAI平台镜像列表中选择tensorflow-serving和对应的镜像版本。建议选择最新版本。
说明如果服务需要使用GPU,则镜像版本必须选择x.xx.x-gpu。
模型配置
单击填写模型配置,进行模型配置。支持多种方式配置模型文件,这里采用OSS。
OSS路径:选择模型文件所在的OSS路径。
挂载路径:挂载服务实例中的目标路径,用来读取模型文件。
运行命令
tensorflow-serving的启动参数,选择tensorflow-serving镜像会自动加载命令:
/usr/bin/tf_serving_entrypoint.sh
,还需设置的参数说明如下。单模型部署启动参数:
--model_name:模型名称,用于服务请求中的URL。如果不配置,默认名称为model。
--model_base_path:用于指定模型存储目录在实例中的路径。如果不配置,默认路径为
/models/model
。
多模型部署启动参数:
--model_config_file:必选。用来指定模型配置文件。
--model_config_file_poll_wait_seconds:选填。如果您希望在服务启动后修改模型配置文件的内容,需要配置轮询模型文件的周期,单位为秒。服务会按照配置的时间定期读取模型配置文件的内容。例如
--model_config_file_poll_wait_seconds=30
表示服务每隔30秒读取一次模型配置文件内容。说明当模型服务读取新的模型配置文件时,只会执行新配置文件中的内容。例如:旧配置文件中包含模型A,而新配置文件将模型A删除并增加了模型B的配置,那么服务会卸载模型A并加载模型B。
--allow_version_labels_for_unavailable_models:选填。默认为false,如果您想预先为尚未加载的模型版本分配标签,需要将该参数配置为true。例如
--allow_version_labels_for_unavailable_models=true
。
参数配置示例:
参数
单模型示例(部署modelA)
多模型示例
部署方式
选择镜像部署服务。
镜像选择
选择PAI平台镜像:tensorflow-serving,版本选择2.14.1。
模型配置
模型配置选择OSS。
OSS路径:
oss://examplebucket/models/tf_serving/
。挂载路径:配置为
/models
。运行命令
/usr/bin/tf_serving_entrypoint.sh --model_name=modelA --model_base_path=/models/modelA
/usr/bin/tf_serving_entrypoint.sh --model_config_file=/models/model_config.pbtxt --model_config_file_poll_wait_seconds=30 --allow_version_labels_for_unavailable_models=true
端口号默认为8501,服务将在8501端口启动HTTP或REST服务,支持HTTP请求。若您需要该服务支持gRPC请求,需要执行以下操作:
模型服务信息中运行命令将端口号修改为8500。
服务功能配置中是否使用GRPC协议打开开关。
对应配置编辑中添加以下配置
"networking": { "path": "/" }
单击部署。
发送服务请求
根据服务部署时运行命令中配置的端口号,分别支持HTTP和gRPC两种请求协议。下面给出modelA的服务请求示例。
测试数据准备
modelA是一个图片分类模型,训练数据集为Fashion-MNIST,样本是28x28的灰度图片,模型输出是样本属于10个分类的概率值。为了测试方便,我们使用
[[[[1.0]] * 28] * 28]
作为modelA服务请求的测试数据。请求示例:
HTTP请求
端口号配置为8501,服务支持HTTP请求。下表总结了单模型和多模型部署情况下的HTTP请求路径:
单模型
多模型
路径格式:
<service_url>/v1/models/<model_name>:predict
其中:
场景化部署:<model_name> 不支持自定义配置,默认为model。
自定义部署:<model_name>为运行命令中配置的模型名称,如果没有配置,默认为model。
支持不指定版本和指定模型版本两种请求方式,路径格式如下:
不指定版本(默认加载最大版本):
<service_url>/v1/models/<model_name>:predict
指定模型版本:
<service_url>/v1/models/<model_name>/versions/<version_num>:predict
如果设置了version_labels:
/v1/models/<model name>/labels/<version label>:predict
其中,<model_name> 为模型配置文件中配置的模型名称。
其中:<service_url> 是您部署的服务访问地址。您可以在模型在线服务(EAS)页面,单击待调用服务服务方式列下的调用信息,在公网地址调用页签查看服务访问地址。在使用控制台在线调试的时候,页面已自动填充该部分路径。
以场景化部署单模型modelA为例,HTTP请求路径为:
<service_url>/v1/models/model:predict
。下面以此为例为您具体说明如何通过控制台发送服务请求和通过Python代码发送服务请求:
通过控制台发送服务请求
服务部署完成后,单击服务操作列下的在线调试,在线调试请求参数中已填充<service_url>,在地址后增加路径
/v1/models/model:predict
,Body中配置服务请求数据:{"signature_name": "serving_default", "instances": [[[[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]], [[1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0], [1.0]]]]}
参数配置完成后,单击发送请求,输出如下类似结果。
通过Python代码发送服务请求
Python代码示例如下:
from urllib import request import json # 请替换为你的服务访问地址和Token。 # 您可以在推理服务列表的服务方式列单击调用信息,在公网地址调用页签查看。 service_url = '<service_url>' token = '<test-token>' # 场景化单模型部署为 model,其他情况参照上面路径说明表格 model_name = "model" url = "{}/v1/models/{}:predict".format(service_url, model_name) # 创建HTTP请求。 req = request.Request(url, method="POST") req.add_header('authorization', token) data = { 'signature_name': 'serving_default', 'instances': [[[[1.0]] * 28] * 28] } # 请求服务。 response = request.urlopen(req, data=json.dumps(data).encode('utf-8')).read() # 查看返回结果。 response = json.loads(response) print(response)
gRPC请求
端口号配置为8500,并添加gRPC相关配置后,服务支持发送gRPC请求。Python代码示例如下:
import grpc import tensorflow as tf from tensorflow_serving.apis import predict_pb2 from tensorflow_serving.apis import prediction_service_pb2_grpc from tensorflow.core.framework import tensor_shape_pb2 # 服务访问地址。格式参见下面的host参数说明。 host = "tf-serving-multi-grpc-test.166233998075****.cn-hangzhou.pai-eas.aliyuncs.com:80" # test-token替换为服务Token。您可以在公网地址调用页签查看Token。 token = "<test-token>" # 模型名称。参见下面的name参数说明。 name = "<model_name>" signature_name = "serving_default" # 配置为模型版本号。每次只能对单个模型版本发送请求。 version = "<version_num>" # 创建gRPC请求。 request = predict_pb2.PredictRequest() request.model_spec.name = name request.model_spec.signature_name = signature_name request.model_spec.version.value = version request.inputs["keras_tensor"].CopyFrom(tf.make_tensor_proto([[[[1.0]] * 28] * 28])) # 请求服务。 channel = grpc.insecure_channel(host) stub = prediction_service_pb2_grpc.PredictionServiceStub(channel) metadata = (("authorization", token),) response, _ = stub.Predict.with_call(request, metadata=metadata) print(response)
其中关键参数配置如下:
参数
描述
host
需要配置为服务访问地址,服务访问地址需要省略
http://
并在末尾添加:80
。您可以在模型在线服务(EAS)页面,单击待调用服务服务方式列下的调用信息,在公网地址调用页签查看服务访问地址。name
单模型发送gRPC请求
场景化部署:配置为model。
自定义部署:配置为运行命令中配置的模型名称,如果没有配置,默认为model。
多模型发送gRPC请求
配置为模型配置文件中配置的模型名称。
version
配置为模型版本号。每次只能对单个模型版本发送请求。
metadata
配置为服务Token。您可以在公网地址调用页签查看Token。
相关文档
如何基于Triton Server推理服务引擎部署EAS服务,请参见Triton Inference Server镜像部署。
如何使用Modelscope、Huggingface镜像将相应的开源模型部署为EAS服务,请参见Modelscope镜像部署和HuggingFace镜像部署。
您也可以开发自定义镜像,使用自定义镜像部署EAS服务。具体操作,请参见服务部署:自定义镜像。