tensorflow serving (TFS) docker 环境搭建

介绍 

tensorflow serving 可以将训练好的模型直接上线并提供服务

tensorflow_model_server 是统一管理一个模型服务器,利于让他人使用这个模型,而且可以动态更新模型,模型也会常住在内存里面,加快结果输出,减少模型加载时间。

tensorFlow_model_server 用于发现新导出的模型,并启动 gRPC、HTTP 用于提供模型服务。

tensorflow-serving-api 客户端

Serving 镜像提供了两种调用方式

    gRPC和HTTP请求

    gRPC默认端口是8500

    HTTP请求的默认端口是8501

    serving镜像中的程序会自动加载镜像内/models下的模型

    通过MODEL_NAME指定/models下的哪个模型


环境搭建 

一、搭建tensorflow docker环境

这一步需要先有docker环境,具体操作请百度,这里就不说了 提供个地址

docker 相关命令和版本搜索
docker 镜像搜索(版本): https://hub.docker.com
docker pull xxx       拉取镜像
docker images         查看镜像
docker ps             查看实例
docker kill 实例ID     杀死实例
docker inspect 实例ID  查看实例详情

docker -e设置环境变量 
docker -p设置端口映射 
docker -v设置磁盘映射 (宿主机 path:docker path)
docker --mount磁盘映射 
docker run -t 表示是否允许伪TTY 
docker run --rm表示如果实例已经存在,则先remove掉该实例再重新启动新实例

拉取tensorflow/serving镜像,可指定版本,我这里使用1.9.0,实际上客户端应该也是和这个对应的,

docker pull tensorflow/serving or docker pull tensorflow/serving:1.9.0

将tensorflow/serving在github上clone下来,做运行测试

git clone https://github.com/tensorflow/serving

运行tensorflow/serving镜像 

设置模型路径,这个模型就是上面clone下来的
MODELDATA="/serving/tensorflow_serving/servables/tensorflow/testdata/saved_model_half_plus_two_cpu"

启动方式一
docker run -t --rm -p 8501:8501 -v "$MODELDATA:/models/half_plus_two" -e MODEL_NAME=half_plus_two tensorflow/serving:1.9.0 &


启动方式二 
docker run -p 8501:8501 --mount type=bind,source=/tfserving/serving/tensorflow_serving/servables/tensorflow/testdata/saved_model_half_plus_two_cpu,target=/models/half_plus_two -e MODEL_NAME=half_plus_two -t tensorflow/serving &

两种运行方式区别在于路径,方式一是映射,方式二是挂载
这里只是加载了一个模型,多个模型还没研究怎么弄
docker具体命令参数参考上面的说明

运行示例-验证并使用这个预测接口
    input: curl -d '{"instances": [1.0, 2.0, 5.0]}' -X POST http://localhost:8501/v1/models/half_plus_two:predict

    output: {
                "predictions": [2.5, 3.0, 4.5]
            }
输出上面的结果表示成功了

说一下上面启动映射的路径,-v 宿主机模型路径:/models/容器里映射的路径 其中/models 这个目录好像是写死的?

二、模型转换和启动自己的模型ckpt 转换 pb

ckpt默认是有四个文件,tfs好像不能直接使用。

ckpt四个文件 checkpoint、xxxxx.ckpt.data-00000-of-00001、xxxxx.ckpt.index、xxxxx.ckpt.meta

pb目录结构

   models

              version(版本,可能有多个版本)

                         variables

                                       variables.data-00000-of-00001

                                       variables.index

                          saved_model.pb

开始模型转换

# coding: utf-8
"""

模型转换 ckpt > pb

"""
import os

import tensorflow as tf

from nets import model #自己的模型网络

tf.app.flags.DEFINE_boolean('debug', False, '')


