Auto Byte

专注未来出行及智能汽车科技

微信扫一扫获取更多资讯

Science AI

关注人工智能与其他前沿技术、基础学科的交叉研究与融合发展

微信扫一扫获取更多资讯

Synonyms,一个开源的中文近义词工具包

近日,Hai Liang Wang 和胡小夕在 GitHub 开放了一个中文近义词工具包 Synonyms,它可用于如文本对齐、推荐算法、相似度计算、语义偏移、关键字提取、概念提取、自动摘要、搜索引擎等很多 NLP 任务。该工具包目前能搜索近义词和比较语句相似度等任务,且词汇量达到了 125,792。机器之心也尝试使用 Synonyms 搜索一段中文的近义词,并有非常不错的反馈。


项目地址:https://github.com/huyingxi/Synonyms


该中文近义词工具包采用的基本技术是 Word2vec,因此在介绍该工具的同时我们会简要介绍词嵌入方法。此外,Synonyms 的安装十分便捷,我们可以直接使用命令 pip install -U synonyms 完成。该工具包兼容 Python 2 和 Python 3,且目前的稳定版为 v2.0,以下是使用 Synonyms 工具的效果:




如果我们想把单词输入机器学习模型,除非使用基于树的方法,否则需要把单词转换成一些数值向量。一种直接的方法是使用「one-hot encoding」方法将单词转换为稀疏表示,如下所示向量中只有一个元素设置为 1,其余为 0。



这种方法的缺点在于一个词的向量长度等于词汇表的大小,且非常稀疏。不仅如此,这种方法剥离了单词的所有局部语境,我们不能通过向量表示这个词的概念。因此,我们需要使用更高效的方法表示文本数据,而这种方法可以保存单词的上下文的信息。这是 Word2Vec 方法的初衷。


一般来说,Word2Vec 方法由两部分组成。首先是将高维 one-hot 形式表示的单词映射成低维向量。例如将 10,000 列的矩阵转换为 300 列的矩阵,这一过程被称为词嵌入。第二个目标是在保留单词上下文的同时,从一定程度上保留其意义。Word2Vec 实现这两个目标的方法有 skip-gram 和 CBOW 等,skip-gram 会输入一个词,然后尝试估计其它词出现在该词附近的概率。还有一种与此相反的被称为连续词袋模型(Continuous Bag Of Words,CBOW),它将一些上下文词语作为输入,并通过评估概率找出最适合(概率最大)该上下文的词。


对于连续词袋模型而言,Mikolov 等人运用目标词前面和后面的 n 个词来同时预测这个词。他们称这个模型为连续的词袋(CBOW),因为它用连续空间来表示词,而且这些词的先后顺序并不重要。



连续的词袋(Mikolov 等人,2013 年)


CBOW 可以看作一个具有先知的语言模型,而 skip-gram 模型则完全改变将语言模型的目标:它不像 CBOW 一样从周围的词预测中间的词;恰恰相反,它用中心语去预测周围的词:



Skip-gram(Mikolov 等人,2013)


在加载 Synonyms 中,我们可以看到会打印出「loaded (125796, 100) matrix from...」,因此 Synonyms 采用的词向量维度为 100。


用法


输出近义词向量:


import synonyms
print("人脸: %s" % (synonyms.nearby("人脸")))
print("识别: %s" % (synonyms.nearby("识别")))
print("NOT_EXIST: %s" % (synonyms.nearby("NOT_EXIST")))


synonyms.nearby(WORD) 会返回一个包含两项的列表:


[[nearby_words], [nearby_words_score]],nearby_words 是 WORD 的近义词向量,也以列表的方式存储,并且按照距离的长度由近及远排列,nearby_words_score 是 nearby_words 中对应词的距离分数,分数在 (0-1) 区间内,越接近于 1,代表越相近。比如:


synonyms.nearby(人脸) = [
    ["图片", "图像", "通过观察", "数字图像", "几何图形", "脸部", "图象", "放大镜", "面孔", "Mii"], 
    [0.597284, 0.580373, 0.568486, 0.535674, 0.531835, 0.530
095, 0.525344, 0.524009, 0.523101, 0.516046]]


在出现集外词的情况下,返回 [[], []],目前的字典大小: 125,792。


机器之心尝试将一整段关于 Word2vec 的中文分割为一个个单词,再使用 Synonyms 工具对分词的结果取近义词,以下是试验结果:

Word2Vec : [[], []]
方法 : [['方式', '手段', '新方法', '原理', '工具', '步骤', '算法', '分析方法', '演算法', '方法有'], [0.770168, 0.698663, 0.696657, 0.693642, 0.690686, 0.687199, 0.675024, 0.673502, 0.657432, 0.656965]]
由 : [[], []]
两 : [[], []]
部分 : [['部份', '大部分', '一小', '其余部分', '外', '大多', '之外', '大部份', '以外', '均'], [0.819274, 0.77532, 0.696557, 0.630384, 0.625031, 0.617763, 0.609703, 0.600958, 0.599957, 0.592684]]
组成 : [['构成', '分成', '组合成', '组合而成', '都由', '组建', '分为', '都是由', '成员', '合组'], [0.672614, 0.633627, 0.622906, 0.601468, 0.579645, 0.576105, 0.56926, 0.558928, 0.555657, 0.55275]]
。 : [[], []]
首先 : [[], []]
是 : [[], []]
将 : [[], []]
高维 : [['萧士达', '浦罗哥', '希尔伯特', '庞加莱', '欧拉', '几何', '群论', '图论', '高斯', '线性代数'], [0.585315, 0.570614, 0.52954, 0.507108, 0.503025, 0.501703, 0.49944, 0.496649, 0.495397, 0.488994]]
独热 : [[], []]
形式 : [['方式', '型式', '性质', '表现形式', '表达方式', '概念', '媒介', '内容', '规则', '简单'], [0.748891, 0.621915, 0.6181, 0.617813, 0.617308, 0.611825, 0.583548, 0.578797, 0.574129, 0.569158]]
表示 : [['回应', '指出', '透露', '表达', '声称', '对此', '暗示', '否认', '说明', '问到'], [0.733619, 0.718644, 0.700614, 0.69215, 0.669181, 0.663752, 0.65325, 0.648804, 0.64028, 0.632011]]
的 : [[], []]
单词 : [['单字', '词语', '短语', '字词', '词根', '词组', '句子', '音节', '词汇', '前缀'], [0.819558, 0.793184, 0.779852, 0.775173, 0.771767, 0.767386, 0.747697, 0.733435, 0.722758, 0.715587]]
映射 : [['同构', '拓扑', '等价', '同态', '算子', '流形', '给定', '子集', '函数', '向量'], [0.751925, 0.737796, 0.736626, 0.723419, 0.72286, 0.717205, 0.70084, 0.699233, 0.698332, 0.682505]]
成 : [['出', '变成', '成为', '演变成', '变为', '起来', '转成', '形成', '已成', '转变成'], [0.626685, 0.597042, 0.578111, 0.554632, 0.55223, 0.538277, 0.498342, 0.482658, 0.480671, 0.479747]]
低 : [['较低', '较高', '高', '更高', '极低', '很低', '低于', '高于', '偏高', '较差'], [0.880493, 0.829336, 0.814844, 0.751234, 0.744953, 0.73976, 0.715566, 0.715089, 0.709287, 0.686807]]
维向量 : [[], []]
。 : [[], []]
例如 : [[], []]
将 : [[], []]
  : [[], []]
10 : [[], []]
, : [[], []]
000 : [[], []]
 列 : [[], []]
的 : [[], []]
矩阵 : [['行列式', '乘积', '向量', '乘法', '算子', '特征值', '线性', '函数', 'formula_', '实数'], [0.78328, 0.743294, 0.7416, 0.735933, 0.705292, 0.697315, 0.697315, 0.690086, 0.689624, 0.683013]]
转换 : [['切换', '变换', '转换成', '叠加', '匹配', '类比', '传输', '交换', '操作', '简化'], [0.733128, 0.718899, 0.715552, 0.628927, 0.621122, 0.612286, 0.611997, 0.609811, 0.604056, 0.603366]]
为 : [[], []]
  : [[], []]
300 : [[], []]
 列 : [[], []]
