小言_互联网的博客

TensorFlow Serving系列之客户端gRPC调用

589人阅读  评论(0)

0 背景

在《TensorFlow Serving系列之导出自己的训练模型》中,我们将训练的目标检测模型导成了TFS所需的格式,本文要实现的是将该模型导入到服务中,并实现客户端调用。TFS支持REST和gRPC两种形式的接口调用,比较常用且效率较高的是gRPC方式,因此,下文中的客户端是基于gRPC接口实现

系列文章目录

(一)TensorFlow Serving系列之安装及调用方法

(二)TensorFlow Serving系列之导出自己的训练模型

(三)TensorFlow Serving系列之客户端gRPC调用

1 创建容器

我们将之前生成的模型保存在detection/1文件夹下,生成如下目录结构,其中1表示版本信息,如果有新模型,依次往后叫2/3等,TFS会自动加载热更新编号最大的模型

├── detection
│   └── 1
│       ├── saved_model.pb
│       └── variables
│           ├── variables.data-00000-of-00001
│           └── variables.index

使用如下指令创建容器

# CPU版本
docker run -p 8500:8500 --mount type=bind,source=$(pwd)/detection,target=/models/detection -e MODEL_NAME=detection -t tensorflow/serving:1.12.3

# GPU版本
docker run --runtime=nvidia -p 8500:8500 --mount type=bind,source=$(pwd)/detection,target=/models/detection -e MODEL_NAME=detection -t tensorflow/serving:1.12.3-GPU

使用docker ps指令,可查看到容器是否成功创建并运行

2 客户端编写

由于要用到object detection API中的可视化接口,即将服务返回的结果画在图片上,因此,我们在object_detection程序中创建myclient_grpc.py文件,先放上完整代码(代码结构详见《TensorFlow之目标检测API接口调试(超详细)》)

from __future__ import print_function
import grpc
import requests
import tensorflow as tf
import cv2
import time
import numpy as np

from tensorflow_serving.apis import predict_pb2
from tensorflow_serving.apis import prediction_service_pb2_grpc

from utils import label_map_util
from utils import visualization_utils as vis_util

tf.app.flags.DEFINE_string('server', '****:8500',
                           'PredictionService host:port')
tf.app.flags.DEFINE_string('image', 'test.jpg', 'path to image in JPEG format')
FLAGS = tf.app.flags.FLAGS

def main(_):
  # 设置grpc
  options = [('grpc.max_send_message_length', 1000 * 1024 * 1024), 
            ('grpc.max_receive_message_length', 1000 * 1024 * 1024)]   
  channel = grpc.insecure_channel(FLAGS.server, options = options)
  stub = prediction_service_pb2_grpc.PredictionServiceStub(channel)
  request = predict_pb2.PredictRequest()
  request.model_spec.name = 'detection'
  request.model_spec.signature_name = 'serving_default'

  # 输入图片并进行请求
  img = cv2.imread(FLAGS.image)
  tensor = tf.contrib.util.make_tensor_proto(img, shape=[1]+list(img.shape))
  request.inputs['inputs'].CopyFrom(tensor)
  start = time.time()
  result = stub.Predict(request, 10.0)  # 10 secs timeout
  stop = time.time() 
  print('time is ', stop - start) 

  # 读取标签配置文件
  NUM_CLASSES = 11
  label_map = label_map_util.load_labelmap('./data/object-detection.pbtxt')
  categories = label_map_util.convert_label_map_to_categories(
    label_map, max_num_classes=NUM_CLASSES, use_display_name=True)
  category_index = label_map_util.create_category_index(categories)

  # 可视化检测结果
  boxes = result.outputs['detection_boxes'].float_val
  classes = result.outputs['detection_classes'].float_val
  scores = result.outputs['detection_scores'].float_val
  result = vis_util.visualize_boxes_and_labels_on_image_array(
      img,
      np.reshape(boxes,[300,4]),
      np.squeeze(classes).astype(np.int32),
      np.squeeze(scores),
      category_index,
      use_normalized_coordinates=True,
      line_thickness=8)       

  # 保存结果图片                    
  cv2.imwrite('result.jpg', result)

