Neil Zhu,简书ID Not_GOD,University AI 创始人 & Chief Scientist,致力于推进世界人工智能化进程。制定并实施 UAI 中长期增长战略和目标,带领团队快速成长为人工智能领域最专业的力量。
作为行业领导者,他和UAI一起在2014年创建了TASA(中国最早的人工智能社团), DL Center(深度学习知识中心全球价值网络),AI growth(行业智库培训)等,为中国的人工智能人才建设输送了大量的血液和养分。此外,他还参与或者举办过各类国际性的人工智能峰会和活动,产生了巨大的影响力,书写了60万字的人工智能精品技术内容,生产翻译了全球第一本深度学习入门书《神经网络与深度学习》,生产的内容被大量的专业垂直公众号和媒体转载与连载。曾经受邀为国内顶尖大学制定人工智能学习规划和教授人工智能前沿课程,均受学生和老师好评。
TensorFlow 从名称上看就是两个部分——张量 tensor 和流 flow。非常形象的组合。众所周知,矩阵已经成为机器学习中的基础单元,若干的针对矩阵的计算优化使得现如今的机器学习成为可能。而一些矩阵的方法也是一些重要的机器学习算法的基础。张量 就是矩阵概念的推广,其表示更多维度的矩阵。而计算流是一种抽象过程,在如今的深度学习领域,这种一层层地计算可以很形象地看做是张量在计算模型上的流动。而这里的流可以看做是更加一般的计算过程,可以在不同的层级间跨越式流动。
本文作者均来自 Google Research:Martín Abadi, Ashish Agarwal, Paul Barham, Eugene Brevdo, Zhifeng Chen, Craig Citro, Greg S. Corrado, Andy Davis, Jeffrey Dean, Matthieu Devin, Sanjay Ghemawat, Ian Goodfellow, Andrew Harp, Geoffrey Irving, Michael Isard, Yangqing Jia, Rafal Jozefowicz, Lukasz Kaiser, Manjunath Kudlur, Josh Levenberg, Dan Mane, Rajat Monga, Sherry Moore, Derek Murray, Chris Olah, Mike Schuster, Jonathon Shlens, Benoit Steiner, Ilya Sutskever, Kunal Talwar, Paul Tucker, Vincent Vanhoucke, Vijay Vasudevan, Fernanda Viegas, Oriol Vinyals, Pete Warden, Martin Wattenberg, Martin Wicke, Yuan Yu, and Xiaoqiang Zheng
摘要
TensorFlow [1] 是一个表达机器学习算法的接口,并且是执行算法的实现框架。使用 TensorFlow 表示的计算可以在众多异构的系统上方便地移植,从移动设别如手机或者平板电脑到成千的GPU计算集群上都可以执行。该系统灵活,可以被用来表示很多的算法包括,深度神经网络的训练和推断算法,也已经被用作科研和应用机器学习系统在若干的计算机科学领域或者其他领域中,例如语言识别、计算机视觉、机器人、信息检索、自然语言理解、地理信息抽取和计算药物发现。该论文描述了 TensorFlow 的接口和我们在 Google 构建的结构实现。TensorFlow API 和参考实现都已经作为开源项目按照 Apache 2.0 协议在 2015 年 11 月释出,可以在 �这里 查看。
1 引言
Google 大脑项目开始于 2011 年,目的是探索在科研和 Google 的产品中超大规模深度神经网络的使用。作为这个项目的早期工作,我们构建了 DistBelief ——第一代的可扩展分布式训练和推断系统 [14],这个系统工作得很不错。我们和其他 Google 的同事使用 DistBelief 进行了广泛的研究包括非监督学习[31]、语言表示[35,52]、图像分类模型和目标检测[16,48]、视频分类[27]、语音识别[56,21,20]、序列预测[47]、Go 的移动选择[34]、�行人检测[2]、强化学习[38] 等等。另外,和 Google Brain 团队合作中,超过 50 个 Google 内部的团队和其他 Alphabet 公司也已经部署了使用 DistBelief 的深度神经网络在众多产品中,包括 Google Search[11]、广告产品、语音识别系统[50,6,46]、Google Photos[43]、Google Maps 和 街景[19]、Google 翻译[18]、Youtube 和很多其他的产品。
基于我们使用 DistBelief 的经验和对于期望用来训练和使用神经网络的系统特性和需求更加完备地理解,我们构建了 TensorFlow——第二代大规模机器学习模型的实现和部署的系统。TensorFlow 使用通过类似数据流模型的计算,将这些计算映射到不同的硬件平台例如使用包含一个或者多个 GPU 显卡的装有 Android 和 iOS 的单个机器上进行推断,到运行在数百台包含数千个 GPU 的大规模系统训练和推断。拥有一个单一的系统可以扩展分布到众多的平台上可以大大简化真实场景中机器学习系统的使用,正如我们在用分离的系统进行大规模训练和小规模的部署,会产生巨大的维护代价和较差的抽象效果。TensorFlow 的计算被表示为含状态的数据流图(在第二节详细讲解),我们聚焦在让这个系统足够灵活能够快速地实验研究中产生的新模型,并同时充分地提升产品级训练的性能和部署机器学习模型健壮性。为扩展神经网络训练搞更大的部署环境,TensorFlow 允许 client 简单地表达不同类型的并行通过复制和并行执行一个核心模型数据流图,依赖不同计算设备合作更新一个共享的参数或者其他的状态。 对计算描述的微妙变动可以使用较低的代价来达到和尝试很多不同的并行的方法。一些 TensorFlow 的用途借助参数更新的一致性来实现灵活性,我们可以在一些更大的部署环境中轻易表达和利用这些同步上的松弛。对比 DistBelief,TensorFlow 的编程模型更加灵活,性能也更好,支持在大规模的异构硬件平台上训练和使用很多的模型。
DistBelief 的内部用户已经切换成 TensorFlow 了。这些客户依赖 TensorFlow 来研究和产品,执行诸如在移动电话计算机视觉模型的推断到使用数百台机器进行千亿级样本的千亿级参数的深度神经网络的训练[11,47,48,18,53,41]。尽管这些应用集中在机器学习和深度神经网络上,我们希望 TensorFlow 的抽象可以用在其他的领域中,例如其他的机器学习算法或者可能其他类型的数值计算。我们按照 Apache 2.0 协议在 2015 年 11 月开源了 TensorFlow API,可以在 www.tensorflow.org 查看。
本文下面的部分更加细致地描述了 TensorFlow。第二节介绍编程模型和 TensorFlow 接口的基本概念,第三节介绍单机和分布式的实现 。第四节给出了基本编程模型的扩展,第五节介绍了一些基本实现的优化方法。第六节给出了一些使用 TensorFlow 的实验结果,第七节描述了一些使用 TensorFlow 编程的 idiom,第九节则是一些在 TensorFlow 核心外围的工具。第十节和第十一节分别讨论了未来和相关的工作,最后一节给出了总结性想法。
2 编程模型和基本概念
TensorFlow 的计算由一个有向图描述,这个图中由一个节点集合组成。该图表达了数据流计算,作出了一些类型的节点保持和更新持久状态和让分支及循环控制结构类似于 Naiad 的行为方式的扩展。客户一般都会使用 TensorFlow 支持的前端语言(C++或者Python)构建一个计算图。在图 1 中展示了一段样例使用 Python 构建并执行了一个 TensorFlow 的计算图,结构计算图在图 2 中展示。
图 1 图 2在一幅 TensorFlow 图中,每个节点(node)有一个或者多个输入和零个或者多个输出,表示一种操作(operation)的实例化。流过图中正常的边(输出到输入)的值都是张量(tensor),任意维度的数组其中基础元素类型是指定的或者在图的构造过程中自动推断出来的。特别的边,我们称之为控制依赖(control dependencies),同样也存在在图中:这类边上没有数据流过,但是他们表示源节点必须在目标节点的控制依赖开始执行前完成运行。�由于我们的模型包括可变状态,控制依赖可以被直接用来确保 happens-before 关系。我们的实现同样会插入控制依赖来确保独立操作之间的顺序,比如说作为控制内存使用最高峰值的方式。
In computer science, the happened-before relation (denoted:
) is a relation between the result of two events, such that if one event should happen before another event, the result must reflect that, even if those events are in reality executed out of order (usually to optimize program flow). This involves ordering events based on the potential causal relationship of pairs of events in a concurrent system, especially asynchronous distributed systems. It was formulated by Leslie Lamport.[1]
In Java specifically, a happens-before relationship is a guarantee that memory written to by statement A is visible to statement B, that is, that statement A completes its write before statement B starts its read.[1]
操作和核(kernel)
一个操作有一个名字。它表示一个抽象的计算(比如说,“矩阵相乘”或者“相加”)。一个操作可以有属性(attribute),所有的属性必须提供或者在图构造的过程中推断出以实例化一个节点来执行操作。属性通常的使用方式是让操作在不同的张量元素类型上多态(例如,两个 float 类型的张量和两个 int32 类型的张量)。核(kernel)是一种操作的特别实现,可以运行在一个特定类型的设备上(如 CPU 或者 GPU)。TensorFlow 的 binary 定义了可以通过注册(registration)机制实现的操作和核的集合上,这个集合可以通过连接额外的操作/核的定义/注册。表 1 展示了内置于 TensorFlow 核心库的一些操作类型。
表 1:TensorFlow 的操作类型会话(session)
客户端通过创建会话(session)和 TensorFlow 系统进行交互。为了创建一个计算图,会话接口支持外部(external)方法来提升当前由包含额外节点和边的会话的图(当会话创建时初始的图是空的)。另一个由会话接口提供的主要的操作就是 Run,以需要计算的输出名称和替换某些输出节点的张量的操作集合作为其参数输入。通过控制 Run 的参数,TensorFlow 的实现可以计算所有节点的必须执行传递闭包来计算需要的输出,然后安排执行合适节点来保证他们的依赖关系(在3.1小节详细讲解)。大多数 TensorFlow 的使用都是针对一个图启动一个会话,然后执行整个图或者通过 Run 调用来执行分离的子图数千或者数百万次。
变量(variable)
在大多数计算中,图都是执行多次的。大多数的张量在一次执行后不会存活。然而,变量(variable)是一种特别的操作可以返回一个在图执行若干次过程中存活的持久化的可变张量的句柄。这个句柄可以传递给一系列特定的操作,例如 Assign 和 AssignAdd(等同于 +=)就可以改变其引用的张量了。对应 TensorFlow 在机器学习中的应用,模型的参数典型地就存放在变量引用的张量中,并作为模型训练图的 Run 的一部分进行更新。
3 实现
TensorFlow 系统的主要部分就是客户端,它使用了会话接口来和 master 及一个或者多个的 worker processes 进行通信,每个 worker process 负责对一个或者多个计算设备(CPU 核或者 GPU card)的任意访问和在这些设备上进行图节点的计算按照 master 的要求执行。我们有本地和分布式实现的 TensorFlow 接口。本地实现通常是客户端、master 和 worker 都是在同一台机器上在一个单一的操作系统进程(可能包括多个设备,比如说装了多个 GPU card的设备)上运行。分布式实现采用了本地实现的很多的代码,但是扩展了对客户端、master 和 worker 可以在不同的机器的不同的进程上运行的场景支持。在我们的分布式环境中,这些不同的任务对应于 cluster 调度系统分配在 job 中的容器中[51]。这两种不同的模式在图 3 中进行的展示。本节剩下的部分讨论了在两种实现中遇到的问题,3.3 节讨论了针对分布式实现的一些问题。
设备
设备是 TensorFlow 的计算核心。每个 worker 负责一个或者多个设备,每个设备有一个设备类型和一个名字。设备名字由识别设备类型的部分,在 worker 中的设备索引,以及在分布式设定中,worker 的 job和任务(或者 localhost 当设备是和进程在同一机器时)的标志构成。一些例子如/job:localhost/device:cpu:0
或者 /job:worker/task:17/device:gpu:3
。我们已实现了 CPU 和 GPU 的设备接口而其他的设备类型也有了通过注册机制完成的设备实现方式。每个设备对象负责管理分配和解除分配设备内存,对在 TensorFlow 实现中的更高层请求任意 kernel 的执行调度管理。
张量
实现中的张量是一种有类型的、多维度数组。我们支持若干张量元素类型,包含大小为从 8 bit 到 64 bit 的带符号和无符号整型,IEEE 浮点数和双精度类型、复数类型和字符串类型(任意长的字节数组)。合适大小的后台存储通过一个分配器进行管理,该分配器�由张量所处的设备确定。张量的后端存储缓存是引用计数的并在没有引用存在时解除分配。
3.1 单设备执行
首先考虑最简单的执行场景:单一的worker进程运行在单一的设备上。图上的节点按照代表节点之间的顺序执行。特别地,我们会在每个节点上保持一个计数来记录这个节点上还没有执行的依赖。一旦这个计数变为 0,节点就可以被调度使用,并会加入到待续的队列中。待续队列按照某个非指定的顺序处理,指派节点执行的kernel 到设备对象上。当一个节点完成执行,所有依赖这个完成的节点的节点的计数都会减少。
3.2 多设备执行
一旦系统有了多个设备,有两个主要的复杂情形出现:确定图中每个节点的计算所处的设备,然后管理由上一步确定的置放决定产生的设备间的所需的数据通信。后续部分讨论这两个问题。
3.2.1 节点的置放
给定计算图,TensorFlow 实现的主要责任之一就是将计算映射到可用的设备集合上。这个算法的简单版本下面给出。参见第 4.3 节有关该算法支持的扩展。
该置放算法的输入是一个代价模型,包括对每个图节点的输入和输出张亮的规模的估计,和对每个节点在给于其输入张量时的计算时间的。这个代价模型或者是基于关联不同操作类型的启发式规则的静态估计,或者基于实际的为更早的图的执行而做的置放决定集合衡量。
置放算法首先运行模拟的图的执行过程。模拟按照下面描述进行,对每个节点使用贪心策略选择一个设备。节点到设备的置放过程也是用作真实执行的置放。
置放算法从计算图的源点开始,在系统中的每个设备上模拟相应的活动。对每个在遍历中抵达的节点,可选 available 设备的集合会被考虑到(设备可能会由于其没能提供实现了特定操作的kernel而不可选)。对那些拥有多个可选设备的节点,置放算法使用一种贪心策略来检查在每个可能谁被上置放节点需要完成的时间的效果完成决策。这种启发式规则考虑了根据代价模型在那种设备上估计的和衡量的执行时间,还有任何用来从其他设备传输输入到该节点的通信的代价。其中节点的操作完成最快的设备会被选作该操作的设备,置放决策然后会继续针对图中其他的节点进行处理,包含那些已经做好模拟执行的下游节点。第 4.3 节描述了一些扩展,让用户可以提供提示和部分限制来指导置放算法。这个算法现在还在持续开发的过程中。
3.2.2 交叉设备通信
一旦节点置放已经计算好,图就被划分成子图的集合,每个子图对应于一个设备。从 x 到 y 任何交叉设备的边都会被移除并用一条从 x 到一个 x 的子图中新的 Send 节点的边和从在 y 子图中对应的 Receive 节点到 y 的边代替。参见图 4 中所进行的变换。
图 4在运行时刻,Send 和 Receive 节点合作进行跨设备的数据交换。这使得我们可以隔离所有在 Send 和 Receive 内部实现的通信,这样简化了运行时刻剩下的部分工作。
当我们插入 Send 和 Receive 节点时,我们将在特定设备上的特定张量的所有使用者进行合并规整来使用单个 Receive 节点,而不是对特定设备上的每个下游使用者都给一个 Receive 节点。这确保了需要使用的张量数据仅仅会从源设备到目的设备传输一次,而在目的设备上的张量内存也只会分配一次(而非多次,参看图 4 的节点 b 和 c)。
通过这种方式处理通信,我们也允许了不同设备上的图中的个别节点调度可以被去中心化到 workers 上:Send 和 Receive 节点传达了在不同的 worker 和 设备间必要的同步信息,master 仅仅需要对每个图的执行给出一个 Run 请求给那些包含图中任意节点的 worker,而不是会对所有节点或者每个跨设备通信都进行调度。这也让系统更加可扩展,并允许比通过 master 来强制进行所有的调度更加精确的节点执行。
3.3 分布式执行
计算图的分布式执行非常类似于多设备执行。在设备置放后,子图会针对每个设备创建。用于 worker 进程之间的通信的 Send/Receive 节点对使用了诸如 TCP 或者 RDMA 这样的远程通信机制进行跨机器的数据迁移。
容错
分布式执行中的错误可以在很多地方进行检测。最主要的有 (a) 在 Send 和 Receive 节点对之间的通信错误,(b) 从 master 进程到每个 worker 进程的周期性的健康状态检测。
如果发现了错误,整个图的执行就会终止,并从头开始。但是回想之前变量节点对应于那些在执行过程中记忆持有(persist)的张量。我们支持在重启过程中的一致的检查点和状态恢复。特别是,每个变量节点连接在一个 Save 节点上。这些 Save 节点周期性地执行,比如说每 N 次迭代,或者每隔 N 秒。他们执行的时候,变量的内容被写到持久化的存储中,比如说,一个分布式的文件系统。类似地,每个变量连接在一个 Restore 节点上,只会在一次重启后的第一个迭代中启用。在 4.2 节有某些节点仅能够在某些图的执行中启用的细节。
4 扩展
本节我们描述在第 2 节给出的编程模型中几个更加高级的特性。
4.1 梯度计算
很多优化算法,包括常用的机器学习训练算法,如随机梯度下降 [45],计算代价函数关于一个输入集合的梯度。由于该算法广泛的应用需求,TensorFlow 已经内置了对自动梯度计算的支持。如果张量 C 通过一个复杂的子图操作依赖于张量 {X_k} 集合,那么就有一个内置的函数可以返回张量 {dC/dX_k}。梯度张量如同其他张量一样通过扩展 TensorFlow 图使用下面的流程进行计算的。
当 TensorFlow 需要计算一个张量 C 关于某个张量 I 时,首先找出计算图中从 I 到 C 的路径。然后从 C 回溯到 I,而对在回溯路径中的每个操作都会添加一个节点到 TensorFlow 的图上,包含回溯路径上使用链式法则的偏导数。新加的节点计算前向路径中相对应的操作的梯度函数。梯度函数可以是任何的操作。这个函数不但可以用在反向路径中计算出的偏导数作为输入,同样也能选择性地用前向操作的输入输出作为输入。图 5 展示了图 2 中例子的代价函数的梯度。Grey arrows show potential inputs to gradient functions that are not used for the particular operations shown. 图 1 所对应的是计算这些梯度:
[db, dW, dx] = tf.gradients(C, [b, W, x])
通常操作可能会有多个输出,C 可能仅仅会依赖于其中一部分。例如,如果操作 O 有两个输出 y_1 和 y_2,C 仅仅依赖于 y_2,那么 O 的梯度函数的第一个输入就可以设置为 0 因为 dC/dy_1 = 0。
自动梯度计算让优化尤其是内存耗用变得复杂。在执行前向计算子图时,那些显式地由用户创建的操作,可能的启发式想法就是通过观察图被构建的次序来确定哪个节点执行下一步。
这通常指的是临时输出会在创建后不久就是用到,所以他们的内存可以很快重新用到。当这个启发式想法不适用时,用户可能会改变图构建的次序,或者增加在第 5 节将会介绍的控制依赖。当梯度节点自动加入到图中时,用户控制就会变弱,启发式想法就失效了。特别地,因为梯度逆转了前向计算的顺序,在图早期用到的张量会在梯度计算的结尾时重新被频繁用到。这样的张量会消耗很多的 GPU 内存,也就不必要地限制了计算的规模。我们正积极地提升内存关联的效果以更好地处理这个问题。是用更加精密的启发式想法来确定图执行的次序,重计算张量而不是存储在内存,将长效的张量从 GPU 内存中移到 CPU 内存中等等都是可以尝试的思路。
4.2 部分执行
常常有 client 希望执行整个图的子图。为了支持这一点,只要 client 在 Session 中构建出一个计算图,我们 Run 的方法运行他们执行整个计算图的任意的子图,并将任意的数据注入到图中的任何边上,以及获取流经任何边上的数据。
图中每个节点都有一个名字,一个节点的每个输出都通过源节点名和节点输出端口确定,从 0 开始计数(例如,“bar:0” 表示 “bar” 节点的第一个输出,而 “bar:1” 则是第二个输出)。
Run 调用的两个参数可以帮助定义准确的将要执行的子图。第一,Run 调用接受输入,一个可选的映射 name:port 名来填充张量值。第二,Run 调用接受 output_names,输出 name[:port] 列表指定了需要执行的节点,并且,如果端口出现在名字中,那么对那个节点的特定输出张量值就应该返回给 client 如果 Run 调用成功完成。
Paste_Image.png计算图是基于输入输出的值进行变换的。每个在输入中指定的 node:port 使用 feed 节点替换,这个会选出从特定初始化的用来给 Run 调用的 Rensezvous 对象的入口选择出给定的输入向量。类似地,每个输出名字关联在一个特定的 fetch 节点上,可供输出张量的存储,并在 Run 调用完成时返回给客户端。最后,一旦图已经被这些特定的 fee 和 fetch 节点重写,将要执行的节点集合可以被从每个被输出命名的节点出发,然后使用图的依赖关系来确定必须在重写图中执行产生输出的整个节点的集合中进行反向传播。图 6 展示了左边的原始图,以及变换的图,其中 Run 被激活输入是 {b} 输出是 {f:0}。因为我们仅仅需要计算节点 f 的输出,我们不需要执行节点 d 和 e,因为他们没有对 f 做出贡献。
4.3 设备限制
TensorFlow 客户端可以通过为一个节点哪些设备可以在其上执行来提供部分的限制来控制节点在设备中的放置。例如,“仅仅放置这个节点在类型为 GPU 的设备上” 或者 “这个节点可以被放置在任何在 /job:worker/task:17 中的设备上”,或者 “共享这个节点和 variable13 节点”。按照这些限制,放置算法就可以负责选择节点的分配来提供快速执行,并且满足不同的设备本身的限制,例如内存的总量的限制来执行子图的计算。
支持这样的限制需要对 3.2.1 节介绍的放置算法进行调整。我们首先计算每个节点的可行设备集合,然后使用 union-find 算法来计算图中必须放在一起的图的组成部分。对每个这样的组成部分,我们计算可行设备集合的交集。计算出的每个节点可行设备和放置算法的模拟器很容易匹配。
4.4 控制流
尽管没有任何显式的控制流数据流图非常强大,但是我们发现了一些例子中支持条件和循环可以得到更加简洁而高效的机器学习算法的表示。
在 Arvind [3] 中描述的数据流机器观点,我们引入了一个小控制流操作的集合进入 TensorFlow 并且推广 TensorFlow 使之能够处理循环的数据流图。Switch 和 Merge 操作符可以让我们基于布尔值的张量来跳过整个子图的执行。Enter Leave NextIteration 操作符可以进行迭代。高级程序构造如 if-conditional 和 while-loop 可以很容易用这些控制流操作编译成数据流图。
TensorFlow 运行时刻实现了一个 tags 和 frames 概念,这与 MIT Tagged Token Machine 类似。每次迭代都使用了唯一一个 tag,这个执行的状态通过 frame 表示的。只要被使用,输入就可以进入一次迭代;因此,多次迭代可以被并发地执行。
TensorFlow 使用分布式协同机制来使用控制流进行图的执行。一般来说,循环可以包含被分配到多个不同的设备上的节点。因此,管理循环的状态成为了一个分布式终止检测的问题。TensorFlow 的解决方案基于图的重写。在图的划分(partitioning)过程中,我们自动地增加控制节点到每个划分(partition)上。这些节点实现了一个小的状态机,来管理每次迭代的开始和终止,确定整个循环的终止。
如上所示,我们通常通过随机梯度下降来训练机器学习模型,将梯度计算表示成数据流图的一部分。在模型包含控制流操作时,我们必须在对应的梯度计算中处理这些操作。例如,使用 if-conditional 来对模型梯度计算时,需要知道哪个分值会被选择,然后应用梯度逻辑到这个分支上。类似地,使用 while-loop 来对模型梯度计算时,需要知道多少次迭代,同样还依赖在这些迭代中出现的中间值。基本技术就是重写这些图来记住需要计算梯度计算的值。我们这里省略掉细节介绍。
4.5 输入操作
尽管输入数据可以被通过 feed 节点提供给计算,另一个用来训练大规模机器学习模型通常的机制是在图中采用特定的输入操作节点,这些节点一般来说会通过文件名配置,并产生一个包含一个或者多个样本的张量来自在每次执行时的那些文件集合中存放的数据。这使得数据可以被直接从底层存储系统读出到将要执行接下来处理的内存中。在配置中,从 worker 进程分隔开的客户端进程,如果数据给定,一般会需要一个额外的网络 hop(从存储系统到客户端然后从客户端到 worker vs. 使用一个输入节点时直接从存储系统到 worker)。
4.6 队列
队列是一个加入到 TensorFlow 中的有用的特性。他们允许图的不同部分来异步地执行,可能按照不同的节奏,来通过 Enqueue 和 Dequeue 操作来处理数据。在队列中空间尚存时,可以进行 Enqueue 操作;在指定的最小数量的元素可以取出时,可以进行 Dequeue 操作。队列的一个用途就是允许输入数据可以从磁盘文件中预先取出,这样可以同时进行前一批的数据的处理。同样还能用来进行其他类型的归类,包括聚合很多梯度来计算某种更加复杂的梯度组合,或者来组织不同的输入语句到递归语言模型到语句的近似同样长度的 bins 中,这样处理得更有效率。
在一般的 FIFO 队列上,我们还实现了一个 shuffling 队列,可以对内存内的缓冲区内的元素进行随机洗牌。洗牌功能在机器学习算法中比较有用,常常需要对样本进行随机化。
4.7 容器
容器 是用来管理长期存在的可变状态的机制。变量 Variable 反向存放在一个容器内。默认的容器是直到进程终止时都是存活的,但是我们也允许其他的容器。容器可以通过清除它的整个内容进行重置。使用容器,就可以分享状态在完全不相交的不同会话中的计算图中。
5 优化
本节,我们给出一些在 TensorFlow 实现中的优化,这些优化对系统性能和资源利用率起到了提升作用。
5.1 共同子表达式消除
因为计算图的构造通常是由很多不同层的抽象完成的,计算图一般都会包含冗余的计算过程的多个副本。为了解决这个问题,我们已经实现了一个共同子表达式过程,类似于 Click[12] 中给出的算法,运行在计算图上,并将操作的多重复制,用这些节点中的一个单独节点代替,并将图的边重定向以满足这个归一化。
5.2 控制数据通信和内存分配
5.3 异步 kernel
5.4 用于 kernel 实现的优化库
5.5 �有损压缩
6 状态和经验
7 常用编程规范
8 性能
9 工具
9.1 TensorBoard: 图结构和总结统计可视化
9.2 性能追踪
10 未来工作
11 相关工作
12 结论
参考文献
完整内容请大家关注我们 UAI 的创业项目:
- 微信公众号:UAI 人工智能
- 微信号:FEIGlobal
如要咨询相关项目可以联系该微信号。
网友评论