---
type: slides
---

# 词向量和语义相似度

Notes: 本节课中我们要学习如何用spaCy来判断
文档document、跨度span或者词符token之间有多相似。

我们还会学到如何在实际的自然语言处理应用中用到词向量。

---

# 对比语义相似度

- `spaCy`可以对比两个实例来判断它们之间的相似度
- `Doc.similarity()`、`Span.similarity()`和`Token.similarity()`
- 使用另一个实例作为参数返回一个相似度分数(在`0`和`1`之间)
- **注意：**我们需要一个含有词向量的流程，比如：
  - ✅ `en_core_web_md` (中等)
  - ✅ `en_core_web_lg` (大)
  - 🚫 **而不是** `en_core_web_sm` (小)

Notes: spaCy能够比较两个实例然后判断它们有多相似，
这包括了文档document、跨度span或者单个的词符token。

`Doc`、`Token`和`Span`实例都有一个`.similarity`的方法可以传进另一个实例作为参数
然后返回一个0到1之间的浮点数，这个浮点数代表了两个实例之间的相似度。

非常重要的一点：要计算相似度，我们必须需要一个比较大的含有词向量的spaCy流程。

比如，我们可以用中等或者大的英文流程，但**不能用**小流程。
所以如果我们一旦要用到词向量就需要寻找那些名字后面是"md"或者"lg"的流程。
更多资料可以参考[文档](https://spacy.io/models)。

---

# 相似度举例(1)

```python
# 读取一个有词向量的较大流程
nlp = spacy.load("en_core_web_md")

# 比较两个文档
doc1 = nlp("I like fast food")
doc2 = nlp("I like pizza")
print(doc1.similarity(doc2))
```

```out
0.8627204117787385
```

```python
# 比较两个词符
doc = nlp("I like pizza and pasta")
token1 = doc[2]
token2 = doc[4]
print(token1.similarity(token2))
```

```out
0.7369546
```

Notes: 我们来看看这个例子。我们想要找到两篇文档是否相似。

首先我们读取中等规模的英文流程"en_core_web_md"，

然后可以创建两个doc实例，用第一个doc的`similarity`方法来与第二个doc实例做比较。

这里我们看到对"I like fast food"和"I like pizza"，
模型给出了一个相对高的相似度分数0.86。

同样的方法在词符上也适用。

在词向量中，词符"pizza"和"pasta"是比较相似的，相似度分数有0.7。

---

# 相似度举例(2)

```python
# 对比一篇文章和一个词符
doc = nlp("I like pizza")
token = nlp("soap")[0]

print(doc.similarity(token))
```

```out
0.32531983166759537
```

```python
# 对比一个跨度span和一篇文档
span = nlp("I like pizza and pasta")[2:5]
doc = nlp("McDonalds sells burgers")

print(span.similarity(doc))
```

```out
0.619909235817623
```

Notes: 我们还可以用`similarity`方法来计算不同种类的实例之间的相似度，

比如一个文档和一个词符。

我们看到这里的相似度分数很低，
也就是说模型认为这两个实例差别比较大。

我们再看另一个例子，把一个跨度span "pizza and pasta"和另一个关于麦当劳的文档做对比。

最后的分数是0.61，所以模型认为这两个也是比较相似的。

---

# spaCy是如何判断相似度的？

- 相似度是通过**词向量**计算的
- 词向量是一个词汇的多维度的语义表示
- 词向量是用诸如[Word2Vec](https://en.wikipedia.org/wiki/Word2vec)
  这样的算法在大规模语料上面生成的
- 词向量可以是spaCy流程的一部分
- 默认我们使用余弦相似度，但也有其它计算相似度的方法
- `Doc`和`Span`的向量默认是由其词符向量的平均值计算得出的
- 短语的向量表示要优于长篇文档，因为后者含有很多不相关的词

Notes: spaCy在后台到底是怎么做到相似度计算的？

相似度是通过词向量计算的，词向量是一个词汇的多维度的语义表示。

可能你听说过Word2Vec，这就是常常被用来从原始语料中训练出词向量的其中一种算法。

词向量可以是spaCy流程的一部分。

默认spaCy会返回两个向量的余弦相似度，但有需要时我们也可以替换为其它计算相似度的方法。

一个包含了多个词符的实例，比如`Doc`和`Span`，
默认的向量值的计算方法是其中所有词符向量的平均值。

这也是为什么通常短语的向量更有价值因为其中不相关的词会比较少。

---

# spaCy中的词向量

```python
# 导入一个含有词向量的较大的流程
nlp = spacy.load("en_core_web_md")

doc = nlp("I have a banana")
# 通过token.vector属性获取向量
print(doc[3].vector)
```

```out
 [2.02280000e-01,  -7.66180009e-02,   3.70319992e-01,
  3.28450017e-02,  -4.19569999e-01,   7.20689967e-02,
 -3.74760002e-01,   5.74599989e-02,  -1.24009997e-02,
  5.29489994e-01,  -5.23800015e-01,  -1.97710007e-01,
 -3.41470003e-01,   5.33169985e-01,  -2.53309999e-02,
  1.73800007e-01,   1.67720005e-01,   8.39839995e-01,
  5.51070012e-02,   1.05470002e-01,   3.78719985e-01,
  2.42750004e-01,   1.47449998e-02,   5.59509993e-01,
  1.25210002e-01,  -6.75960004e-01,   3.58420014e-01,
 -4.00279984e-02,   9.59490016e-02,  -5.06900012e-01,
 -8.53179991e-02,   1.79800004e-01,   3.38669986e-01,
  ...
```

Notes: 这个例子能让我们大致了解这些向量长什么样。

首先我们再次读取中等大小的流程，这个流程含有词向量。

然后我们处理一段文本，用`.vector`属性来查找一个词符的向量。

结果是"banana"这个词的一个300维的向量。

---

# 基于应用场景的相似度

- 对很多应用都很有用，比如推荐系统、查重系统等
- “相似度”并没有一个客观的定义方法
- 主要还是决定于实际场景和所支持的应用是要做什么

```python
doc1 = nlp("I like cats")
doc2 = nlp("I hate cats")

print(doc1.similarity(doc2))
```

```out
0.9501447503553421
```

Notes: 判断相似度对很多种不同的应用都很有帮助。比如基于用户的阅读历史来推荐相似的文章。
相似度还可以帮助标记重复内容，比如在线平台里面的帖子。

然而我们要明确知道并没有一个绝对客观的定义来判断什么是相似的什么不是，
这要决定于实际场景和所支持的应用是要做什么。

我们看一个例子：spaCy默认的词向量会对"I like cats"和"I hate cats"这两句哈给出非常高的相似度分数。这是有道理的，因为两个文本都在讲关于猫的看法。
但在另一个应用场景里，你可能希望这两句话是 _非常不同_ 的，因为它们的看法是完全相反的。

---

# 上手练习吧！

Notes: 轮到你自己上手练习了。我们来试试spaCy的词向量
并用它来判断一些相似度吧。
