如何使用gensim库的word2vec模型和Python计算句子相似度

145
根据 Gensim Word2Vec,我可以使用 gensim 包中的 word2vec 模型来计算 2 个单词之间的相似度。
例如:
trained_model.similarity('woman', 'man') 
0.73723527

然而,word2vec模型无法预测句子的相似性。我在gensim中发现了具有句子相似度的LSI模型,但它似乎不能与word2vec模型结合使用。我拥有的每个句子的语料库长度不是很长(少于10个单词)。那么,有没有简单的方法来实现这个目标呢?


4
有一个 ACL 教程讨论了这个问题(以及其他事情):https://www.youtube.com/watch?v=_ASOqXiWBVo&feature=youtu.be - Emiel
7
现在您可以使用gensim的doc2vec模块,从中获取句子相似度。 - kampta
@kampta。你能推荐一些展示实现的文章吗? - Ian_De_Oliveira
1
请查看https://rare-technologies.com/doc2vec-tutorial/。 - kampta
14个回答

99

这实际上是一个相当具有挑战性的问题。计算句子相似度需要建立一个句子的语法模型,了解等效结构(例如,“他昨天走到商店”和“昨天,他走到商店”),不仅在代词和动词中找到相似性,还要在专有名词中找到统计共现/关系,并在许多真实文本示例中找到相似之处。

最简单的方式是- 虽然我不知道它的表现如何,而且肯定不能为您提供最佳结果- 首先删除所有“停用词”(像“the”,“an”等不太影响句子意义的词),然后在两个句子中运行word2vec,汇总一个句子中的向量,汇总另一个句子中的向量,然后找到总和之间的差异。通过对它们进行求和而不是逐个单词进行差异,你至少不会受到单词顺序的影响。也就是说,这种方式将在许多方面失败,并且绝不是一个好的解决方案(尽管解决此问题的好方法几乎总涉及一些NLP、机器学习和其他巧妙的技术)。

所以,简而言之,答案是否定的,没有容易的方法(至少没有做得好的方法)。


5
我认为你是正确的。最简单的方法是累加一个句子中所有词向量,并找到这些总和之间的差异。顺便问一下,这种简单的方法会受到单词数量的影响吗?因为一个句子中的单词越多,直方图累加的就越多。 - zhfkt
2
@zhfkt,很可能是这样。因此,您可能需要通过单词数量或其他方式进行划分,以尝试消除这种影响。无论如何,任何这样的启发式方法都会严重存在缺陷。 - Michael Aaron Safyan
2
请参见 https://datascience.stackexchange.com/questions/23969/sentence-similarity-prediction。 - Matt L.

83

由于您正在使用gensim,您应该使用它的doc2vec实现。doc2vec是对phrase、sentence和document级别的word2vec的扩展。这是一个相当简单的扩展,可以在此处找到描述。

http://cs.stanford.edu/~quocle/paragraph_vector.pdf

Gensim很棒,因为它直观、快速和灵活。更棒的是,你可以从官方的word2vec页面获取预训练的词向量,并且gensim的Doc2Vec模型中的syn0层被公开,这样你就可以使用这些高质量的向量来生成词向量!

GoogleNews-vectors-negative300.bin.gz(在Google Code中链接)

我认为gensim肯定是将句子嵌入向量空间最简单(到目前为止也是最好的)的工具。

除了Le&Mikolov论文中提出的技术外,还存在其他的句子到向量的技术。斯坦福大学的Socher和Manning无疑是这个领域中最著名的两位研究人员。他们的工作基于组合的原则-句子的语义来自于:

1. semantics of the words

2. rules for how these words interact and combine into phrases

They've proposed a few models (getting increasingly more complex) for how to use compositionality to build sentence-level representations.
2011 - Unfolding Recursive Autoencoder (comparatively simple. start here if interested)
2012 - Matrix-Vector Neural Network 2013 - Neural Tensor Network 2015 - Tree LSTM 他的论文都可以在socher.org上找到。其中一些模型是可用的,但我仍然建议使用gensim的doc2vec。首先,2011年的URAE不是特别强大。此外,它预先训练了适用于重述新闻数据的权重。他提供的代码不允许您重新训练网络。您也无法更换不同的词向量,因此您将被限制使用来自Turian的2011年pre-word2vec嵌入。这些向量肯定不及word2vec或GloVe的水平。
尚未使用Tree LSTM,但它似乎非常有前途!
总之,使用gensim的doc2vec。但是还存在其他方法!

1
你是否有更多关于如何使用预训练的word2vec值初始化doc2vec模型的信息? - Simon Hessner

53

如果您正在使用word2vec,则需要计算每个句子/文档中所有单词的平均向量,并在向量之间使用余弦相似度:

import numpy as np
from scipy import spatial

index2word_set = set(model.wv.index2word)

def avg_feature_vector(sentence, model, num_features, index2word_set):
    words = sentence.split()
    feature_vec = np.zeros((num_features, ), dtype='float32')
    n_words = 0
    for word in words:
        if word in index2word_set:
            n_words += 1
            feature_vec = np.add(feature_vec, model[word])
    if (n_words > 0):
        feature_vec = np.divide(feature_vec, n_words)
    return feature_vec

计算相似性:

s1_afv = avg_feature_vector('this is a sentence', model=model, num_features=300, index2word_set=index2word_set)
s2_afv = avg_feature_vector('this is also sentence', model=model, num_features=300, index2word_set=index2word_set)
sim = 1 - spatial.distance.cosine(s1_afv, s2_afv)
print(sim)

> 0.915479828613

