TensorFlow 实战(七)(2)

简介: TensorFlow 实战(七)

TensorFlow 实战(七)(1)https://developer.aliyun.com/article/1522940

在 Ubuntu 上安装 libcupti

要在 Linux 上安装 libcupti,只需运行sudo apt-get install libcupti-dev

在 Windows 上安装 libcupti

在 Windows 上安装 libcupti 需要更多工作:

  • 确保你已经安装了推荐的 CUDA 版本(例如 CUDA 11 [>= TensorFlow 2.4.0])。有关 CUDA 版本的更多信息,请访问www.tensorflow.org/install/source#gpu
  • 接下来,打开 NVIDIA 控制面板(通过右键单击桌面并选择菜单项)进行几项更改(mng.bz/rJVJ):
  • 确保你点击桌面 > 设置开发者模式,设置开发者模式。
  • 确保你为所有用户启用了 GRU 性能分析,而不仅仅是管理员(图 14.9)。
  • 更多可能遇到的错误,请参考来自官方 NVIDIA 网站的mng.bz/VMxy
  • extras\CUPTI\lib64中的libcupti_.dllnvperf_host.dllnvperf_target .dll文件复制到bin文件夹中。确保 libcupti 文件的名称为libcupti_110.dll
  • extras\CUPTI\lib64中的所有文件复制到lib\x64中。
  • extras\CUPTI\include中的所有文件复制到include中。


图 14.9 为所有用户启用 GPU 性能分析

确保你已经在你所使用的环境中正确安装了 libcupti(例如 Ubuntu 或 Windows)。否则,你将看不到预期的结果。然后,要启用性能分析,你只需要将参数profile_batch传递给 TensorBoard 回调函数。该值是两个数字的列表:起始步骤和结束步骤。通常情况下,性能分析是跨越几个批次进行的,因此值需要一个范围。但是,也可以对单个批次进行性能分析:

batch_size = 32
tr_ds, v_ds = get_flower_datasets(
    os.path.join(
        'data', '17flowers','jpg'), batch_size=batch_size, 
➥ flatten_images=False
)
tb_callback = tf.keras.callbacks.TensorBoard(
    log_dir=profile_log_dir, profile_batch=[10, 20]
)
conv_model.fit(
    tr_ds, validation_data=v_ds, epochs=2, callbacks=[tb_callback]
)

训练完成后,您可以在 TensorBoard 上查看结果。TensorBoard 提供了大量有价值的信息和对模型性能的洞察。它将计算分解为更小的子任务,并根据这些子任务提供细粒度的计算时间分解。此外,TensorBoard 提供了关于改进空间的建议(图 14.10)。现在让我们更深入地了解该页面提供的信息。


图 14.10 TensorBoard 性能分析界面。它提供了有关在 GPU 上运行模型涉及的各种子任务的宝贵信息。此外,它还提供了改进模型性能的建议。

平均步骤时间是几个较小任务的总和:

  • 输入时间—用于读取与数据相关的操作(例如,tf.data.Dataset 操作)的时间。
  • 主机计算时间—在主机上执行的与模型相关的计算(例如,CPU)。
  • 设备到设备时间—要在 GPU 上运行东西,首先需要将数据传输到 GPU。这个子任务测量了这种传输所花费的时间。
  • 内核启动时间—为了使 GPU 执行传输的数据上的操作,CPU 需要为 GPU 启动内核。内核封装了对数据执行的原始计算(例如,矩阵乘法)。这测量了启动内核所需的时间。
  • 设备计算时间—发生在设备上的与模型相关的计算(例如,GPU)。
  • 设备集体通信时间—与在多设备(例如,多个 GPU)或多节点环境中通信所花费的时间相关。
  • 所有其他时间(例如,编译时间、输出时间、所有其他剩余时间)。

在这里,我们可以看到大部分时间都花在了设备计算上。从某种意义上说,这是好的,因为它意味着大多数计算发生在 GPU 上。下一个最大的时间消耗者是输入时间。这是有道理的,因为我们没有对我们的 tf.data 流水线进行任何优化,并且它是一个高度依赖磁盘的流水线,因为图像是从磁盘中读取的。

然后,在下面,您可以看到更多信息。接近 80%的 TensorFlow 操作被放置在此主机上,而仅有 20%在 GPU 上运行。此外,所有操作都是 32 位操作,没有 16 位操作;16 位(半精度浮点)操作比 32 位(单精度浮点)数据类型运行得更快,节省了大量内存。GPU 和 Tensor 处理单元(TPU)是经过优化的硬件,可以比 32 位操作更快地运行 16 位操作。因此,必须尽可能地将它们纳入其中。话虽如此,我们必须小心如何使用 16 位操作,因为不正确的使用可能会严重影响模型性能(例如,模型准确性)。将 16 位操作与 32 位操作一起用于训练模型称为混合精度训练

如果你看推荐部分,你会看到两个主要的建议:

  • 优化输入数据管道。
  • 在模型训练中利用更多的 16 位操作。

Brain Floating Point 数据类型(bfloat16)

Brain Floating Point 值,或称 bfloat16 值,是 Google 提出的一种数据类型。它与 float16(即,16 位)具有相同的位数,但能够表示 float32 值的动态范围,但在精度上会有一定损失。这种变化是通过增加更多的指数位(小数点左侧)和减少小数位(小数点右侧)来实现的。这种数据类型可以在优化的硬件上获得显著的优势,比如 TPU 和 GPU(假设它们有 Tensor 核心;developer.nvidia.com/tensor-cores))。

