kubeflow系列(三):模型即服务,关于tensorflow serving的使用
kubeflow 中采用了 tensorflow serving 作为官方的tensorflow模型接口, TensorFlow Serving是GOOGLE开源的一个服务系统,适用于部署机器学习模型,灵活、性能高、可用于生产环境。 TensorFlow Serving可以轻松部署新算法和实验,同时保持相同的服务器架构和API。
Tensorflow Serving 直接加载模型即可生成接口,不过 serving 支持的模型只有 SaveModel,因此这里主要介绍 SaveModel。
SaveModel
SaveModel
是一种专门用于tf模型 拓扑结构(topology) 和 权重(weights) ,基于 SaveModel
不需要运行原始的模型构建代码,这样非常利于共享或部署模型,因此一般模型部署都用 SaveModel
。
- 拓扑结构(Topology): 这是一个描述模型结构的文件(例如它使用的了哪些操作)。它包含对存储在外部的模型权重的引用。
- 权重(Weights): 这些是以有效格式存储给定模型权重的二进制文件。它们通常存储在与拓扑结构相同的文件夹中。
SaveModel
文件目录:
assets saved_model.pb variables
查看MetaGraphDefs
和SignatureDefs
:
saved_model_cli show --dir <SaveModel路径> --all
生成模型需要模型的MetaGraphDefs
和SignatureDefs
,MetaGraphDefs
就是我们常见的meta graph,其中包含了四种主要的信息:
- MetaInfoDef: 存放了一些元信息,例如版本和其他用户信息;
- GraphDef: 描述的Graph中序列化得到的图,由Protocol Buffer组成;
- SaverDef: 图的Saver信息,例如最多同时保存的checkpoint数量,需要保存的Tensor名字等,不保存Tensor中的实际内容;
- CollectionDef: 任何需要特殊注意的python对象,需要特殊的标注以方便import_meta_graph后取回,如"prediction"。
SignatureDefs
则是模型的签名定义,定义了 输入 和 输出函数`。
SignatureDefs
SignatureDef 定义了 TensorFlow graph 计算的签名,定义了 输入 和 输出函数,SignatureDef 结构 :
inputs
as a map of string to TensorInfo.
outputs
as a map of string to TensorInfo.method_name
(which corresponds to a supported method name in the loading tool/system).
Classification SignatureDef例子
必须要一个输入 Tensors inputs
和两个输出Tensors: classes
和 scores
signature_def: { key : "my_classification_signature" value: { inputs: { key : "inputs" value: { name: "tf_example:0" dtype: DT_STRING tensor_shape: ... } } outputs: { key : "classes" value: { name: "index_to_string:0" dtype: DT_STRING tensor_shape: ... } } outputs: { key : "scores" value: { name: "TopKV2:0" dtype: DT_FLOAT tensor_shape: ... } } method_name: "tensorflow/serving/classify" } }
Predict SignatureDef例子
signature_def: { key : "my_prediction_signature" value: { inputs: { key : "images" value: { name: "x:0" dtype: ... tensor_shape: ... } } outputs: { key : "scores" value: { name: "y:0" dtype: ... tensor_shape: ... } } method_name: "tensorflow/serving/predict" } }
Regression SignatureDef例子
signature_def: { key : "my_regression_signature" value: { inputs: { key : "inputs" value: { name: "x_input_examples_tensor_0" dtype: ... tensor_shape: ... } } outputs: { key : "outputs" value: { name: "y_outputs_0" dtype: DT_FLOAT tensor_shape: ... } } method_name: "tensorflow/serving/regress" } }
生成 SaveModel 文件
生成 SaveModel文件的方式:
- (1)tf.saved_model # 最直接简单
- (2)Estimator的export_savedmodel # 高级API Estimator模型导出
classifier = classifier = tf.estimator.Estimator( model_fn=conv_model, model_dir=args.tf_model_dir, config=training_config, params=model_params) classifier.export_savedmodel(args.tf_export_dir, serving_input_receiver_fn=serving_fn)
- (3)keras.Model.save(output_path)
将 checkpoint 模型文件 改为 SaveModel 文件
import sys, os, io import tensorflow as tf model_version = "1" model_name = "object" 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 ("name scope: ",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(model_name+"/"+model_version)) print('Exporting trained model to', export_path) builder = tf.saved_model.builder.SavedModelBuilder(export_path) # 模型的各种operator 可以通过 graph.get_operations() 获得 # input 为输入层operator inputs = tf.saved_model.utils.build_tensor_info(graph.get_operation_by_name("Placeholder").outputs[0]) print(inputs) # output 为输出层operator, 这里的输出层 type 为 Softmax outputs = tf.saved_model.utils.build_tensor_info(graph.get_operation_by_name("final_result").outputs[0]) print(outputs) """ signature_constants:SavedModel保存和恢复操作的签名常量。 在序列标注的任务中,这里的method_name是"tensorflow/serving/predict" """ # 定义模型的输入输出,建立调用接口与tensor签名之间的映射 labeling_signature = ( tf.saved_model.signature_def_utils.build_signature_def( inputs={ "Placeholder": inputs, }, outputs={ "final_result": outputs, }, method_name="tensorflow/serving/predict")) """ 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], 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")
Run server
生成好 SaveModel
模型文件,就可以直接运行 serving 来实现模型服务:
(1)用DOCKER运行:
docker run --rm -it -p 8500:8500 \ --mount type=bind,source=/root/inception/models,target=/models \ -e MODEL_NAME=1 tensorflow/serving
挂载的默认目录为两级目录:./<模型名称>/<版本号>/save_model.pb
, 版本号必须为数字。
(2)或者可以用k8s运行deployment(kubeflow):
apiVersion: extensions/v1beta1 kind: Deployment metadata: labels: app: inception name: inception-service-local namespace: kubeflow spec: template: metadata: labels: app: inception version: v1 spec: containers: - args: - --port=9000 - --rest_api_port=8500 - --model_name=1 - --model_base_path=/mnt/export command: - /usr/bin/tensorflow_model_server env: - name: modelBasePath value: /mnt/export image: tensorflow/serving:1.11.1 imagePullPolicy: IfNotPresent livenessProbe: initialDelaySeconds: 30 periodSeconds: 30 tcpSocket: port: 9000 name: mnist ports: - containerPort: 9000 - containerPort: 8500 volumeMounts: - mountPath: /mnt name: local-storage
构建请求测试
测试模型接口
import requests from PIL import Image import numpy as np filename = "./CES/astra_mini/1576210854440.png" # 图片 img=Image.open(filename) img_arr=np.array(img,dtype=np.uint8) print(img_arr.shape) # (299, 299, 3) data = json.dumps({"instances": [img_arr.tolist()]}) headers = {"content-type": "application/json"} json_response = requests.post('http://127.0.0.1:8501/v1/models/object:predict', data=data, headers=headers) print(json_response.text)
参考文献
https://www.tensorflow.org/tfx/tutorials/serving/rest_simple
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
2019年全网最热门的123个Java并发面试题总结
前言 并发编程几乎是所有互联网公司面试必问的问题,并发编程是Java程序员最重要的技能之一,也是最难掌握的一种技能。它要求编程者对计算机最底层的运作原理有深刻的理解,同时要求编程者逻辑清晰、思维缜密,这样才能写出高效、安全、可靠的多线程并发程序。 关于Java并发编程的知识总结了个思维导图,分享给大家 整理了网络上热门的123道Java并发面试题,看看你都遇到过哪些? Java 并发编程(一) 1、在 java 中守护线程和本地线程区别?2、线程与进程的区别?3、什么是多线程中的上下文切换?4、死锁与活锁的区别,死锁与饥饿的区别?5、Java 中用到的线程调度算法是什么?6、什么是线程组,为什么在 Java 中不推荐使用?7、为什么使用 Executor 框架?8、在 Java 中 Executor 和 Executors 的区别?9、如何在 Windows 和 Linux 上查找哪个线程使用的 CPU 时间最长?10、什么是原子操作?在 Java Concurrency API 中有哪些原子类(atomic classes)?11、Java Concurrency API 中的 Lo...
- 下一篇
JFinal-event 3.1.1 发布,添加类扫描的开关
一、说明 在 JFinal-event 3.0 比较激进的采用了注解处理器,在不使用 maven、gradle 等包管理或者开发工具不支持, 会导致不生成 dream.events 配置文件。对于这种情况你可以采用开启类扫描的形式。 EventPlugin plugin = new EventPlugin(); // 设置为异步,默认同步,或者使用`threadPool(ExecutorService executorService)`自定义线程池。 plugin.async(); // 开启类扫描,默认为 false,用于不支持注解处理器的情况,用于不使用 maven 或者不支持注解处理器的情况。 plugin.enableClassScan(); // 扫描 jar 里的 监听器,默认不扫描,在开启 enableClassScan 有效果 plugin.scanJar(); // 扫描的包,默认全扫描,可提升启动速度,在开启 enableClassScan 有效果 plugin.scanPackage("com.xxx.包名"); // 手动启动插件,用于...
相关文章
文章评论
共有0条评论来说两句吧...