├── README.md ├── 0-practical-bayesian-optimization-of-machine-learning-algorithms.md └── 1-tf-white-paper.md /README.md: -------------------------------------------------------------------------------- 1 | # Paper-zh 2 | 翻译一些比较好的论文 3 | 4 | - 原文:[practical-bayesian-optimization-of-machine-learning-algorithms](http://papers.nips.cc/paper/4522-practical-bayesian-optimization-of-machine-learning-algorithms.pdf) 5 | - 译文:[贝叶斯优化机器学习算法实践](0-practical-bayesian-optimization-of-machine-learning-algorithms.md) 6 | 7 | - 原文:[TensorFlow: A system for large-scale machine learning](http://arxiv.org/pdf/1605.08695v2.pdf) 8 | - 译文:[TensorFlow:大规模机器学习系统](http://www.jianshu.com/p/65dc64e4c81f) 9 | -------------------------------------------------------------------------------- /0-practical-bayesian-optimization-of-machine-learning-algorithms.md: -------------------------------------------------------------------------------- 1 | # 机器学习算法贝叶斯优化实践 2 | ## 摘要 3 | 使用机器学习算法经常需要小心地调参(学习参数或者模型参数)。不幸的是,这种调节往往是需要专业经验,运气,或者一些暴力搜索,这些东西构成的黑魔法。因此,能够优化任何给出的学习算法的自动调参有着巨大的吸引力,在这篇文章中,我们在贝叶斯优化的角度考虑这个问题,学习算法泛化的性能由高斯过程来模拟。我们说明了几种高斯过程本质,比如内核类型,和对超参数的处理,可能在获取一个好的优化器这个问题上可以达到专家级的表现。我们描述了新的算法,考虑变量在学习算法实验中的代价(周期),这可以在多核并行实验作为表征。我们展示了这些合适的算法优化了先前的自动化过程,并且在许多算法上达到甚至超过了人类专家级优化,包裹LDA,SVM,CNN。 4 | ## 1 介绍 5 | 机器学习算法几乎没有参数无关的:参数控制来学习率,模型的容量,都需要指定。通常认为这些参数是一种麻烦。人们倾向于发展拥有更少参数的机器学习算法。另一方面,这个话题另一种灵活的方式是,将超参数的优化视为一个自动化过程。特别的,我们可以将这种优化视为一个未知黑箱函数,然后调用处理这种问题的算法。一个好的选择是贝叶斯优化,已经在大量挑战性的优化问题上证明它比其他全局优化算法更佳。对于连续函数,贝叶斯优化通常通过假设未知函数被一个高斯过程筛选过并且为这个函数维护一个后分布作为观察,或者,在我们的例子里,用不同的参数运行学习算法实验。为了挑选下一个实验的参数,我们可以在当前最好结果上优化期望提高(EI)或者优化高斯过程的上置信度(UCB)。EI和UCB在大量寻找全局最优问题的多模黑箱函数评估问题上被证明有效。 6 | 7 | 机器学习算法,然而,有着特定的与其他黑箱优化问题不同的特征。首先,每个函数评估需要大量的时间,训练一个有着10个隐含单元的小型神经网络需要的时间比一个更大的有着1000个隐含单元的神经网络消耗更少的时间。即使不考虑耗时,云计算的出现使得需要的大内存学习机器,改变了不同隐含单元所需的实际经济价值。其次,机器学习实验通常是并行的,多核或多机的。在这些情况中,标准顺序GP优化过程可能不理想。 8 | 9 | 在这篇文章中,我们实践了好的机器学习算法贝叶斯优化。我们证明,基于GP超参数的优化比基于GP内核的优化要更好,正如前面所说的。我们第二个贡献是描述了新的算法,考虑了变量和实验的未知代价,以及多核可用性以并行运行实验。 10 | 11 | 高斯过程已经被证明在计算实验的代理模型上有效,基于此,合适性分析,标准化和预测的实践被建立起来。这些策略在优化的时候却不被考虑,实际上,这些对于机器学习研究者理解不同参数时模型的有效性很有帮助。哈特已经发展了一个顺序的基于模型的优化策略,以以设置满意度,用随机森林混合整数编程。然而我们考虑的机器学习算法,构造一个完全贝叶斯手段,因他们昂贵的自然必要性以最小化评估的时间。贝叶斯优化策略也被用于调节蒙特卡洛算法的超参数。最近本格斯特拉已经探索了大量优化机器学习算法的策略。他们证明网格搜索策略比随机搜索差,建议使用高斯过程贝叶斯优化以优化一个平方到指数的分布,和TPA。 12 | 13 | ## 2 高斯过程优先的贝叶斯优化 14 | 由于在其他类型的优化中,在贝叶斯优化里,我们感兴趣的是找到一个函数在自变量的某个范围内的最小值,我们会把他作为R^D的自己。让贝叶斯优化和其他过程不同的是他构建了f(x)的一个概率模型,然后推导这个模型以决定下一个评估函数的自变量区间在哪里,同时集成不确定性。关键的逻辑是,使用上一次f(x)评估中所有的可用的信息,而非仅仅依赖于局部梯度和hessian近似。这导致一个能用相对少得多的步骤找到困难非凸函数的最小值的过程,代价是决定下一个要尝试的点需要的计算更多了。当评估f(x)执行起来很昂贵的时候,也就是需要训练机器学习算法的时候,容易调整一些额外的计算以取得更好的决策,从贝叶斯优化规范注意的大方向上,以及先前工作的回顾看。在这个部分,我们简单地回顾了通常的贝叶斯优化途径,在第三部分我们将讨论我们的创新性贡献。 15 | 16 | ### 2.1 高斯过程 17 | 高斯过程是一个方便而强大的函数优先分布,我们在这里用它作为这个表达式:f:X -> R。高斯过程由任意有限N个点的集合{Xn ∈ X}定义,推导出多变量的RN空间上的高斯分布。第n个点被作为f(xn)的函数值,高斯分布优雅的边缘化属性允许我们计算边缘,以闭合的形式计算各种情况。函数结果的分布的支持和属性由一个平均函数m: X -> R决定,以及一个正定分布函数K: X * X -> R。我们会在section3.1 讨论分布函数的影响。作为高斯过程的一个概览,参考Rasmussen和Williams。 18 | 19 | ### 2.2 贝叶斯优化的获取函数 20 | 我们假设函数f(x)有一个高斯过程优先绘制,我们的的观察是{Xn, yn}的形式,yn ~ N(f(xn), v), v是引入函数观察中的噪声的方差。这个优先因子和这些数据导出了一个函数的落后因子,也就是获取函数,我们定义为a:X->R+,通过一个代理优化Xnext = argmax(a(x))决定了X中的哪些点需要在下一次评估,并且多个不同的函数已被得到推荐。通常,这些获取函数依赖于先前的观察,包括GP超参数,我们定义这种以来为a(x;{Xn,yn}, θ)。有几种流行的获取函数的选择。在高斯过程优先中,这些函数仅仅通过预测均值函数μ(X; {Xn,yn}, θ)和预测方差函数对函数依赖于模型。在处理过程中,我们将会定义最好的当前值为Xbest = argminxn f(xn), 以及用标准格式定义累计分布函数为Φ(·)。 21 | 22 | **提升的可能性** 一种直观的策略是在当前的最佳值之上最大化提升概率。在GP的情况下,这种情况可以分析计算: 23 | 24 | > 原文第三页2.2 25 | 26 | **期望提升** 另一种可选图形,我们可以选择在当前最佳值上获得最大化期望提升。这在高斯过程下也有闭合形式。 27 | 28 | > 原文第三页2.2 29 | 30 | **GP上置信度** 一个最近的发展是,探索更低的置信边界(上界,认为是最值)以构建最小化优化过程中最小化反悔程度的函数。这类获取函数有着这样的形式: 31 | 32 | > 原文第三页2.2 33 | 34 | 有着可调的k以平衡操作与探索。 35 | 36 | 在这篇文章中我们会关注EI标准,因为它被证明比概率可能性要好,但不像GP上置信度,它不需要有自己的调节参数,尽管EI算法在最小化问题上运行得很好,我们希望记下反悔形式可能在某些设置下更加适合。我们在Setion4.1做了基于EI和GP-UCB的比较。 37 | 38 | 39 | ## 3 贝叶斯优化超参数的实践考虑 40 | 尽管我们希望有一个优化昂贵函数的优雅框架,但有许多因素限制它成为一个在机器学习问题上优化超参数广泛使用的技术。首先,对于实际问题,分布函数和相关的超参数的合适选择是什么是不确定的。其次,因为函数评估本身可能包括耗时的优化过程,不同问题在耗时上可能有很大的不同,而这在框架设计上也值得考虑。第三,优化算法必须利用多核并行思想,以良好匹配现代计算环境。在本节中,我们提供这三个问题的解决措施。 41 | 42 | ### 3.1 分布函数以及对分布超参数的处理 43 | 高斯过程表达函数复杂分布的能力仅仅依赖于分布函数。而非退化分布函数对应无线基线,它们不会与强假设关于相似函数对应。特别的,自动相关决策平方指数内核: 44 | 45 | > 原文第三页3.1 (4) 46 | 47 | 经常是高斯过程回归的默认选择。然而,这样的分布函数的采样函数对与实际优化问题不现实地慢。想法,我们使用ARD matern 5/2 内核: 48 | 49 | > 原文第三页3.1 (5) 50 | 51 | 这个分布函数导致了二分的采样函数,与quasi-Newton方法做出的假设对应,但不需要平方指数平滑。 52 | 53 | 选择分布形式后,我们必须管理控制行为的超参数(这些超参数与其他全贝叶斯优化有区别),也必须管理均值。对于我们感兴趣的问题,通常我们有D + 3个高斯过程超参数: D是θ1:D的长度,分布的振幅为θ0,观察噪声v,常量均值m。最常见的主张的途径是通过在高斯过程中,优化边缘可能性,使用一个点来估计这些超参数, p(y | {x n } N 54 | n=1 , θ, ν, m) = N (y | m1, Σ θ + νI), y = [y 1 , y 2 , · · · , y N ] T,Σ θ是N个输入点在超参数θ下的分布矩阵。 55 | 56 | 然而,对于全贝叶斯超参数处理来说,用超参数优化边缘,计算集成获取函数是必要的: 57 | 58 | 59 | > 原文第四页 (6) 60 | 61 | â(x ; {x n , y n }) = a(x ; {x n , y n }, θ) p(θ | {x n , y n } N n=1 ) dθ, 62 | 63 | a(x)依赖于θ和所有观察。对于概率提升和期望提升,这些提升对于超参数的不确定是正确的泛化。我们因此可以根据高斯过程超参数获得的采样结果调整获取函数,并对集成期望提升做蒙特卡洛估计。这些采样可以用切片采样高效地获得,优化和蒙特卡洛计算都比解决N维线性问题多立方倍的代价,经验告诉我们全贝叶斯处理更加合适。图1展示了集成期望提升是如何改变获取函数的。 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /1-tf-white-paper.md: -------------------------------------------------------------------------------- 1 | TensorFlow 白皮书 2 | 字数8092 阅读12538 评论21 喜欢30 3 | TensorFlow 从名称上看就是两个部分——张量 tensor 和流 flow。非常形象的组合。众所周知,矩阵已经成为机器学习中的基础单元,若干的针对矩阵的计算优化使得现如今的机器学习成为可能。而一些矩阵的方法也是一些重要的机器学习算法的基础。张量 就是矩阵概念的推广,其表示更多维度的矩阵。而计算流是一种抽象过程,在如今的深度学习领域,这种一层层地计算可以很形象地看做是张量在计算模型上的流动。而这里的流可以看做是更加一般的计算过程,可以在不同的层级间跨越式流动。 4 | 本文作者均来自 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 5 | 6 | 摘要 7 | TensorFlow [1] 是一个表达机器学习算法的接口,并且是执行算法的实现框架。使用 TensorFlow 表示的计算可以在众多异构的系统上方便地移植,从移动设别如手机或者平板电脑到成千的GPU计算集群上都可以执行。该系统灵活,可以被用来表示很多的算法包括,深度神经网络的训练和推断算法,也已经被用作科研和应用机器学习系统在若干的计算机科学领域或者其他领域中,例如语言识别、计算机视觉、机器人、信息检索、自然语言理解、地理信息抽取和计算药物发现。该论文描述了 TensorFlow 的接口和我们在 Google 构建的结构实现。TensorFlow API 和参考实现都已经作为开源项目按照 Apache 2.0 协议在 2015 年 11 月释出,可以在 这里 查看。 8 | 9 | 1 引言 10 | 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 和很多其他的产品。 11 | 12 | 基于我们使用 DistBelief 的经验和对于期望用来训练和使用神经网络的系统特性和需求更加完备地理解,我们构建了 TensorFlow——第二代大规模机器学习模型的实现和部署的系统。TensorFlow 使用通过类似数据流模型的计算,将这些计算映射到不同的硬件平台例如使用包含一个或者多个 GPU 显卡的装有 Android 和 iOS 的单个机器上进行推断,到运行在数百台包含数千个 GPU 的大规模系统训练和推断。拥有一个单一的系统可以扩展分布到众多的平台上可以大大简化真实场景中机器学习系统的使用,正如我们在用分离的系统进行大规模训练和小规模的部署,会产生巨大的维护代价和较差的抽象效果。TensorFlow 的计算被表示为含状态的数据流图(在第二节详细讲解),我们聚焦在让这个系统足够灵活能够快速地实验研究中产生的新模型,并同时充分地提升产品级训练的性能和部署机器学习模型健壮性。为扩展神经网络训练搞更大的部署环境,TensorFlow 允许 client 简单地表达不同类型的并行通过复制和并行执行一个核心模型数据流图,依赖不同计算设备合作更新一个共享的参数或者其他的状态。 对计算描述的微妙变动可以使用较低的代价来达到和尝试很多不同的并行的方法。一些 TensorFlow 的用途借助参数更新的一致性来实现灵活性,我们可以在一些更大的部署环境中轻易表达和利用这些同步上的松弛。对比 DistBelief,TensorFlow 的编程模型更加灵活,性能也更好,支持在大规模的异构硬件平台上训练和使用很多的模型。 13 | 14 | DistBelief 的内部用户已经切换成 TensorFlow 了。这些客户依赖 TensorFlow 来研究和产品,执行诸如在移动电话计算机视觉模型的推断到使用数百台机器进行千亿级样本的千亿级参数的深度神经网络的训练[11,47,48,18,53,41]。尽管这些应用集中在机器学习和深度神经网络上,我们希望 TensorFlow 的抽象可以用在其他的领域中,例如其他的机器学习算法或者可能其他类型的数值计算。我们按照 Apache 2.0 协议在 2015 年 11 月开源了 TensorFlow API,可以在 www.tensorflow.org 查看。 15 | 16 | 本文下面的部分更加细致地描述了 TensorFlow。第二节介绍编程模型和 TensorFlow 接口的基本概念,第三节介绍单机和分布式的实现 。第四节给出了基本编程模型的扩展,第五节介绍了一些基本实现的优化方法。第六节给出了一些使用 TensorFlow 的实验结果,第七节描述了一些使用 TensorFlow 编程的 idiom,第九节则是一些在 TensorFlow 核心外围的工具。第十节和第十一节分别讨论了未来和相关的工作,最后一节给出了总结性想法。 17 | 18 | 2 编程模型和基本概念 19 | TensorFlow 的计算由一个有向图描述,这个图中由一个节点集合组成。该图表达了数据流计算,作出了一些类型的节点保持和更新持久状态和让分支及循环控制结构类似于 Naiad 的行为方式的扩展。客户一般都会使用 TensorFlow 支持的前端语言(C++或者Python)构建一个计算图。在图 1 中展示了一段样例使用 Python 构建并执行了一个 TensorFlow 的计算图,结构计算图在图 2 中展示。 20 | 21 | 22 | 图 1 23 | 24 | 图 2 25 | 在一幅 TensorFlow 图中,每个节点(node)有一个或者多个输入和零个或者多个输出,表示一种操作(operation)的实例化。流过图中正常的边(输出到输入)的值都是张量(tensor),任意维度的数组其中基础元素类型是指定的或者在图的构造过程中自动推断出来的。特别的边,我们称之为控制依赖(control dependencies),同样也存在在图中:这类边上没有数据流过,但是他们表示源节点必须在目标节点的控制依赖开始执行前完成运行。由于我们的模型包括可变状态,控制依赖可以被直接用来确保 happens-before 关系。我们的实现同样会插入控制依赖来确保独立操作之间的顺序,比如说作为控制内存使用最高峰值的方式。 26 | 27 | In computer science, the happened-before relation (denoted: 28 | 29 | 30 | ) 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] 31 | 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] 32 | 操作和核(kernel) 33 | 一个操作有一个名字。它表示一个抽象的计算(比如说,“矩阵相乘”或者“相加”)。一个操作可以有属性(attribute),所有的属性必须提供或者在图构造的过程中推断出以实例化一个节点来执行操作。属性通常的使用方式是让操作在不同的张量元素类型上多态(例如,两个 float 类型的张量和两个 int32 类型的张量)。核(kernel)是一种操作的特别实现,可以运行在一个特定类型的设备上(如 CPU 或者 GPU)。TensorFlow 的 binary 定义了可以通过注册(registration)机制实现的操作和核的集合上,这个集合可以通过连接额外的操作/核的定义/注册。表 1 展示了内置于 TensorFlow 核心库的一些操作类型。 34 | 35 | 36 | 表 1:TensorFlow 的操作类型 37 | 会话(session) 38 | 客户端通过创建会话(session)和 TensorFlow 系统进行交互。为了创建一个计算图,会话接口支持外部(external)方法来提升当前由包含额外节点和边的会话的图(当会话创建时初始的图是空的)。另一个由会话接口提供的主要的操作就是 Run,以需要计算的输出名称和替换某些输出节点的张量的操作集合作为其参数输入。通过控制 Run 的参数,TensorFlow 的实现可以计算所有节点的必须执行传递闭包来计算需要的输出,然后安排执行合适节点来保证他们的依赖关系(在3.1小节详细讲解)。大多数 TensorFlow 的使用都是针对一个图启动一个会话,然后执行整个图或者通过 Run 调用来执行分离的子图数千或者数百万次。 39 | 40 | 变量(variable) 41 | 在大多数计算中,图都是执行多次的。大多数的张量在一次执行后不会存活。然而,变量(variable)是一种特别的操作可以返回一个在图执行若干次过程中存活的持久化的可变张量的句柄。这个句柄可以传递给一系列特定的操作,例如 Assign 和 AssignAdd(等同于 +=)就可以改变其引用的张量了。对应 TensorFlow 在机器学习中的应用,模型的参数典型地就存放在变量引用的张量中,并作为模型训练图的 Run 的一部分进行更新。 42 | 43 | 3 实现 44 | TensorFlow 系统的主要部分就是客户端,它使用了会话接口来和 master 及一个或者多个的 worker processes 进行通信,每个 worker process 负责对一个或者多个计算设备(CPU 核或者 GPU card)的任意访问和在这些设备上进行图节点的计算按照 master 的要求执行。我们有本地和分布式实现的 TensorFlow 接口。本地实现通常是客户端、master 和 worker 都是在同一台机器上在一个单一的操作系统进程(可能包括多个设备,比如说装了多个 GPU card的设备)上运行。分布式实现采用了本地实现的很多的代码,但是扩展了对客户端、master 和 worker 可以在不同的机器的不同的进程上运行的场景支持。在我们的分布式环境中,这些不同的任务对应于 cluster 调度系统分配在 job 中的容器中[51]。这两种不同的模式在图 3 中进行的展示。本节剩下的部分讨论了在两种实现中遇到的问题,3.3 节讨论了针对分布式实现的一些问题。 45 | 46 | 设备 47 | 设备是 TensorFlow 的计算核心。每个 worker 负责一个或者多个设备,每个设备有一个设备类型和一个名字。设备名字由识别设备类型的部分,在 worker 中的设备索引,以及在分布式设定中,worker 的 job和任务(或者 localhost 当设备是和进程在同一机器时)的标志构成。一些例子如/job:localhost/device:cpu:0 或者 /job:worker/task:17/device:gpu:3。我们已实现了 CPU 和 GPU 的设备接口而其他的设备类型也有了通过注册机制完成的设备实现方式。每个设备对象负责管理分配和解除分配设备内存,对在 TensorFlow 实现中的更高层请求任意 kernel 的执行调度管理。 48 | 49 | 张量 50 | 实现中的张量是一种有类型的、多维度数组。我们支持若干张量元素类型,包含大小为从 8 bit 到 64 bit 的带符号和无符号整型,IEEE 浮点数和双精度类型、复数类型和字符串类型(任意长的字节数组)。合适大小的后台存储通过一个分配器进行管理,该分配器由张量所处的设备确定。张量的后端存储缓存是引用计数的并在没有引用存在时解除分配。 51 | 52 | 3.1 单设备执行 53 | 首先考虑最简单的执行场景:单一的worker进程运行在单一的设备上。图上的节点按照代表节点之间的顺序执行。特别地,我们会在每个节点上保持一个计数来记录这个节点上还没有执行的依赖。一旦这个计数变为 0,节点就可以被调度使用,并会加入到待续的队列中。待续队列按照某个非指定的顺序处理,指派节点执行的kernel 到设备对象上。当一个节点完成执行,所有依赖这个完成的节点的节点的计数都会增加。 54 | 55 | 3.2 多设备执行 56 | 一旦系统有了多个设备,有两个主要的复杂情形出现:确定图中每个节点的计算所处的设备,然后管理由上一步确定的置放决定产生的设备间的所需的数据通信。后续部分讨论这两个问题。 57 | 58 | 3.2.1 节点的置放 59 | 给定计算图,TensorFlow 实现的主要责任之一就是将计算映射到可用的设备集合上。这个算法的简单版本下面给出。参见第 4.3 节有关该算法支持的扩展。 60 | 61 | 该置放算法的输入是一个代价模型,包括对每个图节点的输入和输出张亮的规模的估计,和对每个节点在给于其输入张量时的计算时间的。这个代价模型或者是基于关联不同操作类型的启发式规则的静态估计,或者基于实际的为更早的图的执行而做的置放决定集合衡量。 62 | 63 | 置放算法首先运行模拟的图的执行过程。模拟按照下面描述进行,对每个节点使用贪心策略选择一个设备。节点到设备的置放过程也是用作真实执行的置放。 64 | 65 | 置放算法从计算图的源点开始,在系统中的每个设备上模拟相应的活动。对每个在遍历中抵达的节点,可选 available 设备的集合会被考虑到(设备可能会由于其没能提供实现了特定操作的kernel而不可选)。对那些拥有多个可选设备的节点,置放算法使用一种贪心策略来检查在每个可能谁被上置放节点需要完成的时间的效果完成决策。这种启发式规则考虑了根据代价模型在那种设备上估计的和衡量的执行时间,还有任何用来从其他设备传输输入到该节点的通信的代价。其中节点的操作完成最快的设备会被选作该操作的设备,置放决策然后会继续针对图中其他的节点进行处理,包含那些已经做好模拟执行的下游节点。第 4.3 节描述了一些扩展,让用户可以提供提示和部分限制来指导置放算法。这个算法现在还在持续开发的过程中。 66 | 67 | 3.2.2 交叉设备通信 68 | 一旦节点置放已经计算好,图就被划分成子图的集合,每个子图对应于一个设备。从 x 到 y 任何交叉设备的边都会被移除并用一条从 x 到一个 x 的子图中新的 Send 节点的边和从在 y 子图中对应的 Receive 节点到 y 的边代替。参见图 4 中所进行的变换。 69 | 70 | 71 | 图 4 72 | 在运行时刻,Send 和 Receive 节点合作进行跨设备的数据交换。这使得我们可以隔离所有在 Send 和 Receive 内部实现的通信,这样简化了运行时刻剩下的部分工作。 73 | 当我们插入 Send 和 Receive 节点时,我们将在特定设备上的特定张量的所有使用者进行合并规整来使用单个 Receive 节点,而不是对特定设备上的每个下游使用者都给一个 Receive 节点。这确保了需要使用的张量数据仅仅会从源设备到目的设备传输一次,而在目的设备上的张量内存也只会分配一次(而非多次,参看图 4 的节点 b 和 c)。 74 | 75 | 通过这种方式处理通信,我们也允许了不同设备上的图中的个别节点调度可以被去中心化到 workers 上:Send 和 Receive 节点传达了在不同的 worker 和 设备间必要的同步信息,master 仅仅需要对每个图的执行给出一个 Run 请求给那些包含图中任意节点的 worker,而不是会对所有节点或者每个跨设备通信都进行调度。这也让系统更加可扩展,并允许比通过 master 来强制进行所有的调度更加精确的节点执行。 76 | 77 | 3.3 分布式执行 78 | 计算图的分布式执行非常类似于多设备执行。在设备置放后,子图会针对每个设备创建。用于 worker 进程之间的通信的 Send/Receive 节点对使用了诸如 TCP 或者 RDMA 这样的远程通信机制进行跨机器的数据迁移。 79 | 80 | 容错 81 | 分布式执行中的错误可以在很多地方进行检测。最主要的有 (a) 在 Send 和 Receive 节点对之间的通信错误,(b) 从 master 进程到每个 worker 进程的周期性的健康状态检测。 82 | 83 | 如果发现了错误,整个图的执行就会终止,并从头开始。但是回想之前变量节点对应于那些在执行过程中记忆持有(persist)的张量。我们支持在重启过程中的一致的检查点和状态恢复。特别是,每个变量节点连接在一个 Save 节点上。这些 Save 节点周期性地执行,比如说每 N 次迭代,或者每隔 N 秒。他们执行的时候,变量的内容被写到持久化的存储中,比如说,一个分布式的文件系统。类似地,每个变量连接在一个 Restore 节点上,只会在一次重启后的第一个迭代中启用。在 4.2 节有某些节点仅能够在某些图的执行中启用的细节。 84 | 85 | 4 扩展 86 | 本节我们描述在第 2 节给出的编程模型中几个更加高级的特性。 87 | 88 | 4.1 梯度计算 89 | 很多优化算法,包括常用的机器学习训练算法,如随机梯度下降 [45],计算代价函数关于一个输入集合的梯度。由于该算法广泛的应用需求,TensorFlow 已经内置了对自动梯度计算的支持。如果张量 C 通过一个复杂的子图操作依赖于张量 {X_k} 集合,那么就有一个内置的函数可以返回张量 {dC/dX_k}。梯度张量如同其他张量一样通过扩展 TensorFlow 图使用下面的流程进行计算的。 90 | 91 | 92 | 图 5 93 | 当 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 所对应的是计算这些梯度: 94 | 95 | [db, dW, dx] = tf.gradients(C, [b, W, x]) 96 | 通常操作可能会有多个输出,C 可能仅仅会依赖于其中一部分。例如,如果操作 O 有两个输出 y_1 和 y_2,C 仅仅依赖于 y_2,那么 O 的梯度函数的第一个输入就可以设置为 0 因为 dC/dy_1 = 0。 97 | 98 | 自动梯度计算让优化尤其是内存耗用变得复杂。在执行前向计算子图时,那些显式地由用户创建的操作,可能的启发式想法就是通过观察图被构建的次序来确定哪个节点执行下一步。 99 | 100 | 这通常指的是临时输出会在创建后不久就是用到,所以他们的内存可以很快重新用到。当这个启发式想法不适用时,用户可能会改变图构建的次序,或者增加在第 5 节将会介绍的控制依赖。当梯度节点自动加入到图中时,用户控制就会变弱,启发式想法就失效了。特别地,因为梯度逆转了前向计算的顺序,在图早期用到的张量会在梯度计算的结尾时重新被频繁用到。这样的张量会消耗很多的 GPU 内存,也就不必要地限制了计算的规模。我们正积极地提升内存关联的效果以更好地处理这个问题。是用更加精密的启发式想法来确定图执行的次序,重计算张量而不是存储在内存,将长效的张量从 GPU 内存中移到 CPU 内存中等等都是可以尝试的思路。 101 | 102 | 4.2 部分执行 103 | 常常有 client 希望执行整个图的子图。为了支持这一点,只要 client 在 Session 中构建出一个计算图,我们 Run 的方法运行他们执行整个计算图的任意的子图,并将任意的数据注入到图中的任何边上,以及获取流经任何边上的数据。 104 | 105 | 图中每个节点都有一个名字,一个节点的每个输出都通过源节点名和节点输出端口确定,从 0 开始计数(例如,“bar:0” 表示 “bar” 节点的第一个输出,而 “bar:1” 则是第二个输出)。 106 | 107 | Run 调用的两个参数可以帮助定义准确的将要执行的子图。第一,Run 调用接受输入,一个可选的映射 name:port 名来填充张量值。第二,Run 调用接受 output_names,输出 name[:port] 列表指定了需要执行的节点,并且,如果端口出现在名字中,那么对那个节点的特定输出张量值就应该返回给 client 如果 Run 调用成功完成。 108 | 109 | 110 | Paste_Image.png 111 | 112 | 计算图是基于输入输出的值进行变换的。每个在输入中指定的 node:port 使用 feed 节点替换,这个会选出从特定初始化的用来给 Run 调用的 Rensezvous 对象的入口选择出给定的输入向量。类似地,每个输出名字关联在一个特定的 fetch 节点上,可供输出张量的存储,并在 Run 调用完成时返回给客户端。最后,一旦图已经被这些特定的 fee 和 fetch 节点重写,将要执行的节点集合可以被从每个被输出命名的节点出发,然后使用图的依赖关系来确定必须在重写图中执行产生输出的整个节点的集合中进行反向传播。图 6 展示了左边的原始图,以及变换的图,其中 Run 被激活输入是 {b} 输出是 {f:0}。因为我们仅仅需要计算节点 f 的输出,我们不需要执行节点 d 和 e,因为他们没有对 f 做出贡献。 113 | 114 | 4.3 设备限制 115 | TensorFlow 客户端可以通过为一个节点哪些设备可以在其上执行来提供部分的限制来控制节点在设备中的放置。例如,“仅仅放置这个节点在类型为 GPU 的设备上” 或者 “这个节点可以被放置在任何在 /job:worker/task:17 中的设备上”,或者 “共享这个节点和 variable13 节点”。按照这些限制,放置算法就可以负责选择节点的分配来提供快速执行,并且满足不同的设备本身的限制,例如内存的总量的限制来执行子图的计算。 116 | 117 | 支持这样的限制需要对 3.2.1 节介绍的放置算法进行调整。我们首先计算每个节点的可行设备集合,然后使用 union-find 算法来计算图中必须放在一起的图的组成部分。对每个这样的组成部分,我们计算可行设备集合的交集。计算出的每个节点可行设备和放置算法的模拟器很容易匹配。 118 | 119 | 4.4 控制流 120 | 尽管没有任何显式的控制流数据流图非常强大,但是我们发现了一些例子中支持条件和循环可以得到更加简洁而高效的机器学习算法的表示。 121 | 122 | 在 Arvind [3] 中描述的数据流机器观点,我们引入了一个小控制流操作的集合进入 TensorFlow 并且推广 TensorFlow 使之能够处理循环的数据流图。Switch 和 Merge 操作符可以让我们基于布尔值的张量来跳过整个子图的执行。Enter Leave NextIteration 操作符可以进行迭代。高级程序构造如 if-conditional 和 while-loop 可以很容易用这些控制流操作编译成数据流图。 123 | 124 | TensorFlow 运行时刻实现了一个 tags 和 frames 概念,这与 MIT Tagged Token Machine 类似。每次迭代都使用了唯一一个 tag,这个执行的状态通过 frame 表示的。只要被使用,输入就可以进入一次迭代;因此,多次迭代可以被并发地执行。 125 | 126 | TensorFlow 使用分布式协同机制来使用控制流进行图的执行。一般来说,循环可以包含被分配到多个不同的设备上的节点。因此,管理循环的状态成为了一个分布式终止检测的问题。TensorFlow 的解决方案基于图的重写。在图的划分(partitioning)过程中,我们自动地增加控制节点到每个划分(partition)上。这些节点实现了一个小的状态机,来管理每次迭代的开始和终止,确定整个循环的终止。 127 | 128 | 如上所示,我们通常通过随机梯度下降来训练机器学习模型,将梯度计算表示成数据流图的一部分。在模型包含控制流操作时,我们必须在对应的梯度计算中处理这些操作。例如,使用 if-conditional 来对模型梯度计算时,需要知道哪个分值会被选择,然后应用梯度逻辑到这个分支上。类似地,使用 while-loop 来对模型梯度计算时,需要知道多少次迭代,同样还依赖在这些迭代中出现的中间值。基本技术就是重写这些图来记住需要计算梯度计算的值。我们这里省略掉细节介绍。 129 | 130 | 4.5 输入操作 131 | 尽管输入数据可以被通过 feed 节点提供给计算,另一个用来训练大规模机器学习模型通常的机制是在图中采用特定的输入操作节点,这些节点一般来说会通过文件名配置,并产生一个包含一个或者多个样本的张量来自在每次执行时的那些文件集合中存放的数据。这使得数据可以被直接从底层存储系统读出到将要执行接下来处理的内存中。在配置中,从 worker 进程分隔开的客户端进程,如果数据给定,一般会需要一个额外的网络 hop(从存储系统到客户端然后从客户端到 worker vs. 使用一个输入节点时直接从存储系统到 worker)。 132 | 133 | 4.6 队列 134 | 队列是一个加入到 TensorFlow 中的有用的特性。他们允许图的不同部分来异步地执行,可能按照不同的节奏,来通过 Enqueue 和 Dequeue 操作来处理数据。在队列中空间尚存时,可以进行 Enqueue 操作;在指定的最小数量的元素可以取出时,可以进行 Dequeue 操作。队列的一个用途就是允许输入数据可以从磁盘文件中预先取出,这样可以同时进行前一批的数据的处理。同样还能用来进行其他类型的归类,包括聚合很多梯度来计算某种更加复杂的梯度组合,或者来组织不同的输入语句到递归语言模型到语句的近似同样长度的 bins 中,这样处理得更有效率。 135 | 136 | 在一般的 FIFO 队列上,我们还实现了一个 shuffling 队列,可以对内存内的缓冲区内的元素进行随机洗牌。洗牌功能在机器学习算法中比较有用,常常需要对样本进行随机化。 137 | 138 | 4.7 容器 139 | 容器 是用来管理长期存在的可变状态的机制。变量 Variable 反向存放在一个容器内。默认的容器是直到进程终止时都是存活的,但是我们也允许其他的容器。容器可以通过清除它的整个内容进行重置。使用容器,就可以分享状态在完全不相交的不同会话中的计算图中。 140 | 141 | 5 优化 142 | 5.1 通常子表达式消除 143 | 5.2 控制数据通信和内存分配 144 | 5.3 异步 kernel 145 | 5.4 用于 kernel 实现的优化库 146 | 5.5 有损压缩 147 | 6 状态和经验 148 | 7 常用编程规范 149 | 8 性能 150 | 9 工具 151 | 9.1 TensorBoard: 图结构和总结统计可视化 152 | 9.2 性能追踪 153 | 10 未来工作 154 | 11 相关工作 155 | 12 结论 156 | 参考文献 157 | --------------------------------------------------------------------------------