自己之前学习了一波word2vec的词向量&&神经网络的embedding,关于这2者的原理和实践,可以参看我之前的博客:

* 利用神经网络的embedding层处理类别特征
<https://blog.csdn.net/anshuai_aw1/article/details/83586404>
* (一)理解word2vec:原理篇
<https://blog.csdn.net/anshuai_aw1/article/details/84241279>
* (二)理解word2vec:实践篇
<https://blog.csdn.net/anshuai_aw1/article/details/84305238>
这篇文章的主题是分析word2vec的词向量&&神经网络的embedding层的关系,以及在实际中,如何同时应用它们。


第一章主要介绍二者之间的关系,第二章给出一个利用NN的embedding层来学习句子的词嵌入的例子,第三章给出一个如何结合二者的例子:即用word2vec学到的词嵌入作为NN的embedding层的初始权重。

一、word2vec的词向量&&神经网络的embedding层

1.1 word embedding

word embedding我在这里就不多做介绍了。具体可以参看我上面的链接。

1.2 Keras embedding layer

关于神经网络的embedding layer,我这里再多说几句,翻译自参考文献【1】。我以Keras嵌入层为例:

Keras提供了一个嵌入 <https://keras.io/layers/embeddings/#embedding>
层,可用于处理文本数据的神经网络。它要求输入数据进行整数编码,以便每个单词都由唯一的整数表示。该数据准备步骤可以使用提供有Keras的Tokenizer API

<https://machinelearningmastery.com/use-word-embedding-layers-deep-learning-keras/#%20https://keras.io/preprocessing/text/%23tokenizer>
来执行。

嵌入层使用随机权重初始化,并将学习数据集中所有词的嵌入。

它是一个灵活的层,可以以各种方式使用,如:

* 它可以单独使用来学习一个字嵌入,以后可以在另一个模型中使用。
* 它可以用作深度学习模型的一部分,其中嵌入与模型本身一起被学习。
* 它可以用于加载训练好的词嵌入模型,一种迁移学习。
嵌入层被定义为网络的第一个隐藏层。它必须指定3个参数:

1.input_dim:这是文本数据中词汇的大小。例如,如果你的数据是整数编码为0-10之间的值,则词表的大小将为11个字。

2.output_dim:这是嵌入单词的向量空间的大小。它为每个单词定义了该层的输出向量的大小。例如,它可以是32或100甚至更大。根据你的问题来定。

3.input_length
:这是输入序列的长度,正如你为Keras模型的任何输入层定义的那样。例如,如果你的所有输入句子里最多包含1000个单词,则为1000。

例如,下面我们定义一个词汇量为200的嵌入层(例如,从0到199(包括整数)的整数编码单词),将词嵌入到32维的向量空间中,以及每次输入50个单词的输入句子。
e = Embedding(200, 32, input_length=50)

嵌入层的输出是一个三维向量(x,y,z)。x代表有多少个句子(样本),y代表这个句子的长度(即有多少个词,句子的词长度需要人为统一)这里指50;z代表嵌入后的设定向量维度,这里指32。

如果希望连接密集(dense)层直接到嵌入层,必须首先将y和z压缩到一行,形成一个(x,yz)的2D矩阵。

1.3 二者之间的关系

不论是word2vec还是NN的embedding layer,我们都可以学到词的稠密的嵌入表示。只不过二者学习的方式不一样。


word2vec是无监督的学习方式,利用上下文环境来学习词的嵌入表示,因此可以学到相关词;而神经网络的嵌入层权重的更新是基于标签的信息进行学习,为了达到较高的监督学习的效果,它本身也可能会学到相关词。

我在1.2中标红那里说到,NN的embedding layer可用于加载训练好的词嵌入模型,这句话通俗来解释就是:先用word2vec学到词向量,然后作为
NN的embedding layer的初始权重,而不是用随机的初始化权重。这也是业界常用的一个手段。我在第3章会给出这么一个例子。

