import collections
import math
import random
import sys
import zipfile
import time
import os
import numpy as np
import tensorflow as tf
from collections import defaultdict
sys.path.append("..")
import d2lzh_tensorflow2 as d2l
print(tf.__version__)
2.1.0
Tensorflow并没有像pytorch或者mxnet那样的glove或fasttext那样的运行库,故我们必须要去Glove的官方网站下载对应的词向量文件到文件夹中,并进行解压。 预训练的GloVe模型的命名规范大致是“模型.(数据集.)数据集词数.词向量维度”。 更多信息可以参考GloVe和fastText的项目网站[1,2]。下面我们使用基于维基百科子集预训练的50维GloVe词向量,下载地址:http://nlp.stanford.edu/data/glove.6B.zip
class tensorflow_glove:
def __init__(self, glove_filename):
self.word_to_index = dict()
self.index_to_embedding = []
self.index_to_word = dict()
with open(glove_filename, 'r', encoding='utf-8') as glove_file:
for (i, line) in enumerate(glove_file):
split = line.split(' ')
word = split[0]
representation = split[1:]
representation = np.array(
[float(val) for val in representation])
self.word_to_index[word] = i
self.index_to_word[i] = word
self.index_to_embedding.append(representation)
_WORD_NOT_FOUND = [0.0] * len(representation)
_LAST_INDEX = i + 1
self.word_to_index = defaultdict(lambda: _LAST_INDEX,
self.word_to_index)
self.index_to_embedding = np.array(self.index_to_embedding +
[_WORD_NOT_FOUND])
glove = tensorflow_glove("C:/Users/HP/dive into d2l/code/chapter10_natural-language-processing/embeddings/ GloVe.6B/glove.6B.50d.txt")
print("一共包含%d个词。" % len(glove.index_to_embedding))
一共包含400001个词。
word_to_index为单词和序号的字典,index_to_embedding为相对应的词嵌入向量。index_to_word为下标到单词的字典
word_to_index, index_to_embedding, index_to_word = glove.word_to_index,glove.index_to_embedding,glove.index_to_word
打印词典大小。其中含有40万个词。
我们可以通过词来获取它在词典中的索引,并获取该词的词嵌入
word_to_index['beautiful'], index_to_word[3366]
(3366, 'beautiful')
vocab_size, embedding_dim = index_to_embedding.shape
vocab_size,embedding_dim
(400001, 50)
下面我们以GloVe模型为例,展示预训练词向量的应用。
这里重新实现10.3节(word2vec的实现)中介绍过的使用余弦相似度来搜索近义词的算法。为了在求类比词时重用其中的求$k$近邻($k$-nearest neighbors)的逻辑,我们将这部分逻辑单独封装在knn
函数中。
def knn(W, x, k):
# 添加的1e-9是为了数值稳定性
cos = tf.reshape(tf.matmul(W, x),shape=[-1])/ tf.sqrt(tf.reduce_sum(W * W, axis=1) * tf.reduce_sum(x * x) + 1e-9)
_, topk = tf.math.top_k(cos, k=k+1)
topk=topk.numpy().tolist()
return topk, [cos[i] for i in topk]
def get_similar_tokens(query_token, k, embed):
index = word_to_index[query_token]
topk, cos = knn(embed.index_to_embedding,
tf.reshape(embed.index_to_embedding[index],(50,1)), k)
for i, c in zip(topk[1:], cos[1:]): # 除去输入词
print('cosine sim=%.3f: %s' % (c, (index_to_word[i])))
已创建的预训练词向量实例glove_6b50d
的词典中含40万个词和1个特殊的未知词。除去输入词和未知词,我们从中搜索与“chip”语义最相近的3个词。
get_similar_tokens('chip', 3, glove)
cosine sim=0.856: chips
cosine sim=0.749: intel
cosine sim=0.749: electronics
接下来查找“baby”和“beautiful”的近义词。
get_similar_tokens('baby', 3, glove)
cosine sim=0.839: babies
cosine sim=0.800: boy
cosine sim=0.792: girl
get_similar_tokens('beautiful', 3, glove)
cosine sim=0.921: lovely
cosine sim=0.893: gorgeous
cosine sim=0.830: wonderful
除了求近义词以外,我们还可以使用预训练词向量求词与词之间的类比关系。例如,“man”(男人): “woman”(女人):: “son”(儿子) : “daughter”(女儿)是一个类比例子:“man”之于“woman”相当于“son”之于“daughter”。求类比词问题可以定义为:对于类比关系中的4个词
def get_analogy(token_a, token_b, token_c, embed):
vecs = [embed.index_to_embedding[glove.word_to_index[t]]
for t in [token_a, token_b, token_c]]
x = vecs[1] - vecs[0] + vecs[2]
topk, cos = knn(embed.index_to_embedding, tf.reshape(x,(50,1)), 1)
return glove.index_to_word[topk[0]]
验证一下“男-女”类比。
get_analogy('man', 'woman', 'son',glove)
'daughter'
“首都-国家”类比:“beijing”(北京)之于“china”(中国)相当于“tokyo”(东京)之于什么?答案应该是“japan”(日本)。
get_analogy('beijing', 'china', 'tokyo', glove)
'japan'
“形容词-形容词最高级”类比:“bad”(坏的)之于“worst”(最坏的)相当于“big”(大的)之于什么?答案应该是“biggest”(最大的)。
get_analogy('bad', 'worst', 'big', glove)
'biggest'
“动词一般时-动词过去时”类比:“do”(做)之于“did”(做过)相当于“go”(去)之于什么?答案应该是“went”(去过)。
get_analogy('do', 'did', 'go', glove)
'went'
- 在大规模语料上预训练的词向量常常可以应用于下游自然语言处理任务中。
- 可以应用预训练的词向量求近义词和类比词。
[1] GloVe项目网站。 https://nlp.stanford.edu/projects/glove/
[2] fastText项目网站。 https://fasttext.cc/