让我们看看如何利用这些建议来减少模型训练时间。

14.4.1 优化输入管道

为了优化数据管道,我们将对 get_flower_datasets() 函数进行两项更改:

  • 使用数据预取以避免模型等待数据可用。
  • 在调用 get_image_and_label() 函数时使用并行化的 map 函数。

就这些变化在代码中的体现来说,它们是小变化。在下面的列表中,这些变化用粗体表示。

列表 14.4 从花数据集生成训练/验证数据集的函数

def get_flower_datasets(image_dir, batch_size, flatten_images=False):
    dataset = tf.data.Dataset.list_files(
        os.path.join(image_dir,'*.jpg'), shuffle=False          ❶
    )
    def get_image_and_label(file_path):                         ❷
        tokens = tf.strings.split(file_path, os.path.sep)       ❸
        label = (tf.strings.to_number(
            tf.strings.split(
                tf.strings.split(tokens[-1],'.')[0], '_')[-1]   ❸
            ) - 1
        )//80
        img = tf.io.read_file(file_path)                        ❹
        img = tf.image.decode_jpeg(img, channels=3)             ❹
        return tf.image.resize(img, [64, 64]), label
    dataset = dataset.map(
        get_image_and_label,
        *num_parallel_calls=tf.data.AUTOTUNE        *             ❺
    ).shuffle(400)
    # Make the validation dataset the first 10000 data
    valid_ds = dataset.take(250).batch(batch_size)
    # Make training dataset the rest
    train_ds = dataset.skip(250).batch(batch_size).prefetch(
        tf.data.experimental.AUTOTUNE                           ❻
 )
    return train_ds, valid_ds

❶ 获取训练数据集,对其进行洗牌,并输出(图像,标签)元组。

❷ 定义一个函数,根据文件名获取图像和标签。

❸ 获取文件路径中的标记并从图像 ID 计算标签。

❹ 读取图像并转换为张量。

❺ 并行化 map 函数。

❻ 结合预取。

为了并行化 dataset.map() 函数,我们在其后添加了 num_parallel_calls=tf.data .AUTOTUNE 参数,这将导致 TensorFlow 在并行执行 map 函数,其中线程数将由主机在执行时承载的工作量确定。接下来,在批处理后我们调用 prefetch() 函数,以确保模型训练不会因为等待数据可用而受阻。

接下来,我们将设置一个特殊的环境变量,称为 TF_GPU_THREAD_MODE。要理解这个变量的影响,你首先需要弄清楚 GPU 如何高效执行指令。当你在一台带有 GPU 的机器上运行深度学习模型时,大多数数据并行操作(即可以并行执行的数据操作)都会在 GPU 上执行。但数据和指令是如何传输到 GPU 的呢?假设使用 GPU 执行两个矩阵之间的逐元素乘法。由于可以并行地对个别元素进行乘法,这是一个数据并行操作。为了在 GPU 上执行此操作(定义为一组指令并称为内核),主机(CPU)首先需要启动内核,以便 GPU 使用该函数对数据进行操作。特别地,CPU 中的一个线程(现代 Intel CPU 每个核心大约有两个线程)将需要触发此操作。想象一下如果 CPU 中的所有线程都非常忙碌会发生什么。换句话说,如果有很多 CPU 绑定的操作正在进行(例如,从磁盘读取大量数据),它可能会导致 CPU 竞争,从而延迟 GPU 内核的启动。这反过来又延迟了在 GPU 上执行的代码。有了 TF_GPU_THREAD_MODE 变量,你可以缓解 CPU 竞争引起的 GPU 延迟。更具体地说,这个变量控制着 CPU 线程如何分配到 GPU 上启动内核。它可以有三个不同的值:

  • 全局—对于为不同的进程分配线程没有特殊的偏好(默认)。
  • gpu_private—分配了一些专用线程来为 GPU 启动内核。这样,即使 CPU 正在执行大量负载,内核启动也不会延迟。如果有多个 GPU,则它们将拥有自己的私有线程。线程的数量默认为两个,并可以通过设置 TF_GPU_THREAD_COUNT 变量进行更改。
  • shared—与 gpu_private 相同,但在多 GPU 环境中,一组线程将在 GPU 之间共享。

我们将此变量设置为 gpu_private。我们将保持专用线程的数量为两个,因此不会创建 TF_GPU_THREAD_COUNT 变量。

设置环境变量

要设置 TF_GPU_THREAD_MODE 环境变量,你可以执行以下操作:

Linux 操作系统(例如 Ubuntu)

设置环境变量

  • 打开一个终端。
  • 运行 export TF_GPU_THREAD_MODE=gpu_private。
  • 通过调用 echo $TF_GPU_THREAD_MODE 来验证环境变量是否设置。
  • 打开一个新的 shell 并启动 Jupyter 笔记本服务器。

Windows 操作系统

环境变量

  • 从开始菜单中,选择编辑系统环境变量。
  • 单击名为环境变量的按钮。
  • 在打开的对话框中添加一个新的环境变量 TF_GPU_THREAD_MODE=gpu_private。
  • 打开一个新的命令提示符并启动 Jupyter 笔记本服务器。

conda 环境(Anaconda)

在 conda 环境中设置环境变量

  • 使用 conda activate manning.tf2 激活 conda 环境。
  • 运行 conda env config vars set TF_GPU_THREAD_MODE=gpu_private。
  • 停用并重新启用环境以使变量生效。
  • 启动 Jupyter 笔记本服务器。

