使用TensorFlow Keras进行训练和评估 (tensorflow2.0官方教程翻译)

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

本指南涵盖了TensorFlow 2.0在两种主要情况下的训练、评估和预测(推理)模型:

一般来说,无论您是使用内置循环还是编写自己的循环,模型训练和评估在每种Keras模型(Sequential顺序模型、使用使用函数式API构建的模型以及通过模型子类从零开始编写的模型)中都严格按照相同的方式工作。

本指南不包括分布式训练。

设置

安装

导入

第一部分:使用内置训练和评估循环

将数据传递给模型的内置训练循环时,您应该使用Numpy数组(如果数据很小并且适合内存)或tf.data数据集对象。在接下来的几段中,我们将使用MNIST数据集作为Numpy数组,以演示如何使用优化器,损失和指标。

API概述:第一个端到端示例

让我们考虑以下模型(这里,我们使用Functional API构建,但它也可以是顺序模型或子类模型):

以下是典型的端到端工作流程的外观,包括训练,对原始训练数据生成的保留集的验证,以及最终对测试数据的评估:

指定损失,指标和优化程序

要训练合适的模型,您需要指定一个损失函数,一个优化器,以及可选的一些要监控的指标。

您将这些作为 compile() 方法的参数传递给模型:

metrics 参数应该是一个列表(您的模型可以包含任意数量的度量标准)。

如果您的模型有多个输出,您可以为每个输出指定不同的损失和度量,并且您可以调整每个输出对模型总损失的贡献。 您将在“将数据传递到多输入、多输出模型”一节中找到更多关于此的详细信息。

注意,在很多情况下,损失和指标是通过字符串标识符指定的,作为一种快捷方式:

为了以后的重用,我们将模型定义和编译步骤放在函数中;我们将在本指南的不同示例中多次调用它们。

许多内置的优化器、损失和指标都是可用的

通常,您不必从头开始创建自己的损失,指标或优化器,因为您需要的可能已经是Keras API的一部分: Optimizers优化器:

Losses损失:

Metrics指标:

编写自定义损失和指标

如果您需要不属于API的指标,则可以通过继承Metric类轻松创建自定义指标。

您需要实现4种方法:

状态更新和结果计算是分开的(分别在update_state()result()中)因为在某些情况下,结果计算可能非常昂贵,并且只能定期进行。

这是一个简单的例子,展示了如何实现一个 CatgoricalTruePositives 指标,它计算了正确分类为属于给定类的样本数量:

处理不符合标准签名的损失和指标

绝大多数损失和指标可以从y_truey_pred计算,其中y_pred是模型的输出。但不是全部。例如,正则化损失可能仅需要激活层(在这种情况下没有目标),并且该激活可能不是模型输出。

在这种情况下,您可以从自定义图层的call方法中调用 self.add_loss(loss_value) 。这是一个添加活动正则化的简单示例(请注意,活动正则化是内置于所有Keras层中的 - 此层仅用于提供具体示例):

您可以执行相同的记录度量标准值:

 

Functional API 中,您还可以调用 model.add_loss(loss_tensor), 或 model.add_metric(metric_tensor, name, aggregation)

这是一个简单的例子:

自动设置验证保持集

在您看到的第一个端到端示例中,我们使用 validation_data 参数将Numpy数组 (x_val, y_val) 的元组传递给模型,以便在每个时期结束时评估验证损失和验证指标。

这是另一个选项:参数 validation_split 允许您自动保留部分训练数据以进行验证。参数值表示要为验证保留的数据的分数,因此应将其设置为大于0且小于1的数字。例如,validation_split=0.2 表示“使用20%的数据进行验证”,validation_split=0.6 表示“使用60%的数据进行验证”。

计算验证的方法是:在任何混洗之前,通过fit调用接收的数组的最后x%样本。

在使用Numpy数据进行训练时,您只能使用 validation_split

输出

来自tf.data数据集的培训和评估

在过去的几段中,您已经了解了如何处理损失,度量和优化器,并且您已经看到,当您的数据作为Numpy数组传递时,如何在fit中使用validation_datavalidation_split 参数

现在让我们看一下您的数据以tf.data数据集的形式出现的情况。

