硬件:华为云服务器,最便宜的那档,反正是练习用的
操作系统:Linux 16.04
使用工具:Tensorflow-serving Docker
模型文件:Tensorflow生成的PB(ProtoBuf)格式模型参数文件
前言:官方文档仅提供了基于Linux的设置教程,Windows下没有官方教程,Youtube上有人提供了Windows版本,但是我进行设置后会返回Error 52: server no response
的错误,因此只能选择linux,如果有大佬知道这个错误的原因的话烦请不吝告知。
1.安装Docker
Docker for linux官方安装教程:https://docs.docker.com/engine/install/ubuntu/">https://docs.docker.com/engine/install/ubuntu/
2.下载Tensorflow serving Docker
# Download the TensorFlow Serving Docker image and repo
docker pull tensorflow/serving
完成以上步骤之后,由于需要进行多模型部署,因此需要创建一个所谓的Model Server config file,直译的话就是含有模型部署相关配置信息的文件,后缀为.config,在启动容器的时候使用参数–model_config_file进行设置。
3.上传模型文件
Tensorflow示例代码里是将整个serving文档存到了根目录下,所以我也在根目录下创建新的部署文档
首先使用scp
命令上传模型文件至主机
scp -r 本地模型文档路径 root@公网IP:/root/路径/版本号(01,02...)
注意:使用的是PB(ProtoBuf)文件格式,使用tf.saved_model.save
可以生成pb格式的模型文件,其他格式没有网络结构tensorflow-serving无法复现
上传的文件应当保存在被命名为版本号的文件夹内,且应当包含下图中的三个文件和文件夹
4.创建配置文件
单个模型可以直接进行设置,但是多个模型的话就需要使用配置文件,假设Tensorflow serving现在需要部署两个模型文件,一个是cnn+ctc,一个是transformer,那么用于部署的文件夹结构应该如下:
models/ # 自定义文件夹
|--cnn_xfmr/ # 自定义文件夹,用于存储所有模型文件
|--cnn_serving/ # 自定义文件夹,用于存储cnn+ctc模型
|--1/ # 自定义文件夹,命名为数字代表该模型的版本号
|--saved_model.pb # MetaGraphDef,包含图形结构,由tf.saved_model.save生成
|--variables # variables 文件夹保存训练所习得的权重,由tf.saved_model.save生成
|--assets # assets文件夹可以添加可能需要的外部文件,由tf.saved_model.save生成
|--xfmr_serving/ # 自定义文件夹,用于存储transformer模型
|--1/ # 自定义文件夹,命名为数字代表该模型的版本号
|--saved_model.pb # MetaGraphDef,包含图形结构,由tf.saved_model.save生成
|--variables # variables 文件夹保存训练所习得的权重,由tf.saved_model.save生成
|--assets # assets文件夹可以添加可能需要的外部文件,由tf.saved_model.save生成
使用nano
命令创建config文件, config文件可以存储在任意路径内,因为在后面启动容器的时候会设置config文件路径
nano model.config #创建名为model.config的模型配置文件
输入以下参数设置并使用ctrl+x
保存,name代表模型的名字(自定义),模型到版本号之前的绝对路径,model_platform代表模型生成的平台,我是使用tensorflow,所以这里要写tensorflow
5.运行tfs容器(container)
目前能找到的运行容器的方法有两种,区别在于共享主机路径设置上,Docker官方网站上说使用–mount进行设置会更灵活,不过Tensorflow官方文档上用的是第二种,所以随便选一种就好。
方法一
# 方法一
docker run -p 8501:8501 --name cnn_xfmr --mount type=bind,source=/root/models/,target=/models/ \
-t tensorflow/serving --model_config_file=/models/cnn_xfmr/models.config &
-p
: 设定映射端口,默认tensorflow serving的8500端口是对gRPC开放,8501是对REST API开放,8501:8501即(主机端口:容器端口),如果不进行设定,则都是默认端口。--name
: 容器名字,cnn_xfmr可以被替换成任意字符串,方便对后期容器进行操作。--mount
: 使用挂载模式。type
: 设置绑定的方式,共有三种,bind
,volume
,tmpfs
,只有bind
可以和主机共享文件夹并且通过主机修改,具体区别参见链接。source
: 主机需要共享的文件夹路径。target
: docker容器内共享文件夹路径,注意,不要修改target里的名称,即models。-t
: 让Docker分配一个伪终端(pseudo-tty)并绑定到容器的标准输入上,在其他应用中经常和 -i
搭配使用,后者是为了让容器的标准输入保持打开,即以互动模式运行。tensorflow/serving
:使用的镜像名。--model_config_file
: 指定configure file的路径。
&
: 用于连接多个run,这是tensorflow 官方文档用于退出docker容器运行界面并保持后台运行的方法。
另外:也可以通过将-t
修改为-t -i
启用交互式状态,从而可以使用Ctrl+P+Q
退出(但保持后台运行)或者使用-t -i -d
,-d
使得退出后保持后台运行,可以直接使用Ctrl+C
退出。如果不使用命令退出,则会一直停留在如下界面:
方法二
#方法二
docker run -t --rm -p 8501:8501 --name cnn_xfmr \
-v "/root/models/:/models/" tensorflow/serving \
--model_config_file=/models/cnn_xfmr/models.config \
--model_config_file_poll_wait_seconds=60 &
-v
:共享主机的某个文件夹,使得该文件夹下的文件自动被复制到docker容器的指定文件夹内。--rm
:在执行结束后删除该容器。-p
: 设定映射端口,8501:8501即(主机端口:容器端口)。-t
:在终端上运行,对应的还有-i,指使用交互式操作。tensorflow/serving
:使用的镜像名。--model_config_file
: 指定configure file的路径。--model_config_file_poll_wait_seconds
:指定部署服务器定时查看是否在改路径下有新的configure file。
6.容器相关信息查看方法
1.创建好容器之后可以使用docker ps -a
进行查看,可以容器使用的镜像以及开放的端口,结果如下图:
2.另外也可以使用 docker port 容器名字
查看开放的端口号,这里容器名可以是容器ID,也可以是我们之前--name
里设置的cnn_xfmr
7.错误排查方法建议
1.如果不记得模型的结构,可以使用tensorflow自带的saved_model_cli命令进行查找 (需要安装tensorflow,之后就可以在命令行内使用了):
saved_model_cli show --dir <模型路径> --all
或者仅查看signature_def下的结构(inputs和outputs的定义):
saved_model_cli show --dir <模型路径> --tag_set serve --signature_def serving_default
2.可以使用 outputs
参数调取返回信息
json_response.raise_for_status()
outputs = json_response.json()['outputs']
常见返回错误: HTTPError: 400 Client Error: Bad Request for url: http://…
大概率是输入的数据格式有错误,利用saved_model_cli检查inputs和使用post传递过去的值进行对比会很有很大帮助。
注:在python中调用requests.post
并用outputs
参数输出错误信息并不完整,如果你知道数据的结构,可以考虑使用curl
命令在主机内使用localhost
进行测试,会返回更详细的错误信息。以本文为例:
curl --header "Content-Type: application/json" --request POST --data '{"signature_name": "serving_default", "inputs": {"input_name": data_you_want_to_test}' http://localhost:8501/v1/models/cnn:predict
- 可以使用
curl http://<ip addrerss>:8501/v1/models/cnn
查看模型调用状况,官方样例显示如下:
$ curl http://localhost:8501/v1/models/saved_model_half_plus_three
{
"model_version_status": [
{
"version": "123",
"state": "AVAILABLE",
"status": {
"error_code": "OK",
"error_message": ""
}
}
]
}
8. 可能在输入过程中遇到的Error (更新 2021/03/22)
1.{ “error”: “inputs is a plain value/list, but expecting an object as multiple input tensors required XXX” }
在本地调试进行多个inputs参数输入的时候遇到了这个错误,一开始有人建议将inputs参数改为instance(来源),但是会报第二个错误↓
Input to reshape is a tensor with 100 values, but the requested shape has 10000
在查找tensorflow文档后发现是json的保存方式出了问题,由于多输入状态下数组维度不同,因此不再适用于row format而是应该使用column format
简单来说,json中的inputs的写法有两种,第一种是
{
"instances": [
{
"tag": "foo",
"signal": [1, 2, 3, 4, 5],
"sensor": [[1, 2], [3, 4]]
},
{
"tag": "bar",
"signal": [3, 4, 1, 2, 5],
"sensor": [[4, 5], [6, 8]]
}
]
}
第二种是
{
"inputs": {
"tag": ["foo", "bar"],
"signal": [[1, 2, 3, 4, 5], [3, 4, 1, 2, 5]],
"sensor": [[[1, 2], [3, 4]], [[4, 5], [6, 8]]]
}
}
第一种必须要求所有key的值的维度相同,样例中的tag,signal和sensor就全是一维的,如果维度不同,则需要使用第二种写法,和inputs或者instances无关,我用inputs本地测试通过了。