二、利用NN的embedding层学习句子的词嵌入的例子

现在,我们来看看我们如何在实践中使用嵌入层。翻译自参考文献1。

我们定义一个小问题,我们有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]
由于神经网络无法认识str类型的输入,接下来我们对每个句子进行整数编码,作为输入,嵌入层将具有整数序列。

Keras <https://keras.io/preprocessing/text/#one_hot>提供one_hot()函数
<https://keras.io/preprocessing/text/#one_hot>
,它创建每个单词的哈希值作为有效的整数编码。我们估计有20个词汇大小,这远远大于减少哈希函数碰撞概率所需的大小。如果你的词汇很多的话,我建议你使用这个函数。
# integer encode the documents vocab_size = 50 encoded_docs = [one_hot(d,
vocab_size) for d in docs] print(encoded_docs) ''' [[6, 19], [7, 5], [19, 5],
[14, 5], [10], [17], [4, 5], [1, 7], [4, 5], [4, 11, 19, 17]] '''
句子具有不同的长度,且Keras需要输入具有相同的长度。因为最大的句子的单词个数为4,我们将所有输入句子的长度设为4。再次,我们可以使用内置的Keras
<https://keras.io/preprocessing/sequence/#pad_sequences>函数(在这种情况下为
pad_sequences()来执行此操作 <https://keras.io/preprocessing/sequence/#pad_sequences>)。
# pad documents to a max length of 4 words max_length = 4
padded_docs=pad_sequences(encoded_docs,maxlen=max_length, padding='post')
print(padded_docs) ''' [[ 6 19 0 0] [ 7 5 0 0] [19 5 0 0] [14 5 0 0] [10 0 0 0]
[17 0 0 0] [ 4 5 0 0] [ 1 7 0 0] [ 4 5 0 0] [ 4 11 19 17]] ''' '''
我们可以看到短的句子给补零了。 '''
我们现在可以将我们的嵌入层定义为神经网络模型的一部分。该嵌入具有50词汇及输入长度为4,我们将选择8尺寸的嵌入空间。


