Keras深度学习框架基础第五讲:层接口(layers API)“核心层Core layers”

1、 输入对象

输入对象使用Input 函数。
“Input object” 在编程和机器学习通常指的是一个用于接收数据或参数的输入对象。在 Keras(一个流行的深度学习框架)中,Input 是一个特殊的类,用于实例化一个输入张量(tensor),这通常是一个表示输入数据的多维数组。

keras.Input(
    shape=None,
    batch_size=None,
    dtype=None,
    sparse=None,
    batch_shape=None,
    name=None,
    tensor=None,
)

用于实例化一个Keras张量。

Keras张量是一个符号张量对象,我们为其增加了一些属性,这些属性允许我们仅通过知道模型的输入和输出来构建Keras模型。

例如,如果a、b和c是Keras张量,那么就可以这样做:model = Model(input=[a, b], output=c)

参数

  • shape:一个形状元组(由整数或None对象组成的元组),不包括批量大小。例如,shape=(32,) 表示预期的输入将是32维向量的批次。这个元组的元素可以是None;None元素代表形状未知的维度,并可能有所变化(例如序列长度)。
  • batch_size:可选的静态批量大小(整数)。
  • dtype:输入期望的数据类型,作为字符串(例如 “float32”、“int32” 等)。
  • sparse:一个布尔值,指定预期的输入是否将是稀疏张量。请注意,如果sparse为False,仍然可以将稀疏张量作为输入传递 - 它们将使用默认值0进行稠密化。此功能仅支持TensorFlow后端。默认为False。
  • name:层的可选名称字符串。在模型中应该是唯一的(不要两次重复使用相同的名称)。如果未提供,则会自动生成。
  • tensor:可选的现有张量,用于包装到Input层中。如果设置,该层将使用这个张量而不是创建一个新的占位符张量。

返回

一个Keras张量。

# This is a logistic regression in Keras
x = Input(shape=(32,))
y = Dense(16, activation='softmax')(x)
model = Model(x, y)

2、InputSpec 对象

InputSpec 对象在 Keras 中是一个描述输入数据期望格式的类。这个对象提供了关于输入数据形状、数据类型(dtype)、名称(name)以及是否应为稀疏(sparse)的详细信息。

这个对象用于描述 Keras 模型输入层所期望的输入数据的格式和属性。它允许开发者明确指定模型输入的形状、数据类型、名称等信息,以确保输入数据与模型结构兼容。
InputSpec 类表述如下

keras.InputSpec(
    dtype=None,
    shape=None,
    ndim=None,
    max_ndim=None,
    min_ndim=None,
    axes=None,
    allow_last_axis_squeeze=False,
    name=None,
)

指定层中每个输入的秩(维度)、数据类型(dtype)和形状。

层可以暴露(如果合适的话)一个input_spec属性:它是一个InputSpec的实例,或者是一个InputSpec实例的嵌套结构(每个输入张量一个)。这些对象使得层能够在调用Layer.__call__的第一个参数时进行输入结构、输入秩、输入形状和输入数据类型的兼容性检查。

形状元组中的None条目与任何维度兼容。

参数

  • dtype:输入的期望数据类型。
  • shape:形状元组,期望的输入形状(对于动态轴,可以是None)。包括批量大小。
  • ndim:整数,输入的期望秩(维度)。
  • max_ndim:整数,输入的最大秩(维度)。
  • min_ndim:整数,输入的最小秩(维度)。
  • axes:一个字典,将整数轴映射到特定的维度值。
  • allow_last_axis_squeeze:如果为True,则允许秩为N+1的输入(只要输入的最后一个轴为1),同时也允许秩为N-1的输入(只要规范的最后一个轴为1)。
  • name:当以字典形式传递数据时,与此输入对应的期望键。

在Keras中,InputSpec是一种机制,用于定义和验证输入到层的张量的属性。这允许开发者确保传递给层的输入数据具有正确的形状、数据类型和秩,从而避免在模型训练或推理时出现不必要的错误。input_spec属性是一个可选的层属性,用于存储这些信息。当使用Layer.__call__方法调用层时,会检查输入是否与input_spec中定义的规范匹配。

