TensorFlow 2.0 的新增功能:第一、二部分

2023-04-26 19:18:14 浏览数 (3)

原文:What’s New in TensorFlow 2.0 协议:CC BY-NC-SA 4.0 译者:飞龙 本文来自【ApacheCN 深度学习 译文集】,采用译后编辑(MTPE)流程来尽可能提升效率。

第 1 部分:TensorFlow 2.0 - 架构和 API 更改

本书的这一部分将为您简要概述 TensorFlow 2.0 中的新增功能,与 TensorFlow 1.x 的比较,惰性求值和急切执行之间的差异,架构级别的更改以及关于tf.kerasEstimator的 API 使用情况。

本节包含以下章节:

  • 第 1 章“TensorFlow 2.0 入门”
  • 第 2 章“Keras 默认集成和急切执行”

一、TensorFlow 2.0 入门

本书旨在使读者熟悉 TensorFlow 2.0TF 2.0)中引入的新功能,并在构建机器学习应用时使您发挥其潜力。 本章概述了 TF 2.0 中新的架构和 API 级别的变化。 我们将介绍 TF 2.0 的安装和设置,并将比较有关 TensorFlow 1.xTF 1.x)的更改,例如 Keras API 和 Layer API。 我们还将涵盖丰富的扩展,例如 TensorFlow 概率,Tensor2Tensor,参差不齐的 Tensors,以及新的针对损失函数的自定义训练逻辑。 本章还总结了对层 API 和其他 API 的更改。

本章将涵盖以下主题:

  • TF 2.0 的主要变化
  • 适用于 TF 2.0 的推荐技术
  • 使代码 TF 2.0 原生
  • 常见问题
  • TF 2.0 的未来

技术要求

在开始执行前人脸分中描述的步骤之前,您需要具备以下条件:

  • Python 3.4 或更高版本
  • 具有 Ubuntu 16.04 或更高版本的计算机(对于大多数基于 *NIX 的系统,例如 macOS 或其他 Linux 变体,说明仍然相似)

什么是新增的?

TF 2.0 的理念基于简单性和易用性。 主要更新包括使用tf.keras轻松构建模型并急切执行,可在任何平台上进行生产和商业使用的强大模型部署,强大的实验技术和研究工具,以及用于简化 API 的 API 简化。

下图简化了 TF 2.0 的新组织:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eDJoommQ-1681703937399)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/whats-new-tf2/img/b7664abb-104a-4e94-a8e4-2b1889ad47f3.png)]

上图着重于使用 Python API 进行训练和部署; 但是,其他受支持的语言(包括 Julia,JavaScript 和 R)也遵循相同的过程。TF 2.0 的流程是…

来自 TF 1.x 的更改

TF 1.x 和 TF 2.0 之间的第一个主要区别是 API 的组织。 TF 2.0 减少了 API 结构中的冗余。 主要更改包括删除tf.apptf.flagstf.logging,以支持其他 Python 模块,例如absl-py和内置的日志记录功能。

tf.contrib库现在也已从主要 TensorFlow 存储库中删除。 该库中实现的代码已移至其他位置或已移至 TensorFlow 附加库。 这样做的原因是contrib模块已经超出了单个存储库中可以维护的范围。

其他更改包括删除QueueRunner模块以支持使用tf.data,删除图集合以及更改如何处理变量。 QueueRunner模块是一种向模型提供数据以进行训练的方法,但是它比tf.data复杂且难于使用,后者现在是将数据提供给模型的默认方法。 在第 3 章“设计和构造输入数据管道”中,说明了将tf.data用于数据管道的其他好处。

TF 2.0 的另一个主要变化是没有更多的全局变量。 在 TF 1.x 中,使用tf.Variable创建的变量将被放在默认图中,并且仍可以通过其名称恢复。 TF 1.x 具有各种机制,旨在帮助用户恢复变量,例如变量作用域,全局集合以及诸如tf.get_global_steptf.global_variables_initializer之类的辅助方法。 对于 TF 中的默认变量行为,所有这些都已在 TF 2.0 中删除。

TF 2.0 安装和设置

本节介绍了使用不同方法和不同系统配置在系统上安装 TF 2.0 所需的步骤。 建议入门级用户从基于pipvirtualenv的方法开始。 对于 GPU 版本的用户,推荐使用docker

安装和使用 PIP

对于初学者来说,pip是 Python 社区中流行的包管理系统。 如果您的系统上未安装此软件,请先安装它,然后再继续进行。 在许多 Linux 安装中,默认情况下安装了 Python 和pip。 您可以通过键入以下命令来检查是否已安装pip

代码语言:javascript复制
python3 -m pip --help

如果看到blurb描述pip支持的不同命令,则说明pip已安装在系统上。 如果未安装pip,您将看到一条错误消息,类似于No module named pip

隔离开发环境通常是一个好主意。 这极大地简化了依赖项管理并简化了软件开发过程。 我们可以使用 Python 中名为virtualenv的工具来实现环境隔离。 此步骤是可选的,但强烈建议:

代码语言:javascript复制
>>mkdir .venv
>>virtualenv --python=python3.6 .venv/
>>source .venv.bin/activate

您可以使用pip安装 TensorFlow,如以下命令所示:

代码语言:javascript复制
pip3 install tensorflow==version_tag

例如,如果要安装版本2.0.0-beta1,则命令应如下所示:

代码语言:javascript复制
pip3 install tensorflow==2.0.0-beta1

最新包更新的完整列表可在这个页面中找到。

您可以通过运行以下命令来测试安装:

代码语言:javascript复制
python3 -c "import tensorflow as tf; a = tf.constant(1); print(tf.math.add(a, a))"

使用 Docker

如果您想将 TensorFlow 安装与系统的其余部分隔离开来,则可能要考虑使用 Docker 镜像进行安装。 这将要求您在系统上安装 Docker。 可在这个页面上获得安装说明。

为了在 Linux 系统上使用不带sudo的 Docker,请执行以下安装后步骤。

TensorFlow 团队正式支持 Docker 镜像作为安装方式。 对于用户而言,这意味着可以在这里下载更新的 Docker 镜像。

使用以下命令在本地下载 Docker 镜像:…

GPU 安装

TensorFlow 的 GPU 版本的安装与 CPU 版本的过程稍有不同。 可以使用pip和 Docker 进行安装。 安装过程的选择归结为最终目标。 基于 Docker 的过程更容易,因为它涉及到安装更少的附加组件。 它还有助于避免库冲突。 但是,这可能会带来管理容器环境的额外开销。 基于pip的版本涉及安装更多的其他组件,但具有更高的灵活性和效率。 它使结果安装无需任何虚拟化即可直接在本地主机上运行。

要继续进行操作,假设您已经设置了必要的硬件,则至少需要以下软件。 NVIDIA GPU 驱动程序的链接中提供了详细的安装说明。

使用 Docker 安装

在撰写本书时,此选项仅适用于在 Linux 主机上运行的 NVIDIA GPU。 如果您遇到平台限制,那么这是一个很好的选择,因为它可以大大简化流程。 通过利用预构建的容器,还可以最大程度地减少需要安装的其他软件组件的数量。 要继续,我们需要安装nvidia-docker。 请参考以下链接以获取更多详细信息:

  • 安装
  • 常见问题解答

完成上述链接中描述的步骤后,请执行以下步骤:

  1. 测试 GPU 是否可用:…

使用 PIP 安装

如果您想将 TensorFlow 与 NVIDIA GPU 一起使用,则需要在系统上安装以下其他软件。 共享的链接中提供了详细的安装说明:

  • CUDA 工具包:TensorFlow 支持 CUDA 10.0 )
  • CUPTI 随附 CUDA 工具包
  • cuDNN SDK(版本 7.4.1 或更高版本)
  • (可选)TensorRT 5.0 可以改善某些模型上的推理延迟和吞吐量

一旦安装了所有先前的组件,这是一个相当简单的过程。

使用pip安装 TensorFlow:

代码语言:javascript复制
pip3 install tensorflow-gpu==version_tag

例如,如果要安装tensorflow-2.0:alpha,则必须输入以下命令:

代码语言:javascript复制
pip3 install tensorflow-gpu==2.0.0-alpha0

有关最新包更新的完整列表,请访问这里。

您可以通过运行以下命令来测试安装:

代码语言:javascript复制
python3 -c "import tensorflow as tf; a = tf.constant(1); print(tf.math.add(a, a))"

使用 TF 2.0

TF 2.0 可以通过两种主要方式使用-使用低级 API 和使用高级 API。 为了在 TF 2.0 中使用低级 API,需要实现诸如tf.GradientTapetf.function之类的 API。

编写低级代码的代码流程是定义函数内部的前向传递,该函数将输入数据作为参数。 然后使用tf.function装饰器对该函数进行注解,以便在图模式下运行它及其所有优点。 为了记录和获得前向通过的梯度,装饰器函数和损失函数都在tf.GradientTape上下文管理器中运行,可以从中计算梯度并将其应用于模型变量。

训练代码也可以使用低级 API 编写,用于…

丰富的扩展

丰富的扩展功能是 TensorFlow 中引入的一组功能,可提高用户的工作效率并扩展功能。 在本节中,我们将介绍参差不齐的张量以及如何使用它们,并且还将介绍 TF 2.0 中引入的新模块。

参差不齐的张量

当训练和服务于机器学习模型时,可变大小的数据很常见。 在不同的基础媒体类型和模型架构中,此问题始终存在。 当代的解决方案是使用最大记录的大小,对较小的记录使用填充。 这不仅效率低下,不仅在内存或存储方面,而且在计算效率方面也是如此; 例如,当处理循环模型的输入时。

参差不齐的张量有助于解决此问题。 在非常高的水平上,参差不齐的张量可以被认为是变长链表的 TensorFlow 模拟。 这里要注意的一个重要事实是,这种可变性也可以存在于嵌套大小中。 这意味着有可能…

真正的参差不齐的张量是什么?

参差不齐的张量也可以定义为具有一个或多个参差不齐的大小的张量。 换句话说,具有可变长度切片的大小。 由于最常见的用例涉及处理有限数量的记录,因此参差不齐的张量要求最外面的维度是统一的,换句话说,该维度的所有切片都应具有相同的长度。 最外部大小之前的大小可以既参差不齐,也可以统一。 总结一下这些要点,我们可以指出,参差不齐的张量的形状目前仅限于以下形式:

  • 单个统一大小
  • 后跟一个或多个参差不齐的大小
  • 后跟零个或更多个统一大小

构造参差不齐的张量

TF 2.0 提供了大量可用于创建或返回锯齿张量的方法。 最简单的方法之一是tf.ragged.constant()。 让我们用它来创建大小为[num_sentences,(num_words)的参差不齐的张量。 请注意,我们使用圆括号来指示参差不齐的大小:

代码语言:javascript复制
sentences = tf.ragged.constant([                          ["Hello", "World", "!"],                       ["We", "are", "testing", "tf.ragged.constant", "."]            ])print(sentences)

您应该会看到以下内容:

代码语言:javascript复制
<tf.RaggedTensor [[b'Hello', b'World', b'!'], [b'We', b'are', b'testing', b'tf.ragged.constant', b'.']]>

也可以从带有填充元素的旧式张量或 Python 列表中创建参差不齐的张量。 这可能非常…

参差不齐的张量的基本操作

在许多情况下,参差不齐的张量可以类似于常规张量的方式使用。 TensorFlow 提供了超过 100 个支持参差不齐的张量的运算符。 这些运算符大致可分为基本数学运算符,数组运算符或字符串运算符。

以下代码块显示了添加两个锯齿张量的过程:

代码语言:javascript复制
x = tf.ragged.constant([
                         [1, 2, 3, 4],
                         [1, 2]
                       ])
y = tf.ragged.constant([
                         [4, 3, 2, 1],
                         [5, 6]
                       ])
print(tf.add(x, y))

结果为以下输出:

代码语言:javascript复制
<tf.RaggedTensor [[5, 5, 5, 5], [6, 8]]>

另一个有趣的功能是为参差不齐的张量定义了运算符重载。 这意味着程序员可以像使用其他张量一样直观地使用 , -, *, //, /, %, **, &, |, ^>=等运算符。

以下代码块显示了使用重载运算符的参差张量的乘法:

代码语言:javascript复制
x = tf.ragged.constant([
                          [1, 2, 3, 4],
                          [1, 2]
                       ])
print(x * 2)  # Multiply a ragged tensor with a scalar
print(x * x)  # Multiply a ragged tensor with another ragged tensor

结果输出如下:

代码语言:javascript复制
<tf.RaggedTensor [[2, 4, 6, 8], [2, 4]]>
<tf.RaggedTensor [[1, 4, 9, 16], [1, 4]]>

此外,tf.ragged包中定义了各种特定于参差不齐的张量的运算符。 可能有必要查看包的文档以了解更多信息。 请参阅以下链接以获取有关此文档的详细文档:

  • RaggedTensor
  • ragged

新的重要包

TF 2.0 的到来还伴随着 TensorFlow 下更多有趣且有用的包的到来,这些包可以单独安装。 其中一些包包括 TensorFlow 数据集,TensorFlow 插件,TensorFlow 文本和 TensorFlow 概率。

TensorFlow 数据集是一个 Python 模块,可轻松访问 100 多个数据集,从音频到自然语言再到图像。 这些数据集可以通过以下代码轻松下载并用于模型中:

代码语言:javascript复制
import tensorflow_datasets as tfdsdataset = tfds.load(name="mnist", split=tfds.Split.TRAIN)dataset = dataset.shuffle(1024).batch(32).prefetch(tf.data.experimental.AUTOTUNE)

从该库中获取的数据集是tf.data.Dataset对象,这些对象…

总结

TF 2.0 包含许多主要更改,例如 API 清理,热切执行和面向对象的哲学。 API 清理包括弃用具有等效标准 Python 库的冗余模块,以及删除tf.contrib模块并将其重新组织到主要 API 和 TensorFlow Addons 包中。 急切的执行和面向对象的 API 使调试更加有效和直接,并且导致变量被视为普通的 Python 变量。 这意味着不再需要变量集合和其他专用于处理全局变量的 API,因此在 TF 2.0 中已将其删除。

TF 2.0 还将默认的高级 API 从 TF 1.x 中的估计器转移到 TF 2.0 中的tf.keras,以简化和扩展。 tf.keras API 具有三种不同的编程类型,每种提供不同级别的抽象和可定制性。 可以使用tf.GradientTape编写低级 TF 2.0 代码,以处理操作的梯度,而使用tf.function编写基于图的执行。

本章还介绍了安装 TF 2.0 的不同方法,包括通过pip和 Docker 进行安装,以及 GPU 版本的安装。 有许多与 TF 2.0 兼容并与之一起发布的模块,这些模块进一步增强和扩展了基本 API 的可能性。 其中包括 TensorFlow 数据集,TensorFlow 插件,TensorFlow 文本和 TensorFlow 概率。

本章还包括参差不齐的张量,这对于存储具有可变长度和形状以及分层输入的数据很有用。 这意味着参差不齐的张量对于存储语言和序列数据很有用。

在下一章中,我们将了解 Keras 的默认集成和急切执行的知识。

二、Keras 默认集成和急切执行

本章涵盖了两个高级 TensorFlow 2.0TF 2.0)API,即 Keras 和估计器。 本章重点关注惰性求值和急切执行的概念,重点介绍如何在 TensorFlow 1.xTF 1.x)和 TF 2.0 中求值基础计算图之间的差异 。 本章还提供了有关使用诸如 Keras 之类的高级 API 构建自定义模型(使用自定义低级操作)的详细指南。

