Keras 3 API 文档 / KerasCV / 模型 / 任务 / CLIP 特征提取器

CLIP 特征提取器

[源代码]

CLIP

keras_cv.models.CLIP(
    embed_dim=512,
    image_resolution=224,
    vision_layers=12,
    vision_width=768,
    vision_patch_size=32,
    context_length=77,
    vocab_size=49408,
    transformer_width=512,
    transformer_heads=8,
    transformer_layers=12,
    **kwargs
)

CLIP 实施对比语言-图像预训练 (CLIP) 架构,它能够为各种下游任务联合学习视觉和文本表示。默认基本模型架构将设置为 clip-vit-base-patch32。

参数

  • embed_dim (int): 图像和文本的联合嵌入空间的维数。
  • image_resolution (int): 输入图像的分辨率(高度和宽度)。
  • vision_layers (int): 视觉(图像)编码器中的层数。vision_width (int): 视觉编码器中隐藏层的宽度。
  • vision_patch_size (int): 输入图像中每个方形 patch 的大小。
  • context_length (int): 上下文化文本序列的最大长度。
  • vocab_size (int): 用于标记化的词汇量大小。
  • transformer_width (int): 基于 Transformer 的文本编码器中隐藏层的宽度。
  • transformer_heads (int): 基于 Transformer 的文本编码器中的注意力头的数量。
  • transformer_layers (int): 基于 Transformer 的文本编码器中的层数。

示例

processor = CLIPProcessor(
    input_resolution=224,
    "path_to_vocab.json",
    "path_to_merges.txt"
)
processed_image = processor.process_images(["cat.jpg"])
tokens = processor(
    ["mountains", "cat on tortoise", "two cats"]
)
model = CLIP.from_preset("clip-vit-base-patch16")
image_logits, text_logits = model(
    {
        "images": processed_image,
        "token_ids": tokens["token_ids"],
        "padding_mask": tokens["padding_mask"],
    }
)

[源代码]

from_preset 方法

CLIP.from_preset()

从预设配置和权重实例化 CLIP 模型。

参数

  • preset: 字符串。必须是以下之一:"clip-vit-base-patch16"、"clip-vit-base-patch32"、"clip-vit-large-patch14"、"clip-vit-large-patch14-336"。如果要查找具有预训练权重的预设,请选择以下之一:"clip-vit-base-patch16"、"clip-vit-base-patch32"、"clip-vit-large-patch14"、"clip-vit-large-patch14-336"。
  • load_weights: 是否将预训练权重加载到模型中。默认为 None,遵循预设是否有预训练权重可用。
  • input_shape : 将传递到主干初始化的输入形状,默认为 None。如果为 None,则使用预设值。

示例

# Load architecture and weights from preset
model = keras_cv.models.CLIP.from_preset(
    "clip-vit-base-patch16",
)