class MyLayer(Layer):
    def __init__(self):
        super().__init__()
        # The layer will accept inputs with
        # shape (*, 28, 28) & (*, 28, 28, 1)
        # and raise an appropriate error message otherwise.
        self.input_spec = InputSpec(
            shape=(None, 28, 28, 1),
            allow_last_axis_squeeze=True)

3、Dense层

keras.layers.Dense(
    units,
    activation=None,
    use_bias=True,
    kernel_initializer="glorot_uniform",
    bias_initializer="zeros",
    kernel_regularizer=None,
    bias_regularizer=None,
    activity_regularizer=None,
    kernel_constraint=None,
    bias_constraint=None,
    lora_rank=None,
    **kwargs
)

这就是你常见的全连接神经网络层。

Dense层实现了以下操作:output = activation(dot(input, kernel) + bias),其中activation是作为activation参数传递的逐元素激活函数,kernel是由层创建的权重矩阵,而bias是由层创建的偏置向量(仅当use_bias为True时适用)。

注意:如果输入到层的张量的秩大于2,Dense层将沿着输入的最后一个轴和权重的第0轴计算点积(使用tf.tensordot)。例如,如果输入的维度是(batch_size, d0, d1),那么我们会创建一个形状为(d1, units)的权重矩阵,并且这个权重矩阵会在输入的每个形状为(1, 1, d1)的子张量上沿着轴2操作(这样的子张量总共有batch_size * d0个)。在这种情况下,输出的形状将是(batch_size, d0, units)

参数

  • units: 正整数,输出空间的维度。
  • activation: 要使用的激活函数。如果不指定,则不应用任何激活(即“线性”激活:a(x) = x)。
  • use_bias: 布尔值,表示该层是否使用偏置向量。
  • kernel_initializer: 权重矩阵的初始化器。
  • bias_initializer: 偏置向量的初始化器。
  • kernel_regularizer: 应用于权重矩阵的正则化函数。
  • bias_regularizer: 应用于偏置向量的正则化函数。
  • activity_regularizer: 应用于层输出(即其“激活”)的正则化函数。
  • kernel_constraint: 应用于权重矩阵的约束函数。
  • bias_constraint: 应用于偏置向量的约束函数。
  • lora_rank: 可选整数。如果设置,该层的正向传递将实现LoRA(低秩适应)并使用提供的秩。LoRA将层的权重设置为不可训练,并用两个低秩的可训练矩阵的乘积替换原始权重,以获取增量。这可以用于减少微调大型密集层的计算成本。你也可以通过调用layer.enable_lora(rank)在现有的Dense层上启用LoRA。

输入形状

N-D张量,形状为:(batch_size, …, input_dim)。最常见的情况是2D输入,形状为(batch_size, input_dim)。

输出形状

N-D张量,形状为:(batch_size, …, units)。例如,对于形状为(batch_size, input_dim)的2D输入,输出将具有形状(batch_size, units)。

4、EinsumDense层

keras.layers.EinsumDense(
    equation,
    output_shape,
    activation=None,
    bias_axes=None,
    kernel_initializer="glorot_uniform",
    bias_initializer="zeros",
    kernel_regularizer=None,
    bias_regularizer=None,
    kernel_constraint=None,
    bias_constraint=None,
    lora_rank=None,
    **kwargs
)

这是一个使用einsum作为底层计算的层。

这个层可以执行任意维度的einsum计算。