本章将涵盖以下主题:

  • TF 2.0 中的新抽象
  • 深入了解 Keras API
  • 估计器
  • 求值 TensorFlow 图

技术要求

为了运行本章中给出的代码摘录,您将需要以下硬件和软件:

  • TF 2.0 或更高版本(足够使用 CPU 或 GPU 版本)
  • Python 3.4 (目前,TensorFlow 支持的最高 Python 版本是 3.6)
  • NumPy(如果不是由 TensorFlow 自动安装)

可在这个页面中获得本章的代码文件。

TF 2.0 中的新抽象

抽象是在编程和软件开发过程中使用的非常流行的工具。 从非常高级的意义上讲,抽象指的是隔离和描述特定任务或一组任务的中心思想而不必指定物理,空间或时间细节的过程。 正确完成后,抽象可以显着减少针对特定任务需要编写的代码量。 它还提高了现有代码的可重用性,并使其与 TF 2.0 兼容。

在使用机器学习系统时,有一些常见的高级任务,例如训练数据,建模,模型评估,预测,模型存储和模型加载,这是常见的…

深入了解 Keras API

TF 2.0 与 Keras 的结合比以前紧密,特别是对于高级 API。 如果您刚开始在 TensorFlow 中构建基于神经网络的模型,则建议您从 Keras 开始。 简而言之,Keras 公开了用户友好的 API,用于执行常见任务,例如加载数据,构建模型,训练模型,评估模型,运行模型以及加载和保存以前的模型。 影响其灵活性的一个重要因素是,它允许您在不同的抽象级别上无缝运行。

什么是 Keras?

Keras 是用于构建和训练深度学习模型的流行的高级 API。 Keras 的核心是高级神经网络 API 规范。 它在机器学习社区中被研究人员,爱好者和软件工程师广泛使用。 它的开发着眼于实现快速实验。 它具有多种机器学习平台和编程语言的实现,例如 TensorFlow,MXNet,TypeScript,JavaScript,CNTK,Theano,PlaidML,Python,Scala 和 CoreML。 TF 2.0 包含 Keras API 规范的完整实现以及 TensorFlow 特定的增强功能和优化功能。 在tf.keras模块中可用。

Keras 是用明确的…

构建模型

机器学习从根本上讲是一系列统计计算,这些统计计算可以实现最终目的。 这些核心统计组件可以封装为模型。 此外,一些标准计算可被视为与此核心的交互。 从程序员的角度来看,将模型看成一个包含大量数学方程的黑匣子可能会很有用。 然后,其他动作可以描述为与此黑匣子的一组交互。

例如,给定一组输入记录,可以将训练模型理解为计算模型参数(或权重)的过程。 推理可以看作是一个过程,使用数学核心和学习到的参数来生成给定输入集的预测。

Keras 大致采用了我们刚刚讨论的抽象范式,以帮助用户使用基于神经网络的模型轻松地构建,训练和预测。 在随后的小节中,我们将详细介绍 Keras 为上述任务中的每一项提供的选项。 我们还将探讨使 Keras 成为不可忽视的强大力量的其他辅助功能。

在 Keras 中,模型是通过组合层来构建的。 每个 Keras 层大致对应于神经网络架构中的层。 模型也可以看作是层的组合。 Keras 提供了多种选择来组合这些层以形成基于神经网络的模型。 接下来的两个小节重点介绍 Keras 为构建模型而公开的两种最流行的 API,也称为数学统计核心

Keras 层 API

在用于模型构建的高级 Keras API 中,Keras 层是基本构建块。 模型通常定义为这些层的某种图形。 这些层也可以被编程为彼此交互。 由于这些是基本的构建块,因此我们可以在训练和推理阶段定义和自定义层的行为。 换句话说,我们具有在前进和后退过程中定义层行为的能力(如果适用)。 从程序员的角度来看,可以将一层视为封装状态和逻辑的数据结构,以从给定的一组输入生成特定的输出。

层…

使用顺序 API 建立简单模型

Sequential API 是 Keras 为构建模型公开的非常简单但功能强大的抽象。 如果刚开始使用 Keras,建议您使用此功能。 如果要使用单输入级模型,这也是推荐的选择。

该 API 的主要组件是tf.keras.Sequential

这对于简单,连续的层组合很有用。 假设您有一个n层神经网络。 假设这些层定义为[layer_1, layer_2, …. , layer_n]

请注意,这些层中的每一层都是 Keras 层,如前所述。 对于我们的实现,这意味着该层对象将是tf.keras.layers中公开的层之一,或者是对基础 Keras 层实现进行子类化的用户定义层。

可以使用tf.keras.Sequential实例的add()方法合并组成层。

按顺序组合它们的一般形式如下:

代码语言:javascript复制
my_model = tf.keras.Sequential()
my_model.add(layer_1)
.
.
my_model.add(layer_n)

假设您要建立一个描述全连接神经网络的模型(也称为多层感知器MLP)),以对具有五个属性的一维记录进行二分类。 我们的模型包括四个全连接层。 纯粹出于说明目的,我们假设每个全连接层包含 10 个节点或神经元。 这些层中的每一层都使用整流线性单元ReLU)激活函数。 最终输出通过softmax层获取。 可以在相应层的构造器中定义特定于层的自定义。 实现此模型的代码如下:

代码语言:javascript复制
model = tf.keras.Sequential()

# Adds a densely-connected layer with 10 units and rectified linear unit activations
# Accepts multiple input tensors of size 5 from user
model.add(layers.Dense(10, activation='relu', input_shape=(5,)))

# Add layer 2 with 10 units and relu activations:
model.add(layers.Dense(10, activation='relu'))

# Add layer 3 with 10 units and relu activations:
model.add(layers.Dense(10, activation='relu'))

# Add layer 4 with 10 units and relu activations:
model.add(layers.Dense(10, activation='relu'))

# Add a softmax layer with 2 output units:
model.add(layers.Dense(2, activation='softmax'))

使用Sequential API 的另一种方法是提供列表中的所有层,或者通常提供某种迭代器。 这些可以在初始化模型对象时传递给Sequential()构造器。 这在分隔层描述和模型创建任务时特别有用。 让我们看下面的示例,以更好地理解这一点。

考虑一下尝试从以下这些层的列表中生成模型的示例:layer_list =[layer_1, layer_2, …. , layer_n]。 现在可以通过将layer_list对象直接传递给构造器来创建模型,如下所示:

代码语言:javascript复制
new_model = tf.keras.Sequential(layer_list)

值得注意的是,前面的语句等同于下面的语句:

代码语言:javascript复制
new_model = tf.keras.Sequential(layers=layer_list)

这也可以用其他方式使用。 一个示例是将层规范和模型创建过程分开。 让我们进一步探讨这个想法。 假设您有一个用例,其中模型需要多个仅在运行时可用的层。

一种简单的方法是编写一个用于创建层的函数。 让我们编写一个示例函数get_layers(n),它使用整数值n并一个接一个地返回许多Dense层。 为了说明 API 的灵活性,让我们使用 Python 生成器实现该函数:

代码语言:javascript复制
def get_layers(n):
    while n > 0:
        yield tf.keras.Dense(10, activation='relu')
        n -= 1

如果您不熟悉 Python 生成器,请在继续操作之前参阅这里。

前一个代码块中定义的函数接受n的正整数值并返回generator对象。 此生成器生成的每个元素都是一个层。 以下代码段显示了如何使用此函数创建模型:

代码语言:javascript复制
model_using_generator = tf.keras.Sequential(layers=get_layers(10))

使用函数式 API 建立高级模型

随着机器学习任务的日益成熟,具有多阶段输入和输出的模型变得越来越普遍。 大量实际使用案例涉及具有多阶段输入和输出的模型。 具有多个输入的真实世界模型的一个示例是文本分类模型,该模型可以查看输入文本中的单词和字符序列。

尽管Sequential API 在以串行方式组合层方面做得非常好,但是它不能用于描述基础层的并行组成。 通常,它不能用于构建不具有线性拓扑的层图。 在需要利用特定层的情况下,其实用性也受到限制。

训练模型

训练模型指的是为不同网络组件学习权重的过程,这些过程在给定的一组示例中将损失函数降至最低。 简而言之,训练神经网络意味着找到网络值的最佳组合。 如您所知,训练过程也与评估和预测过程紧密相关。 借助抽象的强大功能,Keras 提供了强大的高级接口来实现和管理端到端的训练过程。 让我们看一下它为使用顺序和函数式 API 创建的模型提供的训练 API。 它为此阶段提供的一些函数如下:

  • model.compile():此函数用于配置训练过程。 用户指定详细信息,例如优化器的类型(以及超参数(如果有的话)),损失函数的类型以及要评估的指标。 这些也是可以使用 TensorBoard 可视化的指标。 下面的示例代码片段描述了一个带有随机梯度下降SGD)优化器,CategoricalCrossentropy损失函数和记录Accuracy指标的样本训练配置:
代码语言:javascript复制
model.compile(
             # Optimizer
             optimizer=tf.keras.optimizers.SGD(),

            # Loss function to minimize
            loss=keras.losses.CategoricalCrossentropy(),

            # List of metrics to monitor
            metrics=[
                    keras.metrics.SparseCategoricalAccuracy()
            ]
)
  • model.fit():此方法用于提供训练数据并控制实际训练过程。 此方法中的一些重要参数和参数是训练记录,训练标签,训练周期数和训练批量大小。 以下样本片段描述了一个样本训练过程,该过程用于在训练记录(train_x)和训练标签(train_y)上以批号32训练30周期的预定义模型:
代码语言:javascript复制
model.fit(
      x=train_x,
      y=train_y,
      epochs=30,
      batch_size=32
)
  • model.evaluate():如前所述,训练和评估过程是相互联系的,并且紧密相连。 训练神经网络需要经常更新权重,以找到最佳的权重集。 为此,有必要在当前阶段计算某种类型的网络状态。 此过程称为评估。 更具体地说,评估是针对给定数据集在当前阶段计算网络的损失和其他指标的过程。 请记住,此方法执行的计算是分批执行的。 该函数返回与损失函数相对应的标量。 它还返回与model.compile()阶段中提供的任何度量对应的值。 以下代码段描述了一个评估函数,该函数以批量大小32来计算记录(test_x)和标签(test_y)上的度量:
代码语言:javascript复制
results = model.evaluate(
      test_x,
      test_y,
      batch_size=32
     )

保存和加载模型

训练后,保存模型以备后用可能非常有用。 在许多用例中,将训练和推理管道分离是一个好主意。 从开发人员的角度来看,模型可以抽象为一个黑匣子,该黑匣子接受一组输入并返回一些输出。 这样,保存模型只不过是导出表示该黑匣子的工件。 然后,还原或加载模型成为使用此黑匣子执行一些实际工作的过程。 这也可以理解为序列化反序列化模型黑匣子的过程。

TF 2.0 支持以多种模式保存和恢复模型:

  • 仅模型架构(Keras)
  • 仅模型权重(Keras)
  • 整个模型:…

分别加载和保存架构和权重

在某些用例中,将模型创建和模型初始化步骤分离是有意义的。 在这种情况下,模型序列化将需要使用单独的过程来加载和保存架构和模型权重。 Keras 为用户提供支持,以独立使用架构和权重。

