通过子类化创建新的层和模型

​Layer​​类:状态(权重)和部分计算的组合

Keras 的一个中心抽象是 ​ ​Layer​​ 类。层封装了状态(层的”权重”)和从输入到输出的转换(“调用”,即层的前向传递)。

下面是一个密集连接的层。它具有一个状态:变量 ​ ​w​​ 和 ​ ​b​​。

您可以在某些张量输入上通过调用来使用层,这一点很像 Python 函数。

请注意,权重 ​ ​w​​ 和 ​ ​b​​ 在被设置为层特性后会由层自动跟踪:

请注意,您还可以使用一种更加快捷的方式为层添加权重:​ ​add_weight()​​ 方法:

层可以具有不可训练权重

除了可训练的重量,您还可以将不可训练的重量添加到层中。在训练层,在反向传播期间不必考虑这样的权重。

[En]

In addition to trainable weights, you can also add untrainable weights to layers. At the training layer, such weights do not have to be considered during back propagation.

以下是如何添加和使用不可训练的重量:

[En]

Here is how to add and use untrainable weights:

它是 ​ ​layer.weights​​ 的一部分,但被归类为不可训练权重:

最佳做法:将权重创建推迟到得知输入的形状之后

上面的 ​ ​Linear​​ 层接受了一个 ​ ​input_dim​​ 参数,用于计算 ​ ​__init__()​​ 中权重 ​ ​w​​ 和 ​ ​b​​ 的形状:

在许多情况下,您可能事先不知道输入的大小,并希望在知道该值时(在实例化层之后的一段时间)延迟创建权重。

[En]

In many cases, you may not know the size of the input in advance and want to delay the creation of weights when you know the value (some time after the layer is instantiated).

在 Keras API 中,我们建议您在层的 ​ ​build(self, inputs_shape)​​ 方法中创建层权重。如下所示:

层的 ​ ​__call__()​​ 方法将在首次调用时自动运行构建。现在,您有了一个延迟并因此更易使用的层:

层可递归组合

如果将一个层实例指定为另一个层的属性,则外层开始跟踪内层的权重。

[En]

If you assign a layer instance as a property of another layer, the outer layer begins to track the weight of the inner layer.

我们建议在 ​ ​__init__()​​ 方法中创建此类子层(由于子层通常具有构建方法,它们将与外部层同时构建)。

​add_loss()​​方法

在编写层的 ​ ​call()​​ 方法时,您可以在编写训练循环时创建想要稍后使用的损失张量。这可以通过调用 ​ ​self.add_loss(value)​​ 来实现:

这些损失(包括由任何内部层创建的损失)可通过 ​ ​layer.losses​​ 取回。此属性会在每个 ​ ​__call__()​​ 开始时重置到顶层,因此 ​ ​layer.losses​​ 始终包含在上一次前向传递过程中创建的损失值。

此外,​ ​loss​​ 属性还包含为任何内部层的权重创建的正则化损失:

在编写培训周期时应考虑到这些损失,如下所示:

[En]

These losses should be taken into account when writing the training cycle, as follows:

这些损失还可以无缝使用 ​ ​fit()​​(它们会自动求和并添加到主损失中,如果有):

​add_metric()​​方法

与 ​ ​add_loss()​​ 类似,层还具有 ​ ​add_metric()​​ 方法,用于在训练过程中跟踪数量的移动平均值。

请思考下面的 “logistic endpoint” 层。它将预测和目标作为输入,计算通过 ​ ​add_loss()​​ 跟踪的损失,并计算通过 ​ ​add_metric()​​ 跟踪的准确率标量。

可通过 ​ ​layer.metrics​​ 访问以这种方式跟踪的指标:

和 ​ ​add_loss()​​ 一样,这些指标也是通过 ​ ​fit()​​ 跟踪的:

可选择在层上启用序列化

请注意,基础 ​ ​Layer​​ 类的 ​ ​__init__()​​ 方法会接受一些关键字参数,尤其是 ​ ​name​​ 和 ​ ​dtype​​。最好将这些参数传递给 ​ ​__init__()​​ 中的父类,并将其包含在层配置中:

如果根据层的配置对层进行反序列化时需要更大的灵活性,还可以重写 ​ ​from_config()​​ 类方法。下面是 ​ ​from_config()​​ 的基础实现:

​call()​​方法中的特权​ ​training​​参数