参数

  • equation: 描述要执行的einsum操作的方程。这个方程必须是一个有效的einsum字符串,形式为ab,bc->ac, ...ab,bc->...ac,或者ab...,bc->ac...,其中'ab', 'bc', 和 'ac'可以是任何有效的einsum轴表达式序列。
  • output_shape: 输出张量(不包括批次维度和由省略号表示的任何维度)的预期形状。对于任何未知或可以从输入形状推断出的维度,你可以指定为None
  • activation: 要使用的激活函数。如果不指定,则不应用任何激活(即“线性”激活:a(x) = x)。
  • bias_axes: 一个字符串,包含要应用偏置的输出维度。bias_axes字符串中的每个字符应该对应于方程字符串输出部分的一个字符。
  • kernel_initializer: 权重矩阵的初始化器。
  • bias_initializer: 偏置向量的初始化器。
  • kernel_regularizer: 应用于权重矩阵的正则化函数。
  • bias_regularizer: 应用于偏置向量的正则化函数。
  • kernel_constraint: 应用于权重矩阵的约束函数。
  • bias_constraint: 应用于偏置向量的约束函数。
  • lora_rank: 可选整数。如果设置,该层的前向传递将实现LoRA(低秩适应)并使用提供的秩。LoRA将层的权重设置为不可训练,并通过两个低秩的可训练矩阵的乘积替换原始权重(分解发生在最后一个维度上)。这可以用于减少微调大型密集层的计算成本。你也可以通过调用layer.enable_lora(rank)在现有的EinsumDense层上启用LoRA。
  • **kwargs: 基础层的关键字参数,如namedtype

带偏置的密集层与einsum

这个示例展示了如何使用einsum操作来实例化一个标准的Keras密集层。这个示例与keras.layers.Dense(64, use_bias=True)是等效的。

在Keras中,Dense层是一个全连接的神经网络层,它会对输入数据进行线性变换(即矩阵乘法)并可能添加偏置项。einsum是一个在NumPy和TensorFlow等库中可用的函数,用于执行高效的张量运算,其中“einsum”代表“爱因斯坦求和约定”(Einstein summation convention)。

虽然标准的Dense层在内部可能不会直接使用einsum(尽管它可能使用类似的底层操作),但你可以通过自定义层并使用einsum来模拟其行为。这个示例中的自定义层应该能够接收输入数据,执行与标准Dense层相同的线性变换(包括偏置项),并产生输出。

这样的自定义层可能对于学习或理解底层操作很有用,尽管在实践中,直接使用Keras的内置Dense层通常更为简单和高效。

>>> layer = keras.layers.EinsumDense("ab,bc->ac",
...                                       output_shape=64,
...                                       bias_axes="c")
>>> input_tensor = keras.Input(shape=[32])
>>> output_tensor = layer(input_tensor)
>>> output_tensor.shape
(None, 64)

应用密集层到序列

这个例子展示了如何实例化一个层,该层对序列中的每个元素应用相同的密集操作。在这里,output_shape有两个值(因为输出中有两个非批量维度);output_shape中的第一个维度是None,因为序列维度b的形状是未知的。

在处理序列数据时,如文本或时间序列,我们经常需要对序列中的每个时间步或元素应用相同的操作。在神经网络中,这通常通过使用某种形式的循环(如RNN)或变换层(如TimeDistributed层与Dense层的组合)来实现。

然而,如果你的目的是简单地对序列中的每个元素应用一个独立的密集层(即没有跨时间步的权重共享),你可以通过自定义层或使用现有的层(如TimeDistributed)来实现。

TimeDistributed层中,你将一个层(如Dense层)包装起来,并指定该层应该对输入数据的每个时间步或元素进行独立操作。这在你希望对序列中的每个元素执行相同操作,但不想跨时间步共享权重时很有用。

>>> layer = keras.layers.EinsumDense("abc,cd->abd",
...                                       output_shape=(None, 64),
...                                       bias_axes="d")
>>> input_tensor = keras.Input(shape=[32, 128])
>>> output_tensor = layer(input_tensor)
>>> output_tensor.shape
(None, 32, 64)

使用省略号将密集层应用于序列

这个示例展示了如何实例化一个层,该层对序列中的每个元素应用相同的密集操作,但使用省略号(ellipsis)符号而不是指定批次和序列维度。

由于我们使用了省略号符号并且只指定了一个轴,output_shape参数是一个单一的值。以这种方式实例化时,该层可以处理任何数量的序列维度——包括没有序列维度的情况。

