# 同义词工具

## 引入

同义词工具在NLP一般在数据预处理, 或者**数据增强**时使用, 词用同义词代替, 可以增加可用于训练的语料, 也能增强模型的鲁棒性.

目前我找到的可用来做同义词替换的方法工具有以下几个.

## 同义词词林

哈工大的同义词词林(扩展版)是一个同义词词典, 以`.txt`文件的形式记录. 文件中数据的格式如下:

```
Aa01A01= 人 士 人物 人士 人氏 人选
Aa01A02= 人类 生人 全人类
Aa01A03= 人手 人员 人口 人丁 口 食指
Aa01A04= 劳力 劳动力 工作者
Aa01A05= 匹夫 个人
Aa01A06= 家伙 东西 货色 厮 崽子 兔崽子 狗崽子 小子 杂种 畜生 混蛋 王八蛋 竖子 鼠辈 小崽子
Aa01A07= 者 手 匠 客 主 子 家 夫 翁 汉 员 分子 鬼 货 棍 徒
Aa01A08= 每人 各人 每位
Aa01A09= 该人 此人
Aa01B01= 人民 民 国民 公民 平民 黎民 庶 庶民 老百姓 苍生 生灵 生人 布衣 白丁 赤子 氓 群氓 黔首 黎民百姓 庶人 百姓 全民 全员 萌
```

每行代表一组同义词, 两两之间做同义词, 可以相互替换. 但除了同义词, 还包含在句子中作用近似, 但直观来看词义不同的词:

```
Ad02B03# 西人 意大利人 缅甸人 西方人 阿拉伯人 日本人 美国人 犹太人 捷克人 芬兰人 德国人 比利时人 ...
Ae02A21# 机工 铣工 刨工 锻工 铸工 钳工 电工 焊工 装配工 保全工 电焊工 ...
```

上面一组其实都是代表外国人的词汇, 只是国家的区别, 在句子中都扮演着同样的角色, 在很多情况下都是可以作为`同义词`替换的(除了一些推导或信息抽取的任务吧).

可以看到每行的开始都有一串编码, 编码的意义参考[同义替换：哈工大同义词词林扩展版](https://blog.csdn.net/sinat_33741547/article/details/80016713). 这里关注编码的最后一个字符. 观察上面两大组可以看到他们的编码结尾分别是`=`和`#`, 这分别代表着`相等`和`同类`的意思, 在整理同义词典时, 选择使用. 编码的详情意义参考上面的链接.

整个文件共包含17817组同义词汇, 每组包含的词的数量不同, 也会出现同一个词汇被包含在不同组中, 显然是一词多义的原因. 在使用处理时, 需要注意这一点.

如何使用, 这里有使用的例子, 其中包含代码, 可以参考:

* [NLP数据预处理——同义词替换程序](https://blog.csdn.net/hfutdog/article/details/81107170)
* \[]

其中使用到了哈工大的`pyplt`, 是一款提供了分词, 词性标注, NER等功能的工具, 官方文档为: [使用 pyltp](https://pyltp.readthedocs.io/zh_CN/latest/api.html)

## Synonyms

**Synonyms**是一个专门做**中文近义词**的Python工具包. 与词林使用字典记录固定的近似词不同, Synonyms在得到某个词的相似时, 是通过word2vec中的词向量计算得到的, 利用了上下文中词之间的关系, 根据词向量的距离得到的.

这种方法有以下缺点:

* 得到的词有可能不是词义相近的严格意义的近义词, 而是经常以成对出现在上下文窗口中的单词, 统计上有着密切的关系. 例如:

  ```python
    synonyms.nearby("人脸")
  ```

  得到的结果为:

  ```
    (['人脸', '图片', '通过观察', '几何图形', '图象', '放大镜', '面孔', '貌似', '奇特', '十分相似'],
    [1.0,
    0.5972838,
    0.56848586,
    0.5318347,
    0.52534395,
    0.52400935,
    0.52310055,
    0.50064117,
    0.4851142,
    0.39761642])
  ```

  可以看到这里得到的词汇都不是近义词, 而是经常在同一个上下文窗口中成对出现的组合.

所幸, 由于synonyms计算近义词是通过词向量的距离得到的, 而且求近义词的方法`nearby`也返回了结果中每个单词的相似度衡量. 我们可以简单地通过阈值, 做进一步的筛选. 例如上式中最大的近义词`图片`对应的相似度为`0.597`, 可以认为太低, 从而人脸没有近义词.

```python
synonyms.nearby("问题")
```

```
(['问题', '难题', '原因', '疑虑', '情况', '缺陷', '疑问', '关键问题', '解决办法', '弊端'],
 [1.0,
  0.74893767,
  0.688038,
  0.6788217,
  0.6591137,
  0.65887266,
  0.6470331,
  0.63049304,
  0.62929237,
  0.62681234])
```

`问题`的近义词列表返回的结果, 相似度就高出不少.

总之, 在使用时, 如果想要比较高质量的近义词, 阈值设置高一点. 低阈值会引入更多的近义词词汇, 用来做数据增强时, 可能会提高模型的鲁棒性, 但也可能引入过多的噪声, 造成模型无法收敛或质量较差.

总结就是, `synonyms`包使用`synonyms.nearby(WORD)`方法获取近义词, 返回的是一个二元list, 形式为`[[nearby_words], [nearby_words_score]]`, 每个list的长度都为10. `nearby_words`是近义词列表, `nearby_words_score`是对应的每个近义词的相似度, 越高越好.

因为是使用word2vec, 所以本身是有一个字典的, 输入只能是字典中的单词, 才能转换为词向量, 进而才能得到与其他单词的距离, 产生近义词列表. 如果输入的单词是**OOV**, 则没有近义词列表, 返回`([], [])`.

项目地址为: [huyingxi/Synonyms](https://github.com/huyingxi/Synonyms).

**另外需要注意的是**, 由于使用的是word2vec, 所以一些开源的训练好的word2vec文件也是可以使用的. 更换模型的方法参考项目主页. 推荐使用[腾讯AI Lab的word2vec embedding](https://ai.tencent.com/ailab/nlp/embedding.html).

详细的使用方法参考项目的主页说明, 也可以参考[Synonyms: 中文近义词工具包](http://www.52nlp.cn/synonyms-%E4%B8%AD%E6%96%87%E8%BF%91%E4%B9%89%E8%AF%8D%E5%B7%A5%E5%85%B7%E5%8C%85).