某些层,尤其是 ​ ​BatchNormalization​​ 层和 ​ ​Dropout​​ 层,在训练和推断期间具有不同的行为。对于此类层,标准做法是在 ​ ​call()​​ 方法中公开 ​ ​training​​(布尔)参数。

通过在 ​ ​call()​​ 中公开此参数,可以启用内置的训练和评估循环(例如 ​ ​fit()​​)以在训练和推断中正确使用层。

​call()​​方法中的特权​ ​mask​​参数

​call()​​ 支持的另一个特权参数是 ​ ​mask​​ 参数。

它会出现在所有 Keras RNN 层中。掩码是布尔张量(在输入中每个时间步骤对应一个布尔值),用于在处理时间序列数据时跳过某些输入时间步骤。

当先前的层生成掩码时,Keras 会自动将正确的 ​ ​mask​​ 参数传递给 ​ ​__call__()​​(针对支持它的层)。掩码生成层是配置了 ​ ​mask_zero=True​​ 的 ​ ​Embedding​​ 层和 ​ ​Masking​​ 层。

​Model​ ​ 类

通常,您会使用 ​ ​Layer​​ 类来定义内部计算块,并使用 ​ ​Model​​ 类来定义外部模型,即您将训练的对象。

​Model​​ 类具有与 ​ ​Layer​​ 相同的 API,但有如下区别:

  • 它会公开内置训练、评估和预测循环(​ ​model.fit()​​​、​ ​model.evaluate()​​​、​ ​model.predict()​​)。
  • 它会通过 ​ ​model.layers​​ 属性公开其内部层的列表。
  • 它会公开保存和序列化 API(​ ​save()​​​、​ ​save_weights()​​…)

实际上,​ ​Layer​​ 类对应于我们在文献中所称的”层”(如”卷积层”或”循环层”)或”块”(如”ResNet 块”或”Inception 块”)。

同时,​ ​Model​​ 类对应于文献中所称的”模型”(如”深度学习模型”)或”网络”(如”深度神经网络”)。

因此,如果您想知道”我应该用 ​ ​Layer​​ 类还是 ​ ​Model​​ 类?”,请问自己:我是否需要在它上面调用 ​ ​fit()​​?我是否需要在它上面调用 ​ ​save()​​?如果是,则使用 ​ ​Model​​。如果不是(要么因为您的类只是更大系统中的一个块,要么因为您正在自己编写训练和保存代码),则使用 ​ ​Layer​​。

例如,我们可以使用上面的 mini-resnet 示例,用它来构建一个 ​ ​Model​​,该模型可以通过 ​ ​fit()​​ 进行训练,并通过 ​ ​save_weights()​​ 进行保存:

汇总:端到端示例

到目前为止,您已经了解了以下内容:

[En]

So far, you have learned the following:

  • ​Layer​​ 封装了状态(在 ​ ​__init__()​​ 或 ​ ​build()​​ 中创建)和一些计算(在 ​ ​call()​​ 中定义)。
  • 层可以递归嵌套以创建新的更大的计算块。
    [En]

    layers can be recursively nested to create new and larger calculation blocks.*

  • 层可以通过 ​ ​add_loss()​​ 和 ​ ​add_metric()​​ 创建并跟踪损失(通常是正则化损失)以及指标。
  • 您要训练的外部容器是 ​ ​Model​​​。​ ​Model​​ 就像 ​ ​Layer​​,但是添加了训练和序列化实用工具。

让我们将这些内容全部汇总到一个端到端示例:我们将实现一个变分自动编码器 (VAE),并用 MNIST 数字对其进行训练。

我们的 VAE 将是 ​ ​Model​​ 的一个子类,它是作为子类化 ​ ​Layer​​ 的嵌套组合层进行构建的。它将具有正则化损失(KL 散度)。

让我们在 MNIST 上编写一个简单的训练循环:

请注意,由于 VAE 是 ​ ​Model​​ 的子类,它具有内置的训练循环。因此,您也可以用以下方式训练它:

超越面向对象的开发:函数式 API

这个示例对您来说是否包含了太多面向对象的开发?您也可以使用​​函数式 API​​ 来构建模型。重要的是,选择其中一种样式并不妨碍您利用以另一种样式编写的组件:您随时可以搭配使用。

例如,下面的函数式 API 示例重用了我们在上面的示例中定义的同一个 ​ ​Sampling​​ 层:

Original: https://blog.csdn.net/SE_JW/article/details/122196291
Author: 语言专家
Title: 通过子类化创建新的层和模型

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/514684/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球