训练好的模型在使用过程中有多种场景。TensorFlow中提供了一种TF_Serving接口,可以将带有签名的模型部署在远端服务器上,并以服务的方式对外提供借口。之前学习过saved_model模块的用法(详情请点击这里),简单点说saved_model模块就是为了实现TensorFlow Serving(以下简称TF_Serving)功能的。
gRPC服务、HTTP/REST API是TF_Serving模块对外支持服务的两种通信技术。通过这两种通信技术,可以远程使用TensorFlow模型。
本文使用的线性回归模型(y≈2x)代码如下:
#使用静态图训练一个具有检查点的回归模型
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt
#(1)生成模拟数据
train_X = np.linspace(-1, 1, 100)
train_Y = 2 * train_X + np.random.randn(*train_X.shape) * 0.3 # y=2x,但是加入了噪声
#图形显示
plt.plot(train_X, train_Y, 'ro', label='Original data')
plt.legend()
plt.show()
tf.reset_default_graph()
#(2)建立网络模型
# 创建模型
# 占位符
X = tf.placeholder("float")
Y = tf.placeholder("float")
# 模型参数
W = tf.Variable(tf.random_normal([1]), name="weight")
b = tf.Variable(tf.zeros([1]), name="bias")
# 前向结构
z = tf.multiply(X, W)+ b
global_step = tf.Variable(0, name='global_step', trainable=False)
#反向优化
cost =tf.reduce_mean( tf.square(Y - z))
learning_rate = 0.01
optimizer = tf.train.GradientDescentOptimizer(learning_rate).minimize(cost,global_step) #梯度下降
# 初始化所有变量
init = tf.global_variables_initializer()
# 定义学习参数
training_epochs = 28
display_step = 2
savedir = "log/"
saver = tf.train.Saver(tf.global_variables(), max_to_keep=1)#生成saver。 max_to_keep=1,表明最多只保存一个检查点文件
#定义生成loss可视化的函数
plotdata = { "batchsize":[], "loss":[] }
def moving_average(a, w=10):
if len(a) < w:
return a[:]
return [val if idx < w else sum(a[(idx-w):idx])/w for idx, val in enumerate(a)]
#(3)建立session进行训练
with tf.Session() as sess:
sess.run(init)
kpt = tf.train.latest_checkpoint(savedir)
if kpt!=None:
saver.restore(sess, kpt)
# 向模型输入数据
while global_step.eval()/len(train_X) < training_epochs:
step = int( global_step.eval()/len(train_X) ) # step为epoch数
for (x, y) in zip(train_X, train_Y): # 每跑一次x,y,global_step就自动加一
sess.run(optimizer, feed_dict={X: x, Y: y})
#显示训练中的详细信息
if step % display_step == 0:
loss = sess.run(cost, feed_dict={X: train_X, Y:train_Y})
print ("Epoch:", step+1, "cost=", loss,"W=", sess.run(W), "b=", sess.run(b))
if not (loss == "NA" ):
plotdata["batchsize"].append(global_step.eval())
plotdata["loss"].append(loss)
saver.save(sess, savedir+"linermodel.cpkt", global_step)
print (" Finished!")
saver.save(sess, savedir+"linermodel.cpkt", global_step)
print ("cost=", sess.run(cost, feed_dict={X: train_X, Y: train_Y}), "W=", sess.run(W), "b=", sess.run(b))
#显示模型
plt.plot(train_X, train_Y, 'ro', label='Original data')
plt.plot(train_X, sess.run(W) * train_X + sess.run(b), label='Fitted line')
plt.legend()
plt.show()
plotdata["avgloss"] = moving_average(plotdata["loss"])
plt.figure(1)
plt.subplot(211)
plt.plot(plotdata["batchsize"], plotdata["avgloss"], 'b--')
plt.xlabel('Minibatch number')
plt.ylabel('Loss')
plt.title('Minibatch run vs. Training loss')
plt.show()
这个步骤会在log文件夹下面生成4个文件
输入如下命令,向“apt-get”添加TF_Serving安装包的下载地址:
echo "deb [arch=amd64] http://storage.googleapis.com/tensorflow-serving-apt stable tensorflow-model-server tensorflow-model-server-universal" | sudo tee /etc/apt/sources.list.d/tensorflow-serving.list
curl https://storage.googleapis.com/tensorflow-serving-apt/tensorflow-serving.release.pub.gpg | sudo apt-key add -
更新apt-get
sudo apt-get update
下载tensorflow-model-server
sudo apt-get install tensorflow-model-server
提示:
默认的tensorflow-model-server版本需要安装在支持SSE4和AVX指令集的服务器上。如果本地的机器过于老旧不支持该指令集,需要安装tensorflow-model-server-universal版本。具体命令为:
sudo apt-get install tensorflow-model-server-universal
如果已经安装了tensorflow-model-server,则需要先卸载tensorflow-model-server后才能再安装tensorflow-model-server-universal。卸载tensorflow-model-server命令为:
sudo apt-get remove tensorflow-model-server
为了让生成的模型支持TF_Serving服务,在TensorFlow中对模型的签名做了统一的规定。在签名中规定,模型在处理分类、预测、回归这三种任务时,必须使用对应的输入与输出签名。具体的签名定义在/usr/local/lib/python3.6/dist-packages/tensorflow/saved_model/signature_constants模块下。见下图
提示
上图中,CLASSIFY_METHOD_NAME、PREDICT_METHOD_NAME和REGRESS_METHOD_NAME这3个签名是必需的,且只能有这3中签名。如果使用其它签名就会报错。除了上述三种签名外,其它的签名的是可选的,但要求服务器端必需与客户端严格匹配。在没有特殊要求情况下,建议使用规定的签名,以避免客户端与服务器端签名名称不匹配的情况发生。
为上面训练好的模型添加签名(PREDICT_METHOD_NAME)
.....
from tensorflow.python.saved_model import tag_constants
builder = tf.saved_model.builder.SavedModelBuilder(savedir+'tfservingmodelv1')
# 定义输入签名
inputs = {'input_x':tf.saved_model.utils.build_tensor_info(x)}
# 定义输出签名
outputs = {'output':tf.saved_model.utils.build_tensor_info(z)}
# 创建签名对象
signature = tf.saved_model.signature_def_utils.build_signature_def(inputs=inputs,outputs=outputs,method_name=tf.saved_model.signature_constants.PREDICT_METHOD_NAME)
# 将签名和标签加入到builder中
builder.add_meta_graph_and_variables(sess,[tag.constants.SERVING],{'my_signature':signature})
builder.save()
执行结果如下图所示,在tfservingmodelv1文件夹下生成一个pb文件和一个文件夹:
提示:
预测任务是最灵活的签名方式,可以覆盖回归和分类两种任务。上面代码中的build_signature_def函数可以换成下面更高级的函数:
这三个函数是在build_signature_def函数基础上进行封装的。它们使用起来更加方便,但是灵活性会差一些。
默认情况下,模型文件必须放在用数字命名的文件夹下,才可以被tensorflow_model_server命令启动。其中的数字代表该模型的版本号。
在tfservingmodelv1下定义一个新的文件夹12345(代表版本号),并将模型文件全部迁移到12345下面。
cd tfservingmodelv1
mkdir 12345
mv saved_model.pb 12345
mv variables 12345
启动HTTP/REST API服务
直接使用tensorflow_model_server命令启动HTTP/REST API服务,并指定端口和文件路径。
tensorflow_model_server --rest_api_port=8500 --model_base_path=/home/boss/Study/TensorFlow_Engineering_Implementation-master/code/log/tfservingmodelv1/ --port=9000 --model_name=md
提示:
gPRC服务会默认自动启动,且默认监听本地8500端口,如果不指定–port端口的话,–rest_api_port端口就不能设定为8500。
运行结果
如果想把该服务作为后台命令启动,可以在后面加上&符号,并指定输出的日志(log)文件。
tensorflow_model_server --port=9000 --model_base_path=/home/boss/Study/TensorFlow_Engineering_Implementation-master/code/log/tfservingmodelv1/ &>log &
启动模型过程中的输出将会被保存到当前目录下的log文件中。
使用HTTP/REST API时,要通过POST方式请求一个URL,并带上json数据完成。具体说明如下:
这里的URL地址可分为3部分:目的IP和端口、固定不变的路径(v1/models)、模型名称和版本号(md/versions/12345)与预测方法(predict、classify、regress)。其中,模型名称和预测方法需要与模型文件中的名称和预测方法一致。例如
http://localhost:8500/v1/models/md/versions/12345:predict
在POST请求中的json数据需要按照模型的具体任务(分类、回归、预测)所对应的格式来构建。
(1)对于分类和回归任务,构建的格式是一样的
{"signature_name":签名字符串,"context":{"公共字段名":值或列表},"examples":[{"字段名":值或列表}]}
具体说明如下:
(2)对于预测任务,构建的格式如下
{"signature_name":签名字符串,"instance":值或列表,"inputs":值或列表}
具体说明如下
提示:
在使用时,inputs与instances不可同时使用
不同的任务返回的JSON格式是不一样的,具体如下
(1)分类任务的返回格式描述
{"result":[[[,],[,],...]...]
在返回的JSON格式中,label是分类的结果,score是该分类的概率结果。
(2)回归任务的返回格式描述
{"result":[,,,...]}
在返回的JSON格式中,每个value都是回归任务的返回值。这些value的顺序是按照输入样本的顺序进行排列。
(3)预测任务的返回格式描述
预测任务的返回格式有两种。
{"predictions":值或列表}
{"outputs":值或列表}
在Linux控制台输入CURL命令
curl -d '{"instances":[1.0,2.0,5.0],"signature_name":"my_signature"}' -X POST http://localhost:8500/v1/models/md/versions/12345:predict
执行该命令后的结果
该命令也可以在windows下使用,在使用时需要对JSON格式的字符串做转义。
在CMD输入以下命令
curl -d "{\"inputs\":[1.0,2.0,5.0],\"signature_name\":\"my_signature\"}" -X POST http://服务器的ip地址:8500/v1/models/md/versions/12345:predict
执行该命令后的结果(Linux下)
将输入关键字由instances换成outputs后,返回结果会以列的形式显示
import requests
import json
url = "http://localhost:8500/v1/models/md/versions/12345:predict"
s = json.dumps({"instances":[1.0,2.0,5.0],"signature_name":"my_signature"})
r = requests.post(url,data=s)
print(r.text)
运行结果同上
参考书籍:《深度学习之TensorFlow工程化项目实战》 李金洪 编著