BT

如何利用碎片时间提升技术认知与能力? 点击获取答案

Zendesk的TensorFlow产品部署经验

| 作者 Wai Chee Yau 关注 0 他的粉丝 ,译者 尚剑 关注 2 他的粉丝 发布于 2017年4月1日. 估计阅读时间: 19 分钟 | QCon上海2018 关注大数据平台技术选型、搭建、系统迁移和优化的经验。

我们如何开始使用TensorFlow

在Zendesk,我们开发了一系列机器学习产品,比如最新的自动应答(Automatic Answers)。它使用机器学习来解释用户提出的问题,并用相应的知识库文章来回应。当用户有问题、投诉或者查询时,他们可以在线提交请求。收到他们的请求后,Automatic Answers将分析请求,并且通过邮件建议客户阅读可能最有帮助的相关文章。

Automatic Answers使用一类目前最先进的机器学习算法来识别相关文章,也就是深度学习。 我们使用Google的开源深度学习库TensorFlow来构建这些模型,利用图形处理单元(GPU)来加速这个过程。Automatic Answers是我们在Zendesk使用Tensorflow完成的第一个数据产品。在我们的数据科学家付出无数汗水和心血之后,我们才有了在Automatic Answers上效果非常好的Tensorflow模型。

但是构建模型只是问题的一部分,我们的下一个挑战是要找到一种方法,使得模型可以在生产环境下服务。模型服务系统将处理大量的业务,所以需要确保为这些模型提供的软件和硬件基础架构是可扩展的、可靠的和容错的,这对我们来说是非常重要的。接下来介绍一下我们在生产环境中配置TensorFlow模型的一些经验。

顺便说一下我们的团队——Zendesk的机器学习数据团队。我们团队包括一群数据科学家、数据工程师、一位产品经理、UX /产品设计师以及一名测试工程师。

TensorFlow模型服务

经过数据科学家和数据工程师之间一系列的讨论,我们明确了一些核心需求:

  • 预测时的低延迟
  • 横向可扩展
  • 适合我们的微服务架构
  • 可以使用A/B测试不同版本的模型
  • 可以与更新版本的TensorFlow兼容
  • 支持其他TensorFlow模型,以支持未来的数据产品

TensorFlow Serving

经过网上的调研之后,Google的TensorFlow Serving成为我们首选的模型服务。TensorFlow Serving用C++编写,支持机器学习模型服务。开箱即用的TensorFlow Serving安装支持:

  • TensorFlow模型的服务
  • 从本地文件系统扫描和加载TensorFlow模型

TensorFlow Serving将每个模型视为可服务对象。它定期扫描本地文件系统,根据文件系统的状态和模型版本控制策略来加载和卸载模型。这使得可以在TensorFlow Serving继续运行的情况下,通过将导出的模型复制到指定的文件路径,而轻松地热部署经过训练的模型。

(点击放大图像)

根据这篇Google博客中报告的基准测试结果,他们每秒记录大约100000个查询,其中不包括TensorFlow预测处理时间和网络请求时间。

有关TensorFlow Serving架构的更多信息,请参阅TensorFlow Serving文档

通信协议(gRPC)

TensorFlow Serving提供了用于从模型调用预测的gRPC接口。gRPC是一个开源的高性能远程过程调用(remote procedure call,RPC)框架,它在HTTP/2上运行。与HTTP/1.1相比,HTTP/2包含一些有趣的增强,比如它对请求复用、双向流和通过二进制传输的支持,而不是文本。

默认情况下,gRPC使用Protocol Buffers (Protobuf)作为其信息交换格式。Protocol Buffers是Google的开源项目,用于在高效的二进制格式下序列化结构化数据。它是强类型,这使它不容易出错。数据结构在.proto文件中指定,然后可以以各种语言(包括Python,Java和C ++)将其编译为gRPC请求类。 这是我第一次使用gRPC,我很想知道它与其他API架构(如REST)相比谁性能更好。

模型训练和服务架构

我们决定将深度学习模型的训练和服务分为两个管道。下图是我们的模型训练和服务架构的概述:

(点击放大图像)

模型训练管道

模型训练步骤:

  • 我们的训练特征是从Hadoop中提供的数据生成的。
  • 生成的训练特征保存在AWS S3中。
  • 然后使用AWS中的GPU实例和S3中的批量训练样本训练TensorFlow模型。

一旦模型被构建并验证通过,它将被发布到S3中的模型存储库。

模型服务管道

验证的模型在生产中通过将模型从模型库传送到TensorFlow Serving实例来提供。

基础结构

我们在AWS EC2实例上运行TensorFlow Serving。Consul在实例之前设置,用于服务发现和分发流量。客户端连接从DNS查找返回的第一个可用IP。或者弹性负载平衡可用于更高级的负载平衡。由于TensorFlow模型的预测本质上是无状态操作,所以我们可以通过旋转加速更多的EC2实例来实现横向可扩展性。