的 : [[], []]
矩阵 : [['行列式', '乘积', '向量', '乘法', '算子', '特征值', '线性', '函数', 'formula_', '实数'], [0.78328, 0.743294, 0.7416, 0.735933, 0.705292, 0.697315, 0.697315, 0.690086, 0.689624, 0.683013]]
。 : [[], []]
这个 : [[], []]
过程 : [['步骤', '流程', '处理过程', '操作过程', '现实生活', '程序', '机制', '方法', '方式', '每一次'], [0.654057, 0.627095, 0.612206, 0.588535, 0.587763, 0.584512, 0.570078, 0.56342, 0.556587, 0.555285]]
被 : [[], []]
称为 : [['称作', '称之为', '被称作', '称做', '专指', '统称', '叫作', '叫做', '称呼', '特指'], [0.931905, 0.888719, 0.883803, 0.833798, 0.741266, 0.73327, 0.725112, 0.720903, 0.714699, 0.695307]]
词 : [['词语', '该词', '名词', '用法', '词汇', '用语', '含义', '辞汇', '字词', '术语'], [0.865391, 0.833362, 0.817649, 0.810989, 0.79878, 0.752893, 0.741603, 0.730943, 0.727771, 0.725913]]
嵌入 : [['映射', '内嵌', '填充', '封装', '插入', '存储', '链接', '浸入', '粘贴', '构造'], [0.640392, 0.630447, 0.618274, 0.60825, 0.604815, 0.59937, 0.566457, 0.55673, 0.554925, 0.554157]]
。 : [[], []]
第二 : [[], []]
个 : [[], []]
目标 : [['目的', '最终目标', '能够', '尽可能', '战略目标', '策略', '能力', '远距离', '任务', '必须'], [0.743574, 0.66224, 0.652209, 0.625992, 0.595744, 0.595451, 0.586327, 0.585261, 0.582277, 0.579012]]
是 : [[], []]
在 : [[], []]
保留 : [['保有', '保存', '留存', '沿用', '原有', '延续', '存留', '更改', '恢复', '维持'], [0.741621, 0.70751, 0.695012, 0.667181, 0.644165, 0.609553, 0.57785, 0.558837, 0.55696, 0.554073]]
单词 : [['单字', '词语', '短语', '字词', '词根', '词组', '句子', '音节', '词汇', '前缀'], [0.819558, 0.793184, 0.779852, 0.775173, 0.771767, 0.767386, 0.747697, 0.733435, 0.722758, 0.715587]]
上下文 : [['语义', '语句', '字符串', 'codice_', '句子', '正则表达式', '变量', '表达式', '子句', '自然语言'], [0.777081, 0.745407, 0.737112, 0.710487, 0.706909, 0.700924, 0.693529, 0.688357, 0.682883, 0.678303]]
的 : [[], []]
同时 : [[], []]
, : [[], []]
从 : [[], []]
一定 : [[], []]
程度 : [['水平', '因素', '高度', '素质', '层面', '能力', '状况', '相对', '情况', '原因'], [0.643124, 0.635219, 0.627448, 0.624889, 0.619942, 0.602435, 0.599929, 0.598476, 0.596007, 0.594679]]
上 : [[], []]
保留 : [['保有', '保存', '留存', '沿用', '原有', '延续', '存留', '更改', '恢复', '维持'], [0.741621, 0.70751, 0.695012, 0.667181, 0.644165, 0.609553, 0.57785, 0.558837, 0.55696, 0.554073]]
其 : [[], []]
意义 : [['涵义', '含义', '意涵', '内涵', '象征意义', '本质', '含意', '意味', '概念', '性质'], [0.784661, 0.767263, 0.758177, 0.749875, 0.734564, 0.730683, 0.671735, 0.670497, 0.666011, 0.658259]]
。 : [[], []]

两个句子的相似度比较:

 sen1 = "发生历史性变革"
    sen2 = "发生历史性变革"
    r = synonyms.compare(sen1, sen2, seg=True)

其中,参数 seg 表示 synonyms.compare 是否对 sen1 和 sen2 进行分词,默认为 True。返回值:[0-1],并且越接近于 1 代表两个句子越相似。


旗帜引领方向 vs 道路决定命运: 0.429
旗帜引领方向 vs 旗帜指引道路: 0.93
发生历史性变革 vs 发生历史性变革: 1.0
句子相似度准确率
在 SentenceSim 上进行测试:

地址:https://github.com/fssqawj/SentenceSim/blob/master/dev.txt

测试语料条数为:7516条.
设定阈值 0.5:
  相似度 > 0.5, 返回相似;
  相似度 < 0.5, 返回不相似.

评测结果:

正确 : 6626,错误 : 890,准确度 : 88.15%

关于距离计算和阀值选取,参考 Synonyms/issues/6。


以友好的方式打印近义词,方便调试,display 调用了 synonyms#nearby 方法。

>>> synonyms.display("飞机")
'飞机'近义词:
  1. 架飞机:0.837399
  2. 客机:0.764609
  3. 直升机:0.762116
  4. 民航机:0.750519
  5. 航机:0.750116
  6. 起飞:0.735736
  7. 战机:0.734975
  8. 飞行中:0.732649
  9. 航空器:0.723945
  10. 运输机:0.720578

最后,Synonyms 项目的作者胡小夕是北京邮电大学研究生,目前实习于今日头条 AI LAB。从事自然语言处理方向研究,在智能客服,知识图谱等领域都有相关研究开发经验。研发模型在文体分类权威数据集 TREC 上达到目前最优精度,申请深度学习与自然语言处理结合的国家发明专利 5 项。

@online{Synonyms:hain2017,
  author = {Hai Liang Wang, Hu Ying Xi},
  title = {中文近义词工具包Synonyms},
  year = 2017,
  url = {https://github.com/huyingxi/Synonyms},
  urldate = {2017-09-27}
}
入门
46
和gensim的word2vec有什么区别吗
看了下代码,好像使用的是一个预训练好的词向量模型。我觉得通过词向量模型计算出的余弦距离表示的是词的“相关词”而不是“近义词”。比如查询“很好”的词向量的余弦距离top 10里会有“很差”,而很差是很好的反义词,不是近义词。
1
同意,我用词向量的时候也遇到了这个问题,距离近的只是相关词而非近义词,有时候甚至是反义词。有文章(https://arxiv.org/abs/1603.00892)提出了方法来改进词向量,但前提是有一个近义词库。 我自己搜集了一个(https://github.com/Keson96/SynoCN),分享一下
1
没你说的那个功能。他训练了一些模型,然后重新做了接口给我们用而已......“很好”的top10里面是有“很差”的。如果训练这个模型的时候有“科学地分词”+“T级以上的语料”那还有点意义。它训的这个模型好像只支持部分“名词”和“动词”的查询。“看”,“就”,“天各一方”,等等都没法查询到近义词。
效果太差了吧~