tf.data API是TensorFlow 2.0中的一组实用程序,用于以快速和可伸缩的方式加载和预处理数据。

有关创建数据集的完整指南,请参阅the tf.data 文档

您可以将数据集实例直接传递给方法 fit(), evaluate(), 和 predict()

输出:

请注意,数据集在每个周期的末尾都会重置,因此可以重复使用下一个周期。

如果您只想从此数据集中对特定数量的批次运行训练,则可以传递 steps_per_epoch 参数,该参数指定在继续下一个周期之前使用此数据集运行模型的训练步数。

如果这样做,数据集不会在每个周期的末尾重置,而是我们只是继续绘制下一批。数据集最终会耗尽数据(除非它是一个无限循环的数据集)。

使用验证数据集

您可以将数据集实例作为fit中的validation_data参数传递:

在每个周期结束时,模型将迭代验证数据集并计算验证损失和验证指标。

如果你想只在这个数据集中特定数量的批次上运行验证,你可以传递“validation_steps”参数,它指定了模型在中断验证并进入下一个周期之前,应该与验证数据集一起运行多少个验证步骤:

请注意,验证数据集将在每次使用后重置(这样您将始终评估从epoch到epoch的相同样本)。从数据集对象进行训练时,不支持参数validation_split(从训练数据生成保持集),因为此功能需要能够索引数据集的样本,这通常是数据集API无法实现的。

支持其他输入格式

除了Numpy数组和TensorFlow数据集之外,还可以使用Pandas数据帧或产生批量的Python生成器来训练Keras模型。

通常,如果数据很小并且适合内存,我们建议您使用Numpy输入数据,否则使用数据集。

使用样本加权和类权重

除了输入数据和目标数据之外,还可以在使用 fit 时将样本权重或类权重传递给模型:

"sample weights" 数组是一个数字数组,用于指定批处理中每个样本在计算总损失时应具有多少权重。它通常用于不平衡的分类问题(这个想法是为了给予很少见的类别更多的权重)。当使用的权重是1和0时,该数组可以用作损失函数的掩码(完全丢弃某些样本对总损失的贡献)。

"class weights" 字典是同一概念的更具体的实例:它将类索引映射到应该用于属于该类的样本的样本权重。例如,如果类“0”比数据中的类“1”少两倍,则可以使用 class_weight={0: 1., 1: 0.5}.

这是一个Numpy示例,我们使用类权重class weights或样本权重sample weights来更加重视 class #5 的正确分类(这是MNIST数据集中的数字“5”)。

这是一个匹配的数据集示例:

 

将数据传递到多输入,多输出模型

在前面的例子中,我们考虑的是一个带有单个输入的模型(形状为 (764,) 的张量)和单个输出(形状为 (10,) 的预测张量)。但是具有多个输入或输出的模型呢?