在更改操作系统或 conda 环境中的环境变量后,重启笔记本服务器非常重要。有关更多详细信息,请参阅以下边栏。

重要:设置环境变量后重新启动笔记本服务器。

当您从 shell(例如,Windows 上的命令提示符或 Linux 上的终端)创建笔记本服务器时,笔记本服务器将作为 shell 的子进程创建。在启动笔记本服务器后对环境进行的更改(例如,添加环境变量)将不会反映在该子进程中。因此,您必须关闭任何现有的笔记本服务器,更改环境变量,然后重新启动笔记本服务器以查看效果。

我们对我们的 tf.data 流水线进行了三项优化:

  • 预取数据批次
  • 使用并行化的 map() 函数而不是标准的 map() 函数
  • 通过设置 TF_GPU_THREAD_MODE=gpu_private 使用专用的内核启动线程。

14.4.2 混合精度训练

正如前面所解释的,混合精度训练是指在模型训练中采用 16 位和 32 位操作的组合。例如,可训练参数(即变量)保持为 32 位浮点值,而操作(例如,矩阵乘法)产生 16 位浮点输出。

在 Keras 中,启用混合精度训练非常简单。您只需从 Keras 中导入 mixed_precision 命名空间,并创建一个使用 mixed precision 数据类型的策略,通过传递 mixed_float16。最后,将其设置为全局策略。然后,每当您定义一个新模型时,它都会使用此策略来确定模型的数据类型:

from tensorflow.keras import mixed_precision
policy = mixed_precision.Policy('mixed_float16')
mixed_precision.set_global_policy(policy)

让我们重新定义我们定义的 CNN 模型并快速检查数据类型,以了解此新策略如何更改模型数据类型:

conv_model = get_cnn_model()

现在我们将选择一个层并检查输入/内部参数(例如,可训练权重)和输出的数据类型:

print("Input to the layers have the data type: {}".format(
    conv_model.get_layer("conv2d_1").input.dtype)
)
print("Variables in the layers have the data type: {}".format(
    conv_model.get_layer("conv2d_1").trainable_variables[0].dtype)
)
print("Output of the layers have the data type: {}".format(
    conv_model.get_layer("conv2d_1").output.dtype)
)

这将打印。

Input to the layers have the data type: <dtype: 'float16'>
Variables in the layers have the data type: <dtype: 'float32'>
Output of the layers have the data type: <dtype: 'float16'>

正如您所见,输入和输出的数据类型为 float16,而变量的数据类型为 float32。这是混合精度训练所采用的设计原则。为了确保在更新权重时保留精度,变量保持为 float32 类型。

损失缩放以避免数值下溢。

使用混合精度训练时,必须小心处理损失。半精度浮点数(float16)值的动态范围比单精度浮点数(float32)值更小。动态范围是指每种数据类型可以表示的值的范围。例如,float16 可以表示的最大值为 65,504 (最小正数为 0.000000059604645),而 float32 可以达到 3.4 × 10³⁸ (最小正数为 1.4012984643 × 10 − 45)。由于 float16 数据类型的动态范围较小,损失值很容易下溢或溢出,导致反向传播时出现数值问题。为了避免这种情况,损失值需要乘以适当的值进行缩放,以使梯度保持在 float16 值的动态范围之内。幸运的是,Keras 会自动处理此问题。

当策略设置为 mixed_float16 且调用 model.compile() 时,优化器会自动包装为 tf.keras.mixed_precision.LossScaleOptimizer() (mng.bz/Aydo)。LossScaleOptimizer() 会在模型优化期间动态缩放损失,以避免数值上的问题。如果您没有使用 Keras 构建模型,则必须手动处理此问题。

现在重新运行模型训练:

batch_size = 32
tr_ds, v_ds = get_flower_datasets(
    os.path.join('data', '17flowers','jpg'), batch_size=batch_size, 
➥ flatten_images=False
)
# This tensorboard call back does the following
# 1\. Log loss and accuracy
# 2\. Profile the model memory/time for 10  batches
tb_callback = tf.keras.callbacks.TensorBoard(
    log_dir=profile_log_dir, profile_batch=[10, 20]
)
conv_model.fit(
    tr_ds, validation_data=v_ds, epochs=2, callbacks=[tb_callback]
)

在加入我们介绍的各种优化步骤后运行模型训练。通过改变 TensorBoard 上的运行来进行比较。例如,我们在概览页面上显示了使用和不使用优化技巧的元素的并排比较。我们可以看到,在引入 tf.data pipeline 相关的优化后,时间大大减少(图 14.11)。


图 14.11 显示了使用和不使用数据和模型相关优化的分析概览的并排比较。引入优化后,输入时间大大减少。

你可能认为,使用 16 位操作后设备的计算时间并没有显著降低。使用 16 位操作最大的优势在于减少了 GPU 的内存消耗。TensorBoard 提供了一个称为 memory profile 的单独视图,用于分析模型的内存占用情况(图 14.12)。您可以使用此视图来分析模型的内存瓶颈或内存泄漏。


图 14.12 显示了经过优化前后的内存占用情况差异。使用 16 位操作减少了模型的内存消耗。差异非常明显。

可以清楚地看出,在使用混合精度训练后,内存需求显著下降。当使用混合精度训练时(从 5.48 GB 至 1.25 GB),模型对内存的需求降低了约 76%。

