Embedding

独自空忆成欢 提交于 2020-10-04 03:59:43

Embedding的含义

根据tf.keras.layers.Embedding的解释

是将正整数转换为固定长度的连续向量,它和one-hot编码的作用类似,都是对数据字符数值进行编码。

不同之处是,embedding是将一个单纯的数值转换成一个长度唯一的概率分布向量,再避免one-hot编码产生的特征稀疏性问题的同时,也能增加特征的描述。

需要注意的是,当embedding进行神经网络构建时,embedding层必须作为第一层对输入数据进行embedding处理。其配置的参数如下

  • input_dim: 配置字典的长度。embedding 是针对词频字典中的索引进行处理的,因此需要配置字典的长度。

  • output_dim: 配置神经网络层输出的维度。

  • embeddings_initializer: 配置embedding 矩阵的初始化

  • embeddings_regularizer: 配置embedding 矩阵的正则化方程

  • embedding_constraint: 配置embedding 的约束函数

  • mask_zero: 配置"0"是否为Padding的值,如果配置为True, 则将所有的"0"去掉

  • input_length: 配置输入语句的长度,将不足长度的用0填充。

    其中 input_dim 和 output_dim必须指定

当时,我看这个解释还是比较懵逼。

举个小例子吧,下面我们定义一个词汇表为200的嵌入层(例如从0到199的整数编码的字,包括0到199),一个32维的向量空间,其中将嵌入单词,以及输入文档,每个单词有50个单词。

e = Embedding(input_dim=200, output_dim=32, input_length=50)

Embedding的实例

下面方法基于keras, 在tensorflow中我们可以使用 tf.keras.preprocessing.text.Tokenizer这个API库下的相关方法

我们将定义一个小问题,我们有10个文本文档,每个文档都有一个学生提交的工作评论。每个文本文档被分类为正的“1”或负的“0”。这是一个简单的情感分析问题。

首先,我们将定义文档及其类别标签。

# define documents 定义文档
docs = ['Well done!',
		'Good work',
		'Great effort',
		'nice work',
		'Excellent!',
		'Weak',
		'Poor effort!',
		'not good',
		'poor work',
		'Could have done better.']
# define class labels 定义分类标签
labels = [1,1,1,1,1,0,0,0,0,0]

接下来,我们来整数编码每个文件。这意味着把输入,嵌入层将具有整数序列。我们可以尝试其他更复杂的bag of word 模型比如计数或TF-IDF。

Keras提供one_hot()函数来创建每个单词的散列作为一个有效的整数编码。我们用估计50的词汇表大小,这大大减少了hash函数的冲突概率。

# integer encode the documents 独热编码
vocab_size = 50
encoded_docs = [one_hot(d, vocab_size) for d in docs]
print(encoded_docs)

[[6, 16], [42, 24], [2, 17], [42, 24], [18], [17], [22, 17], [27, 42], [22, 24], [49, 46, 16, 34]]

每个词都能有0-49的一个索引值。例如 'well done' = [6, 16], 但是由于不同的序列有不同的长度,方便进行处理,因此我们需要进行padding, 将所有的输入序列的长度填充为4.

# pad documents to a max length of 4 words 将不足长度的用0填充为长度4
max_length = 4
padded_docs = pad_sequences(encoded_docs, maxlen=max_length, padding='post')
print(padded_docs)
[[ 6 16  0  0]
 [42 24  0  0]
 [ 2 17  0  0]
 [42 24  0  0]
 [18  0  0  0]
 [17  0  0  0]
 [22 17  0  0]
 [27 42  0  0]
 [22 24  0  0]
 [49 46 16 34]]

我们现在准备将我们的嵌入层定义为我们的神经网络模型的一部分。

嵌入的词汇量为50,输入长度为4,我们将选择一个8维的嵌入空间。

该模型是一个简单的二元分类模型。重要的是,嵌入层的输出将是每个8维的4个矢量,每个单词一个。我们将其平铺到一个32个元素的向量上以传递到密集输出层。

# define the model 定义模型
model = Sequential()
model.add(Embedding(vocab_size, 8, input_length=max_length))
model.add(Flatten())
model.add(Dense(1, activation='sigmoid'))
# compile the model 编译
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['acc'])
# summarize the model 打印模型信息
print(model.summary())

进行到这,我们一直需要关注的是在进行维度的转换,原本是10x4, 但是经过Embedding之后,其实会变成10x4x8.

  1. input_dim: vocal_size = 50 字典的大小,因为我们在进行one-hot编码时,每个词都是由 0-49 中的某个值作为索引值, 因而字典大小为 49+1 = 50;

  2. input_length: 4, 如果输入的向量长度不足为4,就会给向量填充0,直到向量长度为4。

  3. output_dim: 8 每个词的向量长度,在embedding之前,每个词只有一个索引值,因此我们需要将每个词的索 引转变成一个长度为8的概率分布向量。(10x4x1 => 10x4x8)

    此处我们可以明白embedding到底做了什么,举个栗子:

    ​ “公主”和“王妃” 使用One-hot编码 可能会得到: 公主 [1 0] 王妃 [0 1] 。

    ​ One-hot编码能够得到完全独立的两个01向量,并且当如果文本集够大,那就会导致稀疏矩阵。

    ​ 由于两者完全独立,表达关联特征的能力几乎就为0, 但王妃和公主是有关系,最基本的她们都是女性。

    ​ 而embedding会将one-hot编码(稀疏态),通过一些线性变换,让相互独立向量变成了有内在联系的关系 向量(密集态)即可能 公主 [ 1.0 0.25] 王妃 [ 0.6 0.75] 。


补充

使用tensorflow2.0中的tokenizer进行处理时。

# 定义word2vec的函数,通过统计所有训练集中的字符出现频率,构建字典,并使用字典中的码值对训练集中的语句进行替换
def tokenize(lang):
    # 使用高阶API tf.keras.preprocessing.text.Tokenize实例化一个转换器,构建字典并使用字典中的码值对训练集中的语句进行替换, oov_token: 配置不在字典中的字符的替换数字,一般使用“3”来替换。
    lang_tokenizer = tf.keras.preprocessing.text.Tokenizer(num_words=gConfig['enc_vocab_size'], oov_token=3)
    # 使用fit_on_texts方法对训练数据进行处理,构建字典
    lang_tokenizer.fit_on_texts(lang)
    # 转换器使用已经构建好的字典,将训练集的数据全部替换为字典的码值
    tensor = lang_tokenizer.texts_to_sequences(lang)
    # 为了提高计算效率,将训练语句的长度统一补全
    tensor = tf.keras.preprocessing.sequence.pad_sequences(tensor, padding='post')

    return tensor, lang_tokenizer

其实里面还有很多方法,可以参考该博客

参考

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!