另一个选择是使用Google Cloud平台提供的Cloud ML,它提供TensorFlow Serving作为完全托管服务。 但是,当我们在大概2016年9月推出TensorFlow Serving时,Cloud ML服务处于alpha阶段,缺少生产使用所需的功能。因此,我们选择在我们自己的AWS EC2实例中托管,以实现更精细的粒度控制和可预测的资源容量。

模型服务的实现

下面是我们实现TensorFlow Serving部署和运行所采取的步骤:

1. 从源编译TensorFlow Serving

首先,我们需要编译源代码来产生可执行的二进制文件。然后就可以从命令行执行二进制文件来启动服务系统。

假设你已经配置好了Docker,那么一个好的开端就是使用提供的Dockerfile来编译二进制文件。请按照以下步骤:

  • 运行该gist中的代码以构建适合编译TensorFlow Serving的docker容器。
  • 在正在运行的docker容器中运行该gist中的代码以构建可执行二进制文件。
  • 一旦编译完成,可执行二进制文件将在你的docker镜像的以下路径中:/work/serving/bazel-bin/tensorflow_serving/model_servers/tensorflow_model_server

2. 运行模型服务系统

上一步生成的可执行二进制文件(tensorflow_model_server)可以部署到您的生产实例中。 如果您使用docker编排框架(如KubernetesElastic Container Service),您还可以在docker容器中运行TensorFlow Serving。

现在假设TensorFlow模型存储在目录/work/awesome_model_directory下的生产主机上。你可以在端口8999上使用以下命令来运行TensorFlow Serving和你的TensorFlow模型:

<path_to_the_binary>/tensorflow_model_server — port=8999 — model_base_path=/work/awesome_model_directory

默认情况下,TensorFlow Serving会每秒扫描模型基本路径,并且可以自定义。此处列出了可作为命令行参数的可选配置。

3.从服务定义(Service Definitions)生成Python gRPC存根

下一步是创建可以在模型服务器上进行预测的gRPC客户端。这可以通过编译.proto文件中的服务定义,从而生成服务器和客户端存根来实现。.proto文件在TensorFlow Serving源码中的tensorflow_serving_apis文件夹中。在docker容器中运行以下脚本来编译.proto文件。运行提交版本号为46915c6的脚本的示例:

./compile_ts_serving_proto.sh 46915c6

运行该脚本后应该在tensorflow_serving_apis目录下生成以下定义文件:

  • model_pb2.py
  • predict_pb2.py
  • prediction_service_pb2.py

你还可以使用grpc_tools Python工具包来编译.proto文件。

4. 从远程主机调用服务

可以使用编译后的定义来创建一个python客户端,用来调用服务器上的gRPC调用。比如这个例子用一个同步调用TensorFlow Serving的Python客户端。

如果您的用例支持异步调用预测,TensorFlow Serving还支持批处理预测以达到性能优化的目的。要启用此功能,你应该运行tensorflow_model_server同时开启flag?—enable_batching。是一个异步客户端的例子。

从其他存储加载模型

如果你的模型没有存储在本地系统中应该怎么办?你可能希望TensorFlow Serving可以直接从外部存储系统(比如AWS S3和Google Storage)中直接读取。

如果是这种情况,你将需要通过Custom Source来拓展TensorFlow Serving以使其可以读取这些源。TensorFlow Serving仅支持从文件系统加载模型。

一些经验

我们在产品中已经使用TensorFlow Serving大概半年的时间,我们的使用体验是相当平稳。它具有良好的预测时间延迟。以下是我们的TensorFlow Serving实例一周内的预测时间(以秒为单位)的第95百分位数的图(约20毫秒):

(点击放大图像)

然而,在生产中使用TensorFlow Serving的过程中,我们也有一些经验教训可以跟大家分享。

1. 模型版本化

到目前为止,我们已经在产品中使用了几个不同版本的TensorFlow模型,每一个版本都有不同的特性,比如网络结构、训练数据等。正确处理模型的不同版本已经是一个重要的任务。这是因为传递到TensorFlow Serving的输入请求通常涉及到多个预处理步骤。这些预处理步骤在不同TensorFlow模型版本下是不同的。预处理步骤和模型版本的不匹配可能导致错误的预测。

1a.明确说明你想要的版本

我们发现了一个简单但有用的防止错误预测的方法,也就是使用在model.proto定义中指定的版本属性,它是可选的(可以编译为model_pb2.py)。这样可以始终保证你的请求有效负载与预期的版本号匹配。

当你请求某个版本(比如从客户端请求版本5),如果TensorFlow Serving服务器不支持该特定版本,它将返回一个错误消息,提示找不到模型。

1b.服务多个模型版本

TensorFlow Serving默认的是加载和提供模型的最新版本。

当我们在2016年9月首次应用TensorFlow Serving时,它不支持同时提供多个模型。这意味着在指定时间内它只有一个版本的模型。这对于我们的用例是不够的,因为我们希望服务多个版本的模型以支持不同神经网络架构的A / B测试。