图表明了两种类型的内存:。这些是程序用于在执行程序时跟踪变量和函数调用的基本内存空间。从这些中,堆将帮助我们了解内存使用模式或与内存相关的问题,因为在程序执行期间创建的各种对象和变量都保存在其中。例如,如果存在内存泄漏,您将看到堆使用的内存量正在增加。在这里,我们可以看到内存使用情况相当线性,并且可以假设没有重大的内存泄漏。您可以在下一页的侧边栏中阅读有关堆和栈的更多信息。

堆 vs. 栈

程序运行时的内存保持在堆栈或堆中。例如,函数调用保持在堆栈中,其中最后一次调用位于堆栈顶部,最早的调用位于底部。当这些函数调用创建对象时,例如,它们被写入堆中(术语“堆”来自与堆数据结构无关的对象集合)。您可以想象堆中包含许多对象和属性,没有特定顺序(因此术语“堆”)。随着函数调用结束,项目将自动从堆栈中弹出。但是,当对象不再使用时,由程序员释放堆的责任,因为它们在函数调用结束后仍然存在。然而,在现代编程语言中,垃圾收集器会自动处理这个问题。 (请参阅mng.bz/VMZ5mng.bz/ZAER。)

您可能听说过“堆栈溢出”的术语,当代码中的递归函数调用没有合理满足终止条件时,大量的函数调用会溢出堆栈。另外,我们不能忽视一个受开发者欢迎的网站的名称是如何产生的(stackoverflow.com)。我认为没有比 Stack Overflow 本身更好的资源来解释这个问题了:mng.bz/R4ZZ

我们还可以看到有关哪些操作使用了多少内存的细节。例如,我们知道 CNN 的主要瓶颈是在一系列卷积/池化层之后的第一个 Dense 层。表 14.1 证实了这一点。也就是说,它显示了 Dense 层,其形状为 [115200, 512](即第一个 Dense 层),使用了最多的内存。

表 14.1 内存分解表。该表显示了各种 TensorFlow 操作的内存使用情况以及它们的数据形状。

操作名称 分配大小(GiBs) 请求大小(GiBs) 发生次数 区域类型 数据类型 形状
预分配/未知 0.743 0.743 1 持久/动态 无效 未知
gradient_tape/sequential/dense/MatMul/Cast/Cast 0.220 0.220 1 输出 浮点 [115200,512]
gradient_tape/sequential/batch_normalisation_3/FusedBatchNormGradV3 0.051 0.029 1 temp half [32,512,31,31]
gradient_tape/sequential/average_pooling2d/AvgPool/AvgPoolGrad 0.036 0.029 1 output half [32,31,31,512]
gradient_tape/sequential/batch_normalisation_3/FusedBatchNormGradV3 0.029 0.029 1 output half [32,31,31,512]
gradient_tape/sequential/batch_normalisation_3/FusedBatchNormGradV3 0.029 0.029 2 temp half [32,512,31,31]

最后,您可以查看trace viewer。这个工具提供了各种操作在 CPU 或 GPU 上是如何执行的纵向视图以及所花费的时间。这提供了关于各种操作何时以及如何被安排和执行的非常详细的视图。

在左侧,您可以看到在 CPU 上执行了什么操作,而在 GPU 上执行了什么操作。例如,您可以看到大多数与模型相关的操作(例如,卷积)在 GPU 上执行,而 tf.data 操作(例如,解码图像)在 GPU 上执行。您还可以注意到,跟踪查看器单独显示了 GPU 私有线程。

TensorBoard 的用途远不止我们在这里列出的。要了解更多,请参考以下侧边栏。

TensorBoard 的其他视图

TensorBoard 有许多不同的视图可用。我们已经讨论了最常用的视图,我将让读者探索我们没有讨论的视图。然而,剩下的视图中有一些值得注意的视图:

Debugger v2

Debugger v2 是 TensorFlow 2.3 以后引入的工具。它的主要目的是调试模型中的数值问题。例如,在模型训练过程中出现 NaN 值是深度网络的一个非常常见的问题。Debugger v2 将提供模型中各种元素(例如,输入和输出张量)的全面逐步分解,以及哪些元素产生了数值错误。有关更多信息,请访问www.tensorflow.org/tensorboard/debugger_v2

HParams

Hparams 是一个视图,帮助超参数优化,并允许您深入研究个别运行,以了解哪些参数有助于改善模型性能。tensorboard.plugins.hparams.api 提供了各种有用的功能和回调,以轻松优化 Keras 模型的超参数。然后,可以在 HParams 视图中查看超参数优化期间发生的试验。有关更多信息,请访问mng.bz/2nKg

What-If 工具

What-If 是一个工具,可以为黑盒模型提供有价值的见解,有助于解释这些模型。例如,您可以使用一些数据运行模型推理。然后,您可以修改数据,并通过 What-If 工具查看输出如何变化。此外,它提供了各种工具,用于分析模型的性能和公平性。有关更多信息,请访问mng.bz/AyjW

在下一节中,我们将讨论如何在 TensorBoard 上可视化和与词向量交互。

练习 4

你已经进行了模型性能分析。你已经看到了以下时间概述:

  • 输入时间:1.5 毫秒
  • 设备计算时间:6.7 毫秒
  • 内核启动时间:9.8 毫秒
  • 输出时间:10.1 毫秒
  • 主机计算时间:21.2 毫秒

对于这种情况,假设超过 5 毫秒的时间是有改进空间的机会。列出三个代码/环境更改建议以提高模型性能。