# Load randomly initialized model from preset architecture with weights
model = keras_cv.models.CLIP.from_preset(
    "clip-vit-base-patch16",
    load_weights=False,
预设名称 参数 描述
clip-vit-base-patch16 149.62M 该模型使用 ViT-B/16 Transformer 架构作为图像编码器,并使用掩码自注意力 Transformer 作为文本编码器。这些编码器经过训练,通过对比损失来最大化(图像,文本)对的相似性。该模型使用 16 的 patch 大小和 (224, 224) 大小的输入图像
clip-vit-base-patch32 151.28M 该模型使用 ViT-B/32 Transformer 架构作为图像编码器,并使用掩码自注意力 Transformer 作为文本编码器。这些编码器经过训练,通过对比损失来最大化(图像,文本)对的相似性。该模型使用 32 的 patch 大小和 (224, 224) 大小的输入图像
clip-vit-large-patch14 427.62M 该模型使用 ViT-L/14 Transformer 架构作为图像编码器,并使用掩码自注意力 Transformer 作为文本编码器。这些编码器经过训练,通过对比损失来最大化(图像,文本)对的相似性。该模型使用 14 的 patch 大小和 (224, 224) 大小的输入图像
clip-vit-large-patch14-336 427.94M 该模型使用 ViT-L/14 Transformer 架构作为图像编码器,并使用掩码自注意力 Transformer 作为文本编码器。这些编码器经过训练,通过对比损失来最大化(图像,文本)对的相似性。该模型使用 14 的 patch 大小和 (336, 336) 大小的输入图像

[源代码]

CLIPAttention

keras_cv.models.feature_extractor.CLIPAttention(
    proj_dim, num_heads, num_hidden_layers, dropout=0.0, **kwargs
)

改编自 https://github.com/huggingface/transformers/blob/main/src/transformers/models/clip/modeling_clip.py # noqa: E501


[源代码]

CLIPEncoder

keras_cv.models.feature_extractor.CLIPEncoder(width, num_layers, heads, **kwargs)

这是所有层继承的类。

层是一个可调用对象,它以一个或多个张量作为输入,并输出一个或多个张量。它涉及在 call() 方法中定义的计算状态(权重变量)。状态可以在

  • __init__() 中创建,例如通过 self.add_weight()
  • 在可选的 build() 方法中创建,该方法由第一个对层的 __call__() 调用,并提供输入的形状,这些形状在初始化时可能未知。

层是递归可组合的:如果您将 Layer 实例分配为另一个 Layer 的属性,则外部层将开始跟踪内部层创建的权重。嵌套层应在 __init__() 方法或 build() 方法中实例化。

用户只需实例化一个层,然后将其视为可调用对象。

参数

  • trainable: 布尔值,表示该层的变量是否应可训练。
  • name: 该层的字符串名称。
  • dtype: 该层计算和权重的 dtype。也可以是 keras.DTypePolicy,它允许计算和权重 dtype 不同。默认为 NoneNone 表示使用 keras.config.dtype_policy(),除非设置为不同的值(通过 keras.config.set_dtype_policy()),否则它是一个 float32 策略。

属性

  • name: 该层的名称(字符串)。
  • dtype: 该层权重的 dtype。layer.variable_dtype 的别名。
  • variable_dtype: 该层权重的 dtype。
  • compute_dtype: 该层计算的 dtype。层会自动将输入转换为此 dtype,这会导致计算和输出也处于此 dtype。当使用 keras.DTypePolicy 使用混合精度时,这将与 variable_dtype 不同。
  • trainable_weights: 要包含在反向传播中的变量列表。
  • non_trainable_weights: 不应包含在反向传播中的变量列表。
  • weights: trainable_weightsnon_trainable_weights 列表的串联(按此顺序)。
  • trainable: 该层是否应接受训练(布尔值),即其可能可训练的权重是否应作为 layer.trainable_weights 的一部分返回。
  • input_spec: 可选的(列表)InputSpec 对象,指定层可以接受的输入的约束。

我们建议 Layer 的子类实现以下方法

  • __init__(): 定义自定义层属性,并使用 add_weight() 或其他状态创建不依赖于输入形状的层权重。
  • build(self, input_shape): 此方法可用于创建依赖于输入形状的权重,使用 add_weight() 或其他状态。__call__() 将自动构建层(如果尚未构建),方法是调用 build()
  • call(self, *args, **kwargs): 在确保 build() 已调用后,在 __call__ 中调用。call() 执行将层应用于输入参数的逻辑。您可以选择在 call() 中使用的两个保留关键字参数是:1. training(布尔值,表示调用是在推理模式下还是训练模式下)。2. mask(布尔张量,对输入中掩盖的时间步长进行编码,例如在 RNN 层中使用)。此方法的典型签名是 call(self, inputs),用户可以选择添加 trainingmask,如果层需要它们。
  • get_config(self): 返回一个字典,其中包含用于初始化此层的配置。如果键与 __init__() 中的参数不同,则也覆盖 from_config(self)。在保存层或包含此层的模型时,将使用此方法。

示例

这是一个基本示例:一个具有两个变量 wb 的层,它返回 y = w . x + b。它展示了如何实现 build()call()。设置为层属性的变量将作为层的权重进行跟踪(在 layer.weights 中)。

class SimpleDense(Layer):
    def __init__(self, units=32):
        super().__init__()
        self.units = units

    # Create the state of the layer (weights)
    def build(self, input_shape):
        self.kernel = self.add_weight(
            shape=(input_shape[-1], self.units),
            initializer="glorot_uniform",
            trainable=True,
            name="kernel",
        )
        self.bias = self.add_weight(
            shape=(self.units,),
            initializer="zeros",
            trainable=True,
            name="bias",
        )

    # Defines the computation
    def call(self, inputs):
        return ops.matmul(inputs, self.kernel) + self.bias

# Instantiates the layer.
linear_layer = SimpleDense(4)

# This will also call `build(input_shape)` and create the weights.
y = linear_layer(ops.ones((2, 2)))
assert len(linear_layer.weights) == 2

# These weights are trainable, so they're listed in `trainable_weights`:
assert len(linear_layer.trainable_weights) == 2

除了可训练权重(通过训练期间的反向传播更新)之外,层还可以具有不可训练权重。这些权重旨在通过 call() 手动更新。以下是一个计算其输入的运行总和的示例层

class ComputeSum(Layer):

  def __init__(self, input_dim):
      super(ComputeSum, self).__init__()
      # Create a non-trainable weight.
      self.total = self.add_weight(
        shape=(),
        initializer="zeros",
        trainable=False,
        name="total",
      )

  def call(self, inputs):
      self.total.assign(self.total + ops.sum(inputs))
      return self.total

my_sum = ComputeSum(2)
x = ops.ones((2, 2))
y = my_sum(x)

assert my_sum.weights == [my_sum.total]
assert my_sum.non_trainable_weights == [my_sum.total]
assert my_sum.trainable_weights == []

[源代码]

CLIPImageEncoder

keras_cv.models.feature_extractor.CLIPImageEncoder(
    input_resolution, patch_size, width, num_layers, heads, output_dim, **kwargs
)

将层分组到具有训练/推理功能的对象中的模型。

有三种方法可以实例化 Model

使用“函数式 API”

您从 Input 开始,链接层调用以指定模型的前向传递,最后,从输入和输出创建模型

inputs = keras.Input(shape=(37,))
x = keras.layers.Dense(32, activation="relu")(inputs)
outputs = keras.layers.Dense(5, activation="softmax")(x)
model = keras.Model(inputs=inputs, outputs=outputs)

注意:仅支持输入张量的字典、列表和元组。不支持嵌套输入(例如,列表的列表或字典的字典)。

还可以通过使用中间张量来创建一个新的函数式 API 模型。这使您能够快速提取模型的子组件。

示例

inputs = keras.Input(shape=(None, None, 3))
processed = keras.layers.RandomCrop(width=128, height=128)(inputs)
conv = keras.layers.Conv2D(filters=32, kernel_size=3)(processed)
pooling = keras.layers.GlobalAveragePooling2D()(conv)
feature = keras.layers.Dense(10)(pooling)

full_model = keras.Model(inputs, feature)
backbone = keras.Model(processed, conv)
activations = keras.Model(conv, feature)

请注意,backboneactivations 模型不是使用 keras.Input 对象创建的,而是使用源自 keras.Input 对象的张量创建的。在后台,层和权重将在这些模型之间共享,因此用户可以训练 full_model,并使用 backboneactivations 进行特征提取。模型的输入和输出也可以是张量的嵌套结构,创建的模型是支持所有现有 API 的标准函数式 API 模型。

通过子类化 Model

在这种情况下,您应该在 __init__() 中定义层,并应该在 call() 中实现模型的前向传递。

class MyModel(keras.Model):
    def __init__(self):
        super().__init__()
        self.dense1 = keras.layers.Dense(32, activation="relu")
        self.dense2 = keras.layers.Dense(5, activation="softmax")

    def call(self, inputs):
        x = self.dense1(inputs)
        return self.dense2(x)

model = MyModel()

如果您子类化 Model,可以选择在 call() 中有一个 training 参数(布尔值),您可以使用它来指定训练和推理中不同的行为

class MyModel(keras.Model):
    def __init__(self):
        super().__init__()
        self.dense1 = keras.layers.Dense(32, activation="relu")
        self.dense2 = keras.layers.Dense(5, activation="softmax")
        self.dropout = keras.layers.Dropout(0.5)

    def call(self, inputs, training=False):
        x = self.dense1(inputs)
        x = self.dropout(x, training=training)
        return self.dense2(x)

model = MyModel()

创建模型后,可以使用 model.compile() 为模型配置损失和指标,使用 model.fit() 训练模型,或使用模型使用 model.predict() 进行预测。

使用 Sequential

此外,keras.Sequential 是一种特殊类型的模型,其中模型纯粹是由单输入、单输出层堆叠而成。

model = keras.Sequential([
    keras.Input(shape=(None, None, 3)),
    keras.layers.Conv2D(filters=32, kernel_size=3),
])

[源代码]

CLIPProcessor

keras_cv.models.feature_extractor.CLIPProcessor(vocabulary, merges, **kwargs)

CLIPProcessor 是一个实用程序类,它提供用于在 CLIP(对比语言-图像预训练)模型的上下文中处理文本的功能。

参数

  • input_resolution (int): 输入图像的分辨率。
  • vocabulary (str): 字符串或字典,将标记映射到整数 ID。 如果它是字符串,它应该是指向 JSON 文件的文件路径。
  • merges: 字符串或列表,包含合并规则。 如果它是字符串,它应该是指向合并规则的文件路径。 合并规则文件应每行包含一条合并规则。

[源代码]

CLIPTextEncoder

keras_cv.models.feature_extractor.CLIPTextEncoder(
    transformer_width,
    transformer_layers,
    transformer_heads,
    vocab_size,
    embed_dim,
    context_length,
    **kwargs
)

将层分组到具有训练/推理功能的对象中的模型。

有三种方法可以实例化 Model

使用“函数式 API”

您从 Input 开始,链接层调用以指定模型的前向传递,最后,从输入和输出创建模型

inputs = keras.Input(shape=(37,))
x = keras.layers.Dense(32, activation="relu")(inputs)
outputs = keras.layers.Dense(5, activation="softmax")(x)
model = keras.Model(inputs=inputs, outputs=outputs)

注意:仅支持输入张量的字典、列表和元组。不支持嵌套输入(例如,列表的列表或字典的字典)。

还可以通过使用中间张量来创建一个新的函数式 API 模型。这使您能够快速提取模型的子组件。

示例

inputs = keras.Input(shape=(None, None, 3))
processed = keras.layers.RandomCrop(width=128, height=128)(inputs)
conv = keras.layers.Conv2D(filters=32, kernel_size=3)(processed)
pooling = keras.layers.GlobalAveragePooling2D()(conv)
feature = keras.layers.Dense(10)(pooling)

full_model = keras.Model(inputs, feature)
backbone = keras.Model(processed, conv)
activations = keras.Model(conv, feature)

请注意,backboneactivations 模型不是使用 keras.Input 对象创建的,而是使用源自 keras.Input 对象的张量创建的。在后台,层和权重将在这些模型之间共享,因此用户可以训练 full_model,并使用 backboneactivations 进行特征提取。模型的输入和输出也可以是张量的嵌套结构,创建的模型是支持所有现有 API 的标准函数式 API 模型。

通过子类化 Model

在这种情况下,您应该在 __init__() 中定义层,并应该在 call() 中实现模型的前向传递。

class MyModel(keras.Model):
    def __init__(self):
        super().__init__()
        self.dense1 = keras.layers.Dense(32, activation="relu")
        self.dense2 = keras.layers.Dense(5, activation="softmax")

    def call(self, inputs):
        x = self.dense1(inputs)
        return self.dense2(x)

model = MyModel()

如果您子类化 Model,可以选择在 call() 中有一个 training 参数(布尔值),您可以使用它来指定训练和推理中不同的行为

class MyModel(keras.Model):
    def __init__(self):
        super().__init__()
        self.dense1 = keras.layers.Dense(32, activation="relu")
        self.dense2 = keras.layers.Dense(5, activation="softmax")
        self.dropout = keras.layers.Dropout(0.5)

    def call(self, inputs, training=False):
        x = self.dense1(inputs)
        x = self.dropout(x, training=training)
        return self.dense2(x)

model = MyModel()

创建模型后,可以使用 model.compile() 为模型配置损失和指标,使用 model.fit() 训练模型,或使用模型使用 model.predict() 进行预测。

使用 Sequential

此外,keras.Sequential 是一种特殊类型的模型,其中模型纯粹是由单输入、单输出层堆叠而成。

model = keras.Sequential([
    keras.Input(shape=(None, None, 3)),
    keras.layers.Conv2D(filters=32, kernel_size=3),
])

[源代码]

QuickGELU

keras_cv.models.feature_extractor.QuickGELU(**kwargs)

这是所有层继承的类。

层是一个可调用对象,它以一个或多个张量作为输入,并输出一个或多个张量。它涉及在 call() 方法中定义的计算状态(权重变量)。状态可以在

  • __init__() 中创建,例如通过 self.add_weight()
  • 在可选的 build() 方法中创建,该方法由第一个对层的 __call__() 调用,并提供输入的形状,这些形状在初始化时可能未知。

层是递归可组合的:如果您将 Layer 实例分配为另一个 Layer 的属性,则外部层将开始跟踪内部层创建的权重。嵌套层应在 __init__() 方法或 build() 方法中实例化。

用户只需实例化一个层,然后将其视为可调用对象。

参数

  • trainable: 布尔值,表示该层的变量是否应可训练。
  • name: 该层的字符串名称。
  • dtype: 该层计算和权重的 dtype。也可以是 keras.DTypePolicy,它允许计算和权重 dtype 不同。默认为 NoneNone 表示使用 keras.config.dtype_policy(),除非设置为不同的值(通过 keras.config.set_dtype_policy()),否则它是一个 float32 策略。

属性

  • name: 该层的名称(字符串)。
  • dtype: 该层权重的 dtype。layer.variable_dtype 的别名。
  • variable_dtype: 该层权重的 dtype。
  • compute_dtype: 该层计算的 dtype。层会自动将输入转换为此 dtype,这会导致计算和输出也处于此 dtype。当使用 keras.DTypePolicy 使用混合精度时,这将与 variable_dtype 不同。
  • trainable_weights: 要包含在反向传播中的变量列表。
  • non_trainable_weights: 不应包含在反向传播中的变量列表。
  • weights: trainable_weightsnon_trainable_weights 列表的串联(按此顺序)。
  • trainable: 该层是否应接受训练(布尔值),即其可能可训练的权重是否应作为 layer.trainable_weights 的一部分返回。
  • input_spec: 可选的(列表)InputSpec 对象,指定层可以接受的输入的约束。

我们建议 Layer 的子类实现以下方法

  • __init__(): 定义自定义层属性,并使用 add_weight() 或其他状态创建不依赖于输入形状的层权重。
  • build(self, input_shape): 此方法可用于创建依赖于输入形状的权重,使用 add_weight() 或其他状态。__call__() 将自动构建层(如果尚未构建),方法是调用 build()
  • call(self, *args, **kwargs): 在确保 build() 已调用后,在 __call__ 中调用。call() 执行将层应用于输入参数的逻辑。您可以选择在 call() 中使用的两个保留关键字参数是:1. training(布尔值,表示调用是在推理模式下还是训练模式下)。2. mask(布尔张量,对输入中掩盖的时间步长进行编码,例如在 RNN 层中使用)。此方法的典型签名是 call(self, inputs),用户可以选择添加 trainingmask,如果层需要它们。
  • get_config(self): 返回一个字典,其中包含用于初始化此层的配置。如果键与 __init__() 中的参数不同,则也覆盖 from_config(self)。在保存层或包含此层的模型时,将使用此方法。

示例

这是一个基本示例:一个具有两个变量 wb 的层,它返回 y = w . x + b。它展示了如何实现 build()call()。设置为层属性的变量将作为层的权重进行跟踪(在 layer.weights 中)。

class SimpleDense(Layer):
    def __init__(self, units=32):
        super().__init__()
        self.units = units

    # Create the state of the layer (weights)
    def build(self, input_shape):
        self.kernel = self.add_weight(
            shape=(input_shape[-1], self.units),
            initializer="glorot_uniform",
            trainable=True,
            name="kernel",
        )
        self.bias = self.add_weight(
            shape=(self.units,),
            initializer="zeros",
            trainable=True,
            name="bias",
        )

    # Defines the computation
    def call(self, inputs):
        return ops.matmul(inputs, self.kernel) + self.bias

# Instantiates the layer.
linear_layer = SimpleDense(4)

# This will also call `build(input_shape)` and create the weights.
y = linear_layer(ops.ones((2, 2)))
assert len(linear_layer.weights) == 2

# These weights are trainable, so they're listed in `trainable_weights`:
assert len(linear_layer.trainable_weights) == 2

除了可训练权重(通过训练期间的反向传播更新)之外,层还可以具有不可训练权重。这些权重旨在通过 call() 手动更新。以下是一个计算其输入的运行总和的示例层

class ComputeSum(Layer):

  def __init__(self, input_dim):
      super(ComputeSum, self).__init__()
      # Create a non-trainable weight.
      self.total = self.add_weight(
        shape=(),
        initializer="zeros",
        trainable=False,
        name="total",
      )

  def call(self, inputs):
      self.total.assign(self.total + ops.sum(inputs))
      return self.total

my_sum = ComputeSum(2)
x = ops.ones((2, 2))
y = my_sum(x)

assert my_sum.weights == [my_sum.total]
assert my_sum.non_trainable_weights == [my_sum.total]
assert my_sum.trainable_weights == []

[源代码]

ResidualAttention

keras_cv.models.feature_extractor.ResidualAttention(
    proj_dim, num_heads, num_hidden_layers, **kwargs
)

这是所有层继承的类。

层是一个可调用对象,它以一个或多个张量作为输入,并输出一个或多个张量。它涉及在 call() 方法中定义的计算状态(权重变量)。状态可以在

  • __init__() 中创建,例如通过 self.add_weight()
  • 在可选的 build() 方法中创建,该方法由第一个对层的 __call__() 调用,并提供输入的形状,这些形状在初始化时可能未知。

层是递归可组合的:如果您将 Layer 实例分配为另一个 Layer 的属性,则外部层将开始跟踪内部层创建的权重。嵌套层应在 __init__() 方法或 build() 方法中实例化。

用户只需实例化一个层,然后将其视为可调用对象。

参数

  • trainable: 布尔值,表示该层的变量是否应可训练。
  • name: 该层的字符串名称。
  • dtype: 该层计算和权重的 dtype。也可以是 keras.DTypePolicy,它允许计算和权重 dtype 不同。默认为 NoneNone 表示使用 keras.config.dtype_policy(),除非设置为不同的值(通过 keras.config.set_dtype_policy()),否则它是一个 float32 策略。

属性

  • name: 该层的名称(字符串)。
  • dtype: 该层权重的 dtype。layer.variable_dtype 的别名。
  • variable_dtype: 该层权重的 dtype。
  • compute_dtype: 该层计算的 dtype。层会自动将输入转换为此 dtype,这会导致计算和输出也处于此 dtype。当使用 keras.DTypePolicy 使用混合精度时,这将与 variable_dtype 不同。
  • trainable_weights: 要包含在反向传播中的变量列表。
  • non_trainable_weights: 不应包含在反向传播中的变量列表。
  • weights: trainable_weightsnon_trainable_weights 列表的串联(按此顺序)。
  • trainable: 该层是否应接受训练(布尔值),即其可能可训练的权重是否应作为 layer.trainable_weights 的一部分返回。
  • input_spec: 可选的(列表)InputSpec 对象,指定层可以接受的输入的约束。

我们建议 Layer 的子类实现以下方法

  • __init__(): 定义自定义层属性,并使用 add_weight() 或其他状态创建不依赖于输入形状的层权重。
  • build(self, input_shape): 此方法可用于创建依赖于输入形状的权重,使用 add_weight() 或其他状态。__call__() 将自动构建层(如果尚未构建),方法是调用 build()
  • call(self, *args, **kwargs): 在确保 build() 已调用后,在 __call__ 中调用。call() 执行将层应用于输入参数的逻辑。您可以选择在 call() 中使用的两个保留关键字参数是:1. training(布尔值,表示调用是在推理模式下还是训练模式下)。2. mask(布尔张量,对输入中掩盖的时间步长进行编码,例如在 RNN 层中使用)。此方法的典型签名是 call(self, inputs),用户可以选择添加 trainingmask,如果层需要它们。
  • get_config(self): 返回一个字典,其中包含用于初始化此层的配置。如果键与 __init__() 中的参数不同,则也覆盖 from_config(self)。在保存层或包含此层的模型时,将使用此方法。

示例

这是一个基本示例:一个具有两个变量 wb 的层,它返回 y = w . x + b。它展示了如何实现 build()call()。设置为层属性的变量将作为层的权重进行跟踪(在 layer.weights 中)。

class SimpleDense(Layer):
    def __init__(self, units=32):
        super().__init__()
        self.units = units

    # Create the state of the layer (weights)
    def build(self, input_shape):
        self.kernel = self.add_weight(
            shape=(input_shape[-1], self.units),
            initializer="glorot_uniform",
            trainable=True,
            name="kernel",
        )
        self.bias = self.add_weight(
            shape=(self.units,),
            initializer="zeros",
            trainable=True,
            name="bias",
        )

    # Defines the computation
    def call(self, inputs):
        return ops.matmul(inputs, self.kernel) + self.bias

# Instantiates the layer.
linear_layer = SimpleDense(4)

# This will also call `build(input_shape)` and create the weights.
y = linear_layer(ops.ones((2, 2)))
assert len(linear_layer.weights) == 2

# These weights are trainable, so they're listed in `trainable_weights`:
assert len(linear_layer.trainable_weights) == 2

除了可训练权重(通过训练期间的反向传播更新)之外,层还可以具有不可训练权重。这些权重旨在通过 call() 手动更新。以下是一个计算其输入的运行总和的示例层

class ComputeSum(Layer):

  def __init__(self, input_dim):
      super(ComputeSum, self).__init__()
      # Create a non-trainable weight.
      self.total = self.add_weight(
        shape=(),
        initializer="zeros",
        trainable=False,
        name="total",
      )

  def call(self, inputs):
      self.total.assign(self.total + ops.sum(inputs))
      return self.total

my_sum = ComputeSum(2)
x = ops.ones((2, 2))
y = my_sum(x)

assert my_sum.weights == [my_sum.total]
assert my_sum.non_trainable_weights == [my_sum.total]
assert my_sum.trainable_weights == []