参考博客:
- https://spaces.ac.cn/archives/4122 (力荐)
embedding的作用大体上有两点:
- 降低one-hot编码带来的特征稀疏与维度过大的问题。
- 通过嵌入矩阵将one-hot编码的输入样例转换为非稀疏向量后,可以通过各种方法(余弦等方法)计算样例之间的相似度,便于理解。
one-hot编码矩阵的优点与问题:
对于由多个词组成的一个句子而言(英文句子),one-hot编码矩阵的shape为(词的数量,词表的长度),当词表特别大的时候,编码矩阵的列数就非常大,同时这样的矩阵是稀疏的。当然,这样也有优点,那就是在计算的时候会容易一些(大量的0,少量的1,计算方便)。但是它的维度太大了,同时大部分都是0,这就造成了存储空间的浪费。
嵌入矩阵的引入
如果有一种方法,能在保持甚至是降低计算量的同时,减少one-hot矩阵的维度与稀疏程度就好了。由此embedding被引入。嵌入矩阵的shape为(词表的长度, 自己定义的输出维度)。可以看到one-hot编码矩阵的列数与嵌入矩阵的行数一致。由此它们可以进行内积,如下图所示。
它们内积的结果就是embedding层的输出结果。在代码实现中,没有进行实际的矩阵运算,而是通过映射(查表)的方式实现。拿上面的例子来说,由于编码矩阵第一行的第一个元素是1,那么我们取右面嵌入矩阵的第一行,编码矩阵的第二行第二个元素是1,那么我们取右面嵌入矩阵的第二行。由此,我们避免了计算,同时降低了原矩阵的稀疏度和维度。
NLP中的word2vec
对于NLP来说,其word2vec有两种算法来进行embedding,这两篇博客讲的很好:
个人认为:
- 传统的embedding层是对每个特征训练一个与之对应的embedding层,通过训练得到的label与真实label来进行反馈。
- word2vec是拿全部语料进行训练,CBOW通过真实的中心词对应的V维向量(y_true:one hot向量,只有中心词对应的位置是1,其余都为0)与前向传播得到的V维向量(y_pred:softmax计算得到的各个类别的概率)进行反馈。同理,skip-gram是通过背景词对应的V维向量与前向传播得到的V维向量进行反馈。
看代码的是时候我发现,embedding层的输入都是词表的索引而不是编码好的one-hot矩阵,去官网看了下。
https://tensorflow.google.cn/api_docs/python/tf/keras/layers/Embedding?hl=en&version=stable
上面这么说的:
Turns positive integers (indexes) into dense vectors of fixed size.
所以,函数的输入维度为index的最大值+1,也就是转换后的one-hot矩阵的列数,输入长度一般为padding的长度(一般为maxlen)。
由word2vec训练好的词向量可以通过设置embedding层weights的方法来使用,同时trainable设置为False
from keras.layers import Embedding
embedding_layer = Embedding(len(word_index) + 1,
EMBEDDING_DIM,
weights=[embedding_matrix],
input_length=MAX_SEQUENCE_LENGTH,
trainable=False)