加载和保存架构

tf.Keras Python API 中,架构交换的基本单元是 Python dict。 Keras 模型使用get_config()方法从现有模型生成此dict。 然后可以使用标准的 Python 序列化和反序列化方法(例如 Pickle 或 HD5)将此dict保存到磁盘或任何其他存储介质中。 您也可以将 Python dict直接写入磁盘上的文件。

假设您要将 Keras 模型的架构my_model保存到磁盘。 以下代码段说明了如何执行此操作:

代码语言:javascript复制
my_model_architecture = my_model.get_config()

现在,您可以使用选择的方法将此 Python dict保存到磁盘。

对于从配置对象生成模型的逆用例,…

加载和保存权重

在 Python API 中,tensorflow.keras使用 NumPy 数组作为权重交换的单元。 这与用于加载和保存架构的 API 非常相似。 这些 NumPy 数组也可以使用原生 Python 技术保存到磁盘中。 get_weights()set_weights()方法大致类似于get_config()from_config()。 前者返回对应于模型中不同层的 NumPy 数组列表。 后者接受 NumPy 数组列表并更新内存中的模型。

以下代码段说明了如何获取现有模型的权重:

代码语言:javascript复制
my_model_weights = my_model.get_weights()

给定一组权重和一个模型副本,可以按以下步骤执行更新内存中模型权重的逆操作:

代码语言:javascript复制
replica_my_model.set_weights(my_model_weights)

如我们所见,可以使用get_config()get_weights()from_configset_weights()的组合存储整个模型。 但是,此过程的局限性在于它不存储有关训练过程的任何信息。

为了更好地理解这一点,让我们看一个例子。 考虑一个具有一个输入层,一个隐藏层和一个输出层的简单模型。 然后,我们将仅使用上一节中讨论的方法来创建此模型的副本。 步骤如下:

  1. 首先,让我们使用functional API 定义此模型:
代码语言:javascript复制
# Define layer chain
input_layer = tf.keras.layers.Input(shape=(5,))
hidden_layer = tf.keras.layers.Dense(10)(input_layer)
output_layer = tf.keras.layers.Dense(5, activation='softmax')(hidden_layer)

# Define Model
my_model = tf.keras.Model(inputs=input_layer, outputs=output_layer)
  1. 这里的目标是创建先前模型的副本。 为此,让我们创建模型架构和模型权重的副本:
代码语言:javascript复制
# Save architecture
my_model_arch = my_model.get_config()

# Save weights
my_model_weights = my_model.get_weights()
  1. 要创建我们的副本模型,我们必须创建一个 Keras 模型,其架构与源模型相同:
代码语言:javascript复制
# Create replica using saved architecture 
my_model_replica = tf.keras.Model.from_config(my_model_arch)
  1. 接下来,我们将权重从源模型复制到模型副本:
代码语言:javascript复制
# Copy saved weights
my_model_replica.set_weights(
    my_model_weights
)

如您所见,我们已经使用save API 成功创建了源模型的副本。 此外,我们已经通过使用前面介绍的加载 API 将其重新加载到单独的对象内存中进行了测试。 换句话说,我们已经使用loadsave API 创建了模型的副本。

保存和加载整个模型

上一节中描述的过程的主要限制之一是它不包括训练过程。 这可能是用例中的主要障碍,这些用例涉及训练过程中某个时刻的检查点。 为了克服它,TensorFlow 可以完整保存模型。 这主要可以通过两种方式实现-使用 Keras API 或使用SavedModel API。

在以下各节中,我们将简要讨论方法及其语法。 我们还提供了有关何时使用它们的见解。

使用 Keras

可以将使用Sequential API 或functional API 构建的模型保存在单个文件中。 也可以从此文件中加载此模型,而与构建模型所用的代码无关。

该文件包括以下内容:

  • 模型的架构
  • 模型的权重值(如果适用,还包括训练中获得的权重)
  • 优化器及其状态(如果有的话)(可用于从特定点恢复训练)
  • 模型的训练配置(已传递来编译)(如果有)

使用Sequentialfunctional API 创建的 Keras 模型可以直接保存到磁盘。 使用 Keras 的本机 HDF5 文件格式保存文件。 实现此目的的代码的一般形式如下:

代码语言:javascript复制
model.save('file_name.h5')

可以使用简单的 Python 单一代码将该模型重新加载到内存中。 通用格式如下:

代码语言:javascript复制
loaded_model = tf.keras.models.load_model(
                                         'path_to_model.h5'
                                         )

这是一种非常直接的方法,在 Python API 中交换模型时效果很好。

使用SavedModel API

SavedModel是在 TensorFlow 生态系统中存储对象的默认方式。 由于这种标准化的性质,它可以用于在不同的 TensorFlow 实现之间交换模型。 使用SavedModel保存的模型除包含模型架构和权重外,还包含实际的 TensorFlow 代码。 SavedModel文件的确切内容可以列出如下:

  • 一个包含模型权重的 TensorFlow 检查点
  • 包含底层 TensorFlow 图的SavedModel原型:
    • 默认情况下,为预测阶段保存了单独的图(训练和评估阶段也分别在适用时存储)
  • 模型的架构配置(如果有)

在 Python API 中,与SavedModel ...进行交互

其它功能

除了非常强大的 API 规范外,TensorFlow 的tf.keras Keras 实现还附带了许多附加组件。 在以下各节中,我们将简要讨论其中最相关的两个。

keras.applications模块

keras.applications模块包含具有流行模型权重的预构建架构。 这些可以直接用于进行预测。 用户还可以使用它们来创建其他网络的输入特征。 该包中突出的预建实现包括:

  • densenet module:Keras 的 DenseNet 模型
  • inception_resnet_v2:Keras 的 Inception-ResNet V2 模型
  • inception_v3:适用于 Keras 的 Inception V3 模型
  • mobilenet:Keras 的 MobileNet v1 模型
  • mobilenet_v2:Keras 的 MobileNet v2 模型
  • nasnet:Keras 的 NASNet-A 模型
  • resnet50:用于 Keras 的 ResNet50 模型
  • vgg16:适用于 Keras 的 VGG16 模型
  • vgg19:适用于 Keras 的 VGG19 模型
  • xception:适用于 Keras 的 Xception V1 模型

每个…

keras.datasets模块

keras.datasets模块包括自动化功能,可以从文件中解析某些流行数据集的数据。 如果本地没有这些文件,它还包括自动通过互联网下载这些文件的功能。 这使用户可以更轻松,更快捷地试验和评估不同的模型。 对于某些用例,此模块可以代替整个数据处理阶段! Keras 随附的各种数据集模块包括以下内容:

  • boston_housing:波士顿房屋价格回归数据集
  • cifar10:CIFAR10 小图像分类数据集
  • cifar100:CIFAR100 小图像分类数据集
  • fashion_mnist:Fashion-MNIST 数据集
  • imdb:IMDB 情感分类数据集
  • mnist:MNIST 手写数字数据集
  • reuters:路透社主题分类数据集

列出的每个数据集都是一个 Python 模块。 可在这个页面中找到其组件的详细列表。

端到端顺序示例

现在,让我们使用上一节中讨论的 Keras API 的组件来完成一个小的实际任务。 让我们使用Sequential API 构建神经网络,以对 MNIST 数据集中的手写数字进行分类。 步骤如下:

  1. 在开始编写任何函数代码之前,我们需要将tensorflowkeras导入内存:
代码语言:javascript复制
import tensorflow as tfimport tensorflow.keras as keras
  1. 然后,让我们开始将数据集加载到内存中。 为此,请使用前面章节中讨论的keras.datasets模块:
代码语言:javascript复制
# Load Data(x_train, y_train), (x_test, y_test) = keras.datasets.mnist.load_data()
  1. 在前面的代码段中,数据作为numpy数组加载到内存中。 …

估计器

从头开始构建机器学习模型时,从业人员通常会经历多个高级阶段。 其中包括训练,评估,预测和装运,以供大规模使用(或导出)。 到目前为止,开发人员必须编写自定义代码才能实现这些步骤中的每个步骤。 在所有应用中,运行这些过程所需的许多样板代码都保持不变。 更糟的是,此代码很容易需要在低抽象级别上进行操作。 这些问题放在一起,可能会在开发过程中造成极大的效率低下。

TensorFlow 团队尝试通过引入 Estimators 来解决此问题,Estimators 是一个高级 API,旨在抽象出在上述阶段执行不同任务时产生的许多复杂性。 具体来说,估计器是用于封装以下类别任务的高级 API:

  • 训练
  • 评价
  • 预测
  • 模型共享(导出和运输模型)

用户可以从一组预先构建的估计器中进行选择,甚至可以实现自己的估计器。 标准库中提供了针对各种常用机器学习和深度学习算法的估计器的实现。

估计器具有以下优点:

  • 基于估计器的模型与硬件和环境无关:
    • 程序员不必担心 Estimator 是在本地计算机上运行还是在远程计算网格上运行。
    • 程序员可以在 CPU,GPU 或 TPU 上运行基于 Estimator 的模型,而无需重新编码他们的模型。
  • 估计器简​​化了团队中不同开发人员之间或使用不同环境或栈的团队之间的共享实现。
  • 程序员可以使用高级直观代码来开发高性能和前沿模型。 换句话说,程序员不必在管理低级 TensorFlow API 的复杂性上浪费时间。
  • 估计器建立在tf.keras.layers本身上,从而简化了自定义。
  • 估计器为您构建图。
  • 估计器提供了一个安全分布的训练循环,该循环控制如何以及何时执行以下操作:
    • 建立图
    • 初始化变量
    • 加载数据
    • 处理异常
    • 创建检查点文件并从故障中恢复
    • 为 TensorBoard 保存摘要
  • 使用 Estimators 编写应用时,程序员可以灵活地将数据输入管道与模型分开。 通过这种分离,可以轻松地尝试使用不同的数据集和不同的数据源。

在 TF 2.0 中,Keras 已经提供了 Estimators 公开的许多功能。 如果您只是入门,那么 Keras 是一个更容易学习的 API。 建议初学者在评估器上使用 Keras API。 一旦用例需要使用 Estimators,就可以查找并了解更多信息。 有关详细指南,请访问这里。

求值 TensorFlow 图

TensorFlow 的中心思想是,要求程序员创建计算图以指定需要执行的操作才能获得所需的结果。 然后,程序员指定了硬件和其他环境参数,以针对给定的一组输入来计算此计算图的输出。 这意味着在程序员明确计算图之前,值和变量没有任何值。 当程序员真正想要的只是数量的值时,这增加了程序员创建和管理会话的开销。

TF 2.0 旨在通过更改求值和计算基础计算图的方式来解决此问题。 用一个句子,TF …

延迟加载与急切执行

延迟加载是一种编程范例,其中直到实际需要数量才计算数量的值。 换句话说,在没有明确请求之前,不会初始化对象。 这样做的主要好处是,当按需计算数量值时,无需使用额外的内存来存储计算结果。 如果正确使用,这将导致非常有效的内存使用并提高速度。

急切执行可以理解为与延迟加载相反。 在此,数量的值一定义就立即计算,而不必等到它被调用。 这意味着当实际请求数量时,该值从内存中返回,而不是从头开始计算。 这有助于最小化返回查询结果所需的时间,因为用户不必等待计算值所花费的时间。

可以通过添加两个常量的简单操作来说明两者之间的区别:ab。 首先,让我们看一下 2.0 之前的 TensorFlow 版本。 这些要求用户定义一个计算图,然后使用会话来运行和求值该图。 这可以理解为延迟加载的示例。 让我们看一下以下代码片段,以获得更好的主意:

代码语言:javascript复制
# Define constants
a = tf.constant(10)
b = tf.constant(32)