14.5 使用 TensorBoard 可视化词向量

你正在一家电影推荐公司担任 NLP 工程师,负责开发一种可以在小设备上训练的电影推荐模型。为了减少训练开销,使用了一种技术:使用预训练的词向量并将其冻结(即不进行训练)。你认为 GloVe 词向量将是一个很好的起点,并计划使用它们。但在此之前,你必须确保这些向量充分捕捉到电影特定术语/单词中的语义/关系。为此,你需要在 TensorBoard 上可视化这些单词的词向量,并分析 GloVe 向量是否表示了单词之间的合理关系。

我们需要做的第一件事是下载 GloVe 词向量。你已经在笔记本中提供了下载 GloVe 向量的代码,它与我们过去下载数据集的方式非常相似。因此,我们不会详细讨论下载过程。GloVe 词向量可从nlp.stanford.edu/projects/glove/获取。GloVe 向量有几个不同的版本;它们具有不同的维度和词汇量:

  • 使用 Wikipedia 2014 + Gigaword 5 数据集进行训练,共有 60 亿个标记;词汇量为 400,000 个;大小写不敏感的标记;词向量维度为 50D、100D、200D 和 300D
  • 使用 Common Crawl 数据集训练,共有 420 亿个标记;词汇量为 1,900,000 个;大小写不敏感的标记;词向量维度为 300D
  • 使用 Common Crawl 数据集训练,共有 8400 亿个标记;词汇量为 2,200,000 个;大小写敏感的标记;词向量维度为 300D
  • 使用 Twitter 数据集进行训练,共有 20 亿个推文;总标记数为 270 亿个;词汇量为 1,200,000 个;大小写不敏感的标记;词向量维度为 25D、50D、100D 和 200D

GloVe 词向量

GloVe(代表 Global Vectors)是一种单词向量算法,通过查看语料库的全局和局部统计信息生成单词向量。例如,像 Skip-gram 或 Continuous Bag-of-Words 这样的单词向量算法仅依赖于给定单词的局部上下文来学习该单词的单词向量。对单词在较大语料库中的使用情况缺乏全局信息的关注会导致次优的单词向量。GloVe 通过计算一个大型共现矩阵来表示所有单词之间的共现频率(即,如果一个给定单词出现在另一个单词的上下文中)来融合全局统计信息。有关 GloVe 向量的更多信息,请参见 mng.bz/1oGX

我们将使用第一类别(最小的)中的 50 维词向量。一个 50 维词向量将对语料库中的每个标记有 50 个值的向量。一旦在笔记本中运行代码提取数据,你将看到一个名为 glove.6B.50d.txt 的文件出现在数据文件夹中。让我们使用 pd.read_csv() 函数将其加载为 pandas DataFrame:

df = pd.read_csv(
    os.path.join('data', 'glove.6B.50d.txt'), 
    header=None, 
    index_col=0, 
    sep=None, 
    error_bad_lines=False, 
    encoding='utf-8'
)
df.head()

这将返回表格 14.2. 现在我们将下载 IMDB 电影评论数据集(ai.stanford.edu/~amaas/data/sentiment/)。由于这个数据集可以轻松地作为 TensorFlow 数据集(通过 tensorflow_datasets 库)获得,我们可以使用它:

review_ds = tfds.load('imdb_reviews')
train_review_ds = review_ds["train"]

一旦我们下载了数据,我们将创建一个包含训练集中所有评论(文本)的语料库,以字符串列表的形式:

corpus = []
for data in train_review_ds:      
    txt = str(np.char.decode(data["text"].numpy(), encoding='utf-8')).lower()
    corpus.append(str(txt))

接下来,我们想要获取此语料库中最常见的 5,000 个单词,以便我们可以比较这些常见单词的 GloVe 向量,以查看它们是否包含合理的关系。为了获得最常见的单词,我们将使用内置的 Counter 对象。Counter 对象计算词汇表中单词的频率:

from collections import Counter
corpus = " ".join(corpus)
cnt = Counter(corpus.split())
most_common_words = [w for w,_ in cnt.most_common(5000)]
print(cnt.most_common(100))

这将打印

[('the', 322198), ('a', 159953), ('and', 158572), ('of', 144462), ('to', 
➥ 133967), ('is', 104171), ('in', 90527), ('i', 70480), ('this', 69714), 
➥ ('that', 66292), ('it', 65505), ('/><br', 50935), ('was', 47024), 
➥ ('as', 45102), ('for', 42843), ('with', 42729), ('but', 39764), ('on', 
➥ 31619), ('movie', 30887), ('his', 29059), 
➥ ... ,
➥ ('other', 8229), ('also', 8007), ('first', 7985), ('its', 7963), 
➥ ('time', 7945), ('do', 7904), ("don't", 7879), ('me', 7722), ('great', 
➥ 7714), ('people', 7676), ('could', 7594), ('make', 7590), ('any', 
➥ 7507), ('/>the', 7409), ('after', 7118), ('made', 7041), ('then', 
➥ 6945), ('bad', 6816), ('think', 6773), ('being', 6390), ('many', 6388), 
➥ ('him', 6385)]


使用了 IMDB 电影评论数据集中最常见的 5,000 个单词的语料库以及 GloVe 向量,我们找到了这两个集合之间的常见标记以进行可视化:

df_common = df.loc[df.index.isin(most_common_words)]

这将给出大约 3,600 个在两个集合中都出现的标记列表。

