Eager Execution 概述

TensorFlow 的 Eager Execution 是一种命令式编程环境,可立即评估操作,无需构建图:操作会返回具体的值,而不是构建以后再运行的计算图。这样能让您轻松地开始使用 TensorFlow 和调试模型,并且还减少了样板代码。要遵循本指南,请在交互式 python 解释器中运行下面的代码示例。

Eager Execution 是一个灵活的机器学习平台,用于研究和实验,可提供:

Eager Execution 支持大多数 TensorFlow 操作和 GPU 加速。

注意:如果启用 Eager Execution,某些模型的开销可能会增加。我们正在改进性能;如果发现问题,请报告错误,并分享您的基准测试结果。

设置和基本用法

升级到最新版本的 TensorFlow:

在Tensorflow 2.0中,默认情况下启用了Eager Execution。

现在您可以运行TensorFlow操作,结果将立即返回:

启用 Eager Execution 会改变 TensorFlow 操作的行为方式(现在它们会立即评估并将值返回给 Python)。tf.Tensor 对象会引用具体值,而不是指向计算图中的节点的符号句柄。由于不需要构建稍后在会话中运行的计算图,因此使用 print() 或调试程序很容易检查结果。评估、输出和检查张量值不会中断计算梯度的流程。

Eager Execution 适合与 NumPy 一起使用。NumPy 操作接受tf.Tensor 参数。TensorFlow 数学运算 将 Python 对象和 NumPy 数组转换为 tf.Tensor 对象。tf.Tensor.numpy 方法返回对象的值作为 NumPy ndarray

动态控制流

Eager Execution 的一个主要好处是,在执行模型时,主机语言的所有功能都可用。因此,编写 fizzbuzz很容易(举例而言):

FizzBuzz问题:举个例子,编写一个程序从1到100.当遇到数字为3的倍数的时候,点击“Fizz”替代数字,5的倍数用“Buzz”代替,既是3的倍数又是5的倍数点击“FizzBuzz”。

这段代码具有依赖于张量值的条件并在运行时输出这些值。

构建模型

许多机器学习模型通过组合层来表示。将 TensorFlow 与 Eager Execution 结合使用时,您可以编写自己的层或使用在 tf.keras.layers 程序包中提供的层。

虽然您可以使用任何 Python 对象表示层,但 TensorFlow 提供了便利的基类 tf.keras.layers.Layer。您可以通过继承它实现自己的层,如果必须强制执行该层,在构造函数中设置 self.dynamic=True

请使用tf.keras.layers.Dense层(而不是上面的MySimpleLayer),因为它具有其功能的超集(它也可以添加偏差)。

将层组合成模型时,可以使用 tf.keras.Sequential 表示由层线性堆叠的模型。它非常适合用于基本模型:

或者,通过继承 tf.keras.Model 将模型整理为类。这是一个本身也是层的层容器,允许 tf.keras.Model对象包含其他 tf.keras.Model 对象。

因为第一次将输入传递给层时已经设置参数,所以不需要为tf.keras.Model 类设置输入形状。

tf.keras.layers 类会创建并包含自己的模型变量,这些变量与其层对象的生命周期相关联。要共享层变量,请共享其对象。

Eager 训练

计算梯度

自动微分对于实现机器学习算法(例如用于训练神经网络的反向传播)来说很有用。在 Eager Execution 期间,请使用 tf.GradientTape 跟踪操作以便稍后计算梯度。

tf.GradientTape 是一种选择性功能,可在不跟踪时提供最佳性能。由于在每次调用期间都可能发生不同的操作,因此所有前向传播操作都会记录到“磁带”中。要计算梯度,请反向播放磁带,然后放弃。特定的 tf.GradientTape 只能计算一个梯度;随后的调用会抛出运行时错误。

训练模型

以下示例将创建一个多层模型,该模型会对标准 MNIST 手写数字进行分类。它演示了在 Eager Execution 环境中构建可训练图的优化器和层 API。

