一、TensorFlow Serving简介
TensorFlow Serving是GOOGLE开源的一个服务系统,适用于部署机器学习模型,灵活、性能高、可用于生产环境。 TensorFlow Serving可以轻松部署新算法和实验,同时保持相同的服务器架构和API,它具有以下特性:
- 支持模型版本控制和回滚
- 支持并发,实现高吞吐量
- 开箱即用,并且可定制化
- 支持多模型服务
- 支持批处理
- 支持热更新
- 支持分布式模型
- 易于使用的inference api
- 为gRPC expose port 8500,为REST API expose port 8501
二、安装与测试
安装TensorFlow Serving有三种方法:docker,二进制,源码编译,这里只介绍通过docker安装的步骤。
- 安装docker,通过docker拉取tensorflow serving镜像
# 此处拉取的是cpu serving镜像,如果要使用gpu,需要安装对应的docker和对应的gpu serving镜像
docker pull tensorflow/serving
- 拉取源码,部署源码中的half_plus_two模型测试serving是否可用
git clone https://github.com/tensorflow/serving
TESTDATA="$(pwd)/serving/tensorflow_serving/servables/tensorflow/testdata"
# 设置端口转发,以下两条命令都可以启动服务
docker run -dt -p 8501:8501 -v "$TESTDATA/saved_model_half_plus_two_cpu:/models/half_plus_two" -e MODEL_NAME=half_plus_two tensorflow/serving
docker run -d -p 8501:8501 --mounttype=bind,source=$TESTDATA/saved_model_half_plus_two_cpu/,target=/models/half_plus_two -e MODEL_NAME=half_plus_two -t --name testserver tensorflow/serving
# 在服务器本机测试模型是否正常工作,这里需要注意,源码中half_plus_two的模型版本是00000123,但在访问时也必须输入v1而不是v000000123
curl -d '{"instances": [1.0, 2.0, 5.0]}' -X POST http://localhost:8501/v1/models/half_plus_two:predict
# 得到{"predictions": [2.5, 3.0, 4.5]}这个结果说明模型部署成功
- 命令说明
docker常用命令
# 启动/停止容器
docker start/stop $container_id或$container_name
# 查看运行容器
docker ps
# 查看全部容器
docker ps -a
# 删除指定容器
docker rm $container_id或$container_name
# 查看运行容器的日志
docker logs -f $container_id或$container_name
# docker 删除镜像
docker rmi image:tag # 例如docker rmi tensorflow/serving:latest
docker启动服务时参数
--mount: 表示要进行挂载
source: 指定要运行部署的模型地址, 也就是挂载的源,这个是在宿主机上的servable模型目录(pb格式模型而不是checkpoint模型)
target: 这个是要挂载的目标位置,也就是挂载到docker容器中的哪个位置,这是docker容器中的目录,模型默认挂在/models/目录下,如果改变路径会出现找不到model的错误
-t: 指定的是挂载到哪个容器
-d: 后台运行
-p: 指定主机到docker容器的端口映射
-e: 环境变量
-v: docker数据卷
--name: 指定容器name,后续使用比用container_id更方便
三、模型格式转换
我们平时使用tf.Saver()保存的模型是checkpoint格式的,但是在TensorFlow Serving中一个servable的模型目录中是一个pb格式文件和一个名为variables的目录,因此需要在模型保存时就保存好可部署的模型格式,或者将已经训练好的checkpoint转换为servable format。
-ckpt_model
-checkpoint
-***.ckpt.data-00000-of-00001
-***.ckpt.index
-***.ckpt.meta
#转换为
-servable_model
-version
-saved_model.pb
-variables
以下以命名实体识别空洞卷积模型为例,展示如何转换模型格式。
#coding:utf-8 import sys, os, io import tensorflow as tf 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(str(count))) 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 """ char_inputs =tf.saved_model.utils.build_tensor_info(graph.get_operation_by_name("ChatInputs").outputs[0]) seg_inputs =tf.saved_model.utils.build_tensor_info(graph.get_operation_by_name("SegInputs").outputs[0]) dropout =tf.saved_model.utils.build_tensor_info(graph.get_operation_by_name("Dropout").outputs[0]) logits =tf.saved_model.utils.build_tensor_info(graph.get_operation_by_name("project/logits").outputs[0]) transition_params =tf.saved_model.utils.build_tensor_info(graph.get_operation_by_name("crf_loss/transitions").outputs[0]) """ signature_constants:SavedModel保存和恢复操作的签名常量。 在序列标注的任务中,这里的method_name是"tensorflow/serving/predict" """ # 定义模型的输入输出,建立调用接口与tensor签名之间的映射 labeling_signature = ( tf.saved_model.signature_def_utils.build_signature_def( inputs={ "charinputs": char_inputs, "dropout": dropout, "seginputs": seg_inputs, }, outputs={ "logits": logits, "transitions": transition_params }, 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], # 保存模型的方法名,与客户端的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") ### 测试模型转换 tf.flags.DEFINE_string(