小言_互联网的博客

Tensorflow Serving部署推荐模型

297人阅读  评论(0)

Tensorflow Serving部署推荐模型

1、找到当前模型中定义的variables,并在此定义一个saver用于保存模型参数


  
  1. def saveVariables( self):
  2. variables_dict = {}
  3. variables_dict[ self.user_embedding.op.name] = self.user_embedding
  4. variables_dict[ self.item_embedding.op.name] = self.item_embedding
  5. for v in self.reduce_dimension_layer. variables:
  6. variables_dict[v.op.name] = v
  7. self.saver = tf.train.Saver(variables_dict)

在模型的输入和输出的地方,尽量自行定义name,这样在之后的部署的时候会方便很多!


  
  1. self.item_input = tf.placeholder( "int32", [ None, 1],name= "gat_iteminput")
  2. self.user_input = tf.placeholder( "int32", [ None, 1],name= "gat_userinput")
  3. ...
  4. self.prediction = tf.sigmoid(tf.reduce_sum(self.predict_vector, 1, keepdims= True),name= "gat_predict")

2、在需要保存模型参数的地方调用save方法,一般建议在模型取到最高指标处保存模型


  
  1. #此处的saver为上面模型中定义的saver
  2. #sess即为session;weights_save_path为自定义的文件路径;global_step表示当前为第几次epoch
  3. model.saver.save(sess, weights_save_path + '/weights', global_step=epoch)

3、最终会保存为如下图所示的文件

110、111、112、119、127是最近5次模型指标最高的5次记录,可以根据自己需要选择最高的记录,也可以在self.saver = tf.train.Saver(variables_dict)这里指定好保存的次数,例如保留最多两次:self.saver = tf.train.Saver(variables_dict,max_to_keep=2)

此处训练中,我得到的最高指标epoch为127,所以我使用127的weights

因为tensorflow serving需要saved_model的格式,所以我们需要将ckpt的格式转成savedModel格式,转换的代码如下:


  
  1. import tensorflow as tf
  2. #两个参数都是文件夹的名称,一个是ckpt文件所在文件夹,一个是之后导出的文件夹
  3. def restore_and_save( input_checkpoint, export_path):
  4. checkpoint_file = tf.train.latest_checkpoint(input_checkpoint)
  5. graph = tf.Graph()
  6. with graph.as_default():
  7. session_conf = tf.ConfigProto(allow_soft_placement= True, log_device_placement= False)
  8. sess = tf.Session(config=session_conf)
  9. with sess.as_default():
  10. # 载入保存好的meta graph,恢复图中变量,通过SavedModelBuilder保存可部署的模型
  11. saver = tf.train.import_meta_graph( "{}.meta". format(checkpoint_file))
  12. saver.restore(sess, checkpoint_file)
  13. print( "graph.get_name_scope()=",graph.get_name_scope())
  14. # for node in graph.as_graph_def().node:
  15. # print(node.name)
  16. builder = tf.saved_model.builder.SavedModelBuilder(export_path)
  17. # 建立签名映射,需要包括计算图中的placeholder(ChatInputs, SegInputs, Dropout)和
  18. # 我们需要的结果(project/logits,crf_loss/transitions)
  19. """
  20. build_tensor_info
  21. 建立一个基于提供的参数构造的TensorInfo protocol buffer,
  22. 输入:tensorflow graph中的tensor;
  23. 输出:基于提供的参数(tensor)构建的包含TensorInfo的protocol buffer
  24. get_operation_by_name
  25. 通过name获取checkpoint中保存的变量,能够进行这一步的前提是在模型保存的时候给对应的变量赋予name
  26. """
  27. user_inputs = tf.saved_model.utils.build_tensor_info(
  28. graph.get_tensor_by_name( "gat_userinput:0"))
  29. item_inputs = tf.saved_model.utils.build_tensor_info(
  30. graph.get_tensor_by_name( "gat_iteminput:0"))
  31. prediction = tf.saved_model.utils.build_tensor_info(
  32. graph.get_tensor_by_name( "gat_predict:0"))
  33. print( "user_inputs=",user_inputs)
  34. print( "item_inputs=",item_inputs)
  35. print( "prediction=",prediction)
  36. sess.run(tf.global_variables_initializer())
  37. sess.run(tf.local_variables_initializer())
  38. """
  39. signature_constants
  40. SavedModel保存和恢复操作的签名常量。
  41. 在序列标注的任务中,这里的method_name是"tensorflow/serving/predict"
  42. """
  43. # 定义模型的输入输出,建立调用接口与tensor签名之间的映射
  44. labeling_signature = (
  45. tf.saved_model.signature_def_utils.build_signature_def(
  46. inputs={
  47. "user_inputs": user_inputs,
  48. "item_inputs": item_inputs
  49. },
  50. outputs={
  51. "prediction": prediction
  52. },
  53. method_name= "tensorflow/serving/predict"
  54. ))
  55. """
  56. tf.group
  57. 创建一个将多个操作分组的操作,返回一个可以执行所有输入的操作
  58. """
  59. # legacy_init_op = tf.group(tf.tables_initializer(), name='legacy_init_op')
  60. """
  61. add_meta_graph_and_variables
  62. 建立一个Saver来保存session中的变量,输出对应的原图的定义,这个函数假设保存的变量已经被初始化;
  63. 对于一个SavedModelBuilder,这个API必须被调用一次来保存meta graph;
  64. 对于后面添加的图结构,可以使用函数 add_meta_graph()来进行添加
  65. """
  66. # 建立模型名称与模型签名之间的映射
  67. builder.add_meta_graph_and_variables(
  68. sess, [tf.saved_model.tag_constants.SERVING],
  69. # 保存模型的方法名,与客户端的request.model_spec.signature_name对应
  70. signature_def_map={
  71. tf.saved_model.signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY:labeling_signature
  72. })
  73. builder.save()
  74. print( "Build Done")
  75. # 模型格式转换
  76. restore_and_save( "ckpt", "saved_model")