接下来,我们可以在 TensorBoard 上可视化这些向量。再次强调,单词向量是给定语料库中标记的数值表示。这些单词向量的特点(与独热编码单词相反)是它们捕捉了单词的语义。例如,如果计算“cat”和“dog”的单词向量之间的距离,它们会比“cat”和“volcano”更接近。但是在分析更大的一组标记之间的关系时,我们喜欢有一个可视化辅助工具。如果有一种方法可以在二维或三维平面上可视化这些单词向量,那将更容易可视化和理解。有降维算法,如主成分分析(PCA)(mng.bz/PnZw)或 t-SNE(distill.pub/2016/misread-tsne/)可以实现这一点。本书不涉及这些特定算法的使用。好消息是,使用 TensorBoard,你可以做到这一点。TensorBoard 可以将这些高维向量映射到一个更小的投影空间。要做到这一点,我们首先要将这些权重加载为一个 TensorFlow 变量,然后将其保存为 TensorFlow 检查点。然后我们还要将单词或标记保存为一个新文件,每行一个标记,对应于我们刚刚保存的一组单词向量中的每个向量。有了这个,你就可以在 TensorBoard 上可视化单词向量(见下一个列表)。

列表 14.5 在 TensorBoard 上可视化单词向量

from tensorboard.plugins import projector
log_dir=os.path.join('logs', 'embeddings')
weights = tf.Variable(df_common.values)                          ❶
checkpoint = tf.train.Checkpoint(embedding=weights)              ❷
checkpoint.save(os.path.join(log_dir, "embedding.ckpt"))         ❷
with open(os.path.join(log_dir, 'metadata.tsv'), 'w') as f:      ❸
    for w in df_common.index:
        f.write(w+'\n')
config = projector.ProjectorConfig()                             ❹
embedding = config.embeddings.add()
embedding.metadata_path = 'metadata.tsv'                         ❺
projector.visualize_embeddings(log_dir, config)

❶ 用我们捕获的嵌入创建一个 tf.Variable。

❷将嵌入保存为 TensorFlow 检查点。

❸ 保存元数据(一个 TSV 文件),其中每个与嵌入对应的单词被附加为新行。

❹创建一个特定于投影仪和嵌入的配置(有关详细信息,请参阅文本)。

❺设置元数据路径,以便 TensorBoard 可以在可视化中包含它。

要可视化来自保存的 TensorFlow 检查点和元数据(即,保存的单词向量对应的标记),我们使用 tensorboard.plugins.projector 对象。然后我们定义一个 ProjectorConfig 对象和一个嵌入配置。我们将保留它们的默认配置,这适合我们的问题。当调用 config.embeddings.add()时,它将生成一个使用默认配置的嵌入配置(类型为 EmbeddingInfo 对象)。ProjectorConfig 包含诸如以下信息:

  • model_checkpoint_directory —— 包含嵌入的检查点的目录

EmbeddingInfo 包含

  • tensor_name —— 如果嵌入使用了特殊的张量名称
  • metadata_path —— 包含嵌入标签的 TSV 文件的路径

要查看可用配置的完整列表,请参考 mng.bz/J2Zo 上的文件。在其当前状态下,投影仪的配置不支持太多的定制。因此,我们将保持默认设置。我们将在 EmbeddingInfo 配置中设置一个配置,即 metadata_path。我们将 metadata_path 设置为包含令牌的文件,最后将其传递给 projecter.visualize_embeddings() 函数。我们给它一个日志目录,投影仪将自动检测 TensorFlow 检查点并加载它。

我们一切都准备就绪。在您的笔记本上,执行以下行以打开 TensorBoard:

%tensorboard --logdir logs/embeddings/ --port 6007

要可视化词向量,它们需要在 --logdir 指向的确切目录中(即不在嵌套文件夹中)。因此,我们需要一个新的 TensorBoard 服务器。这行代码将在端口 6007 上打开一个新的 TensorBoard 服务器。图 14.13 描述了在 TensorBoard 中显示的内容。

关于 %tensorboard 魔术命令的有趣事实

%tensorboard 魔术命令足够智能,能够知道何时打开新的 TensorBoard 服务器以及何时不需要。如果您一遍又一遍地执行相同的命令,它将重用现有的 TensorBoard。但是,如果您执行带有不同 --logdir 或 --port 的命令,它将打开一个新的 TensorBoard 服务器。


图 14.13 在 TensorBoard 上的词向量视图。您可以选择使用哪种降维算法(以及参数)来获取词向量的二维或三维表示。在可视化中悬停在点上将显示由该点表示的单词。

您可以在可视化中悬停在显示的点上,它们将显示它们代表的语料库中的哪个单词。您可以通过切换维度控制器来可视化二维或三维空间中的词向量。您可能想知道我们选择的词向量。它们最初有 50 个维度 —— 我们如何在二维或三维空间中可视化这样高维度的数据呢?有一套降维算法可以为我们做到这一点。一些示例是 t-SNE (mng.bz/woxO),PCA(主成分分析; mng.bz/ZAmZ),以及 UMAP(Uniform Manifold Approximation and Projection; arxiv.org/pdf/1802.03426.pdf)。参考附带的链接以了解更多关于这些算法的信息。

您可以在 TensorBoard 上做的不仅仅是词向量的简单可视化。您可以通过突出显示可视化中的特定单词进行更详细的分析。为此,您可以使用正则表达式。例如,图 14.14 中显示的可视化是使用正则表达式(?:fred|larry|mrs.|mr.|michelle|sea|denzel|beach|comedy|theater|idiotic|sadistic|marvelous|loving|gorg|bus|truck|lugosi)生成的。