该模型是一个简单的二分类模型。重要的是,嵌入层的输出将是4个向量,每个向量8个维度,每个单词一个。我们将其平坦化为一个32维度的向量,以传递到Dense输出层。
# 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()) '''
______________________________________________________________ Layer (type)
Output Shape Param #
=========================================================== embedding_1
(Embedding) (None, 4, 8) 400
______________________________________________________________ flatten_1
(Flatten) (None, 32) 0
______________________________________________________________ dense_1 (Dense)
(None, 1) 33 =========================================================== Total
params: 433 Trainable params: 433 Non-trainable params: 0 '''
embedding_1 (Embedding) 的shape为  (None, 4,
8),None代表的是样本个数,即不确定的值。对于一个句子来说,可以看出,embedding层将(1,
4)的一个输入sample(最长为4个单词的句子,其中每个单词表示为一个int数字),嵌入为一个(1, 4,
8)的向量,即将每个单词embed为一个8维的向量,而整个embedding层的参数就由神经网络学习得到,数据经过embedding层之后就方便地
转换为了可以由CNN或者RNN进一步处理的格式(本例中,只是直接接了个Dense层)。

最后,我们可以训练和评估该分类模型。
# fit the model model.fit(padded_docs, labels, epochs=50, verbose=0) #
evaluate the model loss, accuracy = model.evaluate(padded_docs, labels,
verbose=0) print('Accuracy: %f' % (accuracy*100)) ''' Accuracy: 100.00 '''
完整的代码清单如下:这个例子就展示了我们如何输入一些句子来用神经网络进行分类。
from keras.preprocessing.text import one_hot from keras.preprocessing.sequence
import pad_sequences from keras.models import Sequential from keras.layers
import Dense from keras.layers import Flatten from keras.layers.embeddings
import Embedding # 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] # integer encode the documents vocab_size = 20
encoded_docs = [one_hot(d, vocab_size) for d in docs] print(encoded_docs) # pad
documents to a max length of 4 words max_length = 4 padded_docs =
pad_sequences(encoded_docs, maxlen=max_length, padding='post')
print(padded_docs) # 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()) # fit the model
model.fit(padded_docs, labels, epochs=50, verbose=0) # evaluate the model loss,
accuracy = model.evaluate(padded_docs, labels, verbose=0) print('Accuracy: %f'
% (accuracy*100))
对于上面的代码,我们进一步分析以下embedding layer输出的结果:
from keras.models import Model weight = model.get_weights() #
'embedding_1'是嵌入层的名字 intermediate_layer_model = Model(inputs=model.input,
outputs=model.get_layer('embedding_1').output) intermediate_output =
intermediate_layer_model.predict(padded_docs) '''
intermediate_output的shape即为(10,4,8),10代表句子个数,4代表句子的长度,8代表嵌入的维度。与之前我们描述得一致。 '''
embeddding后的结果其实是一个索引的结果表。关于这一点,在《利用神经网络的embedding层处理类别特征
<https://blog.csdn.net/anshuai_aw1/article/details/83586404>
》中我详细地探讨过,有兴趣的读者可以去看看。

三、结合word2vec的词向量和神经网络的embedding层

在这里,我们应用一个已经训练好的词嵌入的数据集GloVe词向量 <http://nlp.stanford.edu/projects/glove/>
作为神经网络的embedding层的输入。GloVe 是 "Global Vectors for Word
Representation"的缩写,一种基于共现矩阵分解的词向量。本文所使用的GloVe词向量是在2014年的英文维基百科上训练的,有400k个不同的词,每个词用(50,100,150,200)维向量表示。
点此下载 <http://nlp.stanford.edu/data/glove.6B.zip> (友情提示,词向量文件大小约为822M)。本文的示例来自
Keras
<https://github.com/fchollet/keras/blob/master/examples/pretrained_word_embeddings.py>
项目中的示例:pretrained_word_embeddings.py
<https://github.com/fchollet/keras/blob/master/examples/pretrained_word_embeddings.py>

下载和解压缩后,你将看到一些文件,其中之一是“glove.6B.100d.txt”,其中包含一个100维版本的嵌入。

如果认真看文件,则会在每行上看到单词,后跟权重(100个数字)。例如,下面是嵌入的ASCII文本文件的第一行,显示“the”的嵌入。



我们仍然用第二章的例子作为我们的训练集。与第二章同样的操作:区别我们用Tokenizer()来数字化句子里的单词。
# 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] #
prepare tokenizer t = Tokenizer() t.fit_on_texts(docs) vocab_size =
len(t.word_index) + 1 # integer encode the documents encoded_docs =
t.texts_to_sequences(docs) print(encoded_docs) # pad documents to a max length
of 4 words max_length = 4 padded_docs = pad_sequences(encoded_docs,
maxlen=max_length, padding='post') print(padded_docs) ''' [[6, 2], [3, 1], [7,
4], [8, 1], [9], [10], [5, 4], [11, 3], [5, 1], [12, 13, 2, 14]] [[ 6 2 0 0] [
3 1 0 0] [ 7 4 0 0] [ 8 1 0 0] [ 9 0 0 0] [10 0 0 0] [ 5 4 0 0] [11 3 0 0] [ 5
1 0 0] [12 13 2 14]] '''
接下来,我们对GloVe词向量 <http://nlp.stanford.edu/projects/glove/>
进行处理,形成一个词典,key为单词,value为embedding的值:
# load the whole embedding into memory embeddings_index = dict() f =
open('datasets\glove.6B\glove.6B.100d.txt', 'rb') for line in f: values =
line.split() word = values[0] coefs = asarray(values[1:], dtype='float32') #
数据集中的word都是bytes类型,必须将bytes类型的word转为str。转换代码:str(word, encoding='utf-8')
embeddings_index[str(word, encoding='utf-8')] = coefs f.close() print('Loaded
%s word vectors.' % len(embeddings_index)) ''' Loaded 400000 word vectors. '''
然后,我们把训练集的单词从embeddings_index遍历,作为embedding层输入的权重的初始化。
# create a weight matrix for words in training docs embedding_matrix =
zeros((vocab_size, 100)) for word, i in t.word_index.items(): embedding_vector
= embeddings_index.get(word) if embedding_vector is not None:
embedding_matrix[i] = embedding_vector
现在我们可以像以前那样定义我们的模型,训练和评估。

关键区别是嵌入层可是用GloVe字嵌入权重进行迁移。我们选择了100维版本,
因此嵌入层必须用output_dim定义为100。最后,我们不更新此模型中学习的单词权重,因此我们将将模型的可训练属性设置为False。
e= Embedding(vocab_size, 100, weights=[embedding_matrix], input_length=4,
trainable=False)
完整的工作代码如下所示:
from numpy import asarray from numpy import zeros from
keras.preprocessing.text import Tokenizer from keras.preprocessing.sequence
import pad_sequences from keras.models import Sequential from keras.layers
import Dense from keras.layers import Flatten from keras.layers import
Embedding # 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]
# prepare tokenizer t = Tokenizer() t.fit_on_texts(docs) vocab_size =
len(t.word_index) + 1 # integer encode the documents encoded_docs =
t.texts_to_sequences(docs) print(encoded_docs) # pad documents to a max length
of 4 words max_length = 4 padded_docs = pad_sequences(encoded_docs,
maxlen=max_length, padding='post') print(padded_docs) # load the whole
embedding into memory embeddings_index = dict() f =
open('datasets\glove.6B\glove.6B.100d.txt', 'rb') for line in f: values =
line.split() word = values[0] coefs = asarray(values[1:], dtype='float32') #
数据集中的word都是bytes类型,必须将bytes类型的word转为str。转换代码:str(word, encoding='utf-8')
embeddings_index[str(word, encoding='utf-8')] = coefs f.close() print('Loaded
%s word vectors.' % len(embeddings_index)) # create a weight matrix for words
in training docs embedding_matrix = zeros((vocab_size, 100)) for word, i in
t.word_index.items(): embedding_vector = embeddings_index.get(word) if
embedding_vector is not None: embedding_matrix[i] = embedding_vector # define
model model = Sequential() e = Embedding(vocab_size, 100,
weights=[embedding_matrix], input_length=4, trainable=False) model.add(e)
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()) # fit the model
model.fit(padded_docs, labels, epochs=50, verbose=0) # evaluate the model loss,
accuracy = model.evaluate(padded_docs, labels, verbose=0) print('Accuracy: %f'
% (accuracy*100)) ''' Accuracy: 100.000000 '''
我们进一步分析将模型的可训练属性设置为False时,embedding的权重是否发生了改变。
weight = model.get_weights()
经过测试,可以发现:


weight[0]是embedding层的权重。与embedding_matrix对比,可以发现,当设置trainable=False时初始时的权值没有改变。当设置trainable=True时,初始时的权值在训练时更新了。

参考文献

【1】How to Use Word Embedding Layers for Deep Learning with Keras
<https://machinelearningmastery.com/use-word-embedding-layers-deep-learning-keras/>

【2】两个小例子带你词嵌入层学习入门——Keras版 <https://yq.aliyun.com/articles/221681>

【3】在Keras模型中使用预训练的词向量
<https://github.com/MoyanZitto/keras-cn/blob/master/docs/legacy/blog/word_embedding.md>

【4】字词的向量表示法 <https://www.tensorflow.org/tutorials/representation/word2vec>

【5】
神经网络中embedding层作用——本质就是word2vec,数据降维,同时可以很方便计算同义词(各个word之间的距离),底层实现是2-gram(词频)+神经网络
<https://blog.csdn.net/m0_37870649/article/details/83956331>

友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:ixiaoyang8@qq.com
QQ群:637538335
关注微信