本文不对tensorflow serving进行介绍,仅仅是记录自己使用tf serving的步骤。因为是第一次使用TF Serving,所以如果文中有错误之处,烦请您指出。
首先,说明我的本机环境:
ubuntu16.04 anaconda2 python2.7
开始使用TensorFlow Serving的最简单方法之一是使用Docker , 可以参考tensorflow的官方文档。
在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来查看是否安装成功
使用以下命令,就可以安装.
首先下载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 的安装就已经成功了。
TensorFlow Serving使用的模型形式是下面这个图一样的: 也就是说,你的模型由两部分组成,一个pb文件,一个variables文件夹,这两部分放在一个编了号的文件夹下面,这个号码表示模型的version。 如果你的模型保存的不是这种形式,也先不用着急,可以将其转换成这种形式就行了。
TensorFlow有三种模型的形式,一种是ckpt形式,一种是pb形式,还有一种是saved_model形式,上图就是saved_model形式。
我的模型是ckpt形式的,如下图所示,我的模型转换路线是从ckpt转换为pb,再转换为saved_model形式。
本小节参考: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()这部分代码参考的是: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文件转换过来的时候全是常量没有变量。这没有关系,不影响使用。 此时,我们来试着启动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文件夹下面一定要有版本号文件夹,模型放在版本号文件夹中,否则会报错。
终端中执行结果为: 此时可以在浏览器地址栏输入: http://localhost:8501/v1/models/ctpn_test_model,结果显示为: 输入http://localhost:8501/v1/models/ctpn_test_model/metadata,可以来检查自己的模型:
本小节代码参考的链接找不到了,如果您是原创,请联系我,我将添加引用。 我使用的模型是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的全部过程已结束。
