TensorFlow 2.0 实用指南:最佳实践及变更内容
2019 年 2 月 13 日
TensorFlow 团队发布
最近的一篇文章 中,我们提到了 TensorFlow 2.0 经过重新设计,侧重于开发人员的生产力、简化性和易用性。

要更详细地了解发生了哪些变化,以及了解最佳实践,请查看新的 TensorFlow 2.0 实用指南(发布在 GitHub 上)。本文提供了您将在其中找到的内容的简要摘要。如果这些主题中的任何一个让您感兴趣,请前往指南了解更多信息!

主要变更的简要概述

TensorFlow 2.0 中做了很多改变,旨在提高用户生产力,包括移除 冗余的 API,使 API 更一致(统一的 RNN统一的优化器),以及通过 Eager 执行 与 Python 运行时更好地集成。

许多 RFC(如果您不熟悉它们,可以查看一下!)解释了 TensorFlow 2.0 中的更改和思路。本指南展示了 TensorFlow 2.0 中开发应该是什么样子的愿景。假设您对 TensorFlow 1.x 有所了解。

API 清理

在 TF 2.0 中,许多 API 已经 消失或被移至其他位置,有些已被其 2.0 等效项替换 - tf.summarytf.keras.metricstf.keras.optimizers。自动应用这些重命名最简单的方法是使用 v2 升级脚本

Eager 执行

TensorFlow 1.X 要求用户通过进行 tf.* API 调用来手动将 抽象语法树(图形)拼接在一起。然后,它要求用户通过将一组输出张量和输入张量传递给 session.run() 调用来手动编译抽象语法树。相比之下,TensorFlow 2.0 以 eager 的方式执行(就像 Python 通常做的那样),在 2.0 中,图形和会话应该感觉像是实现细节。

不再有全局变量

TensorFlow 1.X 严重依赖隐式全局命名空间。当您调用 tf.Variable() 时,它将被放入默认图形中,并且它将保留在那里,即使您丢失了指向它的 Python 变量。然后,您可以恢复该 tf.Variable,但前提是您知道它创建时使用的名称。如果您无法控制变量的创建,这将很难做到。因此,各种机制层出不穷,试图帮助用户再次找到他们的变量。

TensorFlow 2.0 消除了所有这些机制(变量 2.0 RFC),转而采用默认机制:跟踪您的变量!如果您丢失了 tf.Variable,它将被垃圾回收。有关更多详细信息,请参阅 指南

函数,而不是会话

session.run() 调用几乎就像函数调用一样:您指定输入和要调用的函数,然后您将获得一组输出。在 TensorFlow 2.0 中,您可以使用 tf.function() 装饰 Python 函数,将其标记为 JIT 编译,以便 TensorFlow 将其作为单个图形运行(函数 2.0 RFC)。

这种机制使 TensorFlow 2.0 能够获得图形模式的所有好处
  • 性能:该函数可以被优化(节点修剪、内核融合等)。
  • 可移植性:该函数可以被导出/重新导入(SavedModel 2.0 RFC),允许用户重用和共享模块化 TensorFlow 函数。






拥有自由穿插 Python 和 TensorFlow 代码的能力,您可以充分利用 Python 的表现力。但可移植的 TensorFlow 在没有 Python 解释器的环境中执行 - 移动设备、C++ 和 JS。为了帮助用户避免在添加 @tf.function 时不得不重写代码,AutoGraph 将转换 Python 结构的子集,使其成为 TensorFlow 等效项。

有关更多详细信息,请参阅 指南

TensorFlow 2.0 习惯用法的建议

将您的代码重构为更小的函数

在 TensorFlow 1.X 中,一种常见的用法模式是“万金油”策略,其中所有可能计算的并集被先发制人地布局,然后通过 session.run() 评估选定的张量。在 TensorFlow 2.0 中,用户应该将代码重构为更小的函数,这些函数根据需要被调用。通常情况下,没有必要使用 tf.function 装饰这些较小的函数;仅当您需要装饰高级计算时才使用 tf.function - 例如,训练的一步,或您模型的前向传播。

