嵌入层和密集层有什么区别?

What is the difference between an Embedding Layer and a Dense Layer?

Keras 中 Embedding Layer 的文档说:

Turns positive integers (indexes) into dense vectors of fixed size. eg. [[4], [20]] -> [[0.25, 0.1], [0.6, -0.2]]

我相信这也可以通过将输入编码为长度 vocabulary_size 的单热向量并将它们输入 Dense Layer.

来实现

嵌入层仅仅是为这个两步过程提供便利,还是在幕后发生了一些更奇妙的事情?

从数学上讲,区别是这样的:

  • 嵌入层执行select操作。在keras中,这一层相当于:

    K.gather(self.embeddings, inputs)      # just one matrix
    
  • 密集层执行点积操作,加上可选的激活:

    outputs = matmul(inputs, self.kernel)  # a kernel matrix
    outputs = bias_add(outputs, self.bias) # a bias vector
    return self.activation(outputs)        # an activation function
    

您可以通过单热编码模拟具有全连接层的嵌入层,但密集嵌入的重点是避免 单热表示。在 NLP 中,单词词汇量可以达到 100k(有时甚至是一百万)的数量级。最重要的是,通常需要批量处理单词序列。处理一批单词索引序列比一批单热向量序列更有效率。此外,gather 操作本身比矩阵点积更快,无论是前向还是后向传播。

嵌入层更快,因为它本质上等同于进行简化假设的密集层。

想象一个具有这些权重的词到嵌入层:

w = [[0.1, 0.2, 0.3, 0.4],
     [0.5, 0.6, 0.7, 0.8],
     [0.9, 0.0, 0.1, 0.2]]

A Dense 层会将这些视为 执行矩阵乘法的实际权重 。嵌入层会将这些权重简单地视为一个向量列表,每个向量代表一个词;词汇表中的第 0 个单词是 w[0],第一个单词是 w[1],等等


举个例子,使用上面的权重和这句话:

[0, 2, 1, 2]

一个天真的基于 Dense 的网络需要将该句子转换为 1-hot 编码

[[1, 0, 0],
 [0, 0, 1],
 [0, 1, 0],
 [0, 0, 1]]

然后做矩阵乘法

[[1 * 0.1 + 0 * 0.5 + 0 * 0.9, 1 * 0.2 + 0 * 0.6 + 0 * 0.0, 1 * 0.3 + 0 * 0.7 + 0 * 0.1, 1 * 0.4 + 0 * 0.8 + 0 * 0.2],
 [0 * 0.1 + 0 * 0.5 + 1 * 0.9, 0 * 0.2 + 0 * 0.6 + 1 * 0.0, 0 * 0.3 + 0 * 0.7 + 1 * 0.1, 0 * 0.4 + 0 * 0.8 + 1 * 0.2],
 [0 * 0.1 + 1 * 0.5 + 0 * 0.9, 0 * 0.2 + 1 * 0.6 + 0 * 0.0, 0 * 0.3 + 1 * 0.7 + 0 * 0.1, 0 * 0.4 + 1 * 0.8 + 0 * 0.2],
 [0 * 0.1 + 0 * 0.5 + 1 * 0.9, 0 * 0.2 + 0 * 0.6 + 1 * 0.0, 0 * 0.3 + 0 * 0.7 + 1 * 0.1, 0 * 0.4 + 0 * 0.8 + 1 * 0.2]]

=

[[0.1, 0.2, 0.3, 0.4],
 [0.9, 0.0, 0.1, 0.2],
 [0.5, 0.6, 0.7, 0.8],
 [0.9, 0.0, 0.1, 0.2]]

然而,Embedding 层简单地查看 [0, 2, 1, 2] 并在索引 0、2、1 和 2 处获取层的权重以立即得到

[w[0],
 w[2],
 w[1],
 w[2]]

=

[[0.1, 0.2, 0.3, 0.4],
 [0.9, 0.0, 0.1, 0.2],
 [0.5, 0.6, 0.7, 0.8],
 [0.9, 0.0, 0.1, 0.2]]

所以这是相同的结果,只是以一种希望更快的方式获得。


Embedding层确实有限制:

  • 输入必须是 [0, vocab_length) 范围内的整数。
  • 没有偏见。
  • 没有激活。

但是,如果您只想将整数编码的单词转换为嵌入,那么 none 这些限制应该很重要。

在这里,我想通过提供更多细节来改进投票答案:

当我们使用嵌入层时,通常是将one-hot个输入向量(稀疏的)减少为更密集的表示。

  1. 嵌入层很像 table 查找。 table小的时候速度快

  2. 当 table 很大时,table 查找会慢很多。实际上,在这种情况下,我们将使用密集层作为降维器来减少 one-hot 输入而不是嵌入层。