# Define add operation
c = a   b
print(f"Value outside session: {c}”)

这给出以下输出:

代码语言:javascript复制
Outside session: Tensor("add_1:0", shape=(), dtype=int32)

在此阶段,我们可以看到c的值(即add对两个常量进行运算的结果)实际上是张量,没有实际数值。 因此,我们可以看到该图已构建但尚未求值。 为了获得加法运算的实际数值结果,我们必须定义一个会话来运行和求值基础图:

代码语言:javascript复制
# Create a session and run graph in it
with tf.Session() as sess:
  print(f"Value inside Session: {c}”)

您将看到以下输出:

代码语言:javascript复制
Value inside Session: 42

这表明添加操作仅在会话中运行后才进行求值。

现在,让我们尝试使用 TF 2.0 及更高版本的相同示例。 我们用相同的变量名称和值定义两个常量。 我们还定义了第三个变量来保存加法的结果。 然后,我们在紧接之后打印加法的值:

代码语言:javascript复制
# Define constants
a = tf.constant(10)
b = tf.constant(32)

#Define add operation
c = a   b
print(f"Value outside session: {c}")

结果输出如下:

代码语言:javascript复制
Value outside session: 42

如我们所见,此阶段的输出在 TensorFlow 2.0 和 <2.0 版本之间有所不同。 在这种情况下,c变量已经包含加法运算的值。 无需程序员求值任何计算图即可进行计算。 换句话说,加法操作急切地执行。 这是 2.0 及更高版本与旧版本之间的主要区别。

TF 2.0 与 Python 编程语言紧密集成。 急切的执行使张量可以无缝用作本机 Python 对象,而不必担心求值计算图以及管理会话或基础硬件。 好处不止于此。 急切的执行使程序员能够利用宿主编程语言的强大控制流结构。 TensorFlow 代码现在与平台的其余部分更加直观地集成,这为开发人员带来了巨大的价值,因为它不再需要特殊的流控制结构。 这也为实验,调试和笔记本环境增加了重要价值。

总结

在本章中,我们了解了 TF 2.0 中可用于模型构建,训练,保存和加载的高级抽象。 深入研究 Keras API,我们了解了如何通过使用Sequentialfunctional API 组合层来构建模型。 我们还了解了如何利用 Keras API 的高级抽象来训练模型。 本章还研究了在各种配置和模式下加载和保存模型的复杂性。 我们已经了解了保存模型,架构和权重的不同方法,本章对每种方法进行了深入的说明,并描述了何时应该选择一种方法。

将讨论的所有概念放在一起…

第 2 部分:TensorFlow 2.0 - 数据和模型训练管道

本书的这一部分将概述总体输入数据和训练模型管道。 它还将详细介绍使用tf.keras API 创建模型,训练和验证流程。

本节包含以下章节:

  • 第 3 章,“设计和构建输入数据管道”
  • 第 4 章,“模型训练和 TensorBoard 的使用”

三、设计和构建输入数据管道

本章将概述如何构建复杂的输入数据管道,以使用由TFRecords组成的tf.data API 来以最常见的格式(例如 CSV 文件,图像,文本等)提取大型训练/推理数据集。 和tf.data.Dataset方法。 您还将获得有关协议缓冲区,协议消息以及如何使用 TensorFlow 2.0TF 2.0)中的TFRecordstf.Example方法实现的一般概念。 本章还说明了在数据的混洗,批量和预取方面使用tf.data.Dataset方法的最佳实践,并针对 TF 2.0 提供了建议。 最后,我们将讨论内置的 TensorFlow 数据集…

技术要求

您应该了解标准数据格式,例如 CSV 文件,图像(PNG 和 JPG)和 ASCII 文本格式。 不用说,本书的大多数章节都假定您了解基本的机器学习概念,Python 编程,numpy Python 模块,并且您已使用 TensorFlow 创建了一些机器学习模型。 尽管不是必需的,但熟悉 TensorFlow 1.xTF 1.x)版本的tf.data API 会有所帮助。 即使您没有tf.data API 的先验知识,您也应该发现本章可以自学以了解它们。

本章中的某些主题需要 Python 模块,例如argparsetqdm,这些模块已在本书的 GitHub 存储库中列出。 可在这个页面中获得本章的代码。

设计和构建数据管道

训练机器学习ML)模型和深度神经网络DNN)时,最重要的要求之一,是在给定的样本空间中具有同分布的大型训练数据集(通常是未知的,我们在 ML 或 DNN 训练中了解到),以便 ML 模型和 DNN 可以从给定的训练数据中学习,并很好地推广到看不见的未来或分离出来的测试数据。 此外,通常与训练集分布来自同一来源的验证数据集对于微调模型超参数至关重要。 在许多情况下,开发人员会从可用的数据(无论是少量还是大量)入手,以训练机器学习模型,包括大容量的深度学习…

原始数据

用于训练 ML 模型的原始数据可以是文本文件,CSV 文件,图像,视频或自定义格式的文件。 原始数据甚至可以是这些文件类型的组合。 原始数据也可以是有序数据,例如时间序列数据,或者,它甚至可以是文本的向量表示,例如单词嵌入。 重要的是要确保在将原始输入数据输入模型之前对其进行管理,因为它会影响运行时模型训练的效率。

在许多情况下,原始数据可以存储在数据库中,例如 MySQL,MS SQL,MongoDB 等。 就本书而言,假设甚至表格数据,SQL 或 NoSQL 数据都是原始数据,并且出于机器/深度学习模型的目的,需要将其拆分并转换为TFRecords。 解释 SQL 和 NoSQL 数据库超出了本书的范围。

将数据拆分为训练,验证和测试数据

ML 模型训练的数据准备的关键特征之一是能够将现有数据分为训练,验证和测试集。 训练数据是已看到并用于拟合或训练模型的数据; 例如,神经网络的学习权重和偏置。 验证数据(有时称为开发数据)用于微调模型的超参数,例如学习率,要使用的优化程序等等。 模型会经常查看此数据(例如,在每次迭代或新周期之后)并评估模型。

请注意,验证数据仅可帮助您微调模型。 它不会更新权重和偏置。

最后,测试数据是…

创建TFRecords

TFRecords 的创建是输入数据管道的核心,因此您可以创建tf.data.Dataset对象。 值得注意的是,您可以直接使用原始数据创建数据集,而无需创建TFRecords(将在下一部分中进行说明)。 但是,推荐的方法是首先从原始(拆分)数据创建TFRecords,然后将其用于数据集管道。 这是 TF 2.0 输入数据管道设计的关键部分。 下图显示了TFRecords的创建流程:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L8PuZZzA-1681703937401)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/whats-new-tf2/img/41f35661-5631-45f1-8c96-42505714c4a3.png)]

TFRecords通过将数据序列化到磁盘来帮助我们有效地读取数据,并且可以存储在一组TFRecords文件中。 每个文件的建议大小为 100 MB 到 200 MB。 应该注意的是TFRecord是可以存储任何类型数据的二进制格式。 由于是二进制格式,因此它占用的磁盘空间更少,并且从磁盘存储进行复制或读取所需的时间也更少。 当训练数据太大而无法存储在内存服务器,GPU 和/或 TPU 中时,还需要TFRecords。 使用带有数据集的TFRecords,可以按批形式从磁盘按需加载数据(将在本章稍后的批量中对此进行解释) 部分)。

TFRecords有四个重要组成部分:

  • TFRecord格式,用于存储二进制记录或数据序列。
  • 协议缓冲区是跨平台的,并且具有跨语言库,用于以协议消息的形式对结构化数据进行有效的序列化。
  • 协议消息是信息的小型逻辑记录,其中包含一系列名称/值对。
  • tf.Example是一种灵活的协议消息(也称为protobuf),旨在与 TensorFlow 一起使用。 TensorFlow 扩展TFX)是 TF 2.0 中的另一个重要功能,用于部署生产级 ML 管道,我们将在第 5 章,“模型推理管道–多平台部署”中进行学习。

请注意,在 TF 2.0 中,tf.Examples已在诸如 TFX 的所有 TensorFlow 高级 API 中使用。。

现在,让我们看看如何将数据存储在TFRecords中。 如前所述,任何转换为​​TFRecords格式的数据都存储为二进制字符串序列。 您可能会猜到,必须先指定数据结构,然后才能从tfrecord文件读取或写入数据。 为了读取和写入tfrecords文件,我们需要使用tf.Example协议消息。 请注意,数据中包含的每条小信息都必须使用Etf.Example进行存储。 此外,为了将信息写入磁盘,使用了tf.io.TFRecordWriter。 要从磁盘读回信息,您可以使用tf.io.TFRecordReader

TensorFlow 协议消息 - tf.Example

tf.Example{'string':tf.train.Feature}映射(Python 词典),其中'string'可以是任何名称; 例如'image''features''label'

tf.train.Feature可以是以下三种类型之一:

  • tf.train.BytesList:用于stringbyte信息
  • tf.train.FloatList:用于floatdouble信息
  • tf.train.Int64List:用于boolenum和所有整数,例如int32uint32int64uint64

通过使用以下快捷函数转换标准 TensorFlow 类型,可以将tf.Example消息序列化,写入和读取到tfrecords文件中:

以下代码块中的函数可用于将值转换为…

tf.data数据集对象创建

如我们前面提到的,tf.data API 集提供了从原始数据构建复杂而有效的输入数据管道的工具。 例如,输入管道可以从分布式文件系统的图像文件构建。 如果您使用的是自然语言处理NLP)模块,也可以从原始文本数据构建它。 下图显示了tf.dataset对象创建的流程:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-e9mxlmxs-1681703937401)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/whats-new-tf2/img/1afeb47b-4688-432f-8b53-be33a927f643.png)]

tf.data.Datasettf.data API 集的主要类,代表一系列元素,其中每个元素包含一个或多个张量对象。 数据集有四种主要类型,如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ENjTXcVO-1681703937401)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/whats-new-tf2/img/2630d32b-20bb-4643-8b10-d30504f6f72d.png)]

在本章中,为简单起见,所有四种类型的数据集都称为数据集和/或tf.data.Dataset。 在需要时将引用显式类型。

从定义上讲,tf.data.Dataset是一个或多个张量对象的元素序列,称为分量; 数据集中的每个元素都具有相同的结构。 要检查数据集的类型和形状,开发人员可以使用两个 Python API tf.data.Dataset.output_typestf.data.Dataset.output_shapes,如以下代码块所示:

代码语言:javascript复制
# Check type and shape of Dataset

dataset = tf.data.Dataset.from_tensor_slices(...)
print(dataset.output_types)
print(dataset.output_shapes)

前面的代码是构建图像数据管道的示例。 数据集的元素可以是单张训练数据,由一对图像和标签张量组成。

在 TF 2.0 中,数据集对象是 Python iterables,这与 TF 1.x 版本的关键区别在于 TF 1.x 版本需要tf.data.Iterator来遍历数据集对象。 以下代码显示了在 TF 1.x 和 TF 2.0 中迭代数据集对象之间的区别:

代码语言:javascript复制
# The following code shows difference in iterating Dataset objects 
# in TensorFlow 1.x and TensorFlow 2.0 

dataset = tf.data.Dataset.from_tensor_slices(...)
dataset = dataset.shuffle(...)
dataset = dataset.map(...)
dataset = dataset.batch(...)

# TensorFlow 1.x (using one shot iterator, get_next)
iterator = dataset.make_one_shot_iterator()
next_element = iterator.get_next()

with tf.Session() as sess:
    for _ in range(...):
    element = sess.run(next_element)
    ...

# TensorFlow 2.0 (extremely simple where Datasets are Python iterables)

for element in dataset:
    ...

正如您在前面的代码块中看到的那样,现在遍历数据集对象非常简单。

创建数据集对象

可以使用两种主要方法创建数据集对象:

  • 从源创建:
    • 来自内存中的numpy / tensorflow对象
    • 使用TFRecords来自磁盘
  • 将转换应用于现有数据集:
    • 从一个或多个数据集构造一个数据集。 这将在“数据集转换”部分中更详细地说明。

由于建议使用TFRecords创建一个tf.data.Dataset,让我们看看它是如何工作的。 然后,我们将介绍从其他类型的输入创建数据集的方法。

使用 TFRecords 创建数据集

创建一些TFRecords后,我们可以直接使用tf.data.Dataset API 读取它们。 以下是使用TFRecords创建数据集的框图:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZfMdtjjR-1681703937401)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/whats-new-tf2/img/f401cf69-c5ac-4b82-b38b-f767f3de4520.png)]

您可以使用以下代码从数据集中读取tfrecords文件:

代码语言:javascript复制
# You can read tfrecord files as below
dataset = tf.data.TFRecordDataset(tfrecords_file_names)

使用内存中的对象和张量创建数据集

从内存中对象创建tf.data.Dataset的最简单方法是使用from_tensor_slices()方法,该方法相对于数据中的第一个索引对数组进行切片。 我们将在此处引用tf.data.Dataset.map() API,该 API 在“数据集转换”部分中详细定义。 就目前而言,map(...)仅表示正在基于应用于数据集对象的每个元素的某些函数来修改(转换)数据集。

您可以使用两种 API 从内存中的张量创建数据集:

  • tf.data.Dataset.from_tensors()
  • tf.data.Dataset.from_tensor_slices()

您可以在这个页面中查看示例代码。

不使用 TFRecords 直接使用其他格式创建数据集

如前所述,您可以使用所有不同的文件格式直接创建tf.data.Dataset。 我们还解释了创建TFRecords的推荐方法。 但是,如果您想直接创建数据集而不经过TFRecords,那也是可能的。 以下是一些直接从原始数据创建tf.data.Dataset的示例:

  • 使用 CSV 文件:

您可以使用td.data.experimental.make_csv_dataset(...) API 查找.csv文件。 您可以如下定义.csv文件和batch_size中可用的列。 完整的代码可以在这个页面中找到:

代码语言:javascript复制
csv_file = "./curated_data/train.csv"
csv_columns = ['square_ft', 'house_type', 'price']
dataset = tf.data.experimental.make_csv_dataset(csv_file, column_names=csv_columns, batch_size=8)

如果需要从 CSV 文件中选择几列,则可以使用select_columns参数来完成。 有关更详细的概述,请参阅 tensorflow.org 。

  • 使用文本数据:

tf.data.TextLineDataset(...) API 旨在从文本文件创建数据集。 这主要用于文本数据,其中每一行包含一个数据样本。 一些示例包括日志消息,问题答案等。 我们将使用与上一节相同的示例向您展示如何使用文本数据创建tf.data.Dataset。 完整代码可在这个页面中找到:

代码语言:javascript复制
def train_decode_line(row):   cols = tf.io.decode_csv(row, record_defaults=[[0.], ['house'], [0.]])   myfeatures = {'sq_footage':cols[0], 'type':cols[1]}   mylabel = cols[2] #price   
  return myfeatures, mylabel

def predict_decode_line(row):
  cols = tf.decode_csv(row, record_defaults=[[0.], ['house']])
  myfeatures = {'sq_footage':cols[0], 'type':cols[1]}
  return myfeatures