在Keras和其他深度学习框架中,省略号(...)通常用于表示多个维度,这些维度在特定的操作中是未知的或不需要明确指定的。在定义层或函数时,它们为层提供了一种处理不同维度数据的方法,同时仍然能够执行期望的计算。

在序列处理中,序列可能具有不同的维度,包括批次大小、时间步长、特征数量等。通过使用省略号,我们可以定义一个层,该层可以应用于具有不同维度组合的序列,而无需为每个可能的维度组合定义单独的层。

在定义这种层时,output_shape参数仅需要指定除了批次和序列维度之外的其他维度的大小。这是因为省略号会自动处理批次和序列维度,而output_shape则指定了这些维度之外的其他维度的大小。

例如,如果我们有一个具有形状(batch_size, sequence_length, input_dim)的输入序列,并且我们想要应用一个具有output_dim输出的密集层到序列中的每个元素,我们可以使用省略号来定义层,并将output_shape设置为(output_dim,)。这样,无论批次大小或序列长度如何变化,该层都能够正确地处理输入并产生具有形状(batch_size, sequence_length, output_dim)的输出。

>>> layer = keras.layers.EinsumDense("...x,xy->...y",
...                                       output_shape=64,
...                                       bias_axes="y")
>>> input_tensor = keras.Input(shape=[32, 128])
>>> output_tensor = layer(input_tensor)
>>> output_tensor.shape
(None, 32, 64)

5、激活层

Activation layer 在神经网络中通常被称为激活层

在深度学习和神经网络中,激活层(Activation layer)是一个重要的组成部分,它负责将非线性特性引入到网络中。激活函数(Activation function)通常被应用于激活层,它们决定了神经元是否应该被激活(即“点火”或输出一个值到网络的下一层)。没有激活函数,神经网络将只能执行线性变换,这大大限制了其建模复杂数据关系的能力。

常见的激活函数包括 Sigmoid、ReLU(Rectified Linear Unit,修正线性单元)、Tanh 等。这些函数在神经网络的不同层中扮演着关键角色,帮助网络学习并识别数据中的复杂模式。

keras.layers.Activation(activation, **kwargs)

应用激活函数到输出。

参数

  • activation: 激活函数。它可以是一个可调用的对象,或者是从keras.activations命名空间中指定的激活函数名称。
  • **kwargs: 基础层的关键字参数,如namedtype

激活层的主要功能是对其输入(通常是某个层的输出)应用一个激活函数。这个激活函数可以通过两种方式指定:一种是直接传递一个可调用的对象(如一个Python函数),另一种是传递一个在keras.activations命名空间内定义的激活函数的名称(如字符串'relu')。此外,该层还支持其他基础层的关键字参数,如层的名称(name)和数据类型(dtype)。这些参数可以通过**kwargs语法进行传递。

>> layer = keras.layers.Activation('relu')
>>> layer([-3.0, -1.0, 0.0, 2.0])
[0.0, 0.0, 0.0, 2.0]
>>> layer = keras.layers.Activation(keras.activations.relu)
>>> layer([-3.0, -1.0, 0.0, 2.0])
[0.0, 0.0, 0.0, 2.0]

6、 嵌入层

Embedding layer 在神经网络中通常被称为嵌入层

在深度学习和自然语言处理(NLP)中,嵌入层(Embedding layer)是一个常用的组件,它用于将离散的类别数据(如单词、标签等)转换为连续向量表示。这种表示方式通常被称为嵌入(embeddings),并且它们可以捕获数据中的语义和上下文信息。

嵌入层通常接收一个整数索引的输入(例如,一个表示单词在词汇表中的位置的整数),并输出一个固定大小的向量。这个向量是在训练过程中学习得到的,可以捕获单词的语义含义,使得相似的单词在向量空间中具有相似的表示。