8
可以请您对index2word_set和model.index2word进行进一步解释吗?谢谢。 index2word_set是一个包含了所有词汇的集合,而model.index2word则是一个列表,其中每个元素都是一个词汇。这两个对象通常在自然语言处理中使用,用于建立单词到索引的映射关系,并且可以帮助我们更轻松地访问词汇表中的单词。 - theteddyboy
3
请注意,计算“平均向量”和不计算它一样是一种任意的选择。 - gented
4
我很惊讶为什么这不是最佳答案,它的效果相当好并且没有像平均方法那样存在序列问题。 - Asim
2
这就是我一直在寻找的答案。解决了我的问题。感谢您提供的解决方案。 - iRunner

28

你可以使用词移距离算法(Word Mover's Distance algorithm)。这里有一个关于WMD的简单描述

#load word2vec model, here GoogleNews is used
model = gensim.models.KeyedVectors.load_word2vec_format('../GoogleNews-vectors-negative300.bin', binary=True)
#two sample sentences 
s1 = 'the first sentence'
s2 = 'the second text'

#calculate distance between two sentences using WMD algorithm
distance = model.wmdistance(s1, s2)

print ('distance = %.3f' % distance)

提示:如果您遇到有关导入pyemd库的错误,您可以使用以下命令进行安装:

pip install pyemd

2
我之前使用过WMD,它的效果相当不错,但是在处理大语料库时会出现问题。建议尝试使用SoftCosineSimilarity。这个工具也可以在gensim中找到(https://twitter.com/gensim_py/status/963382840934195200)。 - krinker
1
WMD 不是很快,但当你想查询一个语料库时非常有用。 - Amartya

18

一旦计算出两组单词向量的总和,应该取向量之间的余弦值而不是差值。可以通过对两个向量进行标准化并计算点积来计算余弦值。因此,单词计数不是一个因素。


1
你能提供一些伪代码来说明如何实现这个功能吗?(我不使用gensim/python) - dcsan

17

我想更新现有的解决方案,以帮助那些要计算句子语义相似度的人。

步骤1:

使用gensim加载适合的模型,并计算句子中单词的词向量,将它们存储为一个单词列表

步骤2: 计算句子向量

以前计算句子间的语义相似性很困难,但最近提出了一篇名为"A SIMPLE BUT TOUGH-TO-BEAT BASELINE FOR SENTENCE EMBEDDINGS"的论文,建议通过计算句子中单词向量的加权平均值,然后去除其在第一主成分上的投影来计算。这里一个词w的权重是a / (a + p(w)),其中a是一个参数,p(w)是(估计的)称为平滑逆频率的词频。这种方法表现得更好。

给出了一个使用SIF(平滑逆频率)方法计算句子向量的简单代码,该方法在论文中提出这里

步骤3: 使用sklearn cosine_similarity加载两个句子向量并计算相似度。

这是最简单和高效的方法来计算句子相似度。


4
很好的论文。注意:SIF实现的链接需要编写get_word_frequency()方法,可以使用Python的Counter()轻松完成,并返回一个字典,其中键为唯一单词w,值为#w/#文档总长度。 - Quetzalcoatl

16

文档中有一个函数,它接受单词列表并比较它们之间的相似性。

s1 = 'This room is dirty'
s2 = 'dirty and disgusting room' #corrected variable name

distance = model.wv.n_similarity(s1.lower().split(), s2.lower().split())

8
我正在使用以下方法,它表现良好。 首先需要运行一个POSTagger,然后过滤掉句子中的停用词(决定词、连词等)。我推荐使用TextBlob APTagger。 然后通过取句子中每个单词向量的平均值来构建word2vec。在Gemsim word2vec中,n_similarity方法正是通过允许传递两组要比较的单词来实现这一点。

1
将向量取平均值和将它们相加以创建句向量有什么区别? - Καrτhικ
1
区别在于对于所有句子,向量大小都是固定的。 - lechatpito
1
只要使用余弦相似度,大小没有任何区别。@lechatpito与向量大小无关。向量是求和的,而不是连接的。 - Wok

7

2
值得一提的是,我们简要介绍一下所呈现算法的工作原理。您基本上需要为每个话语添加一个唯一的“标记”,并计算word2vec向量。最终,您将获得语料库中每个单词的单词向量(前提是您要求所有单词,包括唯一的单词)。话语中的每个唯一“标记”都将代表该话语。关于论文中呈现的结果存在一些争议,但这是另外一个故事了。 - Vladislavs Dovgalecs

6

Gensim 实现了一个名为 Doc2Vec 的模型,用于段落嵌入

有不同的教程以 IPython 笔记本的形式呈现:

另一种方法会依赖于 Word2VecWord Mover's Distance (WMD),如此教程所示:

另一种解决方案是依赖于平均向量:

from gensim.models import KeyedVectors
from gensim.utils import simple_preprocess    

def tidy_sentence(sentence, vocabulary):
    return [word for word in simple_preprocess(sentence) if word in vocabulary]    

def compute_sentence_similarity(sentence_1, sentence_2, model_wv):
    vocabulary = set(model_wv.index2word)    
    tokens_1 = tidy_sentence(sentence_1, vocabulary)    
    tokens_2 = tidy_sentence(sentence_2, vocabulary)    
    return model_wv.n_similarity(tokens_1, tokens_2)

wv = KeyedVectors.load('model.wv', mmap='r')
sim = compute_sentence_similarity('this is a sentence', 'this is also a sentence', wv)
print(sim)

最后,如果您可以运行Tensorflow,可以尝试:https://tfhub.dev/google/universal-sentence-encoder/2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接