一、概述
词嵌入(Word embedding)是一种使用密集向量表示来表示单词和文档的 NLP 技术,与使用大稀疏向量表示的词袋技术相比。嵌入是一类 NLP 方法,旨在将单词的语义含义投影到几何空间中。
这是通过将数字向量链接到字典中的每个单词来实现的,以便任何两个向量之间的距离捕获两个相关单词之间的部分语义关系。这些向量形成的几何空间称为嵌入空间。
学习词嵌入的两种最流行的技术是用于词表示的全局向量 (GloVe) 和词到向量表示 (Word2vec)。
Keras 提供了一个嵌入层,可用于文本或自然语言数据。 输入数据应进行数字编码,以便每个单词都由数字或整数值表示。 我们可以使用 Keras 的 tokenizer API 来执行此操作。 在我们使用没有预训练嵌入的 Keras API 的情况下,嵌入层使用随机权重进行初始化。
2. 数据准备
首先创建示例文档和相应的标签,将每个文档分类为正面或负面,如以下代码片段所示:
# 定义文档
documents = [
'Well done!',
'Good work',
'Great effort',
'nice work',
'Excellent!',
'Weak',
'Poor effort',
'not good',
'poor work',
'Could have done better.',
]
# 定义标签
labels = np.array([1, 1, 1, 1, 1, 0, 0, 0, 0, 0])
我们现在将使用 Keras 文本处理 API 对文档进行一次热编码。 one-hot 方法是将分类特征表示为二进制向量。 首先,分类值被映射到整数/数值。 稍后,整数/数值被呈现为二进制向量,除了整数索引处的值之外,该向量都是零值。
我们通常将文档表示为整数值序列,其中文档中的每个单词都表示为单个整数。
3. 不要使用预训练的词嵌入
Keras 提供了 one_hot() 函数,您可以使用它来标记和编码文本文档。 它不会创建 one-hot 编码,而是该函数执行一个 hashing_trick() 函数。 散列技巧将文本转换为固定大小散列空间中的索引序列
最后,该函数返回文档的整数编码版本:
vocab_size = 50
encodeDocuments = [tf.keras.preprocessing.text.one_hot(doc, vocab_size) for doc in documents]
print(encodeDocuments)
输出如下
[[48, 9], [47, 8], [36, 5], [43, 8], [14], [48], [4, 5], [26, 47], [4, 8], [20, 29, 9, 12]]
然后我们将文档填充到最大长度为 4,如下所示,因为现有向量的最大长度是 4 个整数,如前所述。 Keras 库中的 pad_sequences() 函数可用于填充可变长度序列。 默认填充值为 0.0,但可以通过 value 参数指定首选值来更改此值。
max_length = 4
paddedDocuments = tf.keras.preprocessing.sequence.pad_sequences(encodeDocuments, maxlen=max_length, padding='post')
print(paddedDocuments)
填充可用于序列的开头或结尾,描述为序列前填充或序列后填充,如下所示:
[[48 9 0 0]
[47 8 0 0]
[36 5 0 0]
[43 8 0 0]
[14 0 0 0]
[48 0 0 0]
[ 4 5 0 0]
[26 47 0 0]
[ 4 8 0 0]
[20 29 9 12]]
现在,我们将从 Keras 库创建一个顺序模型,该模型在内部表示为一系列层。 首先,我们创建一个新的序列模型并添加层来开发网络拓扑。 模型定义好后,我们用后端作为 TensorFlow 编译它。
我们将嵌入层定义为网络建模的一部分,如以下代码片段所示。 如前所述,嵌入的词汇量为 50,输入长度为 4。 我们将选择一个八维的嵌入空间。在这种情况下,该模型是一个二元分类器。 重要的是,嵌入层的输出将是四个八维向量,每个向量一个。 我们将其展平为一个 32 元素向量以传递到密集输出层。 最后拟合和评估分类模型。
我们必须指定一个损失函数来评估一组权重,一个用于搜索网络不同权重的优化器,以及我们希望在训练期间收集和报告的任何可选指标。代码显示如下:
model = Sequential()
model.add(layers.Embedding(vocab_size, 8, input_length=max_length))
model.add(layers.Flatten())
model.add(layers.Dense(1, activation='sigmoid'))
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['acc'])
print(model.summary())
打印模型如下
Model: “sequential”
_________________________________________________________________
Layer (type) Output Shape Param #
================================================== === ===============
embedding (Embedding) (None, 4, 8) 400
flatten (Flatten) (None, 32) 0
dense (Dense) (None, 1) 33
================================================== === ===============
Total params: 433
Trainable params: 433
Non-trainable params: 0
_________________________________________________________________
开始训练模型
model.fit(paddedDocuments, labels, epochs=50, verbose=1)
最后,评估神经网络在给定文档上的性能。为简单起见,准确性是在训练数据本身上测试的,在本章后面,我们将使用训练集和测试集来评估我们模型的性能。代码显示如下:
loss, accuracy = model.evaluate(paddedDocuments, labels, verbose=1)
print('Accuracy:%f' % accuracy*100)
输出如下:
Accuracy:0.800000
4. 使用预训练的词嵌入
现在使用来自 Keras 的预训练词嵌入。 让我们重用前面的数据和代码。
Keras 提供了用于准备文本的标记器 API,这些文本可以适合和重用以准备多个文本文档。 构建标记器,然后将其适配到文本文档或整数编码的文本文档。 这里,单词被称为tokens,将文本划分为tokens的方法被描述为tokenization。 Keras 为我们提供了 text_to_word_sequence API,可用于将文本拆分为单词列表,如下所示:
tokenizer = tf.keras.preprocessing.text.Tokenizer()
tokenizer.fit_on_texts(documents)
vocab_size = len(tokenizer.word_index) + 1
encodeDocuments = tokenizer.texts_to_sequences(documents)
print(encodeDocuments)
输出如下
[[6, 2], [3, 1], [7, 4], [8, 1], [9], [10], [5, 4], [11, 3], [5, 1], [12, 13, 2, 14]]
Keras 的 Tokenizer 文本 API 比以前使用 one-hotencoding 的方法更复杂,更适合生产用例。
max_length = 4
paddedDocuments = keras.preprocessing.sequence.pad_sequences(encodeDocuments, maxlen=max_length, padding='post')
print(paddedDocuments)
还是向之前一样将文档填充到最大长度为 4
[[ 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 嵌入。 GloVe 方法提供了一套预训练的词嵌入。 我们将使用经过 60 亿词和100维训练的 GloVe,即glove.6B.100d.txt。 如果我们查看文件内部,我们可以看到一个标记(单词),后跟每行的权重(100 个数字)。
在这一步,我们将整个 GloVe 词嵌入文件加载到内存中,作为 word-to-embedding 数组的字典。
# load glove model
inMemooryGlove = dict()
f = open('D:\deeplearn\dataset\glove.6B\glove.6B.100d.txt', encoding='utf8')
for line in f:
values = line.split()
word = values[0]
coefficients = np.asarray(values[1:], dtype='float32')
inMemooryGlove[word] = coefficients
f.close()
print(len(inMemooryGlove))
上述代码的输出如下:
400000
然后为为训练数据创建系数矩阵。 我们通过遍历 Tokenizer.word_index 中的所有唯一词并从加载的 GloVe 嵌入中定位嵌入权重向量来实现这一点。
# create coefficient matrix for training data
trainingToEmbeddings = np.zeros((vocab_size, 100))
for word,i in tokenizer.word_index.items():
gloveVector = inMemooryGlove.get(word)
if gloveVector is not None:
trainingToEmbeddings[i] = gloveVector
定义网络模型
model = Sequential()
model.add(layers.Embedding(vocab_size, 100, weights=[trainingToEmbeddings], input_length=max_length, trainable=False))
model.add(layers.Flatten())
model.add(layers.Dense(1, activation='sigmoid'))
model.compile(optimizer='adam', loss='binary_crossentropy', metrics=['acc'])
print(model.summary())
打印模型如下
Model: “sequential”
_________________________________________________________________
Layer (type) Output Shape Param #
================================================== === ===============
embedding (Embedding) (None, 4, 100) 1500
flatten (Flatten) (None, 400) 0
dense (Dense) (None, 1) 401
================================================== === ===============
Total params: 1,901
Trainable params: 401
Non-trainable params: 1,500
_________________________________________________________________
执行训练并打印结果
model.fit(paddedDocuments, labels, epochs=50, verbose=1)
loss, accuracy = model.evaluate(paddedDocuments, labels, verbose=0)
print('Accuracy:%f' % accuracy*100)
输出如下
Accuracy:1.000000
文章出处登录后可见!