def convert():
    #保存转换好的模型目录
    savedModelDir = "./model_name"
    #每次转换都生成一个版本目录
    for i in range(100000, 9999999):
        cur = os.path.join(savedModelDir, str(i))
        if not tf.gfile.Exists(cur):
            savedModelDir = cur
            break
    #原ckpt模型
    savePath = "../../model/ctpn-2019-08-14-15-14-05-2.ckpt"
    #输入张量
    ph_input_image = tf.placeholder(tf.float32, shape=[None, None, None, 3], name='ph_input_image')
    _, classes = model.model(ph_input_image)
    print(classes)
    session = tf.Session()
    session.run(tf.global_variables_initializer())
    saver = tf.train.Saver()
    saver.restore(sess=session, save_path=savePath)

    # 保存转换训练好的模型
    builder = tf.saved_model.builder.SavedModelBuilder(savedModelDir)
    inputs = {
        # ph_input_image 就是模型里面定义的输入placeholder
        "input": tf.saved_model.utils.build_tensor_info(ph_input_image)
    }
    # model > classes 是模型的输出, 预测的时候就是这个输出
    output = {"output": tf.saved_model.utils.build_tensor_info(classes)}
    prediction_signature = tf.saved_model.signature_def_utils.build_signature_def(
        inputs=inputs,
        outputs=output,
        method_name=tf.saved_model.signature_constants.PREDICT_METHOD_NAME
    )
    builder.add_meta_graph_and_variables(
        session,
        [tf.saved_model.tag_constants.SERVING],
        {tf.saved_model.signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY: prediction_signature}
    )
    builder.save()


if __name__ == '__main__':
    convert()

使用docker启动自己的模型镜像

MODELDATA="/demo/model_name"
docker run -t --rm -p 8501:8501 -p 8500:8500 -v "$MODELDATA:/models/rotate" -e MODEL_NAME=rotate tensorflow/serving:1.9.0 &

这里的目录映射就是把刚转换好的模型根目录映射到容器里模型的目录,容器里的目录名应该就是你自己项目名,或随便写,但是这个名字在后面客户端调用时使用到。
注意这里指定了两个端口映射 -p 8501:8501 -p 8500:8500 分别是HTTP 和 gRPC服务端口,因为客户端里使用的是gRPC

客户端调用 需要pip install tensorflow-serving-api==1.9.0

# -*- coding: utf-8 -*-
"""
多模型部署、转换、客户端请求:参考1 https://www.jianshu.com/p/d11a5c3dc757
参考2 https://blog.csdn.net/shin627077/article/details/78592729
"""

import cv2
import tensorflow as tf
from grpc.beta import implementations
from tensorflow_serving.apis import predict_pb2
from tensorflow_serving.apis import prediction_service_pb2
from utils import data_util
import os

#建立连接 连接到服务端
channel = implementations.insecure_channel("127.0.0.1", 8500)
stub = prediction_service_pb2.beta_create_PredictionService_stub(channel)
#预测请求
request = predict_pb2.PredictRequest()

# 指定启动tensorflow serving时配置的model_name,是容器里映射目录的模型路径名 /models/rotate
request.model_spec.name = "rotate"
request.model_spec.signature_name = "serving_default"
#这是我要验证图片的目录
dir = "../../data/validate"
lstdir = os.listdir(dir)
for fn in lstdir:
    # 进行预处理,得到list格式的输入数据,然后将其转为tensor后序列化
    fname = os.path.join(dir, fn)
    print(fname)
    img = cv2.imread(fname)
    input = data_util.prepare4vgg([img])
    #这个input就是模型转换时那个输入
    request.inputs["input"].ParseFromString(
        tf.contrib.util.make_tensor_proto(input, dtype=tf.float32).SerializeToString())
    #发送预测请求
    response = stub.Predict(request, 10.0)
    print(response)
    results = {}
    for key in response.outputs:
        tensor_proto = response.outputs[key]
        results[key] = tf.contrib.util.make_ndarray(tensor_proto)
    #不同的网络返回的结果也不一样,这里是你自己的结果
    print(results)
    print("")

输出结果

../../data/validate/ocr_o_fB9j2w391563251537990_hqZqvwQD1563251950868_3798571663843989382.jpg
outputs {
  key: "output"
  value {
    dtype: DT_INT64
    tensor_shape {
      dim {
        size: 1
      }
    }
    int64_val: 2
  }
}
model_spec {
  name: "rotate"
  version {
    value: 100001
  }
  signature_name: "serving_default"
}

{'output': array([2])}

../../data/validate/ocr_o_YbgmOIti1563264729515_vmCKrvJV1563264821019_7369658828897476337.JPG
outputs {
  key: "output"
  value {
    dtype: DT_INT64
    tensor_shape {
      dim {
        size: 1
      }
    }
    int64_val: 3
  }
}
model_spec {
  name: "rotate"
  version {
    value: 100001
  }
  signature_name: "serving_default"
}

{'output': array([3])}

结束。。

因为我还是个初学者,还有挺多地方不怎么明白,有问题可以留言或加QQ 122798224一起交流

你可能感兴趣的:(深度学习,AI,tensorflow)