最后转换成功后的文件夹目录如下:

下面是一些查看节点和张量的代码:


  
  1. import tensorflow as tf
  2. # from parser_predict import parse_args
  3. from tensorflow.python import pywrap_tensorflow
  4. from tensorflow.python.framework import graph_util
  5. import os
  6. def get_pretrain_path():
  7. pretrain_path= "ckpt"
  8. print( "get_pretrain_path="+ str(pretrain_path))
  9. return pretrain_path
  10. def get_ckpt_file():
  11. return tf.train.get_checkpoint_state(os.path.dirname(get_pretrain_path() + '/checkpoint'))
  12. def get_tensors_name():
  13. ckpt = get_ckpt_file()
  14. if ckpt and ckpt.model_checkpoint_path:
  15. reader = pywrap_tensorflow.NewCheckpointReader(ckpt.model_checkpoint_path)
  16. var_to_shape_map = reader.get_variable_to_shape_map()
  17. for key in var_to_shape_map:
  18. print( 'tensor_name: ', key)
  19. else:
  20. print( 'wrong path')
  21. def get_nodes_name():
  22. ckpt = get_ckpt_file()
  23. print( "ckpt.model_checkpoint_path="+ str(ckpt.model_checkpoint_path))
  24. if ckpt and ckpt.model_checkpoint_path:
  25. saver = tf.train.import_meta_graph(ckpt.model_checkpoint_path + '.meta', clear_devices= True)
  26. with tf.Session() as sess:
  27. saver.restore(sess, ckpt.model_checkpoint_path)
  28. graph_def = tf.get_default_graph().as_graph_def(add_shapes= True)
  29. node_list = [n.name for n in graph_def.node]
  30. for node in node_list:
  31. print( 'node_name: ', node)
  32. def get_operations_name():
  33. ckpt = get_ckpt_file()
  34. if ckpt and ckpt.model_checkpoint_path:
  35. with tf.Session() as sess, open( '../log/gat_operation_log', 'w', encoding= 'utf-8') as log:
  36. sess.run(tf.global_variables_initializer())
  37. tf.train.import_meta_graph(ckpt.model_checkpoint_path + '.meta', clear_devices= True)
  38. for operation in tf.get_default_graph().get_operations():
  39. log.write(operation.name + '\n')
  40. def freeze_graph( pb_file_path):
  41. ckpt = get_ckpt_file()
  42. if ckpt and ckpt.model_checkpoint_path:
  43. saver = tf.train.import_meta_graph(ckpt.model_checkpoint_path + '.meta', clear_devices= True)
  44. graph = tf.get_default_graph()
  45. input_graph_def = graph.as_graph_def()
  46. with tf.Session() as sess:
  47. # 恢复图并得到数据
  48. print(sess.run(tf.global_variables_initializer()))
  49. print(sess.run(tf.local_variables_initializer()))
  50. saver.restore(sess, ckpt.model_checkpoint_path)
  51. # 模型持久化,将变量值固定
  52. output_graph_def = graph_util.convert_variables_to_constants(
  53. sess=sess,
  54. input_graph_def=input_graph_def,
  55. output_node_names=[ 'gat_predict']) # 注意是节点名称不是张量名称
  56. # 保存模型
  57. with tf.gfile.GFile(pb_file_path, 'wb') as f:
  58. # 序列化输出
  59. f.write(output_graph_def.SerializeToString())
  60. # 得到当前图的操作节点
  61. nodes = output_graph_def.node
  62. print( '%d ops in the final graph.' % len(nodes))
  63. def load_freeze_graph( pb_file_path):
  64. with tf.Session() as sess:
  65. graph = load_pb(pb_file_path)
  66. # 定义输入的张量名称,对应网络结构的输入张量
  67. input_user_id = graph.get_tensor_by_name( 'user_embedding')
  68. # 定义输出的张量名称
  69. output_tensor_name = graph.get_tensor_by_name( 'item_embedding')
  70. # 测试读出来的模型是否正确
  71. # 注意这里传入的是输出和输入节点的tensor的名字,不是操作节点的名字
  72. out = sess.run(output_tensor_name, feed_dict={input_user_id: [ 0]})
  73. print( 'out:', out)
  74. def load_pb( pb_file_path):
  75. with tf.gfile.GFile(pb_file_path, "rb") as f:
  76. graph_def = tf.GraphDef()
  77. graph_def.ParseFromString(f.read())
  78. with tf.Graph().as_default() as graph:
  79. tf.import_graph_def(graph_def, name= '')
  80. return graph
  81. if __name__ == '__main__':
  82. # get_tensors_name()
  83. # get_nodes_name()
  84. #freeze_graph('gat.pb') #此处功能不正确,但是可以参考下
  85. # get_operations_name()
  86. # load_freeze_graph('gat.pb')#此处功能不正确,但是可以参考下

