tensorflow模型的deploy有多种方法,tensorflow serving是一款面向tensorflow模型对外提供服务的web容器,部署之后只需要更新指定位置的模型文件即可实现模型的在线替换,非常适合生产环境下的运行。
网上此类教程不多,但还是能搜到一些,我参考的是这位仁兄的:
https://blog.csdn.net/u011734144/article/details/82107610?utm_source=oschina-app
一、问题的提出
安装好了,之后找了些例子跑了下,没什么问题。可是当我用自己的模型进行预测的时候,却报错了:
(1) 当预测样本为1的时候,返回为0:
客户端代码:
p_data0 = {"input_x": input_x[0], 'keep_prob': 1.0}
param = {
"signature_name":signature_key,
"instances": [p_data0]
}
返回结果:
{
"predictions": [[0.0, 0.0, 0.0]]
}
(2) 当预测样本为多个的时候,直接报错:
客户端代码:
p_data0 = {"input_x": input_x[0], 'keep_prob': 1.0}
p_data1 = {"input_x": input_x[1], 'keep_prob': 1.0}
param = {
"signature_name":signature_key,
"instances": [p_data0, p_data1]
}
返回错误:
{ "error": "Incompatible shapes: [2,1024] vs..."}
二、问题排查
1、解决单个预测结果为0的问题
在请教了@小浩之后,才知道问题原来出在batch_norm上。之前是根据公式写的代码,如果只有一个样本,那么样本等于其均值,output就为0了
详细解答可以参考:https://stackoverflow.com/questions/48320854/tensorflow-and-batch-normalization-with-batch-size-1-outputs-all-zeros?rq=1
后面改正只需要把batch内样本求mean和var的公式改为总体样本的滑动方式就可以了,预测的时候使用训练集总体滑动的mean以及var。
在线下将pb模型加载运行一遍,发现这个问题得到了解决。
2、解决批量预测出现问题
但是当我把修改代码训练后的模型加载到tensorflow serving、进行客户端访问时,却发现连单个样本的访问也无法成功了。
客户端代码:
p_data0 = {"input_x": input_x[0], 'keep_prob': 1.0, 'is_train': False}
param = {
"signature_name":signature_key,
"instances": [p_data0]
}
报错:
{ "error": "The second input must be a scalar, but it has shape [1]..."}
批量访问的错误也发生了变化
客户端代码:
p_data0 = {"input_x": input_x[0], 'keep_prob': 1.0, 'is_train': False}
p_data1 = {"input_x": input_x[1], 'keep_prob': 1.0, 'is_train': False}
param = {
"signature_name":signature_key,
"instances": [p_data0, p_data1]
}
报错:
{ "error": "The second input must be a scalar, but it has shape [2]..."}
跟上一次唯一的不同,只是增加了一个is_train的placeholder,发生这样的错误感觉很匪夷所思。
后来我在参考的那篇文章里面看到,作者之所以这样写客户端的请求代码,是参考了tensorflow/serving里面json_tensor.h这个文件,于是我也去github上面把这个文件读了一下,里面有这么一段话。
// "instances" is used to format input tensors in "row" format and "inputs"
// is used to format them in "columnar" format. The former is easy to read
// but requires all inputs to have same 0-th dimension whereas the latter
// can represent input tensors with varying sizes.
使用"instances"作为参数传入的时候要求所有的输入具有相同的0维,反之"inputs"可以输入具有不同大小的张量,说实话不太懂,什么是具有相同的0维,"instances"列表里的元素还是单个元素里面各个不同的参数?又看了下"inputs"的使用方法:
// {
// "inputs": {
// "tag": ["foo", "bar"],
// "signal": [1, 2, 3, 4, 5, 3, 4, 1, 2, 4],
// "sensor": [[1, 2], [3, 4], [4, 5], [6, 8]]
// }
// }
貌似更符合输入习惯,于是我抱着试一试的心态改了下:
p_data0 = {"input_x": input_x[0:1], 'keep_prob': 1.0, 'is_train': False}
param = {
"signature_name": signature_key,
"inputs": p_data0
}
这下没问题了!上面是单个样本的预测,多个样本的话直接"input_x": input_x[0:n]就可以了。
事实上我用到的只是predict API,RESTful API 还有classify API和regress API,它们两者的API很像,但与predict API不同。
除了RESTful API 之外tensorflow serving主打的gRPC API好像才是亮点,这个以后再继续尝试吧。
三、总结
(1) 对于tensorflow serving-docker的安装、以及基础用法参考最上面那个文章就可以了,写的还是蛮清晰的;
(2)如果进行单个模型预测(不局限于tensorflow serving)返回的结果为0,那么多半是batch norm的问题, 这类问题很常见,网上的资料已经很多了。
(3)如果在客户端请求tensorflow serving接口时,发生返回错误,基本上是参数传入的问题。
后面在这里找到一篇翻译,关于客户端请求参数的介绍,写得挺好的:
https://www.jianshu.com/p/a9dbf1e63c88?utm_source=oschina-app