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
: 基础层的关键字参数,如name
和dtype
。
带偏置的密集层与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
: 基础层的关键字参数,如name
和dtype
。
激活层的主要功能是对其输入(通常是某个层的输出)应用一个激活函数。这个激活函数可以通过两种方式指定:一种是直接传递一个可调用的对象(如一个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)
恒等层应该用作占位符,当不需要执行任何操作时。该层只是将其输入参数作为输出返回。