如何在 Keras 中使用词嵌入层进行深度学习

词嵌入提供了词及其相对含义的密集表示。

它们是对更简单的词袋模型表示中使用的稀疏表示的改进。

词嵌入可以从文本数据中学习并在项目之间重用。它们也可以作为在文本数据上拟合神经网络的一部分来学习。

在本教程中,您将了解如何使用 Keras 在 Python 中使用词嵌入进行深度学习。

完成本教程后,您将了解:

关于词嵌入以及 Keras 通过嵌入层支持词嵌入。
如何在拟合神经网络的同时学习词嵌入。
如何在神经网络中使用预训练的词嵌入。
从我的新书《自然语言处理的深度学习》开始您的项目,包括分步教程和所有示例的 Python 源代码文件。

让我们开始吧。

2018 年 2 月更新:修复了由于底层 API 更改而导致的错误。
2019 年 10 月更新:针对 Keras 2.3 和 TensorFlow 2.0 进行了更新。

教程概述
本教程分为 3 个部分; 他们是:

词嵌入
Keras 嵌入层
学习嵌入的例子
使用预训练手套嵌入的示例

1.词嵌入
词嵌入是一类使用密集向量表示来表示词和文档的方法。

这是对传统的词袋模型编码方案的改进,在传统的词袋模型编码方案中,使用大的稀疏向量来表示每个单词或对向量中的每个单词进行评分以表示整个词汇表。这些表示是稀疏的,因为词汇量很大,并且给定的单词或文档将由一个主要由零值组成的大向量表示。

相反,在嵌入中,单词由密集向量表示,其中向量表示单词在连续向量空间中的投影。

向量空间中单词的位置是从文本中学习的,并且基于使用该单词时围绕该单词的单词。

单词在学习向量空间中的位置称为它的嵌入。

从文本中学习词嵌入的两个流行示例包括:

Word2Vec。
GloVe。
除了这些精心设计的方法之外,还可以将词嵌入作为深度学习模型的一部分进行学习。这可能是一种较慢的方法,但会根据特定的训练数据集定制模型。

2、Keras嵌入层
Keras 提供了一个嵌入层,可用于文本数据上的神经网络。

它要求对输入数据进行整数编码,以便每个单词都由一个唯一的整数表示。这个数据准备步骤可以使用 Keras 提供的 Tokenizer API 来执行。

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

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

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

它必须指定 3 个参数:

input_dim:这是文本数据中词汇的大小。例如,如果您的数据被整数编码为 0-10 之间的值,那么词汇表的大小将为 11 个单词。
output_dim:这是嵌入单词的向量空间的大小。它为每个单词定义了该层的输出向量的大小。例如,它可以是 32 或 100 甚至更大。为您的问题测试不同的值。
input_length:这是输入序列的长度,就像您为 Keras 模型的任何输入层定义的一样。例如,如果您的所有输入文档都包含 1000 个单词,那么这将是 1000。
例如,下面我们定义了一个包含 200 个词汇表的 Embedding 层(例如,从 0 到 199 的整数编码词,包括 0 到 199),一个 32 维的向量空间,其中将嵌入词,输入文档每个有 50 个词。


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

Embedding 层具有学习的权重。 如果您将模型保存到文件中,这将包括嵌入层的权重。

Embedding 层的输出是一个 2D 向量,对于输入的单词序列(输入文档)中的每个单词都有一个嵌入。

如果您希望将 Dense 层直接连接到 Embedding 层,则必须首先使用 Flatten 层将 2D 输出矩阵展平为 1D 矢量。

现在,让我们看看如何在实践中使用嵌入层。

3、学习嵌入的例子
在本节中,我们将研究如何在将神经网络拟合到文本分类问题时学习词嵌入。

我们将定义一个小问题,我们有 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 = array([1,1,1,1,1,0,0,0,0,0])

接下来,我们可以对每个文档进行整数编码。 这意味着作为输入,嵌入层将具有整数序列。 我们可以尝试其他更复杂的词模型编码包,如计数或 TF-IDF。

Keras 提供了 one_hot() 函数,该函数将每个单词的散列创建为有效的整数编码。 我们将估计词汇量为 50,这比减少散列函数冲突概率所需的要大得多。

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

序列具有不同的长度,Keras 更喜欢对输入进行矢量化,并且所有输入都具有相同的长度。 我们将填充所有输入序列的长度为 4。同样,我们可以使用内置的 Keras 函数来完成此操作,在本例中为 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)

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

Embedding 的词汇量为 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=['accuracy'])
# 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))

下面提供了完整的代码清单。

from numpy import array
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 = array([1,1,1,1,1,0,0,0,0,0])
# integer encode the documents
vocab_size = 50
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=['accuracy'])
# 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))

运行示例首先打印整数编码的文档。

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

然后打印每个文档的填充版本,使它们都具有统一的长度。

 
[[ 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]]

定义网络后,将打印层的摘要。 我们可以看到,正如预期的那样,Embedding 层的输出是一个 4×8 矩阵,它被 Flatten 层压缩为一个 32 元素的向量。

 
_________________________________________________________________
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
_________________________________________________________________

注意:您的结果可能会因算法或评估过程的随机性或数值精度的差异而有所不同。考虑运行该示例几次并比较平均结果。

最后,打印出训练模型的准确率,表明它完美地学习了训练数据集(这并不奇怪)。

精度:100.000000


您可以将学习到的权重从嵌入层保存到文件中,以供以后在其他模型中使用。

您通常也可以使用此模型对测试数据集中具有相同类型词汇的其他文档进行分类。

接下来,让我们看看在 Keras 中加载预训练的词嵌入。

4、使用预训练手套嵌入的示例
Keras Embedding 层也可以使用在其他地方学习的词嵌入。