即使没有训练,也可以在 Eager Execution 中调用模型并检查输出:

虽然 keras 模型具有内置训练循环(使用 fit 方法),但有时您需要更多自定义设置。下面是一个用 eager 实现的训练循环示例:

该示例使用了 TensorFlow MNIST 示例 中的 dataset.py 模块,请将该文件下载到本地目录。运行以下命令以将 MNIST 数据文件下载到工作目录并准备要进行训练的 tf.data.Dataset:

变量和优化器

tf.Variable 对象会存储在训练期间访问的可变 tf.Tensor 值,以更加轻松地实现自动微分。模型的参数可以作为变量封装在类中。

通过将 tf.Variabletf.GradientTape 结合使用可以更好地封装模型参数。例如,上面的自动微分示例可以重写为:

在Eager Execution期间将对象用于状态

使用 TF 1.x的 Graph Execution 时,程序状态(如变量)存储在全局集合中,它们的生命周期由 tf.Session 对象管理。相反,在Eager Execution期间,状态对象的生命周期由其对应的 Python 对象的生命周期决定。

变量是对象

在 Eager Execution 期间,变量会一直存在,直到相应对象的最后一个引用被移除,然后变量被删除。

基于对象的保存

本节是训练检查点指南的简短版本。

tf.train.Checkpoint 可以将 tf.Variable 保存到检查点并从中恢复:

要保存和加载模型,tf.train.Checkpoint 会存储对象的内部状态,而不需要隐藏变量。要记录 modeloptimizer 和全局步的状态,请将它们传递到 tf.train.Checkpoint

注意:在许多训练循环中,在调用tf.train.Checkpoint.restore之后创建变量。这些变量将在创建后立即恢复,并且可以使用断言来确保检查点已完全加载。有关详细信息,请参阅训练检查点指南

面向对象的指标

tf.keras.metrics存储为对象。通过将新数据传递给可调用对象来更新指标,并使用 tf.keras.metrics.result方法检索结果,例如:

自动微分高级内容

动态模型

tf.GradientTape 也可用于动态模型。这个回溯线搜索算法示例看起来像普通的 NumPy 代码,除了存在梯度并且可微分,尽管控制流比较复杂:

自定义梯度

自定义梯度是一种覆盖梯度的简单方法。在正向函数中,定义相对于输入、输出或中间结果的梯度。例如,下面是在反向传播中截断梯度范数的一种简单方式:

自定义梯度通常用于为一系列操作提供数值稳定的梯度:

0.5

nan

在此处,log1pexp 函数可以通过自定义梯度进行分析简化。下面的实现重用了在前向传播期间计算的tf.exp(x)的值,通过消除冗余计算,变得更加高效:

性能

在Eager Execution期间,计算会自动分流到 GPU。如果要控制计算运行的位置,可以将其放在tf.device('/gpu:0') 块(或 CPU 等效块)中:

tf.Tensor对象可以复制到不同的设备来执行其操作:

基准

对于计算量繁重的模型(如在 GPU 上训练的 ResNet50),Eager Execution 性能与 tf.function Execution 相当。但是对于计算量较小的模型来说,这种性能差距会越来越大,并且有很多工作要做,以便为具有大量小操作的模型优化热代码路径。

使用tf.function

虽然Eager Execution使开发和调试更具交互性,但TensorFlow 1.x样式图执行在分布式训练,性能优化和生产部署方面具有优势。为了弥补这一差距,TensorFlow 2.0通过tf.function API引入此功能。有关更多信息,请参阅Autograph指南

最新版本:https://www.mashangxue123.com/tensorflow/tf2-guide-eager.html 英文版本:https://tensorflow.google.cn/beta/guide/eager 翻译建议PR:https://github.com/mashangxue/tensorflow2-zh/edit/master/r2/guide/eager.md