docker+tensroflow_serving部署tensorflow模型(模型部署)

tensorflow模型部署:采用flask+docker+tensorflow_serving,提供模型线上预测服务.

上篇文章链接
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 就完成了 整个预测服务的部署了.

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