Keras 文档并不清楚这实际上是什么。我知道我们可以使用它来将输入特征空间压缩成一个更小的空间。但是从神经设计的角度来看,这是如何做到的呢?它是自动编码器,RBM 吗?
据我所知,Embedding 层是一个简单的矩阵乘法,将单词转换为对应的单词嵌入。
Embedding 层的权重是形状 (vocabulary_size, embedding_dimension)。对于每个训练样本,它的输入都是整数,代表某些单词。整数在词汇表大小的范围内。嵌入层将每个整数 i 转换为嵌入权重矩阵的第 i 行。
为了以矩阵乘法的形式快速执行此操作,输入整数不存储为整数列表,而是存储为 one-hot 矩阵。因此,输入形状是 (nb_words, words_size),每行有一个非零值。如果将其乘以嵌入权重,您将得到形状中的输出
(nb_words, vocab_size) x (vocab_size, embedding_dim) = (nb_words, embedding_dim)
因此,通过简单的矩阵乘法,您可以将样本中的所有单词转换为相应的单词嵌入。
Keras
Embedding
层不执行任何矩阵乘法,而只是:
1. 创建一个 (vocabulary_size)x(embedding_dimension) 维度的权重矩阵
2.索引这个权重矩阵
查看源代码以了解类的作用总是很有用的。在这种情况下,我们将看看从名为 Layer 的基础层 class
继承的 class
Embedding。
(1) - 创建 (vocabulary_size)x(embedding_dimension) 维度的权重矩阵:
这发生在 Embedding 的 build
函数中:
def build(self, input_shape):
self.embeddings = self.add_weight(
shape=(self.input_dim, self.output_dim),
initializer=self.embeddings_initializer,
name='embeddings',
regularizer=self.embeddings_regularizer,
constraint=self.embeddings_constraint,
dtype=self.dtype)
self.built = True
如果您查看基类 Layer,您会发现上面的函数 add_weight
只是创建了一个可训练权重矩阵(在本例中为 (vocabulary_size)x(embedding_dimension) 维度) :
def add_weight(self,
name,
shape,
dtype=None,
initializer=None,
regularizer=None,
trainable=True,
constraint=None):
"""Adds a weight variable to the layer.
# Arguments
name: String, the name for the weight variable.
shape: The shape tuple of the weight.
dtype: The dtype of the weight.
initializer: An Initializer instance (callable).
regularizer: An optional Regularizer instance.
trainable: A boolean, whether the weight should
be trained via backprop or not (assuming
that the layer itself is also trainable).
constraint: An optional Constraint instance.
# Returns
The created weight variable.
"""
initializer = initializers.get(initializer)
if dtype is None:
dtype = K.floatx()
weight = K.variable(initializer(shape),
dtype=dtype,
name=name,
constraint=constraint)
if regularizer is not None:
with K.name_scope('weight_regularizer'):
self.add_loss(regularizer(weight))
if trainable:
self._trainable_weights.append(weight)
else:
self._non_trainable_weights.append(weight)
return weight
(2) - 索引这个权重矩阵
这发生在 Embedding 的 call
函数中:
def call(self, inputs):
if K.dtype(inputs) != 'int32':
inputs = K.cast(inputs, 'int32')
out = K.gather(self.embeddings, inputs)
return out
此函数返回 Embedding
层的输出,即 K.gather(self.embeddings, inputs)
。 tf.keras.backend.gather 的确切作用是根据应该是正整数列表的 inputs
索引权重矩阵 self.embeddings
(参见上面的 build
函数)。
可以检索这些列表,例如,如果您将文本/单词输入传递给 Keras 的 one_hot 函数,该函数将文本编码为大小为 n 的单词索引列表(这不是一种热编码 - 另请参阅此示例以获取更多信息信息:https://machinelearningmastery.com/use-word-embedding-layers-deep-learning-keras/)。
因此,仅此而已。没有矩阵乘法。
相反,Keras
Embedding
层之所以有用,是因为它恰好避免了执行矩阵乘法,因此它节省了一些计算资源。
否则,您可以只使用 Keras
Dense 层(在对输入数据进行编码之后)来获得可训练权重矩阵((vocabulary_size)x(embedding_dimension) 维度),然后只需进行乘法运算即可得到与 Embedding
层的输出完全相同的输出。
在 Keras 中,Embedding
层不是简单的矩阵乘法层,而是一个查找表层(参见下面的调用函数或原始的 definition)。
def call(self, inputs):
if K.dtype(inputs) != 'int32':
inputs = K.cast(inputs, 'int32')
out = K.gather(self.embeddings, inputs)
return out
它所做的是将inputs
中的每个已知整数n
映射到一个可训练的特征向量W[n]
,其维度就是所谓的嵌入特征长度。
Embedding
层是确实是一个矩阵乘法。
简单来说(从功能的角度来看),它是一个单热编码器和全连接层。层权重是可训练的。