【记录】记录第一次使用tensorflow serving全过程

文章目录

          • 1. 使用Docker安装TF serving
            • 1.1 安装Docker
            • 1.2 安装TF Serving
          • 2. 使用自己训练的模型
            • 2.1 ckpt 2 pb
            • 2.2 pb 2 saved_model
          • 3. 向该docker TF Serving 传递参数,进行预测

在完成模型的训练之后,要将其部署到公司的CPU服务器上,为解决这一问题,谷歌发布了TensorFlow (TF) Serving,希望能解决ML模型部署到生产的一系列问题。

本文不对tensorflow serving进行介绍,仅仅是记录自己使用tf serving的步骤。因为是第一次使用TF Serving,所以如果文中有错误之处,烦请您指出。

1. 使用Docker安装TF serving

首先,说明我的本机环境:

ubuntu16.04
anaconda2 python2.7

开始使用TensorFlow Serving的最简单方法之一是使用Docker , 可以参考tensorflow的官方文档。

1.1 安装Docker

在ubuntu的终端中,运行以下命令来安装docker

$ sudo apt-get update
$ sudo apt-get install apt-transport-https ca-certificates curl software-properties-common
$ curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
$ sudo add-apt-repository “deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable”
$ sudo apt-get update
$ sudo apt-get install docker-ce

为了避免每次运行docker命令都加上sudo,我们在终端下运行如下命令:

$ sudo groupadd docker
$ sudo usermod -aG docker $USER

接着可以试着输入docker --help来查看是否安装成功

1.2 安装TF Serving

使用以下命令,就可以安装.

首先下载TensorFlow Serving 镜像以及github代码

$ docker pull tensorflow/serving
$ git clone https://github.com/tensorflow/serving

然后启动TensorFlow Servin容器(使用REST API端口)

$ TESTDATA="$(pwd)/serving/tensorflow_serving/servables/tensorflow/testdata"
$ docker run -t --rm -p 8501:8501
-v “$TESTDATA/saved_model_half_plus_two_cpu:/models/half_plus_two”
-e MODEL_NAME=half_plus_two
tensorflow/serving &

这里解释一下各个参数的意义:

8501: 对应的是REST API端口
-v 后面的一串 冒号之前是你的模型所在绝对地址(这里是tensorflow serving自己带的模型),冒号之后,/models/是固定的, half_plus_two这个名字可以随便起
-e MODEL_NAME= 后面的名字就是/models/后面你起的名字
tensorflow/serving 使用的镜像
& 表示后台运行

再打开一个新的终端,在新终端中输入以下命令:

curl -d ‘{“instances”: [1.0, 2.0, 5.0]}’
-X POST http://localhost:8501/v1/models/half_plus_two:predict

如果有 Returns => { “predictions”: [2.5, 3.0, 4.5] } 出现,那么就TF Serving 的安装就已经成功了。

2. 使用自己训练的模型

TensorFlow Serving使用的模型形式是下面这个图一样的:
【记录】记录第一次使用tensorflow serving全过程_第1张图片
也就是说,你的模型由两部分组成,一个pb文件,一个variables文件夹,这两部分放在一个编了号的文件夹下面,这个号码表示模型的version。
如果你的模型保存的不是这种形式,也先不用着急,可以将其转换成这种形式就行了。

TensorFlow有三种模型的形式,一种是ckpt形式,一种是pb形式,还有一种是saved_model形式,上图就是saved_model形式。

我的模型是ckpt形式的,如下图所示,我的模型转换路线是从ckpt转换为pb,再转换为saved_model形式。
【记录】记录第一次使用tensorflow serving全过程_第2张图片

2.1 ckpt 2 pb

本小节参考:ckpt模型使用的是该github代码提供的CTPN的模型。模型转换代码参考该github下328号issue“生成pb文件”。

首先使用show_ckpt()函数获取ckpt节点名称和参数,然后使用ckpt_to_pb()将ckpt模型转换为pb模型。

import os
import tensorflow as tf
from tensorflow.python import pywrap_tensorflow
from tensorflow.python.framework import graph_util


def show_ckpt():
    # 获取ckpt节点名称,参数
    checkpoint_path = '../models/checkpoints_mlt/ctpn_50000.ckpt'
    checkpoint_path = os.path.join(checkpoint_path)
    # Read data from checkpoint file
    reader = pywrap_tensorflow.NewCheckpointReader(checkpoint_path)
    var_to_shape_map = reader.get_variable_to_shape_map()
    # Print tensor name and values
    for key in var_to_shape_map:
        print("tensor_name: ", key)
        # print(reader.get_tensor(key))


def ckpt_to_pb():
    checkpoint_path = '../models/checkpoints_mlt/ctpn_50000.ckpt'
    output_graph = './model_pb/ctpn.pb'
    output_node_names = 'model_0/bbox_pred/Reshape_1,model_0/cls_prob' # 两个输出节点
    with tf.Session(config=tf.ConfigProto(allow_soft_placement=True, log_device_placement=True)) as sess:
        saver = tf.train.import_meta_graph(checkpoint_path + '.meta', clear_devices=True)
        saver.restore(sess, checkpoint_path)
        graph = tf.get_default_graph()
        input_graph_def = graph.as_graph_def()
        output_graph_def = graph_util.convert_variables_to_constants(
            sess=sess,
            input_graph_def=input_graph_def,
            output_node_names=output_node_names.split(",")
        )
        with tf.gfile.GFile(output_graph, 'wb') as fw:
            fw.write(output_graph_def.SerializeToString())
        print('{} ops in the final graph.'.format(len(output_graph_def.node)))


