手把手教你如何将DeepCTR中的模型用TF Serving部署在线服务。
关键点:
- DeepCTR模型导出为SavedModel格式的模型;
- 逐步演示DeepCTR模型部署到TF Serving的流程;
- 调用服务的示例代码;
【前期准备】
主要环境介绍:
- python==3.7.3
- tensorflow==2.0.0 (如果有GPU,可用tensorflow-gpu)
- tensorflow-serving-api==2.0.0
- deepctr==0.9.0
- grpcio==1.46.3
- grpcio-tools==1.46.3
- docker镜像选择:tensorflow/serving:2.0.0 (如果有GPU,也可用gpu版本的)
【注意】
- 我这边是基于tensorflow 2.X的版本来操作的;如果用tensorflow 1.X,可能在导出SavedModel模型和docker版本选择时多注意一下,尽量保持版本一致即可。
- 出于成本考虑,部署时我用的是CPU服务器,因为推荐的模型不是很复杂。亲测特征维度150维左右,用CPU的服务请求时间也只要10+ms。
- grpcio 和 deepctr 的版本不强求,我这边只是贴出来我用的。
【DeepCTR模型导出】
首先要将训练好的模型导出为SavedModel格式的模型。 deepctr 开源包用的是 tensorflow 框架,而实际用的是 keras。具体的导出的代码如下:
import tensorflow as tf
model_name = "DeepFM"
version = 1
tf.saved_model.save(
obj=model,
export_dir='./export_model/{}/{}'.format(model_name, version),
signatures=None)
【说明】
- 这边输出模型文件时,会统一输出到"export_model"文件夹下面。在用docker部署模型时,如果有多个模型要同时部署,可通过配置model_config_file,就只要路径挂载一次即可,比较方便。(具体操作步骤可参考相关文档,这边不演示)
- 输出版本号(version)可以自己设定,保存时会生成对应版本号的文件夹,这个应该是一个整数。人为设定这个version的原因是,在模型部署的时候需要将模型位置精确到saved_model_dir,tf.Serving会在saved_model_dir下搜索版本号最大的路径下的模型进行服务。
保存结果展示
保存好的 SavedModel 格式的 Keras模型 的输出目录如下:
|--export_model
| |-- DeepFM
| |--1
| |--saved_model.pb
| |--variables
| |--variables.data-00000-of-00001
| |--variables.index
如果在指定目录下面生成一个pb文件,以及一个variables文件夹,就说明Keras模型导出成功了。
如果想要模型训练代码,可以参考github项目:https://github.com/MachineCYL/CTR
【DeepCTR模型部署】
接下来是演示 DeepFM 模型部署到TF Serving的流程,具体如下:
1.1 拉取 tf.Serving 镜像
Google官方已经做好了很多tf.Serving镜像,我们可以直接下载合适的版本。官方所有的Tensorflow Docker都在这个Docker Hub代码库中,下载时需要根据这个标记和变体来确定我们要下载哪一个版本的镜像。标记有四种
- latest:TensorFlow CPU 二进制映像的最新版本。(默认版本)
- nightly:TensorFlow 映像的每夜版。(不稳定)
- version:指定 TensorFlow 二进制映像的版本,例如:2.1.0
- devel:TensorFlow master开发环境的每夜版。包含 TensorFlow 源代码。
这里我选择tensorflow 2.0.0版本的镜像,指令如下:
docker pull tensorflow/serving:2.0.0
再看看拉取镜像是否成功:
# 查看现有镜像命令
docker images
# 输出
REPOSITORY TAG IMAGE ID CREATED SIZE
tensorflow/serving 2.0.0 048f8669e211 2 years ago 214MB
如果有出现上面的输出日志,就说明我们已经成功下载了一个tf.serving的Docker镜像。
1.2 模型部署(单模型)
如果只需要部署一个模型,会比较简单,可以用如下指令启动:
docker run -d \
-p 8500:8500 \
-p 8501:8501 \
--name "tf_serving" \
-v "/data/MachineCYL/CTR/export_model/DeepFM/:/models/model/" \
tensorflow/serving:2.0.0
【注意】
- “-p 8501:8501”和“-p 8500:8500”是端口映射,可不变。其中8500是GRPC的调用端口、8501是RESTful API的调用端口。
- “-v path1:path2”中path1指的是模型在机器种储存的路径(用绝对路径| 就是用上面导出的模型文件的export_model目录即可);path2指的是模型在docker容齐中储存的路径(保持不变即可)。
- –name 可以为启动的docker 容器命名,这里我命名为:tf_serving,方便后续查看日志。
1.3 查看模型部署是否正常
可以通过以下指令查看docker是否启动正常:
# 查看容器是否启动
docker ps
# 输出
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
f1266508696e tensorflow/serving:2.0.0 "/usr/bin/tf_serving…" 6 days ago Up 2 days 0.0.0.0:8500-8501->8500-8501/tcp tf_serving
# 查看tf_serving服务的输出日志
docker logs -f tf_serving
# 如果没有报错,就说明模型部署成功了
另外,可以通过下面的指令,看docker中的端口是否有映射出来:
telnet 127.0.0.1 8500
# 输出
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
telnet 127.0.0.1 8501
# 输出
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
如果出现上面的输出内容,就说明8500和8501端口都有成功映射出来。
1.4 模型部署(多模型)
如果有多个模型需要同时部署,可以用配置文件来同时部署多个模型。相关的TF Serving的参数为model_config_file。
假设我有两个模型以及配置文件的路径如下:
|--export_model
| |-- config
| |-- models.config
| |-- test-DeepFM
| |--1
| |--saved_model.pb
| |--variables
| |-- all-DeepFM
| |--1
| |--saved_model.pb
| |--variables
这里的配置文件的存放路径如:./export_model/config/models.config
配置文件的内容如下:
model_config_list {
config {
name: 'test'
base_path: '/models/test-DeepFM/'
model_platform: 'tensorflow'
}
config {
name: 'all'
base_path: '/models/all-DeepFM/'
model_platform: 'tensorflow'
}
}
docker 启动指令如下:
docker run -d \
-p 8500:8500 \
-p 8501:8501 \
-v "/data/MachineCYL/CTR/export_model/:/models/" \
--name "tf_serving" \
tensorflow/serving:2.0.0 \
--model_config_file="/models/config/models.config"
【注意】
- 注意这里的挂载路径只到 “export_model” 这一级即可。就能把2个模型和配置文件都挂载了。
- 另外在使用的时候,也要留意 --model_config_file 的路径开头是 “/models”。
【请求示例】
查看metadata的数据结构。可以通过这个输出的元数据的格式,我们可以构造请求字段与内容:
curl -X GET http://127.0.0.1:8501/v1/models/model/metadata
输出结果如下,可以看到请求的字段会比较多,如releaseYear、movieAvgRating等(不过为了方便展示,我这边就贴出一部分内容):
{
"model_spec":{
"name": "model",
"signature_name": "",
"version": "1"
}
,
"metadata": {"signature_def": {
"signature_def": {
"__saved_model_init_op": {
"inputs": {},
"outputs": {
"__saved_model_init_op": {
"dtype": "DT_INVALID",
"tensor_shape": {
"dim": [],
"unknown_rank": true
},
"name": "NoOp"
}
},
"method_name": ""
},
"serving_default": {
"inputs": {
"releaseYear": {
"dtype": "DT_INT32",
"tensor_shape": {
"dim": [
{
"size": "-1",
"name": ""
},
{
"size": "1",
"name": ""
}
],
"unknown_rank": false
},
"name": "serving_default_releaseYear:0"
},
"movieAvgRating": {
"dtype": "DT_FLOAT",
"tensor_shape": {
"dim": [
{
"size": "-1",
"name": ""
},
{
"size": "1",
"name": ""
}
],
"unknown_rank": false
},
"name": "serving_default_movieAvgRating:0"
},
... 下面内容省略 ...
根据metadata的内容,构造DeepFM模型的请求指令如下:
curl -d '{"inputs": {
"userId": [[1]],
"movieId": [[1]],
"releaseYear": [[1]],
"userAvgRating": [[1]],
"movieAvgRating": [[1]],
"userRatingCount": [[1]],
"userAvgReleaseYear": [[1]],
"userReleaseYearStddev": [[1]],
"userRatingStddev": [[1]],
"userRatedMovie1": [[1]],
"userRatedMovie2": [[1]],
"userRatedMovie3": [[1]],
"userRatedMovie4": [[1]],
"userRatedMovie5": [[1]],
"userGenre1": [[1]],
"userGenre2": [[1]],
"userGenre3": [[1]],
"userGenre4": [[1]],
"userGenre5": [[1]],
"movieGenre1": [[1]],
"movieGenre2": [[1]],
"movieGenre3": [[1]],
"movieRatingCount": [[1]],
"movieRatingStddev": [[1]]
}
}' -X POST http://127.0.0.1:8501/v1/models/model:predict
请求返回的结果如下:
{
"outputs": [
[
0.936053038
]
]
}
到此,整个模型部署到TF Serving的流程就全部完成了。如果有说得不对的地方或者遇到其他问题,欢迎在评论区讨论。