在自然语言处理领域,学习、保存和免费提供词嵌入是很常见的。

例如,GloVe 方法背后的研究人员在他们的网站上提供了一套预训练的词嵌入,并在公共领域许可下发布。看:

GloVe:单词表示的全局向量
嵌入的最小包为 822Mb,称为“glove.6B.zip”。它在 10 亿个标记(单词)的数据集上进行了训练,词汇量为 40 万个单词。有几种不同的嵌入向量大小,包括 50、100、200 和 300 维。

您可以下载此嵌入集合,我们可以使用来自训练数据集中单词的预训练嵌入的权重为 Keras 嵌入层播种。

这个例子的灵感来自 Keras 项目中的一个例子:pretrained_word_embeddings.py。

下载解压后会看到几个文件,其中一个是“glove.6B.100d.txt”,里面包含了一个100维版本的embedding。

如果您查看文件内部,您将在每行看到一个标记(单词),然后是权重(100 个数字)。例如,下面是嵌入 ASCII 文本文件的第一行,显示了“the”的嵌入。

 
the -0.038194 -0.24487 0.72812 -0.39961 0.083172 0.043953 -0.39141 0.3344 -0.57545 0.087459 0.28787 -0.06731 0.30906 -0.26384 -0.13231 -0.20757 0.33395 -0.33848 -0.31743 -0.48336 0.1464 -0.37304 0.34577 0.052041 0.44946 -0.46971 0.02628 -0.54155 -0.15518 -0.14107 -0.039722 0.28277 0.14393 0.23464 -0.31021 0.086173 0.20397 0.52624 0.17164 -0.082378 -0.71787 -0.41531 0.20335 -0.12763 0.41367 0.55187 0.57908 -0.33477 -0.36559 -0.54857 -0.062892 0.26584 0.30205 0.99775 -0.80481 -3.0243 0.01254 -0.36942 2.2167 0.72201 -0.24978 0.92136 0.034514 0.46745 1.1079 -0.19358 -0.074575 0.23353 -0.052062 -0.22044 0.057162 -0.15806 -0.30798 -0.41625 0.37972 0.15006 -0.53212 -0.2055 -1.2526 0.071624 0.70565 0.49744 -0.42063 0.26148 -1.538 -0.30223 -0.073438 -0.28312 0.37104 -0.25217 0.016215 -0.017099 -0.38984 0.87424 -0.72569 -0.51058 -0.52028 -0.1459 0.8278 0.27062

与上一节一样,第一步是定义示例,将它们编码为整数,然后将序列填充为相同的长度。

在这种情况下,我们需要能够将单词映射到整数以及将整数映射到单词。

Keras 提供了一个适合训练数据的 Tokenizer 类,可以通过调用 Tokenizer 类的 texts_to_sequences() 方法将文本一致地转换为序列,并提供对 word_index 属性中单词到整数的字典映射的访问。

# 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 = array([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)

接下来,我们需要将整个 GloVe 词嵌入文件加载到内存中,作为词到嵌入数组的字典。

# load the whole embedding into memory
embeddings_index = dict()
f = open('glove.6B.100d.txt')
for line in f:
	values = line.split()
	word = values[0]
	coefs = asarray(values[1:], dtype='float32')
	embeddings_index[word] = coefs
f.close()
print('Loaded %s word vectors.' % len(embeddings_index))

这很慢。 过滤训练数据中唯一单词的嵌入可能会更好。

接下来,我们需要为训练数据集中的每个单词创建一个嵌入矩阵。 我们可以通过枚举 Tokenizer.word_index 中的所有唯一词并从加载的 GloVe 嵌入中定位嵌入权重向量来做到这一点。

结果是一个权重矩阵,仅适用于我们将在训练期间看到的单词。

# 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 维的版本,因此 Embedding 层必须定义 output_dim 设置为 100。最后,我们不想更新该模型中的学习词权重,因此我们将模型的 trainable 属性设置为 False .

e = Embedding(vocab_size, 100, weights=[embedding_matrix], input_length=4, trainable=False)

下面列出了完整的代码示例。

from numpy import array
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 = array([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('../glove_data/glove.6B/glove.6B.100d.txt')
for line in f:
	values = line.split()
	word = values[0]
	coefs = asarray(values[1:], dtype='float32')
	embeddings_index[word] = 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=['accuracy'])
# 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))

注意:您的结果可能会因算法或评估过程的随机性或数值精度的差异而有所不同。 考虑运行该示例几次并比较平均结果。

运行该示例可能需要更长的时间,但随后证明它同样能够解决这个简单的问题。

[[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]]
 
Loaded 400000 word vectors.
 
_________________________________________________________________
Layer (type)                 Output Shape              Param #
=================================================================
embedding_1 (Embedding)      (None, 4, 100)            1500
_________________________________________________________________
flatten_1 (Flatten)          (None, 400)               0
_________________________________________________________________
dense_1 (Dense)              (None, 1)                 401
=================================================================
Total params: 1,901
Trainable params: 401
Non-trainable params: 1,500
_________________________________________________________________
 
 
Accuracy: 100.000000

在实践中,我鼓励您尝试使用固定的预训练嵌入来学习词嵌入,并尝试在预训练嵌入之上执行学习。

看看什么最适合您的特定问题。

总结
在本教程中,您了解了如何通过 Keras 在 Python 中使用词嵌入进行深度学习。

具体来说,您了解到:

关于词嵌入以及 Keras 通过嵌入层支持词嵌入。
如何在拟合神经网络的同时学习词嵌入。
如何在神经网络中使用预训练的词嵌入。

关注公众号“大模型全栈程序员”回复“小程序”获取1000个小程序打包源码。更多免费资源在http://www.gitweixin.com/?p=2627

发表评论

邮箱地址不会被公开。 必填项已用*标注