4、安装好docker,并下载tensorflow serving的镜像

5、编写dockerfile

from:表示使用tensorflow/serving:latest的docker基础镜像

RUN:在镜像变成容器时,可以执行的命令(直接docker run进行/挂载也是一样的,这里为了方便);此处的命令意思为创建文件夹models,并在models下创建gat文件夹,并在gat文件夹下面创建1文件夹

ADD:有两个参数,前一个参数指的是当前机器上的文件夹名称,后一个参数指的是创建出来的容器的文件夹名称。此处的命令意思是将当前机器上的gat(相对路径)文件夹下的所有东西,都复制到docker创建出来的容器中的/models/gat/1/文件夹下

ENV:设置系统变量


  
  1. from tensorflow/serving:latest
  2. RUN mkdir -p /models/gat/1
  3. ADD gat /models/gat/1/
  4. ENV MODEL_NAME=gat

当前dockerfile文件名称为:GatDockerFile

6、构建镜像


  
  1. #-f后面跟着dockerfile文件名称;-t后面跟着自定义的创建出来的镜像名称;冒号后面的1.0代表自定义的版本号;最后的那个点号一定要加上
  2. docker build -f GatDockerFile -t gat: 1.0 .

构建完成后,会出现下面的提示:

然后使用docker image ls可以看到已经成功构建了镜像:

7、创建容器实例:


  
  1. #8508:指的是本机的端口号(此处可以更改为其他,只要本机上的这个端口号没有被占用就可以)
  2. #8501:指的是容器中的端口号,因为tensorflow serving是通过8501提供服务的,所以此处最好不要修改
  3. #--name:后面跟着的是自己创建的容器名称,自定义
  4. #-d:指的是后台运行
  5. #gat:1.0是刚才我们创建的镜像名称+版本号
  6. docker run -p 8508: 8501 --name gatContainer -d gat:1.0

输入命令后,如果出现一串很长的字符,并且没有报错,则代表创建成功,可以使用docker ps 查看

STATUS为Up… 说明已经启动成功


  
  1. http://主机ip地址:8508/v1/models/gat
  2. http://主机ip地址:8508/v1/models/gat/metadata

整体格式如下:

细节:

此处就是模型的名称:

此处是模型的输入和输出:

  • 输入就是:user_inputsitem_inputs
  • 输出就是:prediction

参数名是上面定义的,具体的数据格式如图所示了


可以使用postman进行测试:

http://主机IP地址:8508/v1/models/gat:predict

参考:

Tensorflow Serving部署推荐模型_默默然咯的博客-CSDN博客_tensorflow 推荐模型

整套流程+多模型部署,强烈建议参考这篇:https://blog.csdn.net/tianyunzqs/article/details/103842894


转载:https://blog.csdn.net/u013250861/article/details/128369361
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场