# 10.6 求近义词和类比词
在10.3节(word2vec的实现)中,我们在小规模数据集上训练了一个word2vec词嵌入模型,并通过词向量的余弦相似度搜索近义词。实际中,在大规模语料上预训练的词向量常常可以应用到下游自然语言处理任务中。本节将演示如何用这些预训练的词向量来求近义词和类比词。我们还将在后面两节中继续应用预训练的词向量。
## 10.6.1 使用预训练的词向量
基于PyTorch的关于自然语言处理的常用包有官方的[torchtext](https://github.com/pytorch/text)以及第三方的[pytorch-nlp](https://github.com/PetrochukM/PyTorch-NLP)等等。你可以使用`pip`很方便地按照它们,例如命令行执行
```
pip install torchtext
```
详情请参见其README。
本节我们使用torchtext进行练习。下面查看它目前提供的预训练词嵌入的名称。
``` python
import torch
import torchtext.vocab as vocab
vocab.pretrained_aliases.keys()
```
输出:
```
dict_keys(['charngram.100d', 'fasttext.en.300d', 'fasttext.simple.300d', 'glove.42B.300d', 'glove.840B.300d', 'glove.twitter.27B.25d', 'glove.twitter.27B.50d', 'glove.twitter.27B.100d', 'glove.twitter.27B.200d', 'glove.6B.50d', 'glove.6B.100d', 'glove.6B.200d', 'glove.6B.300d'])
```
下面查看查看该`glove`词嵌入提供了哪些预训练的模型。每个模型的词向量维度可能不同,或是在不同数据集上预训练得到的。
``` python
[key for key in vocab.pretrained_aliases.keys()
if "glove" in key]
```
输出:
```
['glove.42B.300d',
'glove.840B.300d',
'glove.twitter.27B.25d',
'glove.twitter.27B.50d',
'glove.twitter.27B.100d',
'glove.twitter.27B.200d',
'glove.6B.50d',
'glove.6B.100d',
'glove.6B.200d',
'glove.6B.300d']
```
预训练的GloVe模型的命名规范大致是“模型.(数据集.)数据集词数.词向量维度”。更多信息可以参考GloVe和fastText的项目网站[1,2]。下面我们使用基于维基百科子集预训练的50维GloVe词向量。第一次创建预训练词向量实例时会自动下载相应的词向量到`cache`指定文件夹(默认为`.vector_cache`),因此需要联网。
``` python
cache_dir = "/Users/tangshusen/Datasets/glove"
# glove = vocab.pretrained_aliases["glove.6B.50d"](cache=cache_dir)
glove = vocab.GloVe(name='6B', dim=50, cache=cache_dir) # 与上面等价
```
返回的实例主要有以下三个属性:
* `stoi`: 词到索引的字典:
* `itos`: 一个列表,索引到词的映射;
* `vectors`: 词向量。
打印词典大小。其中含有40万个词。
``` python
print("一共包含%d个词。" % len(glove.stoi))
```
输出:
```
一共包含400000个词。
```
我们可以通过词来获取它在词典中的索引,也可以通过索引获取词。
``` python
glove.stoi['beautiful'], glove.itos[3366] # (3366, 'beautiful')
```
## 10.6.2 应用预训练词向量
下面我们以GloVe模型为例,展示预训练词向量的应用。
### 10.6.2.1 求近义词
这里重新实现10.3节(word2vec的实现)中介绍过的使用余弦相似度来搜索近义词的算法。为了在求类比词时重用其中的求` $ k $`近邻(`$ k $`-nearest neighbors)的逻辑,我们将这部分逻辑单独封装在`knn`函数中。
``` python
def knn(W, x, k):
# 添加的1e-9是为了数值稳定性
cos = torch.matmul(W, x.view((-1,))) / (
(torch.sum(W * W, dim=1) + 1e-9).sqrt() * torch.sum(x * x).sqrt())
_, topk = torch.topk(cos, k=k)
topk = topk.cpu().numpy()
return topk, [cos[i].item() for i in topk]
```
然后,我们通过预训练词向量实例`embed`来搜索近义词。
``` python
def get_similar_tokens(query_token, k, embed):
topk, cos = knn(embed.vectors,
embed.vectors[embed.stoi[query_token]], k+1)
for i, c in zip(topk[1:], cos[1:]): # 除去输入词
print('cosine sim=%.3f: %s' % (c, (embed.itos[i])))
```
已创建的预训练词向量实例`glove_6b50d`的词典中含40万个词和1个特殊的未知词。除去输入词和未知词,我们从中搜索与“chip”语义最相近的3个词。
``` python
get_similar_tokens('chip', 3, glove)
```
输出:
```
cosine sim=0.856: chips
cosine sim=0.749: intel
cosine sim=0.749: electronics
```
接下来查找“baby”和“beautiful”的近义词。
``` python
get_similar_tokens('baby', 3, glove)
```
输出:
```
cosine sim=0.839: babies
cosine sim=0.800: boy
cosine sim=0.792: girl
```
``` python
get_similar_tokens('beautiful', 3, glove)
```
输出:
```
cosine sim=0.921: lovely
cosine sim=0.893: gorgeous
cosine sim=0.830: wonderful
```
### 10.6.2.2 求类比词
除了求近义词以外,我们还可以使用预训练词向量求词与词之间的类比关系。例如,“man”(男人): “woman”(女人):: “son”(儿子) : “daughter”(女儿)是一个类比例子:“man”之于“woman”相当于“son”之于“daughter”。求类比词问题可以定义为:对于类比关系中的4个词 `$ a : b :: c : d $`,给定前3个词`$ a $` 、`$ b $`和`$ c $`,求`$ d $`。设词`$ w $`的词向量为`$ \text{vec}(w) $`。求类比词的思路是,搜索与`$ \text{vec}(c)+\text{vec}(b)-\text{vec}(a) $`的结果向量最相似的词向量。
``` python
def get_analogy(token_a, token_b, token_c, embed):
vecs = [embed.vectors[embed.stoi[t]]
for t in [token_a, token_b, token_c]]
x = vecs[1] - vecs[0] + vecs[2]
topk, cos = knn(embed.vectors, x, 1)
return embed.itos[topk[0]]
```
验证一下“男-女”类比。
``` python
get_analogy('man', 'woman', 'son', glove) # 'daughter'
```
“首都-国家”类比:“beijing”(北京)之于“china”(中国)相当于“tokyo”(东京)之于什么?答案应该是“japan”(日本)。
``` python
get_analogy('beijing', 'china', 'tokyo', glove) # 'japan'
```
“形容词-形容词最高级”类比:“bad”(坏的)之于“worst”(最坏的)相当于“big”(大的)之于什么?答案应该是“biggest”(最大的)。
``` python
get_analogy('bad', 'worst', 'big', glove) # 'biggest'
```
“动词一般时-动词过去时”类比:“do”(做)之于“did”(做过)相当于“go”(去)之于什么?答案应该是“went”(去过)。
``` python
get_analogy('do', 'did', 'go', glove) # 'went'
```
## 小结
* 在大规模语料上预训练的词向量常常可以应用于下游自然语言处理任务中。
* 可以应用预训练的词向量求近义词和类比词。
## 参考文献
[1] GloVe项目网站。 https://nlp.stanford.edu/projects/glove/
[2] fastText项目网站。 https://fasttext.cc/
-----------
> 注:本节除代码外与原书基本相同,[原书传送门](https://zh.d2l.ai/chapter_natural-language-processing/similarity-analogy.html)
- Home
- Introduce
- 1.深度学习简介
- 深度学习简介
- 2.预备知识
- 2.1环境配置
- 2.2数据操作
- 2.3自动求梯度
- 3.深度学习基础
- 3.1 线性回归
- 3.2 线性回归的从零开始实现
- 3.3 线性回归的简洁实现
- 3.4 softmax回归
- 3.5 图像分类数据集(Fashion-MINST)
- 3.6 softmax回归的从零开始实现
- 3.7 softmax回归的简洁实现
- 3.8 多层感知机
- 3.9 多层感知机的从零开始实现
- 3.10 多层感知机的简洁实现
- 3.11 模型选择、反向传播和计算图
- 3.12 权重衰减
- 3.13 丢弃法
- 3.14 正向传播、反向传播和计算图
- 3.15 数值稳定性和模型初始化
- 3.16 实战kaggle比赛:房价预测
- 4 深度学习计算
- 4.1 模型构造
- 4.2 模型参数的访问、初始化和共享
- 4.3 模型参数的延后初始化
- 4.4 自定义层
- 4.5 读取和存储
- 4.6 GPU计算
- 5 卷积神经网络
- 5.1 二维卷积层
- 5.2 填充和步幅
- 5.3 多输入通道和多输出通道
- 5.4 池化层
- 5.5 卷积神经网络(LeNet)
- 5.6 深度卷积神经网络(AlexNet)
- 5.7 使用重复元素的网络(VGG)
- 5.8 网络中的网络(NiN)
- 5.9 含并行连结的网络(GoogLeNet)
- 5.10 批量归一化
- 5.11 残差网络(ResNet)
- 5.12 稠密连接网络(DenseNet)
- 6 循环神经网络
- 6.1 语言模型
- 6.2 循环神经网络
- 6.3 语言模型数据集(周杰伦专辑歌词)
- 6.4 循环神经网络的从零开始实现
- 6.5 循环神经网络的简单实现
- 6.6 通过时间反向传播
- 6.7 门控循环单元(GRU)
- 6.8 长短期记忆(LSTM)
- 6.9 深度循环神经网络
- 6.10 双向循环神经网络
- 7 优化算法
- 7.1 优化与深度学习
- 7.2 梯度下降和随机梯度下降
- 7.3 小批量随机梯度下降
- 7.4 动量法
- 7.5 AdaGrad算法
- 7.6 RMSProp算法
- 7.7 AdaDelta
- 7.8 Adam算法
- 8 计算性能
- 8.1 命令式和符号式混合编程
- 8.2 异步计算
- 8.3 自动并行计算
- 8.4 多GPU计算
- 9 计算机视觉
- 9.1 图像增广
- 9.2 微调
- 9.3 目标检测和边界框
- 9.4 锚框
- 10 自然语言处理
- 10.1 词嵌入(word2vec)
- 10.2 近似训练
- 10.3 word2vec实现
- 10.4 子词嵌入(fastText)
- 10.5 全局向量的词嵌入(Glove)
- 10.6 求近义词和类比词
- 10.7 文本情感分类:使用循环神经网络
- 10.8 文本情感分类:使用卷积网络
- 10.9 编码器--解码器(seq2seq)
- 10.10 束搜索
- 10.11 注意力机制
- 10.12 机器翻译