在NLP任务中,嵌入层通常位于模型的开始部分,用于将文本数据转换为模型可以处理的数值表示。通过嵌入层,模型可以充分利用词汇表中的语义信息,并在后续层中进一步处理这些嵌入向量以完成特定任务。

keras.layers.Embedding(
    input_dim,
    output_dim,
    embeddings_initializer="uniform",
    embeddings_regularizer=None,
    embeddings_constraint=None,
    mask_zero=False,
    weights=None,
    lora_rank=None,
    **kwargs
)

这个层将正整数(索引)转换为固定大小的密集向量。

例如:[[4], [20]] -> [[0.25, 0.1], [0.6, -0.2]]

这个层只能用于固定范围内的正整数输入。

嵌入层在深度学习模型中很常见,特别是在处理如文本数据这样的离散型数据时。它将每个唯一的索引(在这里是正整数)映射到一个固定大小的密集向量(即一个实数数组)。

在这个例子中,[[4], [20]] 是索引的输入,而 [[0.25, 0.1], [0.6, -0.2]] 是对应的嵌入向量。这些嵌入向量是模型在训练过程中学习到的,它们捕获了索引之间的某种关系或语义。

“这个层只能用于固定范围内的正整数输入”意味着嵌入层只能映射那些事先定义好的、在固定范围内的索引。例如,如果你有一个包含10000个唯一单词的词汇表,那么嵌入层将能够映射从0到9999的整数索引。如果你尝试输入一个超出这个范围的索引(如10000或负数),那么将会出现错误。

>>> model = keras.Sequential()
>>> model.add(keras.layers.Embedding(1000, 64))
>>> # The model will take as input an integer matrix of size (batch,
>>> # input_length), and the largest integer (i.e. word index) in the input
>>> # should be no larger than 999 (vocabulary size).
>>> # Now model.output_shape is (None, 10, 64), where `None` is the batch
>>> # dimension.
>>> input_array = np.random.randint(1000, size=(32, 10))
>>> model.compile('rmsprop', 'mse')
>>> output_array = model.predict(input_array)
>>> print(output_array.shape)
(32, 10, 64)

参数

  • input_dim: 整数。词汇表的大小,即最大整数索引加1。
  • output_dim: 整数。密集嵌入的维度。
  • embeddings_initializer: 嵌入矩阵的初始化器(参见keras.initializers)。
  • embeddings_regularizer: 应用于嵌入矩阵的正则化函数(参见keras.regularizers)。
  • embeddings_constraint: 应用于嵌入矩阵的约束函数(参见keras.constraints)。
  • mask_zero: 布尔值,是否将输入值0视为特殊的“填充”值并应被屏蔽。这在使用可能需要可变长度输入的循环层时很有用。如果设置为True,则模型中的所有后续层都需要支持屏蔽,否则将引发异常。如果mask_zero设置为True,作为结果,索引0不能在词汇表中使用(input_dim应该等于词汇表大小加1)。
  • weights: 可选的浮点型矩阵,大小为(input_dim, output_dim)。要使用的初始嵌入值。
  • lora_rank: 可选的整数。如果设置,该层的正向传递将使用提供的秩实现LoRA(Low-Rank Adaptation)。LoRA将层的嵌入矩阵设置为不可训练,并通过乘以两个较低秩的可训练矩阵来替换它,以获得对原始矩阵的增量。这可以用于减少微调大型嵌入层的计算成本。您也可以通过调用layer.enable_lora(rank)在现有嵌入层上启用LoRA。

输入形状

二维张量,形状为:(batch_size, input_length)。

输出形状

三维张量,形状为:(batch_size, input_length, output_dim)。

7、掩码层

在神经网络中,特别是处理序列数据时(如文本数据),掩码层(Masking layer)被用来跳过填充(padding)的部分,因为填充部分在模型中通常不包含有用的信息。通过掩码层,模型可以只关注那些非填充的、真正包含信息的部分。这在处理变长序列数据时非常有用,因为通常需要确保所有序列具有相同的长度以便能够批处理,而较短的序列通常会用特定的值(如0)进行填充。