line_dataset = tf.data.TextLineDataset('./curated_data/train.csv')

train_dataset = line_dataset.map(train_decode_line)
  • 使用图像:

最常见的输入数据管道之一是图像,可以是.jpeg.png格式。 您的数据集中可能有成千上万的图像。 由于硬件内存(CPU 内存或 GPU 内存)的限制,我们无法将所有图像存储到内存中。 tf.data.Dataset提供了构建此管道的有效方法。

在以下示例中,我们有几个.jpeg/.jpg文件,我们将使用它们全部创建tf.data.Dataset。 您可以在这个页面中找到更多详细信息:

代码语言:javascript复制
# Get images files
file_pattern = ["./curated_dahttps://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/whats-new-tf2/img/*.jpeg", "./curated_dahttps://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/whats-new-tf2/img/*.jpg"]
image_files = tf.io.gfile.glob(file_pattern)
# Get labels
labels = []
for img_path in image_files:
  labels.append(get_label(img_path))

# preprocess images
def preprocess_image(img_path, label):
  img_data = tf.io.read_file(img_path)
  feat = tf.image.decode_jpeg(img_data, channels=3)
  feat = tf.image.convert_image_dtype(feat, tf.float32)
  return feat, label, img_path

# Create dataset of all image files
image_path_dataset = tf.data.Dataset.from_tensor_slices((image_files, labels))

# Convert to image dataset
image_dataset = image_path_dataset.map(preprocess_image)
  • 使用多个数据集:

我们还可以使用tf.data.Dataset.map()tf.data.Dataset.zip()tf.data.Dataset.concatenate() API 从现有数据集中创建数据集。 这些将在下一节中解释,我们将在其中讨论数据集的转换。

转换数据集

创建数据集对象后,需要根据模型要求对其进行转换。 下图显示了数据集转换的流程:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s7g7Fpyy-1681703937402)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/whats-new-tf2/img/c68890ab-1e05-4ea9-9525-c0e187b0bf7e.png)]

一些最重要的转换如下:

  • 数据重排:选择部分数据而不是获取整个数据集可能需要这些。 它们对于使用数据子集进行实验很有用。
  • 数据清除:这些非常重要。 就像清除日期格式(例如从YYYY/MM/DDMM-DD-YYYY)或删除具有缺失值或错误数字的数据一样简单。

map函数

此转换 API 在数据集的每个元素上执行map_func输入。 对于那些使用 PandasDataframe.apply(...)的人来说,map(...)与之非常相似。 作为map(...) API 的自变量,它采用了一个应用于数据集每个元素的函数。 该函数继而从输入数据集中获取表示单个元素的tf.Tensor对象,并返回新转换的tf.Tensor对象。 请注意,输出中元素的顺序与输入数据集的顺序相同:

代码语言:javascript复制
ds = tf.data.Dataset.range(1, 6) # [1, 2, 3, 4, 5]
ds.map(lambda x: x   1)

根据数据集中每个元素的结构,正确定义map_func的输入签名非常重要:

代码语言:javascript复制
a = [1, 2, 3, 4, 5]
ds = tf.data.Dataset.from_tensor_slices(a)
result = a.map(lambda x: ...)

b = [(2, 1), (3, 5), (6, 6)]
ds = tf.data.Dataset.from_tensor_slices(b)
def map_func(input):
  output1 = input[0]   1
  output2 = input[1]   2
  return output1, output2

ds=ds.map(map_func)

flat_map函数

此转换将map_func输入映射到输入数据集并展平结果。 这用于确保数据集的顺序保持不变。 map_func必须在此处返回数据集:

代码语言:javascript复制
a = Dataset.from_tensor_slices([ [1, 2, 3], [4, 5, 6], [7, 8, 9] ])a.flat_map(lambda x: Dataset.from_tensor_slices(x   1)) # ==># [ 2, 3, 4, 5, 6, 7, 8, 9, 10 ]

zip函数

该 API 与 Python 的内置zip(...)函数相似。 Python 的zip(...)函数和tf.data.Dataset.zip(...)函数之间的区别在于,后者可以采用数据集的嵌套结构:

代码语言:javascript复制
a = Dataset.range(1, 4) # ==> [ 1, 2, 3 ]
b = Dataset.range(4, 7) # ==> [ 4, 5, 6 ]
c = Dataset.range(7, 13).batch(2) # ==> [ [7, 8], [9, 10], [11, 12] ]
d = Dataset.range(13, 15) # ==> [ 13, 14 ]

# The nested structure of the `datasets` argument determines the
# structure of elements in the resulting dataset.
Dataset.zip((a, b)) # ==> [ (1, 4), (2, 5), (3, 6) ]
Dataset.zip((b, a)) # ==> [ (4, 1), (5, 2), (6, 3) ]

# The `datasets` argument may contain an arbitrary number of
# datasets.
Dataset.zip((a, b, c)) # ==> [ (1, 4, [7, 8]),
                        # (2, 5, [9, 10]),
                        # (3, 6, [11, 12]) ]

# The number of elements in the resulting dataset is the same as
# the size of the smallest dataset in `datasets`.
Dataset.zip((a, d)) # ==> [ (1, 13), (2, 14) ]

concatenate函数

此转换 API 通过将输入数据集与此数据集连接来创建新的数据集:

代码语言:javascript复制
a = tf.data.Dataset.range(1, 4) # ==> [ 1, 2, 3 ]
b = tf.data.Dataset.range(4, 8) # ==> [ 4, 5, 6, 7 ]
c = a.concatenate(b) # ==> [ 1, 2, 3, 4, 5, 6, 7 ]

interleave函数

该 API 使用map_func转换数据集的每个元素,并交织结果。 例如,您可以使用Dataset.interleave()同时处理许多输入文件:

代码语言:javascript复制
# Preprocess 4 files concurrently, and interleave blocks of 16 records from
# each file.
filenames = ["/var/data/file1.txt", "/var/data/file2.txt", ...]
dataset = (Dataset.from_tensor_slices(filenames)
           .interleave(lambda x:
               TextLineDataset(x).map(parse_fn, num_parallel_calls=1),
               cycle_length=4, block_length=16))

cycle_lengthblock_length参数控制元素生成的顺序。 cycle_length控制同时处理的输入元素的数量。 例如,如果将cycle_length设置为 1,则此转换将一次处理一个输入元素,并将产生与tf.data.Dataset.flat_map相同的结果。 通常,此转换会将map_func应用于cycle_length输入元素,在返回的数据集对象上打开迭代器,并对其进行循环,从每个迭代器生成block_length连续元素,然后在每次到达迭代器的末尾时就使用下一个输入元素:

代码语言:javascript复制
a = Dataset.range(1, 6) # ==> [ 1, 2, 3, 4, 5 ]

# NOTE: New lines indicate "block" boundaries.
a.interleave(lambda x: Dataset.from_tensors(x).repeat(6),
            cycle_length=2, block_length=4)
 # ==> [1, 1, 1, 1,
 # 2, 2, 2, 2,
 # 1, 1,
 # 2, 2,
 # 3, 3, 3, 3,
 # 4, 4, 4, 4,
 # 3, 3,
 # 4, 4,
 # 5, 5, 5, 5,
 # 5, 5]

只要map_func是纯函数,此变换产生的元素的顺序就是确定性的。 如果map_func包含任何有状态操作,则该状态的访问顺序不确定。

take(count)函数

take(count)函数使用当前数据集中的计数最多的元素创建一个新数据集。 通常,这可用于减少数据集的大小,以用于调试或简化目的。 此外,如果将计数指定为-1,或者如果计数大于数据集的大小,则新数据集将包含先前数据集的所有元素。

filter函数

此 API 根据条件谓词函数过滤当前数据集:

代码语言:javascript复制
ds = tf.data.Dataset.from_tensor_slices([1, 2, 3])
ds = ds.filter(lambda x: x > 3) # ==> [1, 2]

打乱和重复tf.data.Dataset

机器学习模型必须从训练,验证和测试步骤的总体分布中合理地表示数据。 通常,原始数据可以按特定顺序存储,例如相对于每个类一起存储,或者数据可以一起存储在特定源中。 必须对原始数据进行混洗,以确保训练,验证和测试数据分布在整个数据分布中。 另外,建议在每个周期之后对数据进行混洗。 下图显示了打乱和重复使用tf.data.Dataset的流程:

良好的随机播放还有助于减少数据的差异,该数据用于模型…

批量

梯度下降与反向传播相结合是最近机器学习或深度神经网络系统中最流行的学习算法。 梯度下降有三种:

  • 批量梯度下降,其中所有数据都呈现给模型以供学习
  • 小批量梯度下降,其中将一批数据提供给模型以供学习
  • 随机梯度下降,其中提供随机采样的数据以训练模型

在这里,由于大型数据集的硬件内存限制,在大多数情况下批量梯度下降是不实际的。 而且,由于模型一次从一个数据中学习,因此随机梯度下降可能会很慢。 由于这些原因,小批量梯度下降法是使用最广泛的算法。 下图显示了批量的流程:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qz0L70WU-1681703937402)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/whats-new-tf2/img/2b600952-f4a1-46eb-8466-fab7d2eaacba.png)]

此外,最近的机器学习算法和深度神经网络在 GPU,TPU 和大量 CPU 上进行了分布式训练。 这些 GPU 或 TPU 中的每一个都有自己的内存限制(例如,NVIDIA 的 1080Ti GPU 具有 11 GB 的可用内存,而 Tesla V100 GPU 具有 16 GB 的可用内存)。 由于基于反向传播的梯度下降用于训练和学习 ML 模型的权重和偏差,因此开发人员使用小批量梯度下降; 因此,重要的是要有足够的批量大小,以确保可用的 GPU(或 TPU)不会耗尽内存。

tf.data.Dataset提供了一种以高效且无缝的方式创建一批样本的好方法,如以下代码块所示:

代码语言:javascript复制
dataset = tf.data.TFRecordsDataset(...)
dataset = dataset.shuffle(buffer_size, seed=None, reshuffle_each_iteration=None)
dataset = dataset.repeat(count = None)
dataset = dataset.batch(batch_size, drop_remainder=True)

batch(...) API 将此数据集的连续元素合并为批量。 batch_size是传递给此 API 的超参数。 在第 4 章,“模型训练和 TensorBoard 的使用”中,我们将讨论并提供批量建议。

预取

批量完成后,建议使用prefetch(...) API。 该 API 将输入数据集转换为新数据集,该数据集可从输入数据集中预提取元素。 该 API 之所以重要,是因为它收集了在模型为当前批量提供服务时将加载到输入管道中的下一个批量:

代码语言:javascript复制
dataset = tf.data.TFRecordsDataset(...)dataset = dataset.shuffle(buffer_size, seed=None, reshuffle_each_iteration=None)dataset = dataset.repeat(count = None)dataset = dataset.batch(batch_size, drop_remainder=True)dataset = dataset.prefetch(buffer_size)

通常,为prefetch(...)函数指定的buffer_size参数应与为batch(...) ...指定的batch_size参数一样大。

在将数据管道输出输入模型之前,先对其进行验证

到目前为止,我们已经学习了使用几种提取和转换数据的方法和技术来构建输入数据管道。 作为建议,在将输入数据管道输入模型之前,验证输入数据管道是否正在提取和转换正确的数据非常有用。 在 TF 2.0 中,这样做非常简单,因为数据集对象现在是 Python 可迭代的。 您可以如下遍历创建的数据集以获取数据的打印值:

代码语言:javascript复制
ds = tf.data.Dataset.from_tensor_slices([1, 2, 3])
for data in ds:
    print(data)

将创建的数据集馈入模型

一旦创建,转换和打乱数据集对象并完成批量,就需要将其馈入模型(从本章开头记住 ETL 的 L)。 此步骤在 TF 2.0 中进行了重大更改。

TF 2.0 中创建输入数据管道的一个主要区别在于其简单性。 TF 1.x 需要一个迭代器才能将数据集提供给模型。 为了做到这一点,有几个迭代器可以迭代一批数据。 一种是通过使用数据集对象中的tf.data.Iterator API。 TF 1.x 中有一个一次性的,可初始化的,可重新初始化的和可填充的迭代器。 尽管这些迭代器功能非常强大,但它们也增加了大量的复杂性,无论从术语上还是…

完整的端到端数据管道示例

到目前为止,我们已经介绍了数据集对象的创建以及如何创建批量数据以馈入模型。 在本节中,我们将看一个端到端输入数据管道和模型训练的示例。 我们将使用 CIFAR10 数据构建图像分类器。

为了运行基于 CIFAR10 的端到端示例,您需要从这里下载必要的数据。 该数据集摘自《从微小图像中学习多层特征》。 该数据集包含以下信息:

  • 50,000 张带有标签的图像用于训练
  • 10,000 张带有标签的图像用于测试
  • 10 个类标签

下载并解压缩数据集后,您将看到一个名为cifar-10-batches-py的文件夹,其中包含以下文件:

  • batches.meta
  • data_batch_2
  • data_batch_4
  • readme.html
  • data_batch_1
  • data_batch_3
  • data_batch_5
  • test_batch

data_batch_*文件包含训练数据,而test_batch文件包含测试数据。 这些文件为 Python pickle格式。 在此端到端示例中,我们将从 pickle 文件中创建tfrecords。 完整的代码文件以及README.md文件可在本书的 GitHub 存储库中找到,两者均可在这里找到。

