上篇文章链接
https://mp.csdn.net/mdeditor/100163680
继上篇环境准备好以后, 接下来就要开始进行模型的准备, 以及flask代码的编写了.
第一步, 先准备好之前训练好的 ckpt模型, 将其转换为 tensorflow_serving 能够使用的PB模型.
#coding:utf-8
import sys, os, io
import tensorflow as tf
'''将ckpt模型文件, 转换为PB模型文件,提供持久化存储'''
def restore_and_save(input_checkpoint, export_path_base):
checkpoint_file = tf.train.latest_checkpoint(input_checkpoint)
graph = tf.Graph()
with graph.as_default():
session_conf = tf.ConfigProto(allow_soft_placement=True, log_device_placement=False)
sess = tf.Session(config=session_conf)
with sess.as_default():
# 载入保存好的meta graph,恢复图中变量,通过SavedModelBuilder保存可部署的模型
saver = tf.train.import_meta_graph("{}.meta".format(checkpoint_file))
saver.restore(sess, checkpoint_file)
print (graph.get_name_scope())
export_path_base = export_path_base
export_path = os.path.join(
tf.compat.as_bytes(export_path_base),
tf.compat.as_bytes('0001'))
print('Exporting trained model to', export_path)
builder = tf.saved_model.builder.SavedModelBuilder(export_path)
# 建立签名映射,需要包括计算图中的placeholder(ChatInputs, SegInputs, Dropout)和我们需要的结果(project/logits,crf_loss/transitions)
"""
build_tensor_info:建立一个基于提供的参数构造的TensorInfo protocol buffer,
输入:tensorflow graph中的tensor;
输出:基于提供的参数(tensor)构建的包含TensorInfo的protocol buffer
get_operation_by_name:通过name获取checkpoint中保存的变量,能够进行这一步的前提是在模型保存的时候给对应的变量赋予name
"""
image =tf.saved_model.utils.build_tensor_info(graph.get_operation_by_name("x").outputs[0])
softmax =tf.saved_model.utils.build_tensor_info(graph.get_operation_by_name("softmax").outputs[0])
pred =tf.saved_model.utils.build_tensor_info(graph.get_operation_by_name("pred").outputs[0])
"""
signature_constants:SavedModel保存和恢复操作的签名常量。
在序列标注的任务中,这里的method_name是"tensorflow/serving/predict"
"""
# 定义模型的输入输出,建立调用接口与tensor签名之间的映射
#根据自己的模型训练时候, 对input, output 定义的名字来设置.
labeling_signature = (
tf.saved_model.signature_def_utils.build_signature_def(
inputs={"image":image},
outputs={"softmax":softmax,"pred":pred},
method_name=tf.saved_model.signature_constants.PREDICT_METHOD_NAME))
"""
tf.group : 创建一个将多个操作分组的操作,返回一个可以执行所有输入的操作
"""
legacy_init_op = tf.group(tf.tables_initializer(), name='legacy_init_op')
"""
add_meta_graph_and_variables:建立一个Saver来保存session中的变量,
输出对应的原图的定义,这个函数假设保存的变量已经被初始化;
对于一个SavedModelBuilder,这个API必须被调用一次来保存meta graph;
对于后面添加的图结构,可以使用函数 add_meta_graph()来进行添加
"""
# 建立模型名称与模型签名之间的映射
builder.add_meta_graph_and_variables(
sess, [tf.saved_model.tag_constants.SERVING],
# 保存模型的方法名,与客户端的request.model_spec.signature_name对应
signature_def_map={
tf.saved_model.signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY:
labeling_signature},
legacy_init_op=legacy_init_op)
builder.save()
print("Build Done")
#自己的ckpt模型的文件夹,运行的时候注意自己的路径, 我这个是相对路径.
input_checkpoint = './output/DL_alexnetmodel/yes_no/0614v1/'
#希望保存pb模型的文件夹
model_path = './output/DL_alexnetmodel/yes_no/'
restore_and_save(input_checkpoint, model_path)
运行完之后会生成类似这样的模型结构.这样就可以使用tensorflow_serving直接调用了.
第二步, ubuntu上运行docker, 部署pb模型, 供flask 调用.
#在ubuntu中运行这段代码, 如果失败记得root用户试试.
sudo docker run -d -p 8500:8500 --mount type=bind,source=/home/ubuntu/models_docker/yesno/,target=/models/yesno\
--mount type=bind,source=/home/ubuntu/models_docker/models.config,target=/models/models.config \
-t --name ner tensorflow/serving --model_config_file=/models/models.config
#这里是models.config的配置.
model_config_list: {
config: {
name: "yesno",
base_path: "/models/yesno",
model_platform: "tensorflow",
},
}
如果需要同时部署多个模型, 在这个文件中增加配置信息, 同时在命令中增加对应的模型路径.
如下:
sudo docker run -d -p 8500:8500 --mount type=bind,source=/home/ubuntu/models_docker/scene/,target=/models/scene\
--mount type=bind,source=/home/ubuntu/models_docker/someone/,target=/models/someone\
--mount type=bind,source=/home/ubuntu/models_docker/yesno/,target=/models/yesno\
--mount type=bind,source=/home/ubuntu/models_docker/models.config,target=/models/models.config \
-t --name ner tensorflow/serving --model_config_file=/models/models.config
model_config_list: {
config: {
name: "scene",
base_path: "/models/scene",
model_platform: "tensorflow",
},
config: {
name: "someone",
base_path: "/models/someone",
model_platform: "tensorflow",
},
config: {
name: "yesno",
base_path: "/models/yesno",
model_platform: "tensorflow",
},
}
这里的source是你ubuntu上放置0001的目录, target是在docker中的目录.
正常起来后就会有之前类似运行docker成功后的编码.这时候我们就可以通过flask去访问 tf-serving 的接口, 来提供预测服务.
第三步, 编写flask代码, 实现对tensorflow_serving的调用, 对外部提供接口.
import pickle
import cv2
import numpy as np
import tensorflow as tf
import warnings
from tensorflow_serving.apis import predict_pb2
from tensorflow_serving.apis import prediction_service_pb2
from grpc.beta import implementations
class predictImg(object):
def __init__(self):
server = '192.168.0.238:8500'
host, port = server.split(':')
channel = implementations.insecure_channel(host, int(port))
self.stub = prediction_service_pb2.beta_create_PredictionService_stub(channel)
self.modelRequest = predict_pb2.PredictRequest()
def dl_yes_no_people(self,image):
classesYN = {0:'no',1:'yes'} #分类名称
self.modelRequest.model_spec.name = 'yesno' #models.config 中的设置名称
self.modelRequest.model_spec.signature_name = 'serving_default'
self.modelRequest.inputs['image'].CopyFrom(tf.contrib.util.make_tensor_proto(image.astype(dtype=np.float32), shape=[1,224,224,3])) #这是模型训练时的input.
response = self.stub.Predict(self.modelRequest, 10.0) #向tensorflow_serving发送请求, 超时等待10s
results = {}
for key in response.outputs:
tensor_proto = response.outputs[key]
results[key] = tf.contrib.util.make_ndarray(tensor_proto)
prob = results['pred'][0] #这是output 与训练时的name对应
return [classesYN[np.argmax(prob)],str(np.max(prob))]
def set_tags(self,CatName,TagName,TagWeight,tags):#将所有的预测信息添加到tags中, 返回给客户端
tag = {"CatName":CatName,"TagName":TagName,"TagWeight":TagWeight}
tags.append(tag)
return tags
def normalizing(self,img):#将图片标准化, 方便深度学习模型使用进行预测
image = cv2.resize(img, (224, 224))
image_array = np.array(image)
image = image_array.astype(np.float32) * (1. / 255.) - 0.5
image = np.reshape(image, [1, 224, 224, 3])
return image
def ml_predict_img(self,image): #接收图片的地址,进行图片的读取及预测
try:
tags = []
img = cv2.imread(image)
normalImg = self.normalizing(img)
label = self.dl_yes_no_people(normalImg)#预测图片是摆拍 / 场布
if label[0] == 'no': #场布 (无人)
tags = self.set_tags("环节", "场布", label[1], tags)
else:#摆拍 (有人)
tags = self.set_tags("环节", "摆拍", label[1], tags)
except Exception as e:
tags = str(e)
finally:
return tags
imgModel = predictImg()
app = Flask(__name__)
@app.route('/api/AnalysisImg',methods=["POST"])
def analysisImg():
try:
'''获取图片数据,格式,存储到本地'''
url = request.headers.get('url')
Format = request.headers.get('format')
if url == None or url == '': #传递的是二进制数据
img_bytes = request.get_data()
else: #传递的是url,读取url,并获取二进制数据
img_bytes = rq.get(url).content
T = time.time()
t = time.strftime("%Y%m%d%H%M%S", time.localtime())
name = t+str(T).split('.')[0][:3] #取北京时间,并拼接小数点后三位的秒数,并存储到picture中
file_path = "./static/picture/" + name + '.' + Format
with open(file_path, 'wb') as f:
f.write(img_bytes)
tags = imgModel.ml_predict_img(file_path)
resp = {"success":True,"message":'',"tags":tags}
except Exception as e:
resp = {"success": False, "message": "图片数据有误"}
finally:
return json.dumps(resp, ensure_ascii=False)
if __name__ == "__main__":
app.run(debug=True)
到这一步, flask的代码就完成了, 这个代码我没有运行时从之前的代码中 摘出来的部分代码, 应该没有问题, 在ubuntu中可以运行下, 如果不报错,就没有问题可以调用这个接口, 进行model的预测了.
接下来 就是最后的最后了, 将flask 部署到服务器中,配合uwsgi和nginx 就完成了 整个预测服务的部署了.