考虑下面的模型,它有一个形状为 (32, 32, 3) 的形状输入(即“(高度,宽度,通道)”)和形状为 (None, 10) 的时间序列输入(即“(时间步长,特征)”)。我们的模型将根据这些输入的组合计算两个输出:“得分”(形状为(1,)和5个类别(形状为(10,))的概率分布。

让我们绘制这个模型,这样你就可以清楚地看到我们在这里做的事情(请注意,图中显示的形状是批量形状,而不是每个样本的形状)。

 

png

 

在编译时,我们可以通过将损失函数作为列表传递给不同的输出指定不同的损失:

如果我们只将单个损失函数传递给模型,则相同的损失函数将应用于每个输出,这在这里是不合适的。

同样适用于指标:

由于我们为输出层指定了名称,因此我们还可以通过dict指定每个输出的损失和指标:

如果您有超过2个输出,我们建议使用显式名称和dicts。

可以给不同的输出特定损失赋予不同的权重(例如,可能希望通过使用loss_weight参数赋予2x类损失的重要性来保留我们示例中的“得分”损失特权:

您还可以选择不计算某些输出的损失,如果这些输出用于预测但不用于训练:

将数据传递给fit中的多输入或多输出模型的工作方式与在compile中指定损失函数的方式类似:

你可以传递Numpy数组列表(1:1映射到接收到损失函数的输出)或者将输出名称映射到Numpy训练数据数组。

这是数据集用例:类似于我们为Numpy数组所做的,数据集应该返回一个dicts元组。

使用回调Using callbacks

Keras中的回调是在训练期间(在周期开始时,批处理结束时,周期结束时等)在不同点调用的对象,可用于实现以下行为:

回调可以作为列表传递给你对 fit 的调用:

许多内置回调都可用

 

编写自己的回调

您可以通过扩展基类 keras.callbacks.Callback 来创建自定义回调。回调可以通过类属性 self.model 访问其关联的模型。

以下是在训练期间保存每批损失值列表的简单示例:

检查点模型

当您在相对较大的数据集上训练模型时,以频繁的间隔保存模型的检查点至关重要。

实现此目的的最简单方法是使用 ModelCheckpoint 回调:

您也可以编写自己的回调来保存和恢复模型。

有关序列化和保存的完整指南,请参见保存和序列化模型指南

使用学习率计划

在训练深度学习模型时,一个常见的模式是随着训练的进展逐步减少学习。这通常被称为“学习速度衰减”。

学习衰减计划可以是静态的(预先固定,作为当前周期或当前批处理索引的函数),也可以是动态的(响应模型当前的行为,特别是验证损失)。

将计划传递给优化程序

您可以通过在优化程序中将计划对象作为learning_rate参数传递,轻松使用静态学习速率衰减计划:

有几个内置的schedule表: ExponentialDecay, PiecewiseConstantDecay, PolynomialDecay, and InverseTimeDecay.

使用回调来实现动态学习速率计划

使用这些schedule对象无法实现动态学习速率计划(例如,当验证损失不再改善时降低学习速率),因为优化器无法访问验证指标。

然而,回调确实可以访问所有指标,包括验证指标! 因此,可以通过使用回调函数来修改优化器上的当前学习率来实现这种模式。 事实上,它甚至内置在 ReduceLROnPlateau 回调函数中。

在训练期间可视化损失和度量

在训练期间密切关注你的模型的最好方法是使用 TensorBoard ,这是一个基于浏览器的应用程序,你可以在本地运行,它为你提供:

如果您已经使用pip安装了TensorFlow,那么您应该能够从命令行启动TensorBoard:

使用TensorBoard回调

在Keras模型和 fit 方法中使用TensorBoard的最简单方法是TensorBoard回调。

在最简单的情况下,只需指定回写日志的位置,就可以了:

TensorBoard回调有许多有用的选项,包括是否记录嵌入,直方图以及写入日志的频率:

第二部分:从头开始编写自己的训练和评估循环

如果你想要训练和评估循环的级别低于 fit()evaluate() 提供的的级别,你应该自己编写。它实际上非常简单!但是你应该准备好自己做更多的调试。

使用GradientTape:第一个端到端的例子

在“GradientTape”范围内调用模型使您可以根据损失值检索图层的可训练权重的梯度。使用优化器实例,您可以使用这些梯度来更新这些变量(可以使用model.trainable_weights检索)。

让我们重用第一部分中的初始MNIST模型,让我们使用带有自定义训练循环的小批量梯度训练它。

指标的低级处理

让我们添加指标。您可以很容易地在这样的训练循环中重用内置的指标(或您编写的自定义指标)。这是流程:

让我们使用这些知识在每个周期结束时计算验证数据的 SparseCategoricalAccuracy

低水平处理额外损失

您在上一节中看到,通过在call方法中调用 self.add_loss(value) ,可以通过图层添加正则化损失。

在一般情况下,您需要在自定义训练循环中考虑这些损失(除非您自己编写模型并且您已经知道它不会造成这样的损失)。

回想一下上一节的这个例子,它的特点是一个层会产生正则化损失:

当您调用模型时,如下所示:

它在前向传递期间产生的损失被添加到 model.losses 属性中:

跟踪损失首先在模型 __call__ 开始时清除,因此您只能看到在这一次前进过程中产生的损失。例如,重复调用模型然后查询 losses 只显示最后一次调用期间创建的最新损失:

要在训练期间考虑这些损失,您所要做的就是修改训练循环,将 sum(model.losses) 添加到您的总损失中:

那是拼图的最后一块!你已经到了本指南的末尾。

现在您已经了解了有关使用内置训练循环以及从头开始编写自己的知识的所有信息。