Keras深度学习实战(24)——从零开始构建单词向量
Posted 盼小辉丶
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Keras深度学习实战(24)——从零开始构建单词向量相关的知识,希望对你有一定的参考价值。
Keras深度学习实战(24)——从零开始构建单词向量
0. 前言
在解决文本相关问题时,传统方法通常需要对单词进行独热编码。但是,如果数据集中具有数千个不同的单词,则生成的独热编码矢量将具有数千个维度,这会导致计算代价十分高昂。此外,在这种情况下,相似的单词并不具备相似的向量。因此,我们需要研究如何对文本数据进行编码,以使相似的数据具有相似的编码向量。
1. 单词向量
1.1 Word2Vec 原理
Word2Vec
是一种可以将相似单词编码为相似向量的方法。在了解 Word2Vec
原理之前,我们首先考虑以下问题,假设我们有以下输入句子:
I love watching movie
I like watching movie
传统方法中,对单词进行独热编码,输出结果如下所示:
单词 | 独热编码 | ||||
---|---|---|---|---|---|
I | 1 | 0 | 0 | 0 | 0 |
love | 0 | 1 | 0 | 0 | 0 |
watching | 0 | 0 | 1 | 0 | 0 |
movie | 0 | 0 | 0 | 1 | 0 |
like | 0 | 0 | 0 | 0 | 1 |
我们知道,在语义上,love
和 like
是相似的词。但是,使用独热编码单词 I
和 like
之间的欧式距离与 love
和 like
之间的欧式距离相同,不能体现出 love
和 like
之间的语义相似性。但是,我们知道 love
与 like
之间的距离应小于 I
与 like
之间的距离,因为 love
和 like
之间的语义更加相似。
1.2 构建单词向量
构建单词向量的核心思想是,在向量空间中,每个单词周围都存在着与之相似的单词。例如:“queen
” 和 “princess
” 单词的周围会出现类似的词,如“kingdom
”。从某种意义上说,这些词的上下文同样是相似的。
依旧使用上一节中的两个句子,当我们一个句子中的某个单词作为输出,而句子中的其余单词作为输入时,可以构造以下数据集:
输入 | 输出 | ||
---|---|---|---|
love | watching | movie | I |
I | watching | movie | love |
I | love | movie | watching |
I | love | watching | movie |
like | watching | movie | I |
I | watching | movie | like |
I | like | movie | watching |
I | like | watching | movie |
当我们将某一个单词用作输出,其余单词用作输入,将输入和输出进行独热编码后得到以下形式的向量:
输入向量 | 输出向量 | ||||||||
---|---|---|---|---|---|---|---|---|---|
0 | 1 | 1 | 1 | 0 | 1 | 0 | 0 | 0 | 0 |
1 | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 0 | 0 |
1 | 1 | 0 | 1 | 0 | 0 | 0 | 1 | 0 | 0 |
1 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 1 | 0 |
0 | 0 | 1 | 1 | 1 | 1 | 0 | 0 | 0 | 0 |
1 | 0 | 1 | 1 | 0 | 0 | 0 | 0 | 0 | 1 |
1 | 0 | 0 | 1 | 1 | 0 | 0 | 1 | 0 | 0 |
1 | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 1 | 0 |
可以看到,输入向量的第一行为 0, 1, 1, 1, 0
,因为输入单词的索引为 1, 2, 3
,输出为 1, 0, 0, 0, 0
,因为输出单词的索引为 0
。
1.3 神经网络架构
如果我们使用的神经网络中隐藏层包含三个神经元,则神经网络架构如下所示:
网络中每层的信息如下:
网络层 | 尺寸 | 描述 |
---|---|---|
输入层 | 5 | 每个输入向量尺寸为 5 |
输入层权重 | 5x3 | 隐藏层中的 3 个神经元各有 5 个连接到输入层的权重 |
隐藏层 | 3 | 隐藏层包含 3 个神经元 |
输出层权重 | 3x5 | 由于有 5 个不同单词,因此 3 个隐藏单元输出映射到输出层的 5 个输出 |
输出层 | 5 | 输出向量的尺寸为 5 ,每一单词对应一个预测单词概率 |
在构建单词向量时,在隐藏层中并不使用激活函数。使用 softmax
函数处理输出层输出值,以便得到单词概率,使用交叉熵损失作为损失函数,使用 Adam
优化器优化网络权重值。当向网络中输入单词(而非输入语句)的独热编码时,给定单词的编码向量可以使用隐藏层的输出值表示。
2. 使用 Keras 从零开始构建单词向量
根据我们在上一小节中介绍的单词向量的生成方式,我们使用 Keras
实现单词编码向量神经网络。
(1) 首先,定义输入句子:
docs = ["I love watching movie", "I like watching movie"]
在以上语句中,我们期望 love
和 like
的词向量是相似的,因为 love
和 like
的上下文是完全相同的。
(2) 然后,我们为每个句子创建一个独热编码:
from sklearn.feature_extraction.text import CountVectorizer
vectorizer = CountVectorizer(min_df=0, token_pattern=r"\\b\\w+\\b")
vectorizer.fit(docs)
vectorizer
定义了将文档转换为向量格式的参数。此外,通过传递参数 min_df
确保在 CountVectorizer
中不会过滤掉诸如 I
之类的词,使用定义的输入句子拟合 vectorizer
得到合适的单词向量化模型。
(3) 将文档 docs
转换为向量格式:
vector = vectorizer.transform(docs)
(4) 验证执行转换后的语句向量:
print(vectorizer.vocabulary_)
print(vector.shape)
print(vector.toarray())
vocabulary_
返回各种单词的索引,而 vector.toarray
将返回句子的独热编码,输出结果如下:
'i': 0, 'love': 2, 'watching': 4, 'movie': 3, 'like': 1
(2, 5)
[[1 0 1 1 1]
[1 1 0 1 1]]
(5) 创建输入和输出数据集:
x = []
y = []
for i in range(len(docs)):
for j in range(len(docs[i].split())):
t_x = []
t_y = []
for k in range(4):
if(j==k):
t_y.append(docs[i].split()[k])
continue
else:
t_x.append(docs[i].split()[k])
x.append(t_x)
y.append(t_y)
x2 = []
y2 = []
for i in range(len(x)):
x2.append(' '.join(x[i]))
y2.append(' '.join(y[i]))
从前面的代码中,我们创建了输入和输出数据集,我们可以打印数据集,查看其内容:
print(x2)
print(y2)
打印出的输入和输出数据如下:
['love watching movie', 'I watching movie', 'I love movie', 'I love watching', 'like watching movie', 'I watching movie', 'I like movie', 'I like watching']
['I', 'love', 'watching', 'movie', 'I', 'like', 'watching', 'movie']
(6) 将前面的输入和输出单词转换为向量:
vector_x = vectorizer.transform(x2)
vector_y = vectorizer.transform(y2)
vector_x = vector_x.toarray()
vector_y = vector_y.toarray()
# 打印输入与输出数组
print('Input: ', vector_x)
print('Output: ' vector_y)
打印出的输入和输出数组如下:
Input: [[0 0 1 1 1]
[1 0 0 1 1]
[1 0 1 1 0]
[1 0 1 0 1]
[0 1 0 1 1]
[1 0 0 1 1]
[1 1 0 1 0]
[1 1 0 0 1]]
Output: [[1 0 0 0 0]
[0 0 1 0 0]
[0 0 0 0 1]
[0 0 0 1 0]
[1 0 0 0 0]
[0 1 0 0 0]
[0 0 0 0 1]
[0 0 0 1 0]]
(7) 根据在上一小节中定义的神经网络,构建神经网络模型:
from keras.layers import Dense
from keras.models import Sequential
model = Sequential()
model.add(Dense(3, input_shape=(5,)))
model.add(Dense(5,activation='sigmoid'))
model.summary()
模型简要架构信息输入如下:
Model: "sequential"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
dense (Dense) (None, 3) 18
_________________________________________________________________
dense_1 (Dense) (None, 5) 20
=================================================================
Total params: 38
Trainable params: 38
Non-trainable params: 0
_________________________________________________________________
(8) 编译并拟合模型:
model.compile(loss='categorical_crossentropy',optimizer='adam')
model.fit(vector_x, vector_y, epochs=1000, batch_size=2,verbose=1)
(9) 通过获取中间层值来提取词向量,其中输入是每个单个词的编码向量:
from keras.models import Model
layer_name = 'dense'
intermediate_layer_model = Model(inputs=model.input,outputs=model.get_layer(layer_name).output)
在以上代码中,我们从目标层中提取输出——通过模型中的名为 dense
的层获取单词编码向量。
(10) 接下来,向网络中传递单词的独热编码向量作为输入,提取中间层的输出:
for i in range(len(vectorizer.vocabulary_)):
word = list(vectorizer.vocabulary_.keys())[i]
word_vec = vectorizer.transform([list(vectorizer.vocabulary_.keys())[i]]).toarray()
print(word, intermediate_layer_model.predict(word_vec))
各个单词的编码向量如下:
i [[-1.41066 0.02432728 -1.0654368 ]]
love [[-1.1692711 1.7719828 0.54331756]]
watching [[ 1.163808 1.908086 -1.5191256]]
movie [[0.01165223 2.0688105 1.532387 ]]
like [[-1.197992 1.662775 0.5817174]]
可以看出,在以上单词编码向量中,“love
” 和 “like
” 这两个单词之间的相关性更高,因此可以更好地表示单词向量。
3. 测量单词向量之间的相似度
有多种用于度量测量单词向量之间的相似度的方法,以下是两种较常见的度量方法:
- 余弦相似度
- 欧氏距离
两个不同向量 A
和 B
之间的余弦相似度计算如下:
s i m i l a r i t y = c o s ( θ ) = A ⋅ B ∥ A ∥ 2 ∥ B ∥ 2 = ∑ i = 1 n A i B i ∑ i = 1 n A i 2 ∑ i = 1 n B i 2 similarity=cos(\\theta)=\\frac A \\cdot B \\Vert A \\Vert_2 \\Vert B \\Vert_2=\\frac \\sum_i=1^nA_iB_i\\sqrt\\sum_i=1^nA_i^2\\sqrt\\sum_i=1^nB_i^2 similarity=cos(θ)=∥A∥2∥B∥2A⋅B=∑i=1nAi2∑i=1nBi2∑i=1nAiBi
例如在上一小节构建的单词向量示例中,“love
” 和 “like
” 之间的余弦相似度计算方法如下:
(1) “love
” 和 “like
” 的单词向量如下:
enjoy = (-1.17, 1.77, 0.54)
like = (-1.20, 1.66, 0.58)
(2) “love
” 向量和 “like
” 向量之间的余弦相似度:
( ( − 1.17 ) × ( − 1.20 ) + 1.77 × 1.66 + 0.54 × 0.58 ) ( − 1.17 ) 2 + 1.7 7 2 + 0.5 4 2 ( − 1.20 ) 2 + 1.6 6 2 + 0.5 8 2 = 0.998826867 \\frac ((-1.17)\\times(-1.20)+1.77\\times1.66+0.54\\times0.58)\\sqrt(-1.17)^2+1.77^2+0.54^2\\sqrt(-1.20)^2+1.66^2+0.58^2=0.998826867 (−1.17)2+1.772+0.542(−1.20)2+1.662+0.582((−1.17)×(−1.20)+1.77×1.66+0.54×0.58)=0.998826867
两个不同向量 A
和 B
之间的欧式距离计算如下:
E
u
c
l
e
d
i
a
n
=
(
A
−
B
)
2
= 以上是关于Keras深度学习实战(24)——从零开始构建单词向量的主要内容,如果未能解决你的问题,请参考以下文章