使用 Pickle 文件创建 TFRecords

出于说明目的,我们将使用data_batch_*文件之一作为验证数据。 我们将其余的用于训练。 例如,如果我们选择data_batch_4作为验证数据,则data_batch_1data_batch_2data_batch_3data_batch_5将用作训练数据。

  1. 让我们使用 CIFAR10 数据创建 TFRecords:
代码语言:javascript复制
def create_tfrecords(cifar10_data_folder, validation_data_idx):  """ function to generate tfrecords  Creates three sub-folders, train, eval, test and put resp   tfr files  """  batch_files = _get_file_names(validation_data_idx)  tfrecords_outdir = './tfrecords'  for data_type in ['train', 'eval', 'test']:    input_files = [os.path.join(cifar10_data_folder, i)  for i in batch_files[data_type]] ...

TF 2.0 中数据管道的最佳实践和性能优化

这是在 TF 2.0 中建立有效的输入数据管道时应遵循的最佳实践的摘要:

  • 建议在重复转换之前使用打乱(shuffle)API。
  • 使用预取转换可以重叠生产者(获取下一批数据)和使用者(使用当前数据进行训练)的工作。 另外,非常重要的一点是要注意,在对数据管道进行打乱(打乱),重复(重复)和批量(批量)之后,应将预取转换添加到输入管道的末尾。 看起来应该像这样:
代码语言:javascript复制
# buffer_size could be either 1 or 2 which represents 1 or 2 batches of data
dataset = dataset.shuffle(count).repeat().batch(batch_size).prefetch(buffer_size)
  • 强烈建议通过启用num_parallel_calls参数来并行化映射 API。
  • 对于远程存储的数据集,建议使用interleave(...)转换来并行读取来自不同文件的数据。

TF 2.0 中的内置数据集

TF 2.0 还提供了可与 TensorFlow 一起使用的数据集的集合。 它负责下载,准备数据,甚至自行构建tf.data.Dataset,然后可以将其直接输入模型中。

请按照以下步骤使用这些内置数据集:

  1. 安装 TensorFlow 数据集:
代码语言:javascript复制
pip3 install tensorflow-datasets

请注意,tensorflow-datasets希望您正确且完整地安装 TF 2.0。

  1. 安装tensorflow-datasets后,可以使用以下代码查看可用数据集的列表:
代码语言:javascript复制
import tensorflow_datasets as tfdstfds.list_builders()

这将给出以下输出:

代码语言:javascript复制
['abstract_reasoning', 'bair_robot_pushing_small', 'caltech101', ...

总结

本章以简单而富于启发性的方式展示了使用 TF 2.0 API 设计和构建输入数据管道的总体方法。 它提供了数据管道的不同组件的构建块,并提供了构建管道所需的 API 的详细信息。 提供了 TF 1.x API 和 TF 2.0 API 之间的比较。

总体流程可以概括为两个主要过程:原始数据管理和数据集处理。 原始数据管理处理原始数据; 将数据分为训练,验证和测试集; 并创建 TFRecords。 通常,这是一个一次性过程,其中还可以包括脱机数据转换。 数据集操作是一个在线转换过程,该过程创建数据集对象,应用转换,对数据进行混洗,然后重复进行此操作并通过预取创建一批数据; 稍后将它们输入模型。

无论模型训练/推理的训练数据大小和生命周期如何,始终建议使用输入数据管道。 由于数据集对象在 2.0 版中是 Python 可迭代的,因此将它们馈送到模型中非常简单。

在下一章中,我们将学习有关模型训练和使用 TensorBoard 的知识。

进一步阅读

尽管本章试图捕获有关如何构建输入数据管道的最新信息,但 TensorFlow 是一个快速变化的平台。 开发人员每天都在增加新功能。 社区中还有成千上万的开源贡献者,他们正在迅速添加功能。 强烈建议尽可能参考这里,以了解正确的 API 使用和/或更改。

四、TensorBoard 的模型训练和使用

本章详细介绍了机器学习训练管道,以构建,训练和验证包括深度神经网络在内的最新机器学习模型。 它描述了如何集成输入数据管道,创建基于tf.keras的模型,以分布式方式进行训练以及运行验证以微调模型的超参数。 它还涉及有关如何导出和保存 TensorFlow 模型以进行部署和推理的各种概念。 模型调试和可视化是用于调试和提高模型准确率和表现的关键工具。 本章还概述了 TensorBoard 的用法,在 TF 2.0 中的更改以及如何使用 TensorBoard 进行模型调试以及对模型的速度和性能进行性能分析。

TensorFlow 1.x 版本强烈支持低级和中级 API,以构建机器学习模型。 它还具有 Estimator API,包括预制的估计器,例如LinearClassifierDNNRegressor,以及用作高级 TF API 的定制估计器。 TF 1.x 中对估计器的支持是提供高级 API,与低级和中级 TF API 相比,它们更易于构建。 从 TensorFlow 2.0 开始,主要变化之一是采用 Keras API 标准作为高级 API 而不是 Estimators。 对于 TensorFlow 开发团队而言,这非常有意义,因为 Keras API 是迄今为止机器学习社区中采用的最大 API 集,并且 Keras 的创建者 Francois Chollet 也是一位出色的人工智能(AI)研究人员,现在已经加入 TensorFlow 开发团队的成员。 TensorFlow 1.x 版本已经提供对tf.keras的初始支持; 但是,在 TF 2.0 版本中可以获得tf.keras的完整而完整的体验。

在本章中,我们将浏览tf.keras API,包括 API 的顺序,函数式和模型子类类型。 您将学习如何使用tf.data.Dataset将输入数据流水线馈入模型流水线,以及特征列的可能分类结构。 我们还将介绍如何定义损失函数,最常见的优化器,基于 TensorBoard 的数据,模型调试,可视化和性能分析等。 从 TensorFlow 2.0 开始,tf.keras API 已紧密集成到 TensorFlow 生态系统中,其中包括对tf.data的改进支持和最新可用的分发策略,可用于跨多种 GPU 和 TPU 进行分布式训练。 tf.keras还无缝支持导出训练有素的模型,这些模型可以使用 TensorFlow 服务和其他技术在 TensorFlow Lite 的移动和嵌入式设备上进行服务和部署。

我们将在本章介绍以下主题:

  • 比较 Keras 和tf.keras
  • 使用tf.keras 2.0 创建模型
  • 模型编译与训练
  • 自定义训练逻辑
  • 分布式训练
  • TensorBoard

技术要求

假定本章和本书的读者都知道机器学习,神经网络和深度神经网络的基础知识。 另外,作为前提条件,假设读者知道 TensorFlow 1.x API。 此外,还需要对深度神经网络中的卷积层,循环层和前馈层有基本的了解。

比较 Keras 和tf.keras

tf.keras是 TensorFlow 对 Keras API 规范的实现。 这是用于构建和训练模型的高级 API,其中包括对 TensorFlow 特定功能的一流支持,例如急切执行,tf.data管道和估计器。 tf.keras使 TensorFlow 易于使用,而不会牺牲灵活性和表现。

Keras(定义 Keras API 标准的原始网站)是一个开源项目,由于其简单和强大而受到 ML 工程师和数据科学家的极大关注。 最初,Keras 的默认后端引擎(请记住,Keras 是一组 API)是 Theano; 但是,最近它发生了变化,现在 TensorFlow 作为其默认后端引擎。 您还可以将默认后端引擎设置为 MXNet,CNTK 等。 Keras API 非常易于使用,模块化且可组合。 此外,还可以轻松扩展您的特定需求。 TensorFlow 采用了 Keras API 标准,从那时起,使用 TensorFlow 核心功能的tf.keras开发就如火如荼地进行。 现在,随着 TF 2.0 的发布,TF 开发团队为tf.keras高级 API 提供了紧密而有效的支持。 另外,值得一提的是 Keras 和tf.keras是两个完全不同的包,作为 TF 2.0 的一部分,应使用tf.keras。 在版本方面,在 TensorFlow 2.0 中,TensorFlow 和tf.keras的版本号仍然存在差异,您可以尝试使用tf.__version__tf.keras.__version__查看此版本。

比较估计器和tf.keras

TensorFlow 1.x 已建议为其高级 API 集使用tf.estimator API,该 API 集具有内置模型(例如LinearRegressorDNNClassifier)可用的预制估计器。 此外,对于更细化和定制的模型,TF 1.x 具有定制的估计器。 从 TF 2.0 开始,建议仅使用与线性分类器,DNN 分类器,组合 DNN 线性分类器和梯度提升树打包在一起的丰富的预制估计器 API 集。 这些模型已准备就绪,可以广泛使用。 对于任何自定义模型,建议直接使用tf.keras而不是tf.estimator API。 另外,值得注意的是,与tf.keras, ...有更好的协同作用

机器学习分类法和 TF 支持的快速回顾

可以使用三种主要的机器学习技术来解决大多数学习问题:

  • 监督学习借助标签数据预测标签
  • 无监督学习,对没有标签的数据进行分组和聚类
  • 强化学习,其中存在一种环境,智能体可以通过该环境通过采取行动并从环境中获取反馈(奖励)来学习实现预期目标

生成模型和判别模型可以与这三种机器学习技术一起使用。 生成模型尝试从具有未知分布的给定数据集中凭经验学习模式和分布,并可能使用学习的模型来生成新数据,就好像它来自同一分布。 一些流行的生成模型是高斯混合模型,隐马尔可夫模型,贝叶斯网络(例如朴素贝叶斯)等。 生成对抗模型是 2014 年非常流行的生成模型,由于其强大的成功和潜力而备受关注。 除了仅学习可用于无监督学习的分布之外,生成模型还可以用于执行分类或预测任务(有监督学习),该任务使用样本x的条件概率, 通过使用朴素贝叶斯定理计算概率P(y | x),属于y类。 与生成模型相反,判别模型用于直接学习条件概率P(y | x),用于回归,分类和其他类型的监督学习问题。 深度神经网络可用于构建生成模型或判别模型。

TensorFlow 提供了丰富的 API 集来构建上述生成模型和判别模型。 此外,在 TF 2.0 中,通过引入急切的执行(在第 2 章, “Keras 默认集成和急切执行”中进行了解释),创建这些模型的理念发生了整体变化, 这使得tf.keras的使用非常简单且易于调试。 此外,TensorFlow 2.0 中的tf.keras API 丰富了 TF 在 TF 1.x 版本中可以执行的全部功能。 在本书中,除非另有说明,否则我们主要使用tf.keras API 来构建,训练和预测神经网络模型,并且不会讨论低级或中级 TF API。

TensorFlow 建立深度学习模型并对其进行训练时的理念是,首先定义神经网络层(也称为构建由节点和边组成的计算图); 定义损失函数,准确率度量和适当的优化器; 然后训练模型以更新梯度。 这三个步骤在使用构建,编译和拟合的tf.keras API 中得到了体现,如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XGeENWQY-1681703937402)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/whats-new-tf2/img/acaa1019-a9a9-4896-bc8e-75554932da62.png)]

在以下部分中,我们将首先了解如何使用tf.keras 2.0 API 构建模型,该 API 将详细介绍计算图节点和边的创建。 然后,我们将介绍编译和拟合,包括损失和准确率函数的定义。

使用 tf.keras 2.0 创建模型

在本节中,我们将学习tf.keras API 的三种主要类型,以定义神经网络层,即:

  • 顺序 API :这些基于堆叠的 NN 层,可以是密集(前馈)层,卷积层或循环层)
  • 函数式 API :这些有助于构建复杂的模型
  • 模型子类 API :这些是完全可自定义的模型; 这些 API 灵活,需要谨慎编写

下图显示了用于构建tf.keras.Model的这三个 API 的 Python 类层次结构:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ekr0SHEn-1681703937403)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/whats-new-tf2/img/16a140db-6687-4c91-a063-0e3495ecfcf3.png)]

让我们创建一个相对简单的神经网络来构建手写识别分类器…

顺序 API

顺序 API 是创建 TF 模型并提供大约 70-75% 模型类型的最简单方法。 您需要创建一个tf.keras.models.Sequential(...) Python 类并将所需的层顺序添加到模型中-这也称为层栈。 这些层可能是密集,卷积甚至是循环层。 您可能需要提供第一层的输入形状。 以下是使用顺序 API 创建 TF 模型的步骤:

  1. 创建一个Sequential模型类:
代码语言:javascript复制
model = tf.keras.models.Sequential()
num_filters = 32
kernel_size = (5, 5)
pool_size = (2, 2)
num_classes = 10
  1. 首先通过调用build()fit()和一些数据来构建模型,或者在第一层中指定input_shape参数以进行自动构建。

(可选)第一层可以接收input_shape参数:

代码语言:javascript复制
model.add(tf.keras.layers.Conv2D(filters=num_filters, 
       kernel_size=kernel_size, 
      padding='valid', activation='relu',
      input_shape=input_shape))
  1. 另一个Conv2D层:
代码语言:javascript复制
model.add(tf.keras.layers.Conv2D(filters=num_filters, 
       kernel_size=kernel_size, 
      padding='same', activation='relu'))
  1. 添加最大池化层:
代码语言:javascript复制
model.add(tf.keras.layers.MaxPooling2D(pool_size=pool_size))
  1. 并添加一个Dropout层:
代码语言:javascript复制
model.add(tf.keras.layers.Dropout(0.5))
  1. 另外,添加Flatten层:
代码语言:javascript复制
model.add(tf.keras.layers.Flatten())
  1. 添加具有 10 个输出单元的softmax层:
代码语言:javascript复制
model.add(tf.keras.layers.Dense(units=num_classes, 
    activation='softmax'))

请注意,使用tf.keras.layers代替tf.layers。 TensorFlow 2.0 明确建议使用tf.keras.layers。 使用tf.keras.layers,您可以指定权重,偏差,初始值设定项和正则化项。 使用tf.layerstf.keras.layers时,权重初始化的方式以及获得确切的 API 定义的方式可能会有一些差异。 建议在各个部分中查看。

函数式 API

函数式 API 比顺序 API 可以构建更高级的模型。 例如,如果您需要一个具有多个输入和多个输出的模型,则无法使用顺序 API。 函数式 API 提供了这种灵活性。 另外,使用函数式 API,您可以定义具有共享层的模型。 此外,只能使用函数式 API 定义具有剩余连接的模型。

使用函数式 API 的神经网络层的创建是通过 Python 可调用对象(可调用的 Python 对象)进行的。 作为构建深度学习模型的一部分,深度学习模型通常是分层的,与顺序 API 相反,在顺序 API 中,您首先创建tf.keras.Sequential模型,然后在函数式 API 中逐层添加层…

模型子类化 API

模型子类化 API 通过对tf.keras.Model类对象进行子类化(派生)来构建完全自定义的模型。 这是通过在派生类的构造器__init__(...)中创建层栈并将其设置为该类的属性来实现的。 此外,您可以在call(...)函数中实现前向通过图。

让我们使用以下类构建模型子类:

代码语言:javascript复制
class MyModel(tf.keras.Model):

  def __init__(self):
    super(MyModel, self).__init__()
self.num_filters = 32
        self.kernel_size = (5, 5)
        self.pool_size = (2, 2)
        self.num_classes = 10
        self.my_input_shape = (28, 28, 1)

现在定义层:

代码语言:javascript复制
        # first conv layer
        self.conv1_layer = tf.keras.layers.Conv2D(filters=self.num_filters,
          kernel_size=self.kernel_size, padding='valid', activation='relu', 
          input_shape=self.my_input_shape)
        # Another conv2d layer
        self.conv2_layer = tf.keras.layers.Conv2D(filters=self.num_filters,
          kernel_size=self.kernel_size, padding='same', activation='relu')

添加最大池化层:

代码语言:javascript复制
        self.mp_layer = tf.keras.layers.MaxPooling2D(pool_size=self.pool_size)

并添加一个丢弃:

代码语言:javascript复制
        self.do_layer = tf.keras.layers.Dropout(0.5)

展平层:

代码语言:javascript复制
        self.ft_layer = tf.keras.layers.Flatten()

添加一个带有 10 个输出单元的 softmax 层:

代码语言:javascript复制
        self.outputs_layer = tf.keras.layers.Dense(self.num_classes, activation='softmax')

  def call(self, inputs, training=False):
    conv1 = self.conv1_layer(inputs)
       conv2 = self.conv2_layer(conv1)
       mp = self.mp_layer(conv2)
       do = tf.keras.layers.Dropout(0.5)(mp)
       ft = tf.keras.layers.Flatten()(do)
       outputs = self.outputs_layer(ft)
       return outputs

使用任何 API 创建模型后,最好使用model.summary()和/或tf.keras.utils.plot_model(...)查看模型详细信息。

模型编译与训练

神经网络对复杂的非线性函数建模,例如sin(x)x ** 2x ** 3,仅举几个简单的函数, 由层的网络(栈)组成。 这些层可以是卷积层,循环层或简单的前馈层的混合。 每层由神经元组成。 神经元有两种模型化非线性的成分:前一层的加权总和,然后是激活函数。 神经网络试图以迭代方式学习给定训练数据的分布。 一旦通过指定激活函数以层栈的形式构建了神经网络,就需要定义一个目标函数(也称为损失函数)以使用适当的模型来改善模型权重。

compile() API

tf.keras.Model.compile(...) API 有助于定义loss函数和优化器,如下所示:

代码语言:javascript复制
model.compile(optimizer='adam',
              loss='categorical_crossentropy',
              metrics=['accuracy'])

可以使用msecategorical_crossentropy之类的字符串或通过指定tf.keras.losses.CategoricalCrossentropy来简单地定义损失,如以下代码块所示。 优化器也是如此。 但是,为了为优化器指定明确的学习率,您必须使用 Python 优化器类,例如tf.keras.optimizers.Adam,如下所示:

代码语言:javascript复制
# Specify the training configuration (optimizer, loss, metrics)
model.compile(optimizer=tf.keras.optimizers.Adam(learning_rate=0.1),
              # Loss function to minimize
              loss=tf.keras.losses.CategoricalCrossentropy(),
              # List of metrics to monitor
              metrics=[tf.keras.metrics.Accuracy()])

fit() API

tf.keras.Model.fit(...)是训练模型的主要 API。 它以输入训练数据以及(可选)批量大小,回调等作为输入:

代码语言:javascript复制
model.fit(train_dataset, epochs=10,      callbacks=[tensorboard_callback],          validation_data=val_dataset)

回调是模型训练期间特定工具的挂钩。 它们被传递给fit(...)函数以自定义和扩展训练过程中模型的行为。 有很多有用的内置回调-以下是其中一些:

  • tf.keras.callbacks.ModelCheckpoint:定期保存模型的检查点
  • tf.keras.callbacks.LearningRateScheduler:动态更改学习率
  • tf.keras.callbacks.EarlyStopping:发生以下情况时中断训练

保存和还原模型

监视训练进度非常重要,并且在每次迭代或训练步骤中都能查看模型对于调试模型的表现同样重要。 此外,训练结束后,需要加载模型以进行推理和部署。 为了能够做到这一点,需要保存模型的训练权重和参数以备将来使用。

TF 2.0 提供了支持,可以轻松完成此操作,因为可以在训练期间和训练后保存模型。 这为用户提供了灵活性,允许从先前的检查点恢复训练,并且避免完全重新启动模型的训练以减少较长的训练时间。 此外,这些保存的模型可以在团队之间共享以进行进一步的工作。 在本节中,我们将主要讨论保存tf.keras.Models

TF 提供了仅保存模型权重或保存整个模型的灵活性,包括模型权重,配置和优化器详细信息,等等。

在训练同时保存检查点

可以使用tf.keras.callbacks轻松实现保存检查点,如下所示:

代码语言:javascript复制
# Create checkpoint callbackcp_callback = tf.keras.callbacks.ModelCheckpoint(checkpoint_path,                                                 save_weights_only=True,                                                 verbose=1)model.fit(train_dataset, epochs=10, callbacks=[cp_callback],          validation_data=val_dataset)

之前的回调会创建多个 TensorFlow 检查点文件,这些文件会在每次训练完成后进行更新。 此外,要使用这些检查点,请使用与保存检查点的原始模型完全相同的架构来重新创建模型,构建模型,然后使用tf.keras.Model.load_weight(...) API 从任何检查点加载权重并将其用于评估:

代码语言:javascript复制
model.load_weights(checkpoint_path) ...

手动保存和恢复权重

模型权重也可以保存在检查点文件中。 这可以用来保存训练后的权重,以便将来进行进一步的训练:

代码语言:javascript复制
# Save the weights
model.save_weights('./checkpoints/my_checkpoint')
# Restore the weights
model = create_model()
model.load_weights('./checkpoints/my_checkpoint')
loss,acc = model.evaluate(test_images, test_labels)
print("Restored model, accuracy: {:5.2f}%".format(100*acc))

保存和还原整个模型

TF 还可以保存和恢复整个模型,包括权重,变量,参数和模型的配置。 这提供了加载整个模型的灵活性,而无需使用训练模型的原始代码。 整个模型可以使用tf.keras.experimental.export_saved_model以 HDF5 文件格式或即将发布的 TF 内部格式存储。 在这一点上,后者仍处于试验阶段,因此我们将不再描述:

代码语言:javascript复制
model = create_model()model.fit(train_images, train_labels, epochs=5)

将整个模型保存到 HDF5 文件中:

代码语言:javascript复制
model.save('my_model.h5')

重新创建完全相同的模型,包括权重和优化器:

代码语言:javascript复制
new_model = keras.models.load_model('my_model.h5') ...

自定义训练逻辑

如前所述,TF 2.0 带来了默认的紧急执行,这意味着基于图的代码流的传统 TF 1.x 自定义训练逻辑实现现在已过时。 为了在 TF 2.0 中实现有关急切执行的自定义训练逻辑,可以使用tf.GradientTapetf.GradientTape的目的是记录用于自动微分的运算,或者用于计算运算或计算相对于其输入变量的梯度。 这可以通过使用tf.GradientTape作为上下文管理器来完成。 TensorFlow 将在tf.GradientTape上下文中执行的所有操作记录到磁带上,然后将其与梯度一起与那些操作关联,以使用反向模式微分计算记录的操作的梯度。

例如,一个简单的立方体操作的梯度可以如下计算:

代码语言:javascript复制
x = tf.constant(2.0)
with tf.GradientTape() as tape:
  tape.watch(x)
  y = x ** 3
dy_dx = tape.gradient(y, x) # 12.0

tf.GradientTape记录所有涉及监视张量的操作,例如上例中的x。 会自动监视tf.GradientTape上下文中出现的所有可训练变量,并将其记录在磁带上。 可以通过将watch_accessed_variables设置为False来禁用此功能,以便仅记录程序员专门监视的变量。

通过将上下文管理器相互堆叠并计算相对于前一阶导数的梯度,也可以使用tf.GradientTape计算高阶导数。

tf.GradientTape还允许使用更多自定义训练逻辑,因为它提供了在使用优化程序之前操纵梯度的选项。 与内置的tf.keras.Model.fit相比,它提供了一种替代的,更加复杂且功能强大的深度学习模型训练方法。 为此,所有前向通过操作都记录在磁带上,并且为了计算这些操作的梯度,将磁带向后播放然后丢弃。 这里要注意的重要一点是,特定的tf.GradientTape模型只能计算一个梯度。

要首先使用tf.GradientTape实现模型的简单训练,请在tf.GradentTape上下文管理器内部的输入张量上调用前向传递,然后计算loss函数。 这样可以确保将所有计算结果记录在梯度磁带上。 然后,针对模型中的所有可训练变量计算梯度。 一旦计算出梯度,就可以在将其传递给优化器以将其应用于模型变量之前执行任何所需的梯度截断,归一化或变换。 看下面的例子:

代码语言:javascript复制
NUM_EXAMPLES = 2000

input_x = tf.random.normal([NUM_EXAMPLES])
noise = tf.random.normal([NUM_EXAMPLES])
input_y = input_x * 5   2   noise

def loss_fn(model, inputs, targets):
  error = model(inputs) - targets
  return tf.reduce_mean(tf.square(error))

def gradients(model, inputs, targets):
  with tf.GradientTape() as tape:
    loss_value = loss_fn(model, inputs, targets)
  return tape.gradient(loss_value, model.trainable_variables)

model = tf.keras.Sequential(tf.keras.layers.Dense(1))
optimizer = tf.keras.optimizers.Adam(learning_rate=0.01)
print("Initial loss: {:.3f}".format(loss_fn(model, input_x, input_y)))
for i in range(500):
  grads = gradients(model, input_x, input_y)
  optimizer.apply_gradients(zip(grads, model.trainable_variables))
  if i % 20 == 0:
    print("Loss at step {:03d}: {:.3f}".format(i, loss_fn(model, input_x, input_y)))
print("Final loss: {:.3f}".format(loss(model, input_x, input_y)))
print("W = {}, B = {}".format(*model.trainable_variables))

TF 2.0 中添加的另一个功能是tf.function装饰器。 用tf.function注解函数时,它仍然像任何其他 Python 函数一样工作,但是将被编译成图,这提供了诸如执行速度更快,GPU 和 TPU 加速之类的好处,并且可以轻松导出到SavedModel

并非所有函数都需要使用tf.function进行注解,因为在带注解的函数内部调用的任何函数也将在图模式下运行。 对于具有多个较小操作的图,此类函数速度更快,但对于其他具有较昂贵操作(例如卷积)的图,改进效果会较小。

tf.function装饰器还可以绘制 Python 控制流图,例如ifwhileforbreakcontinuereturn。 运行这些功能可实现更快的求值和硬件加速。

tf.function也可以在tf.keras模型和训练循环中使用。 tf.function装饰器通常用于模型的call方法上,以提供图模型来求值。 另一种更常见的做法是将tf.function用于一个训练循环,因为它仅控制流程。 这样,训练过程的更多计算可以带入 TensorFlow 内,并将受益于优化的操作。

以下代码段是tf.kerastf.function的示例:

代码语言:javascript复制
class CustomModel(tf.keras.models.Model):

  @tf.function
  def call(self, input_data):
    if tf.reduce_mean(input_data) > 0:
      return input_data
    else:
      return input_data // 2

以下代码段是训练中的tf.function示例:

代码语言:javascript复制
compute_loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)

compute_accuracy = tf.keras.metrics.SparseCategoricalAccuracy()

def train_one_step(model, optimizer, x, y):
  with tf.GradientTape() as tape:
    logits = model(x)
    loss = compute_loss(y, logits)

  grads = tape.gradient(loss, model.trainable_variables)
  optimizer.apply_gradients(zip(grads, model.trainable_variables))

  compute_accuracy(y, logits)
  return loss