keras.layers.Masking(mask_value=0.0, **kwargs)

掩码层通过使用一个掩码值来跳过时间步。

对于输入张量中的每个时间步(张量的第1维),如果该时间步上输入张量中的所有值都等于掩码值,那么该时间步将在所有下游层中被掩码(跳过),只要这些层支持掩码操作。

如果任何下游层不支持掩码操作但接收了这样的输入掩码,则会引发异常。

示例

考虑一个形状为 (samples, timesteps, features) 的 NumPy 数据数组 x,它将被馈送到 LSTM 层。你想要掩码时间步 #3 和 #5,因为你缺少这些时间步的数据。你可以:

将 x[:, 3, :] 设置为 0,并将 x[:, 5, :] 也设置为 0。
在 LSTM 层之前插入一个掩码层,并设置 mask_value=0。

samples, timesteps, features = 32, 10, 8
inputs = np.random.random([samples, timesteps, features]).astype(np.float32)
inputs[:, 3, :] = 0.
inputs[:, 5, :] = 0.

model = keras.models.Sequential()
model.add(keras.layers.Masking(mask_value=0.)
model.add(keras.layers.LSTM(32))
output = model(inputs)
# The time step 3 and 5 will be skipped from LSTM calculation.

注意:在Keras的掩码约定中,被掩码的时间步用False作为掩码值来表示,而非掩码(即可用)的时间步则用True作为掩码值来表示。

8、匿名函数层

在 Keras 中,Lambda 层允许你定义一个简单的、无状态的层,该层封装了一个任意的表达式或函数。这在你需要应用一个简单的操作,但又不想编写整个层的代码时特别有用。Lambda 层主要用于将任何表达式封装为一个层对象,该对象可以像其他 Keras 层一样被包含在模型中。

keras.layers.Lambda(function, output_shape=None, mask=None, arguments=None, **kwargs)

将任意表达式包装为 Layer 对象。

Lambda 层存在是为了在构建 Sequential 和 Functional API 模型时,能够将任意表达式用作 Layer。Lambda 层最适合用于简单的操作或快速实验。对于更高级的使用场景,建议编写 Layer 的新子类。

警告:Lambda 层具有(反)序列化限制!

不使用 Lambda 层而继承 Layer 的主要原因是保存和检查模型。Lambda 层是通过序列化 Python 字节码来保存的,这从根本上说是不具备可移植性的,且可能不安全。它们应该只在保存它们的环境中加载。通过重写它们的 get_config() 方法,继承的层可以以更可移植的方式保存。依赖继承层的模型也通常更容易进行可视化和推理。
示例

# add a x -> x^2 layer
model.add(Lambda(lambda x: x ** 2))

参数

  • function 参数指的是定义在 Lambda 层中要执行的具体函数或表达式。
  • output_shape 参数允许你指定函数输出的预期形状。如果未指定,则 Keras 会尝试推断它。你可以提供一个元组来指定除第一个维度(通常是样本数量)以外的形状,或者提供一个函数来根据输入形状计算完整的输出形状。
  • mask 参数用于处理掩码,这在处理变长序列或包含缺失数据的序列时很有用。你可以提供一个掩码函数或张量,以在 Lambda 层的输出上应用掩码。
  • arguments 参数允许你向函数传递额外的关键字参数。这在需要向 Lambda 层中的函数传递配置或超参数时很有用。

9、恒等层

在神经网络中,恒等层(Identity layer)是一个特殊的层,它不会对输入数据进行任何变换或修改,直接将其传递给输出。换句话说,对于恒等层,其输出与输入完全相同。这种层在神经网络中可能看起来没有实际的计算作用,但在某些情况下,如残差网络(Residual Networks)中,它们被用作跳跃连接(skip connection),帮助信息在网络中更直接地传播,从而有助于训练更深的网络结构。

keras.layers.Identity(**kwargs)

恒等层应该用作占位符,当不需要执行任何操作时。该层只是将其输入参数作为输出返回。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

MUKAMO

你的鼓励是我们创作最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值