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.
input_dim: vocal_size = 50 字典的大小,因为我们在进行one-hot编码时,每个词都是由 0-49 中的某个值作为索引值, 因而字典大小为 49+1 = 50;
input_length: 4, 如果输入的向量长度不足为4,就会给向量填充0,直到向量长度为4。
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
其实里面还有很多方法,可以参考该博客。
参考
- http://frankchen.xyz/2017/12/18/How-to-Use-Word-Embedding-Layers-for-Deep-Learning-with-Keras/
- https://machinelearningmastery.com/use-word-embedding-layers-deep-learning-keras/
来源:oschina
链接:https://my.oschina.net/u/4386991/blog/4289782