if __name__ == '__main__':
    show_ckpt()
    ckpt_to_pb()

2.2 pb 2 saved_model

这部分代码参考的是:https://zhuanlan.zhihu.com/p/103131661。

import tensorflow.compat.v1 as tf
tf.disable_v2_behavior()
from tensorflow.python.saved_model import signature_constants
from tensorflow.python.saved_model import tag_constants


export_dir = './model_pb/saved_model/0000003'
graph_pb = './model_pb/ctpn.pb'

builder = tf.saved_model.builder.SavedModelBuilder(export_dir)

with tf.gfile.GFile(graph_pb, "rb") as f:
    graph_def = tf.GraphDef()
    graph_def.ParseFromString(f.read())

sigs = {}

with tf.Session(graph=tf.Graph()) as sess:
    # name="" is important to ensure we don't get spurious prefixing
    tf.import_graph_def(graph_def, name="")
    g = tf.get_default_graph()

    inp = g.get_tensor_by_name("input_image:0")  # 输入节点名字
    input_im_info = tf.placeholder(tf.float32, shape=[None, 3], name='input_im_info')  # 输入节点名字
	
	# 两个输出结点
    output_cls_prob = sess.graph.get_tensor_by_name('model_0/cls_prob:0')
    output_box_pred = sess.graph.get_tensor_by_name('model_0/bbox_pred/Reshape_1:0')

    # out = [output_cls_prob, output_box_pred]

    sigs[signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY] = \
        tf.saved_model.signature_def_utils.predict_signature_def(
            inputs={"in": inp, 'info': input_im_info},
            outputs={"out_cls": output_cls_prob, 'out_box': output_box_pred})

    builder.add_meta_graph_and_variables(sess,
                                         [tag_constants.SERVING],
                                         signature_def_map=sigs)

builder.save()
print('Done.')

当执行完这部分代码后,就会生成如下的模型,但是variables文件夹为空,因为从pb文件转换过来的时候全是常量没有变量。这没有关系,不影响使用。
【记录】记录第一次使用tensorflow serving全过程_第3张图片
此时,我们来试着启动docker TF Serving,看一下效果。在终端中输入下面的命令:

docker run -t --rm -p 8501:8501 -v /your_path/model_pb/saved_model:/models/ctpn_test_model -e MODEL_NAME=ctpn_test_model tensorflow/serving

上述命令需要注意:①当saved_model下有多个版本的模型时,它会自动选择最大版本号的模型。②模型的存放路径需要是绝对路径。③saved_model文件夹下面一定要有版本号文件夹,模型放在版本号文件夹中,否则会报错。

终端中执行结果为:
【记录】记录第一次使用tensorflow serving全过程_第4张图片
此时可以在浏览器地址栏输入: http://localhost:8501/v1/models/ctpn_test_model,结果显示为:
【记录】记录第一次使用tensorflow serving全过程_第5张图片
输入http://localhost:8501/v1/models/ctpn_test_model/metadata,可以来检查自己的模型:
【记录】记录第一次使用tensorflow serving全过程_第6张图片

3. 向该docker TF Serving 传递参数,进行预测

本小节代码参考的链接找不到了,如果您是原创,请联系我,我将添加引用。
我使用的模型是CTPN模型,从上面的代码中也可以了解到,模型接受两个输入"input_image" 和 ‘input_im_info’,产生两个输出:‘model_0/cls_prob’ 和 ‘model_0/bbox_pred/Reshape_1’。

import cv2
import json
import numpy as np
import requests


def resize_image(img):
    img_size = img.shape
    im_size_min = np.min(img_size[0:2])
    im_size_max = np.max(img_size[0:2])

    im_scale = float(600) * 1.0 / float(im_size_min)
    if np.round(im_scale * im_size_max) > 1000:
        im_scale = float(1000) * 1.0 / float(im_size_max)
    new_h = int(img_size[0] * im_scale)
    new_w = int(img_size[1] * im_scale)

    new_h = new_h if new_h // 16 == 0 else (new_h // 16 + 1) * 16
    new_w = new_w if new_w // 16 == 0 else (new_w // 16 + 1) * 16

    re_im = cv2.resize(img, (new_w, new_h), interpolation=cv2.INTER_LINEAR)
    return re_im, (new_h * 1.0 / img_size[0], new_w * 1.0 / img_size[1])

# 得到输入数据
file_path = '../main/data/demo/business_license_mask.jpeg'
im = cv2.imread(file_path)[:, :, ::-1]
img, (rh, rw) = resize_image(im)
img = np.expand_dims(img, axis=0)
print('img.shape:', img.shape)

img = img.astype('float16')
n, h, w, c = img.shape

im_info = np.array([h, w, c]).reshape([1, 3])

payload = {
    "inputs": {'info': im_info.tolist(), 'in': img.tolist()}
}

# sending post request to TensorFlow Serving server
r = requests.post('http://localhost:8501/v1/models/ctpn_test_model:predict', json=payload)
pred = json.loads(r.content.decode('utf-8'))

print(type(pred))
print(pred.keys())

jsObj = json.dumps(pred)

fileObject = open('./pred.json', 'w')
fileObject.write(jsObj)
fileObject.close()

运行该代码,可以看到第一次运行的结果是error,你可以去./pred.json中查看具体的错误原因,最后一次运行成功,结果放在./pred.json中,可以自己查看。
【记录】记录第一次使用tensorflow serving全过程_第7张图片
至此,第一次使用tensorflow serving的全部过程已结束。

你可能感兴趣的:(tensorflow,tensorflow)