![6a8557918185803b08cc9683036918f8.png](https://i-blog.csdnimg.cn/blog_migrate/ea183e3f6bb9a1fbf23405e62d81e014.jpeg)
Tensorflow 2.0.0出来后,1.x版本的API有些已经改变,19年年初写的这一篇《TensorFlow Serving + Docker + Tornado机器学习模型生产级快速部署》 文章,在tf 2.0.0版本里面有较大的变动,另外Tensorflow官方也推荐大家使用tf.keras,因此本文将会教大家如何使用tensorflow serving部署keras模型,适用tensorflow 2.0.0以后的版本。注:下面“tensorflow serving”将会简写为“tfs”。
导出Keras模型
keras模型训练完毕后,一般我们都会使用model.save(filepath)储存为h5文件,包含模型的结构和参数,而我们需要把这个h5文件导出为tensorflow serving所需要的模型格式:
from
注意,这里要使用tf.keras.models.load_model来导入模型,不能使用keras.models.load_model,只有tf.keras.models.load_model能导出成tfs所需的模型文件。导出的文件路径结构如下:
.
└── 0
├── assets
├── saved_model.pb
└── variables
├── variables.data-00000-of-00002
├── variables.data-00001-of-00002
└── variables.index
最大的改变是,以往导出keras模型需要写一大段定义builder的代码,如这篇文章《keras、tensorflow serving踩坑记》 的那样,现在只需使用简单的model.save就可以导出了。当然以前这种写法应该还能继续使用,能自定义signature、input、output的名称,但为了简单起见(用keras不就是为了简单嘛),直接一键导出更舒服。
导出以后,我们在终端执行以下命令,查看模型的signature、input、output的名称,后面要用到:
saved_model_cli show --dir 0/ --all
# 将会输出:
MetaGraphDef with tag-set: 'serve' contains the following SignatureDefs:
signature_def['__saved_model_init_op']:
The given SavedModel SignatureDef contains the following input(s):
The given SavedModel SignatureDef contains the following output(s):
outputs['__saved_model_init_op'] tensor_info:
dtype: DT_INVALID
shape: unknown_rank
name: NoOp
Method name is:
signature_def['serving_default']:
The given SavedModel SignatureDef contains the following input(s):
inputs['input_1'] tensor_info:
dtype: DT_FLOAT
shape: (-1, 224, 224, 3)
name: serving_default_input_1:0
The given SavedModel SignatureDef contains the following output(s):
outputs['fc2'] tensor_info:
dtype: DT_FLOAT
shape: (-1, 4)
name: StatefulPartitionedCall:0
Method name is: tensorflow/serving/predict
可以看到,signature name为“serving_default”,input name为“input_1”,output name为“fc2”。记下来,一会用到。
Docker部署模型
一律使用docker来部署你的模型,如果还不知道docker是什么,不知道怎么用docker来拉取tfs的镜像,请查阅我之前的文章:
Justin ho:TensorFlow Serving + Docker + Tornado机器学习模型生产级快速部署zhuanlan.zhihu.com![526245c2bceb6de25925da3e34464ee0.png](https://i-blog.csdnimg.cn/blog_migrate/d382b42336ceef26433cd00dfed34e89.png)
这里默认你已经会拉取tfs模型到本地,现在执行以下容器启动命令:
sudo nvidia-docker run -p 8500:8500
-v /home/projects/resnet/weights/:/models
--name resnet50
-itd --entrypoint=tensorflow_model_server tensorflow/serving:2.0.0-gpu
--port=8500 --per_process_gpu_memory_fraction=0.5
--enable_batching=true --model_name=resnet --model_base_path=/models/season &
这里涉及的参数意义一律看上面那篇文章,这里不解释了。
请求客户端
模型部署起来后,我们要写一个grpc客户端来请求模型,代码参考:
from tensorflow_serving.apis import predict_pb2
from tensorflow_serving.apis import prediction_service_pb2_grpc
import grpc
def request_server(img_resized, server_url):
'''
用于向TensorFlow Serving服务请求推理结果的函数。
:param img_resized: 经过预处理的待推理图片数组,numpy array,shape:(h, w, 3)
:param server_url: TensorFlow Serving的地址加端口,str,如:'0.0.0.0:8500'
:return: 模型返回的结果数组,numpy array
'''
# Request.
channel = grpc.insecure_channel(server_url)
stub = prediction_service_pb2_grpc.PredictionServiceStub(channel)
request = predict_pb2.PredictRequest()
request.model_spec.name = "resnet" # 模型名称,启动容器命令的model_name参数
request.model_spec.signature_name = "serving_default" # 签名名称,刚才叫你记下来的
# "input_1"是你导出模型时设置的输入名称,刚才叫你记下来的
request.inputs["input_1"].CopyFrom(
tf.make_tensor_proto(img_resized, shape=[1, ] + list(img_resized.shape)))
response = stub.Predict(request, 5.0) # 5 secs timeout
return np.asarray(response.outputs["fc2"].float_val) # fc2为输出名称,刚才叫你记下来的
tensorflow 2.0.0请使用以上这段代码,之前那篇tfs的部署文章里面的api已经变了。
我们测试一下,读取一张图片,发送请求到tfs:
from PIL import Image
import numpy as np
imgpath = '20171101110450_48901.jpg'
x = Image.open(imgpath)
x = np.array(x).astype('float32')
x = (x - 128.) / 128.
# grpc地址及端口,为你容器所在机器的ip + 容器启动命令里面设置的port
server_url = '0.0.0.0:8500'
request_server(x, server_url)
我们将会得到(我这里的resnet只输出4类多标签结果):
array([0.58116215, 0.04240507, 0.74790353, 0.1388033 ])
完。
宣告:我的B站主页已经悄悄地营业了,来看看我的往期视频:
【铁人贾斯廷】真·AI工程师评测三款steam AI/编程向游戏 《While true learn》、《battle bot》、《hacknet》_哔哩哔哩 (゜-゜)つロ 干杯~-bilibiliwww.bilibili.com![86ea2ea159c7a825b3b1a3ab26e0c400.png](https://i-blog.csdnimg.cn/blog_migrate/77d0d8ef3d7b82aba1a58ad12d3ed952.jpeg)
![fe02d32db2d4af9d120f918af4a49de0.png](https://i-blog.csdnimg.cn/blog_migrate/f06adc0a8dc6a294391abeb6dfd19ed4.jpeg)
![951ac7a8703ec26f44a5c7e56b64e743.png](https://i-blog.csdnimg.cn/blog_migrate/5c2405d687e0efcdc59ca32f1c8de27f.jpeg)
![5b6a2724f625bef8a60161945268ac15.png](https://i-blog.csdnimg.cn/blog_migrate/a34238258cac1ca036d701619ced7629.jpeg)
本文已收录于本人的个人网站,欢迎浏览收藏:铁人贾斯廷的基地 | 技术宅的碎碎念