使用 Keras 层和模型管理变量

Keras 模型和层提供了便捷的变量和 trainable_variables 属性,它们递归地收集所有依赖的变量。这使得将变量本地管理到其使用位置变得很容易。Keras 层/模型继承自 tf.train.Checkpointable 并与 @tf.function 集成,这使得可以从 Keras 对象直接检查点或导出 SavedModels。您不一定要使用 Keras’s.fit() API 来利用这些集成。

有关更多详细信息,请参阅 指南

tf.data.Datasets@tf.function 结合起来

当迭代适合内存的训练数据时,请随意使用常规 Python 迭代。否则,tf.data.Dataset 是从磁盘流式传输训练数据的最佳方式。数据集是 可迭代的(不是迭代器),并且在 Eager 模式下就像其他 Python 可迭代对象一样工作。通过将代码包装在 tf.function() 中,您可以充分利用数据集异步预取/流式传输功能,这将使用 AutoGraph 将 Python 迭代替换为等效的图形操作。
@tf.function
def train(model, dataset, optimizer):
 for x, y in dataset:
  with tf.GradientTape() as tape:
   prediction = model(x)
   loss = loss_fn(prediction, y)
  gradients = tape.gradients(loss, model.trainable_variables)
  optimizer.apply_gradients(gradients, model.trainable_variables)
如果您使用 Keras .fit() API,您将不必担心数据集迭代。
model.compile(optimizer=optimizer, loss=loss_fn)
model.fit(dataset)

利用 AutoGraph 使用 Python 控制流

AutoGraph 提供了一种方法,可以将数据依赖的控制流转换为图形模式等效项,例如 tf.condtf.while_loop

数据依赖的控制流出现的一个常见地方是在序列模型中。tf.keras.layers.RNN 包装了一个 RNN 单元,允许您静态或动态地展开递归。为了说明,您可以按照如下方式重新实现动态展开

class DynamicRNN(tf.keras.Model):
def __init__(self, rnn_cell):
 super(DynamicRNN, self).__init__(self)
 self.cell = rnn_cell
def call(self, input_data):
 # [batch, time, features] -> [time, batch, features]
 input_data = tf.transpose(input_data, [1, 0, 2])
 outputs = tf.TensorArray(tf.float32, input_data.shape[0])
 state = self.cell.zero_state(input_data.shape[1], dtype=tf.float32)
 for i in tf.range(input_data.shape[0]):
  output, state = self.cell(input_data[i], state)
  outputs = outputs.write(i, output)
 return tf.transpose(outputs.stack(), [1, 0, 2]), state
有关更多详细信息,请参阅 指南

使用 tf.metrics 聚合数据,并使用 tf.summary 记录数据

最后,一组完整的 tf.summary 符号即将推出。您可以通过以下方式访问 tf.summary 的 2.0 版本
from tensorflow.python.ops import summary_ops_v2
有关更多详细信息,请参阅 指南

下一步

本文简要概述了 TensorFlow 2.0 实用指南(如果您对这些主题感兴趣,请前往那里了解更多信息!)。要了解更多关于 TensorFlow 2.0 的信息,我们还推荐您阅读以下最近的文章并且请在 3 月 6 日和 7 日收看 TensorFlow 开发者峰会。与往常一样,所有演讲都将上传到 YouTube,供无法亲临现场的人观看。
下一篇文章
Effective TensorFlow 2.0: Best Practices and What’s Changed

- TensorFlow 团队发布
最近的一篇文章 中,我们提到了 TensorFlow 2.0 经过重新设计,侧重于开发人员的生产力、简化性和易用性。

要更详细地了解发生了哪些变化,以及了解最佳实践,请查看新的 TensorFlow 2.0 实用指南(发布在 GitHub 上)。本文提供了您将在其中找到的内容的简要摘要。如果任何…