if __name__ == '__main__':
  tf.app.run()

有几处需要注意:

1、设置grpc中,增加grpc.max_send_message_length和grpc.max_receive_message_length设置,如果不加该设置,会报错如下

grpc._channel._Rendezvous: <_Rendezvous of RPC that terminated with:
        status = StatusCode.RESOURCE_EXHAUSTED
        details = "Sent message larger than max (6220875 vs. 1048576)"
        debug_error_string = "{"created":"@1569462805.550691433","description":"Sent message larger than max (6220875 vs. 1048576)","file":"src/core/ext/filters/message_size/message_size_filter.cc","file_line":202,"grpc_status":8}"

2、signature_name设置方法

signature_name要与模型中的定义保持一致,使用如下指令可查看模型情况,

saved_model_cli show --dir='./detection/1' --all

返回结果如下

MetaGraphDef with tag-set: 'serve' contains the following SignatureDefs:

signature_def['serving_default']:
  The given SavedModel SignatureDef contains the following input(s):
    inputs['inputs'] tensor_info:
        dtype: DT_UINT8
        shape: (-1, -1, -1, 3)
        name: image_tensor:0
  The given SavedModel SignatureDef contains the following output(s):
    outputs['detection_boxes'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 300, 4)
        name: detection_boxes:0
    outputs['detection_classes'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 300)
        name: detection_classes:0
    outputs['detection_features'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, -1, -1, -1, -1)
        name: detection_features:0
    outputs['detection_multiclass_scores'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 300, 13)
        name: detection_multiclass_scores:0
    outputs['detection_scores'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 300)
        name: detection_scores:0
    outputs['num_detections'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1)
        name: num_detections:0
    outputs['raw_detection_boxes'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 300, 4)
        name: raw_detection_boxes:0
    outputs['raw_detection_scores'] tensor_info:
        dtype: DT_FLOAT
        shape: (-1, 300, 13)
        name: raw_detection_scores:0
  Method name is: tensorflow/serving/predict

其中就包含signature_def,以及输入和输出的详细信息,输入输出的shape中-1表示大小不确定,实际使用时会变化

修改完毕后,运行客户端程序,即可生成并保存结果图片,至此,完成一次成功的客户端调用

3 创建自己的镜像

我们从镜像仓库中拉下TFS的镜像后,做了一些自己的配置,比如导入自己的模型、配置安装其它软件,为了能快速使用和迁移,可以将修改完的容器打包成镜像,下一次启动时,直接基于自己的镜像来创建容器即可,免去重复的工作

首先使用docker ps指令来查看我们运行的容器

CONTAINER ID        IMAGE                           COMMAND                  CREATED             STATUS              PORTS                              NAMES
80d16401b050        tensorflow/serving:1.12.3-gpu   "/usr/bin/tf_serving…"   19 hours ago        Up 17 hours         0.0.0.0:8500->8500/tcp, 8501/tcp   vigilant_shockley

然后使用docker commit指令来得到一个新的镜像,其命令格式为

docker commit [OPTIONS] CONTAINER [REPOSITORY[:TAG]]

主要参数选项是

  • -a ,–author=”” 作者信息
  • -m,–message=”” 提交信息
  • -p,–pause=true 提交是暂停容器运行
docker commit --change "ENV MODEL_NAME detection" 80d16401b050 myimg

上面我们把有了模型目录的镜像commit得到新的名为myimg的镜像,同时用–change设置环境变量 MODEL_NAME为detection,这样启动的时候就不需要设置环境变量了,运行后返回镜像ID,然后就可以基于自己的镜像创建容器,例如

docker run --runtime=nvidia -p 8500:8500 --mount type=bind,source=$(pwd)/detection,target=/models/detection -t myimg

如果想要将镜像上传到镜像仓库,包括官方或自己的私有镜像仓库,则使用docker push指令,指令格式

docker push NAME[:TAG]

默认上传需要登录,更详细的内容可搜索docker使用教程


转载:https://blog.csdn.net/zong596568821xp/article/details/101421337
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场