图 14.14 在可视化中搜索单词。您可以使用正则表达式来搜索单词的组合。

这就结束了我们关于 TensorBoard 的讨论。在下一章中,我们将讨论 TensorFlow 如何帮助我们轻松创建机器学习流水线并部署模型。

练习 5

如果您想在 TensorBoard 中显示单词向量时包含唯一标识符,而不仅仅是单词本身,例如,您想要看到“loving; 218”而不是“loving”,其中 218 是给予该单词的唯一标识符。为此,您需要更改写入 metadata.tsv 文件的内容。不仅仅是单词,每行上都写一个用分号分隔的递增 ID。例如,如果单词是[“a”, “b”, “c”],那么新行应该是[“a;1”, “b;2”, “c;3”]。您如何进行更改?

摘要

  • TensorBoard 是一个用于可视化数据(例如图像)和实时跟踪模型性能的优秀工具。
  • 在使用 Keras 构建模型时,您可以使用方便的 tf.keras.callbacks.TensorBoard() 回调来记录模型性能、层激活直方图等。
  • 如果您有自定义指标想要记录到 TensorBoard 中,您可以在 tf.summary 命名空间中使用相应的数据类型(例如,如果您想要记录随时间变化的模型精度等,可以使用 tf.summary.scalar())。
  • 每次将信息记录到 TensorBoard 中的会话称为一次运行。您应该为不同的运行制定一个可读且健壮的命名约定。一个好的命名约定应该捕捉您所做的主要更改以及运行执行的日期/时间。
  • TensorBoard Profile 提供了各种各样的性能分析结果(使用 NVIDIA 的 libcupti 库),例如模型训练过程中各个子任务所花费的时间(例如,设备计算时间、主机计算时间、输入时间等)、模型使用的内存以及各种操作是如何进行的顺序视图。
  • TensorBoard 是一个用于可视化高维数据(如图像和单词向量)的强大工具。

练习答案

练习 1

image_writer = tf.summary.create_file_writer(image_logdir)
with image_writer.as_default():
    for bi, batch in enumerate(steps_image_batches):
        tf.summary.image(
            “batch_{}”.format(bi), 
            batch, 
            max_outputs=10, 
            step=bi
        )

练习 2

log_dir = "./logs "
classif_model.compile(
    loss=’binary_crossentropy', 
    optimizer=’adam’, 
    metrics=[tf.keras.metrics.Precision(), tf.keras.metrics.Recall()]
)
tb_callback = tf.keras.callbacks.TensorBoard(
    log_dir=log_dir, histogram_freq=1, profile_batch=0
)
classif_model.fit(tr_ds, validation_data=v_ds, epochs=10, callbacks=[tb_callback])

练习 3

writer = tf.summary.create_file_writer(log_dir)
 x_n_minus_1 = 1
 x_n_minus_2 = 0
 with writer.as_default():        
     for i in range(100):
         x_n = x_n_minus_1 + x_n_minus_2
         x_n_minus_1 = x_n
      x_n_minus_2 = x_n_minus_1
      tf.summary.scalar("fibonacci", x_n, step=i)
      writer.flush()

练习 4

  1. 主机上正在进行大量的计算。这可能是因为设备(例如,GPU)的内存不足。使用混合精度训练将有助于缓解这个问题。此外,可能有太多无法在 GPU 上运行的非 TensorFlow 代码。为此,使用更多的 TensorFlow 操作并将这样的代码转换为 TensorFlow 将获得加速。
  2. 内核启动时间增加了。这可能是因为工作负载严重受限于 CPU。在这种情况下,我们可以合并 TF_GPU_THREAD_MODE 环境变量,并将其设置为 gpu_private。这将确保有几个专用线程用于为 GPU 启动内核。
  3. 输出时间显著偏高。这可能是因为频繁向磁盘写入过多输出。为解决此问题,我们可以考虑将数据在内存中保存更长时间,并仅在少数时刻将其刷新到磁盘上。

练习 5

log_dir=os.path.join('logs', 'embeddings')
weights = tf.Variable(df_common.values)
   checkpoint = tf.train.Checkpoint(embedding=weights)
checkpoint.save(os.path.join(log_dir, "embedding.ckpt"))
with open(os.path.join(log_dir, 'metadata.tsv'), 'w') as f:
    for i, w in enumerate(df_common.index):
        f.write(w+'; '+str(i)+'\n')

第十五章:TFX:MLOps 和使用 TensorFlow 部署模型

本章涵盖内容

  • 使用 TFX(TensorFlow-Extended)编写端到端数据流水线
  • 通过 TFX Trainer API 训练一个简单的神经网络
  • 使用 Docker 将模型服务(推理)容器化,并将其作为服务呈现
  • 在本地机器上部署模型,以便通过 API 使用

在第十四章,我们研究了一个非常多功能的工具,它与 TensorFlow 捆绑在一起:TensorBoard。TensorBoard 是一个可视化工具,可以帮助你更好地理解数据和模型。除其他外,它可以方便

  • 监控和追踪模型性能
  • 可视化模型的数据输入(例如图片、音频)
  • 对模型进行分析以了解其性能或内存瓶颈