其中一个选择是在不同的主机或端口上运行多个TensorFlow Serving进程,以使每个进程提供不同的模型版本。这样的话就需要:

  • 用户应用程序(gRPC客户端)包含切换逻辑,并且需要知道对于给定的版本需要调用哪个TensorFlow Serving实例。这增加了客户端的复杂度,所以不是首选。
  • 一个可以将版本号映射到TensorFlow Serving不同实例的注册表。

更理想的解决方案是TensorFlow Serving可以支持多个版本的模型。

所以我决定使用一个“lab day”的时间来扩展TensorFlow Serving,使其可以服务多个版本的时间。在Zendesk,“lab day”就是我们可以每两周有一天的时间来研究我们感兴趣的东西,让它成为能够提高我们日常生产力的工具,或者一种我们希望学习的新技术。我已经有8年多没有使用C++代码了。但是,我对TensorFlow Serving代码库的可读性和整洁性印象深刻,这使其易于扩展。支持多个版本的增强功能已经提交,并且已经合并到主代码库中。TensorFlow Serving维护人员对补丁和功能增强的反馈非常迅速。从最新的主分支,你可以启动TensorFlow Serving,用model_version_policy中附加的flag来服务多个模型版本:

/work/serving/bazel-bin/tensorflow_serving/model_servers/tensorflow_model_server 
— port=8999 — model_base_path=/work/awesome_model_directory — model_version_policy=ALL_VERSIONS

一个值得注意的要点是,服务多个模型版本,需要权衡的是更高的内存占用。所以上述的flag运行时,记住删除模型基本路径中的过时模型版本。

2.活用压缩

当你部署一个新的模型版本的时候,建议在复制到model_base_path之前,首先将导出的TensorFlow模型文件压缩成单个的压缩文件。Tensorflow Serving教程中包含了导出训练好的Tensorflow模型的步骤。导出的检查点TensorFlow模型目录通常具有以下文件夹结构:

一个包含版本号(比如0000001)和以下文件的父目录:

  • saved_model.pb:序列化模型,包括模型的图形定义,以及模型的元数据(比如签名)。
  • variables:保存图形的序列化变量的文件。

压缩导出的模型:

tar -cvzf modelv1.tar.gz 0000001

为什么需要压缩?

  1. 压缩后转移和复制更快
  2. 如果你将导出的模型文件夹直接复制到model_base_path中,复制过程可能需要一段时间,这可能导致导出的模型文件已复制,但相应的元文件尚未复制。如果TensorFlow Serving开始加载你的模型,并且无法检测到源文件,那么服务器将无法加载模型,并且会停止尝试再次加载该特定版本。

3.模型大小很重要

我们使用的TensorFlow模型相当大,在300Mb到1.2Gb之间。我们注意到,在模型大小超过64Mb时,尝试提供模型时将出现错误。这是由于protobuf消息大小的硬编码64Mb限制,如这个TensorFlow Serving在Github上的问题所述。

最后,我们采用Github问题中描述的补丁来更改硬编码的常量值。(这对我们来说还是一个问题。如果你可以找到在不改变硬编码的情况下,允许服务大于64Mb的模型的替代方案,请联系我们。)

4.避免将源移动到你自己的分支下

从实现时开始,我们一直从主分支构建TensorFlow Serving源,最新的版本分支(v0.4)在功能和错误修复方面落后于主分支。因此,如果你只通过检查主分支来创建源,一旦新的更改被合并到主分支,你的源也可能改变。为了确保人工制品的可重复构建,我们发现检查特定的提交修订很重要:

  • TensorFlow Serving
  • TensorFlow(TensorFlow Serving里的Git子模块)

期待未来加入的一些功能增强清单

这里是一些我们比较感兴趣的希望以后TensorFlow Serving会提供的功能:

  • 健康检查服务方法
  • 一个TensorFlow Serving实例可以支持多种模型类型
  • 直接可用的分布式存储(如AWS S3和Google存储)中的模型加载
  • 直接支持大于64Mb的模型
  • 不依赖于TensorFlow的Python客户端示例

阅读英文原文:How Zendesk Serves TensorFlow Models in Production


感谢冬雨对本文的审校。

给InfoQ中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家通过新浪微博(@InfoQ@丁晓昀),微信(微信号:InfoQChina)关注我们。

评价本文

专业度
风格

您好,朋友!

您需要 注册一个InfoQ账号 或者 才能进行评论。在您完成注册后还需要进行一些设置。

获得来自InfoQ的更多体验。

告诉我们您的想法

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我
社区评论

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

讨论

登陆InfoQ,与你最关心的话题互动。


找回密码....

Follow

关注你最喜爱的话题和作者

快速浏览网站内你所感兴趣话题的精选内容。

Like

内容自由定制

选择想要阅读的主题和喜爱的作者定制自己的新闻源。

Notifications

获取更新

设置通知机制以获取内容更新对您而言是否重要

BT