在了解如何利用TesnsorFlow构建和训练各种模型——从基本的机器学习模型到复杂的深度学习网络后,我们就要考虑如何将训练好的模型投入于产品,以使其能够为其他应用所用,本文对此将进行详细介绍。文章节选自《面向机器智能的TensorFlow实践》第7章。
本文将创建一个简单的Web App,使用户能够上传一幅图像,并对其运行Inception模型,实现图像的自动分类。
搭建TensorFlow服务开发环境
Docker镜像
TensorFlow服务是用于构建允许用户在产品中使用我们提供的模型的服务器的工具。在开发过程中,使用该工具的方法有两种:手工安装所有的依赖项和工具,并从源码开始构建;或利用Docker镜像。这里准备使用后者,因为它更容易、更干净,同时允许在其他不同于Linux的环境中进行开发。
如果不了解Docker镜像,不妨将其想象为一个轻量级的虚拟机镜像,但它在运行时不需要以在其中运行完整的操作系统为代价。如果尚未安装Docker,请在开发机中安装它,点击查看具体安装步骤。
为了使用Docker镜像,还可利用笔者提供的文件,它是一个用于在本地创建镜像的配置文件。要使用该文件,可使用下列命令:
docker build --pull -t $USER/tensorflow-serving-devel
https://raw.githubusercontent.com/tensorflow/serving/master/
tensorflow_serving/tools/docker/Dockerfile.devel
请注意,执行上述命令后,下载所有的依赖项可能需要一段较长的时间。
上述命令执行完毕后,为了使用该镜像运行容器,可输入下列命令:
docker run -v $HOME:/mnt/home -p 9999:9999 -it $USER/
tensorflow-serving-devel
该命令执行后会将你的home目录加载到容器的/mnt/home路径中,并允许在其中的一个终端下工作。这是非常有用的,因为你可使用自己偏好的IDE或编辑器直接编辑代码,同时在运行构建工具时仅使用该容器。它还会开放端口9999,使你可从自己的主机中访问它,并供以后将要构建的服务器使用。
键入exit命令可退出该容器终端,使其停止运行,也可利用上述命令在需要的时候启动它。
Bazel工作区
由于TensorFlow服务程序是用C++编写的,因此在构建时应使用Google的Bazel构建工具。我们将从最近创建的容器内部运行Bazel。
Bazel在代码级管理着第三方依赖项,而且只要它们也需要用Bazel构建,Bazel便会自动下载和构建它们。为了定义我们的项目将支持哪些第三方依赖项,必须在项目库的根目录下定义一个WORKSPACE文件。
我们需要的依赖项是TensorFlow服务库。在我们的例子中,TensorFlow模型库包含了Inception模型的代码。
不幸的是,在撰写本书时,TensorFlow服务尚不支持作为Git库通过Bazel直接引用,因此必须在项目中将它作为一个Git的子模块包含进去:
# 在本地机器上
mkdir ~/serving_example
cd ~/serving_example
git init
git submodule add https://github.com/tensorflow/serving.git
tf_serving
git.submodule update - -init - -recursive
下面利用WORKSPACE文件中的local_repository规则将第三方依赖项定义为在本地存储的文件。此外,还需利用从项目中导入的tf_workspace规则对TensorFlow的依赖项初始化:
# Bazel WORKSPACE文件
workspace(name = "serving")
local_repository(
name = "tf_serving",
path = _workspace_dir__ + "/tf_serving",
local_repository(
name = "org_tensorflow",
path = _workspace_dir__ + "/tf_serving/tensorflow",
)
load('//tf_serving/tensorflow/tensorflow:workspace.bzl',
'tf_workspace')
tf_workspace("tf_serving/tensorflow/", "@org_tensorflow")
bind(
name = "libssl",
actual = "@boringssl_git//:ssl",
)
bind(
name = "zlib",
actual = "@zlib_archive//:zlib"
)
# 仅当导入inception 模型时需要
local_repository(
name = "inception_model",
path = __workspace_dir__ + "/tf_serving/tf_models/
inception”,
)
最后,需要从容器内为Tensorflow运行./configure:
# 在Docker容器中
cd /mnt/home/serving_example/tf_serving/tensorflow
./configure
导出训练好的模型
一旦模型训练完毕并准备进行评估,便需要将数据流图及其变量值导出,以使其可为产品所用。
模型的数据流图应当与其训练版本有所区分,因为它必须从占位符接收输入,并对其进行单步推断以计算输出。对于Inception模型这个例子,以及对于任意一般图像识别模型,我们希望输入是一个表示了JPEG编码的图像字符串,这样就可轻易地将它传送到消费App中。这与从TFRecord文件读取训练输入颇为不同。
定义输入的一般形式如下:
def convert_external_inputs (external_x):
#将外部输入变换为推断所需的输入格式
def inference(x):
#从原始模型中……
external_x = tf.placeholder(tf.string)
x = convert_external_inputs(external_x)
y = inference(x)
在上述代码中,为输入定义了占位符,并调用了一个函数将用占位符表示的外部输入转换为原始推断模型所需的输入格式。例如,我们需要将JPEG字符串转换为Inception模型所需的图像格式。最后,调用原始模型推断方法,依据转换后的输入得到推断结果。
例如,对于Inception模型,应当有下列方法:
import tensorflow as tf
from tensorflow_serving.session_bundle import exporter
from inception import inception_model
def convert_external_inputs (external_x)
# 将外部输入变换为推断所需的输入格式
# 将图像字符串转换为一个各分量位于[0,1]内的像素张量
image =
tf.image.convert_image_dtype(tf.image.decode_jpeg(external_x,
channels=3), tf.float32)