我们学习了如何使用 TensorBoard 来可视化像图片和词向量这样的高维数据。我们探讨了如何将 Keras 回调嵌入到 TensorBoard 中,以便可视化模型性能(准确率和损失)以及自定义指标。然后,我们使用 CUDA 性能分析工具来分析模型的执行,以理解执行模式和内存瓶颈。

在本章中,我们将探索最近引起极大关注的机器学习新领域:MLOps。MLOps 源自 ML 和 DevOps(源自开发和运维)术语。根据亚马逊网络服务(AWS)的说法,“DevOps 是文化哲学、实践和工具的组合,它增加了组织交付应用和服务的能力:以比使用传统软件开发和基础设施管理流程的组织更快的速度进化和改进产品。”还有一个与 MLOps 密切相关的术语,即模型的实际投入使用。很难区分这两个术语,因为它们有重叠之处,有时可以互换使用,但我倾向于这样理解这两个事物:MLOps 定义了一个工作流,将自动化大部分步骤,从收集数据到交付在该数据上训练的模型,几乎不需要人工干预。实际投入使用是部署训练好的模型(在私有服务器或云上),使客户能够以稳健的方式使用模型进行设计目的。它可以包括任务,例如设计可扩展的 API,可以扩展以处理每秒数千个请求。换句话说,MLOps 是一段旅程,让你到达的目的地是模型的实际投入使用。

让我们讨论为什么拥有(大部分)自动化的流水线来开发机器学习模型是重要的。要实现其价值,你必须考虑到规模问题。对于像谷歌、Facebook 和亚马逊这样的公司,机器学习已经深深扎根于他们提供的产品中。这意味着数以百计甚至数千个模型每秒产生预测。此外,对于拥有数十亿用户的公司来说,他们不能容忍他们的模型变得过时,这意味着不断地训练/微调现有模型以适应新数据的收集。MLOps 可以解决这个问题。MLOps 可用于摄取收集的数据、训练模型、自动评估模型,并在它们通过预定义的验证检查后将其推送到生产环境中。验证检查是为了确保模型达到预期的性能标准,并防范对抗不良表现的模型(例如,由于新的入站训练数据发生大幅变化、推送了新的未经测试的超参数变更等,可能会生成不良的模型)。最后,模型被推送到生产环境,通过 Web API 访问以获取输入的预测。具体而言,API 将为用户提供一些端点(以 URL 的形式),用户可以访问这些端点(可选地带上需要完成请求的参数)。话虽如此,即使对于依赖机器学习模型的较小公司来说,MLOps 也可以极大地标准化和加速数据科学家和机器学习工程师的工作流程。这将大大减少数据科学家和机器学习工程师在每次开展新项目时从头开始创建这些工作流程所花费的时间。阅读有关 MLOps 的更多信息,请访问mng.bz/Pnd9

我们如何在 TensorFlow 中进行 MLOps?无需寻找其他,TFX(TensorFlow 扩展)就是答案。TFX 是一个库,提供了实现摄取数据、将数据转换为特征、训练模型和将模型推送到指定生产环境所需的所有功能。这是通过定义一系列执行非常具体任务的组件来完成的。在接下来的几节中,我们将看看如何使用 TFX 来实现这一目标。

TensorFlow 实战(七)(3)https://developer.aliyun.com/article/1522944

相关实践学习
基于阿里云DeepGPU实例,用AI画唯美国风少女
本实验基于阿里云DeepGPU实例,使用aiacctorch加速stable-diffusion-webui,用AI画唯美国风少女,可提升性能至高至原性能的2.6倍。
相关文章
|
30天前
|
机器学习/深度学习 自然语言处理 TensorFlow
TensorFlow 实战(六)(2)
TensorFlow 实战(六)
24 0
|
16天前
|
机器学习/深度学习 TensorFlow API
TensorFlow与Keras实战:构建深度学习模型
本文探讨了TensorFlow和其高级API Keras在深度学习中的应用。TensorFlow是Google开发的高性能开源框架,支持分布式计算,而Keras以其用户友好和模块化设计简化了神经网络构建。通过一个手写数字识别的实战案例,展示了如何使用Keras加载MNIST数据集、构建CNN模型、训练及评估模型,并进行预测。案例详述了数据预处理、模型构建、训练过程和预测新图像的步骤,为读者提供TensorFlow和Keras的基础实践指导。
151 59
|
30天前
|
机器学习/深度学习 自然语言处理 TensorFlow
TensorFlow 实战(五)(5)
TensorFlow 实战(五)
20 1
|
30天前
|
自然语言处理 算法 TensorFlow
TensorFlow 实战(六)(3)
TensorFlow 实战(六)
21 0
|
30天前
|
机器学习/深度学习 数据可视化 TensorFlow
TensorFlow 实战(六)(1)
TensorFlow 实战(六)
25 0
|
30天前
|
存储 自然语言处理 TensorFlow
TensorFlow 实战(五)(4)
TensorFlow 实战(五)
22 0
|
30天前
|
数据可视化 TensorFlow 算法框架/工具
TensorFlow 实战(八)(4)
TensorFlow 实战(八)
25 1
|
30天前
|
TensorFlow API 算法框架/工具
TensorFlow 实战(八)(3)
TensorFlow 实战(八)
25 1
|
30天前
|
机器学习/深度学习 自然语言处理 TensorFlow
TensorFlow 实战(八)(5)
TensorFlow 实战(八)
25 0
|
30天前
|
并行计算 TensorFlow 算法框架/工具
TensorFlow 实战(八)(2)
TensorFlow 实战(八)
21 0

相关实验场景

更多