@tf.function
def train(model, optimizer):
  train_ds = mnist_dataset()
  step = 0
  loss = 0.0
  accuracy = 0.0
  for x, y in train_ds:
    step  = 1
    loss = train_one_step(model, optimizer, x, y)
    if tf.equal(step % 10, 0):
      tf.print('Step', step, ': loss', loss, '; accuracy', compute_accuracy.result())
  return step, loss, accuracy

TF 2.0 还提供了一种创建自定义梯度以覆盖默认梯度计算的方法。 这是通过使用tf.custom_gradient装饰器完成的。 使用自定义梯度的一个常见原因是为一系列操作提供数值稳定的梯度,并且它们也可以用于限制梯度的范数。

要使用tf.custom_gradient装饰器,我们必须定义一个函数,该函数既返回所需的计算结果,又返回计算的梯度。 一个示例是在反向传播过程中实现梯度裁剪:

代码语言:javascript复制
@tf.custom_gradient
def clip_gradient_by_norm(x, norm):
  y = tf.identity(x)
  def grad_fn(dresult):
    return [tf.clip_by_norm(dresult, norm), None]
  return y, grad_fn

正如我们在前面的示例中看到的那样,该函数不仅返回输入张量的副本,而且还返回以默认梯度作为参数并返回裁剪后的梯度的函数。

tf.custom_gradient装饰器的主要用途是允许在一系列操作的梯度上进行细粒度设置,并可用于创建一系列操作的更有效,更稳定的实现。

有关如何使用tf.custom_gradient的更多示例,请参见这里。

分布式训练

TF 2.0 的优点之一是能够在多个 GPU 和 TPU 上以分布式方式训练和推断模型,而无需编写大量代码。 使用分发策略 API tf.distribute.Strategy(...)可以简化此过程,该 API 随时可用。 “fit() API”部分介绍了tf.keras.Model.fit(...),该部分说明了如何使用此功能训练模型。 在本节中,我们将展示如何使用分布策略跨多个 GPU 和 TPU 训练基于tf.keras的模型。 值得注意的是,tf.distribute.Strategy(...)可与tf.kerastf.estimator等高级 API 一起使用,并支持自定义训练循环或…中的任何计算。

TensorBoard

TensorBoard 是 TensorFlow 平台最重要的优势之一,而有了 TF 2.0,TensorBoard 进入了一个新的高度。 在机器学习中,要改善模型权重,通常需要能够对其进行度量。 TensorBoard 是用于提供机器学习工作流程期间所需的测量和可视化的工具。 它可以跟踪实验指标,例如损失和准确率,可视化模型图,将嵌入投影到较低维度的空间等。 与 TF 1.x 相比,TF 2.0 提供了一种非常简单的方法来使用回调来集成和调用 TensorBoard,在“fit() API”部分中对此进行了解释。 TensorBoard 还提供了一些技巧来测量和可视化您的数据和模型图,并且具有假设分析和分析工具。 它还扩展了自身以进行调试。

使用回调和调用连接 TensorBoard

TensorBoard 可以在 TF 2.0 中以两种主要方式使用。 一种方法是在使用tf.keras.Model.fit()训练模型时将其用作回调,另一种方法是将tf.summary用于使用tf.GradientTape的较低级模型。

要在 Keras 模型训练中使用 TensorBoard,我们需要指定一个 TensorBoard 回调,该回调以logdir作为参数。 TensorBoard 回调的其他参数包括histogram_freqwrite_graphwrite_imagesupdate_freqhistogram_freq允许用户指定应该多久计算一次激活和权重直方图,并需要指定验证数据。 write_graph指定是否要在 TensorBoard 中可视化模型的图,…

可视化标量,度量,张量和图像数据

TensorBoard 还提供了可视化自定义标量和图像数据的功能。 这是前面描述的度量可视化的补充。 自定义标量日志记录可用于记录动态学习率。 为此,请使用以下步骤:

  1. 使用tf.summary.create_file_writer()创建文件编写器:
代码语言:javascript复制
logdir = "logs/scalars/"   datetime.now().strftime("%Y%m%d-%H%M%S")
file_writer = tf.summary.create_file_writer(logdir   "/metrics")
file_writer.set_as_default()
  1. 然后,定义一个自定义学习率函数,然后将其传递给 Keras LearningRateScheduler回调并将自定义学习率记录在该函数内:
代码语言:javascript复制
def lr_schedule(epoch):
  """
  Returns a custom learning rate that decreases as epochs progress.
  """
  learning_rate = 0.2
  if epoch > 10:
    learning_rate = 0.02
  if epoch > 20:
    learning_rate = 0.01
  if epoch > 50:
    learning_rate = 0.005
  tf.summary.scalar('learning rate', data=learning_rate, step=epoch)
  return learning_rate
lr_callback = keras.callbacks.LearningRateScheduler(lr_schedule)
tensorboard_callback = keras.callbacks.TensorBoard(log_dir=logdir)
model = keras.models.Sequential([
    keras.layers.Dense(16, input_dim=1),
    keras.layers.Dense(1),
])
model.compile(
    loss='mse', # keras.losses.mean_squared_error
    optimizer=keras.optimizers.SGD(),
)
  1. 最后,将LearningRateScheduler与 TensorBoard 回调一起传递给model.fit回调:
代码语言:javascript复制
training_history = model.fit(
    x_train, # input
    y_train, # output
    batch_size=train_size,
    epochs=100,
    validation_data=(x_test, y_test),
    callbacks=[tensorboard_callback, lr_callback],
)

一般来说,要记录自定义标量,我们需要将tf.summary.scalar()与文件编写器一起使用,该文件编写器负责将运行数据写入特定目录并被隐式使用。

在 TensorBoard 中写入用于可视化的图像数据时,也使用文件编写器。 TensorFlow 图像摘要 API 可用于轻松记录张量和任意图像并在 TensorBoard 中查看它们。 这有助于采样和检查输入数据,并可视化模型权重和生成的图像。

为了可视化图像,在文件编写器的上下文中调用tf.summary.image()记录一个或多个图像。 此功能采用(批量,高度,宽度,通道)形式的 4 级张量,因此任何非该格式的图像都必须在将其记录到 TensorBoard 之前进行重塑。 只要将其转换为张量,该 API 还可用于记录任何种类的任意图像数据,例如 Matplotlib 图形。

以下代码段是如何将单个图像记录到 TensorBoard 的示例:

代码语言:javascript复制
img = np.reshape(train_images[0], (-1, 28, 28, 1))
# Sets up a timestamped log directory.
logdir = "logs/train_data/"   datetime.now().strftime("%Y%m%d-%H%M%S")
# Creates a file writer for the log directory.
file_writer = tf.summary.create_file_writer(logdir)
# Using the file writer, log the reshaped image.
with file_writer.as_default():
  tf.summary.image("Training data", img, step=0)

图形仪表板

TensorBoard 的图形仪表板提供可视化和检查 TensorFlow 模型的功能。 我们可以使用它来快速查看模型结构的概念图以验证其设计或查看操作级图以了解 TensorFlow 如何理解和执行程序。 检查操作级图还可以深入了解如何重新设计模型以获得更佳的运行时间。

在 TF 2.0 中,按照以下步骤查看操作级图非常简单:

  1. 将 TensorBoard 回调添加到Model.fit以确保图数据记录在 TensorBoard 中。
  2. 运行后,打开 TensorBoard 并导航到顶部栏上的“图”选项卡以查看图。 默认情况下,TensorBoard 显示操作级别图,该图显示…

超参数调优

建立良好的深度学习模型最重要的部分之一就是选择最佳超参数来训练模型本身。 超参数是工程师在模型训练之前设置的参数。 一些常见的超参数包括丢弃率,学习率和所用优化器的类型。 超参数的优化是一个耗时的过程,其中涉及对具有不同超参数的模型进行多次训练以找到最佳模型,因为目前尚无关于如何选择超参数的见解。

这样,TF 2.0 提供了一种智能执行超参数调优的工具,它可以协助确定执行最佳实验和尝试最有前途的超参数的过程。

为此,请使用以下步骤:

  1. 列出要为特定超参数尝试的值,并将实验配置记录到 TensorBoard。 然后,修改 TensorFlow 模型以在模型构建中包括超参数。 完成此操作后,将hp.KerasCallback回调添加到model.fit函数中:
代码语言:javascript复制
def train_test_model(hparams):
  model = tf.keras.models.Sequential([
    tf.keras.layers.Flatten(),
    tf.keras.layers.Dense(hparams[HP_NUM_UNITS], activation=tf.nn.relu),
    tf.keras.layers.Dropout(hparams[HP_DROPOUT]),
    tf.keras.layers.Dense(10, activation=tf.nn.softmax),
  ])
  model.compile(
      optimizer=hparams[HP_OPTIMIZER],
      loss='sparse_categorical_crossentropy',
      metrics=['accuracy'],
  )
model.fit(
    ...,
    callbacks=[
        tf.keras.callbacks.TensorBoard(logdir),  # log metrics
        hp.KerasCallback(logdir, hparams),  # log hparams
    ],
)
  _, accuracy = model.evaluate(x_test, y_test)
  return accuracy
  1. 定义模型后,下一步就是定义一种算法来循环遍历所有可能的超参数,例如网格搜索。 这将遍历离散超参数的所有值以及实值超参数的上限和下限:
代码语言:javascript复制
session_num = 0
for num_units in HP_NUM_UNITS.domain.values:
  for dropout_rate in (HP_DROPOUT.domain.min_value, HP_DROPOUT.domain.max_value):
    for optimizer in HP_OPTIMIZER.domain.values:
      hparams = {
          HP_NUM_UNITS: num_units,
          HP_DROPOUT: dropout_rate,
          HP_OPTIMIZER: optimizer,
      }
      run_name = "run-%d" % session_num
      print('--- Starting trial: %s' % run_name)
      print({h.name: hparams[h] for h in hparams})
      run('logs/hparam_tuning/'   run_name, hparams)
      session_num  = 1

对于更复杂的超参数调优,随机搜索更加有效。 可以通过随机选择每个超参数并运行实验来进行,这可以比网格搜索快得多地探索超参数空间。 也可以使用其他更复杂的算法。

  1. 最后,可以通过在logdir上运行 TensorBoard 来查看超参数日志,日志在其中写入:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qnEZ1ZK7-1681703937403)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/whats-new-tf2/img/360da61e-9b3a-4d8a-b54a-74a79d9bec0a.png)]

仪表板的左窗格允许用户按超参数过滤日志,以方便访问和使用。 超参数仪表板具有三个视图-表视图,并行坐标视图和“散点图矩阵视图”-每个视图都提供了一种不同的方式来可视化结果。 TABLE VIEW列出运行和超参数并显示指标。 并行坐标视图将每个运行显示为一条穿过每个超参数和指标的轴的线,可用于查看哪个超参数更重要。 SCATTER PLOT MATRIX VIEW显示比较每个超参数和度量的图,并有助于识别相关性。

该工具可轻松调整超参数和详细的日志,并在 TensorBoard 中直观显示结果。

What-If 工具

TensorFlow 2.0 引入了一个非常强大的工具,即 What-If 工具WIT),该工具可在 TensorBoard 仪表板内部提供易于使用的界面。 但是,仅当使用 TensorFlow 服务为模型提供服务时,才可以使用 WIT。 在第 5 章,“模型推理管道 – 多平台部署”中解释了 TensorFlow 服务。 另外,为了使用 WIT,推理数据集必须为TFRecords格式。

WIT 的某些功能是可以将具有相同工作流程的多个模型进行比较,推理结果的可视化,基于相似度的数据排列以及通过编辑数据点执行模型的敏感性分析的能力。

分析工具

使用 TF 2.0 随附的 TensorBoard 时,如果您使用tf.keras API 构建和训练模型,则已经有一个 PROFILE 仪表板选项卡,可用于查看模型所花费的各种训练时间:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l2lGcA8F-1681703937403)(https://gitcode.net/apachecn/apachecn-dl-zh/-/raw/master/docs/whats-new-tf2/img/399c1540-e609-4b46-8f9e-529b55c2beba.png)]

总结

本章详细介绍了如何使用 TF 2.0 tf.keras API 构建训练管道,以及如何使用分布策略在 GPU 上以分布方式在 GPU 上使用各种可用的损失函数,优化器和超参数查看构建,编译和拟合模型。 。 它还详细介绍了如何在训练时保存,恢复模型以进行将来的训练以及进行推断。 TensorBoard 是 TF 2.0 的主要优势之一,我们提供了有关如何有效地使用它来监视训练表现损失和准确率以及如何调试和分析它的详细信息。

在下一章中,我们将学习模型推理管道并将其部署在多平台上。

问题

我应该使用tf.keras API 还是 TF 的低级和中级 API?

查看本章,然后尝试找到答案。

我应何时使用tf.keras顺序和函数式 API? 为什么需要模型子类化?

通常,对于更简单的模型,应使用tf.keras顺序。 大部分模型可以使用顺序 API 编写。 但是,对于那些需要多个输入和输出以及某些特定连接(例如残差)的模型,应使用函数式 API。 对于真正定制的模型,可以使用模型子类化。

进一步阅读

鼓励用户阅读这里的迁移学习指南,该指南重用了预训练的模型权重和变量,并将学习表示迁移到另一个数据集。

1 人点赞