找寻近义词的三种方法

找寻近义词的三种方法

比如我手里有两个词语:孩子知道,我想获得他们的近义词;或者我想判断它们和另外几个词如:孩童父母清楚失信家长小子 等是不是近义词;

这类找寻近义词问题的基本解决思路都是差不多的,可以分为两步:

  • 先将词语转换成向量
  • 然后计算向量间的相似度

其中,生成词向量有很多种方法,如:使用Word2Vec训练生成、使用Bert生成、使用开放的训练好的词向量等;计算相似度也有多种方式。

我此次尝试,试了下谷歌预训练过的中文 Bert 模型 https://github.com/google-research/bert#pre-trained-models 加上封装好的 Bert as Service 模块https://github.com/hanxiao/bert-as-service 来获得词向量,也试了试腾讯AI实验室开源的词向量数据集https://ai.tencent.com/ailab/nlp/embedding.html来获得词向量。

至于相似度,我是用的基本的余弦相似度计算方法,没有去了解其他优化方法。

在了解上面两个方法的时候,我还发现了一个中文近义词工具包 Synonyms

所以,下面将分别对这三种方式做简单记录。

Synonyms 工具包

这是一个python的模块,使用前需要安装一下:

pip install synonyms -i https://mirrors.aliyun.com/pypi/simple

使用起来十分简单:

>>> import synonyms  # 先导入模块
# 这里会有一些提示,是在加载字典和模型
>>> synonyms.nearby("小孩")  # 获取“小孩”的近义词
(['小孩''孩子''孩童''小孩子''男孩''孩子们''女孩''妈妈''小女孩''男孩子'], [1.00.84272430.80692580.79651320.768802760.755099950.744812550.73796490.72262520.71569645])

上面这个例子的结果显示,和“小孩”这个词最相近的词是“小孩”,相似度1.0;其次是“孩子”,相似度0.8427243;以此类推。这个工具包已经能够满足很多需求了,它还自带了分词、获取词向量、计算两个句子的相似度等功能。

Bert中文预训练模型 + Bert as Service

其实有了Bert中文预训练模型就可以自己写代码生成词向量了,但是,过程稍微繁琐一点。所以,就发现了一个偷懒的方法……使用 Bert as Service 这个python模块。这个模块分为服务端和客户端,服务端和客户端可以安装在两台不同的机器上,服务端负责读取预训练模型和运算,客户端负责发送和接收词语。

使用这个方法首先需要下载Bert中文预训练模型(网址上面有,大小300多兆),然后安装Bert as Service的服务端模块和客户端模块。

pip install bert-serving-server -i https://mirrors.aliyun.com/pypi/simple  # 服务端
pip install bert-serving-client -i https://mirrors.aliyun.com/pypi/simple  # 客户端

本人并没有两台机器,所以我将两个模块都安装到了同一台机器上。

然后我开了一个终端执行命令启动服务端程序:

bert-serving-start -model_dir ./chinese_L-12_H-768_A-12/ -num_worker=1

其中 ./chinese_L-12_H-768_A-12/ 是下载的预训练模型的路径,我的就在当前目录下。-num_worker=1是指使用1个CPU或GPU,这里的官方示例是写的4,视机器情况改改。

经过一段时间的等待,服务端启动了完成了,此时会看到 ready 之类的提示输出,提示我们服务端已经就绪。

这时候,就可以通过客户端来获取词向量了:

from bert_serving.client import BertClient  # 引入相应模块
bc = BertClient()  # 创建一个客户端实例
vectors = bc.encode(["小孩""清楚"])  # 传入需要转换成向量的词语列表,返回结果是对应的向量列表

拿到了vectors,那就可以向量之间计算余弦相似度,排序寻找近义词了。

使用腾讯AI实验室开放的词向量数据集

首先是下载这个数据集,一共6个G

下载完成之后解压,会得到一个 README.txt 说明文件和数据 Tencent_AILab_ChineseEmbedding.txt

使用这个数据集可以借助 gensim 这个模块

pip install gensim -i https://mirrors.aliyun.com/pypi/simple  # 安装这个模块

然后理论上就可以导入数据使用了,也就是:

from gensim.models import KeyedVectors  # 导入gensim模块下的KeyedVectors类,用于导入数据
wv = KeyedVectors.load_word2vec_format('Tencent_AILab_ChineseEmbedding.txt', binary=False)  # 导入词向量数据

但是,这里有但是了,6G大小的数据在解压后是15.5G大小,据说,完整载入内存需要18个G的内存空间,这显然不是手中的笔记本电脑能hold的住的,所以我的做法是从数据集中筛选出我需要的那些词语和向量,得到一份缩减版的数据,然后再导入使用。

wv.most_similar("小孩", topn=3)  # 获取数据中和“小孩”最相近的前三个词语

上面这行代码就可以获取近义词了,这里不需要自己去计算相似度和排序了。

部分结果

以上就是三种获取近义词方法的记录

我用这三种方法试着对哈工大同义词词林https://github.com/BiLiangLtd/WordSimilarity/tree/master/data里的部分数据做了下计算

下面是部分结果:

  • 这是用Bert做的
知道,失信,0.9092331528663635
知道,闻名,0.9076200127601624
知道,霸道,0.9073370695114136

孩子,孩儿,0.9516780376434326
孩子,孩童,0.9505244493484497
孩子,小子,0.9290173053741455
  • 这是用腾讯AI实验室公开的词向量数据做的
知道,明白,0.7958440184593201
知道,可是,0.7454869151115417
知道,清楚,0.7410669326782227

孩子,家长,0.8679977655410767
孩子,父母,0.8400565385818481
孩子,小孩,0.8278622031211853
  • 这是用synonyms工具包做的
知道,继续,0.6520803570747375
知道,如此,0.630182683467865
知道,经典,0.6178067326545715

孩子,来到,0.8424192667007446
孩子,下岗,0.7280047535896301
孩子,苹果,0.6778959035873413

结尾

代码保留在GitHub:

https://github.com/NICE-FUTURE/three-method-to-find-synonyms

希望以后有时间能对上面涉及到的Word2Vec和Bert有更深入的了解,而不仅仅是调几个包看看效果,以上。

9条评论

  1. 从你的结果来看,直接取bert的词向量按相似度聚类得到同义词,效果还不如腾讯开源的词向量呢。

  2. 您好,請問Bert取詞向量然後算相似度,現在的結果是取最高的三個,請問要怎麼呈現與全部詞彙相似度的數據呢?程式上找不到可以改數量的地方,謝謝。

    1. 你好~ 对应的程序在这个位置(https://github.com/NICE-FUTURE/three-method-to-find-synonyms/blob/eb2db0c68a085333ea30f565ad93fb53d07bf12e/method_bert.py#L7),内容为 `cosine = Cosine(n_recommendation=4)`

      这个名为 `n_recommendation` 的参数控制的就是返回的结果数量。我给它传入的值是 4 ,所以最终会得到相似度最高的 4 个结果,然后除去自己和自己的相似度就是博客中展示的 3 个结果。

      如果要呈现全部词语的相似度,将 `cosine = Cosine(n_recommendation=4)` 中的 4 改成数据集的词语总数就可以了。或者,将 4 改成一个远大于词语总数的值也是可以的,因为本质上这一步是通过列表的切片操作实现的。其中 `Cosine()` 是我自己封装的一个类,比较简陋,你可以直接修改它的源码来实现你的需求,对应的代码文件是 `cosine.py`,链接为:https://github.com/NICE-FUTURE/three-method-to-find-synonyms/blob/master/cosine.py

      另,我发现当年使用的 `bert-as-service` 库已经升级成了 `clip-as-service`(https://github.com/jina-ai/clip-as-service),不知 API 变化大不大,有需要还需查看对应文档学习学习。

      感谢交流,祝使用愉快 ~

  3. 您好,請問有辦法將兩組不同的詞彙檔案使用bert-as-service算出相對的相似度嗎?因為現在好像是只有一組詞彙檔案再算相似度,謝謝。

    1. 抱歉,这个问题我也回答不了(可能因为我太久没做文本处理的工作了)。如果有其他人看到此评论,可以帮忙答复一下。

留下评论

您的邮箱地址不会被公开。 必填项已用 * 标注