如何使用 gensim 的 word2vec 模型与 python 计算句子相似度

Posted

技术标签:

【中文标题】如何使用 gensim 的 word2vec 模型与 python 计算句子相似度【英文标题】:How to calculate the sentence similarity using word2vec model of gensim with python 【发布时间】:2014-04-03 12:11:57 【问题描述】:

根据Gensim Word2Vec,我可以使用gensim包中的word2vec模型来计算两个词之间的相似度。

例如

trained_model.similarity('woman', 'man') 
0.73723527

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

【问题讨论】:

有一个 ACL 教程讨论这个问题(除其他外):youtube.com/watch?v=_ASOqXiWBVo&feature=youtu.be 您现在可以使用 gensim 的 doc2vec 并从同一个模块中获取句子相似度 @kampta 。嗨,您能推荐任何显示实现的帖子吗? 结帐rare-technologies.com/doc2vec-tutorial 【参考方案1】:

这实际上是您要问的一个非常具有挑战性的问题。计算句子相似度需要建立句子的语法模型,理解等价结构(例如“他昨天去商店”和“昨天,他去商店”),不仅在代词和动词中,而且在专有名词,在大量真实文本示例中查找统计共现/关系等。

您可以尝试的最简单的方法——虽然我不知道它的效果如何,而且它肯定不会给你最佳的结果——首先是删除所有“停止”词(像“the”这样的词) , "an" 等并没有给句子增加太多意义) 然后对两个句子中的单词运行 word2vec, 将一个句子中的向量相加, 将另一个句子中的向量相加, 然后找到总和之间的差额。通过总结它们而不是进行逐字区分,您至少不会受制于词序。话虽如此,这在很多方面都会失败,并且无论如何都不是一个好的解决方案(尽管这个问题的好的解决方案几乎总是涉及一定数量的 NLP、机器学习和其他聪明才智)。

所以,简短的回答是,不,没有简单的方法可以做到这一点(至少不能做好)。

【讨论】:

我认为你是对的。最简单的方法是把一个句子中所有单词的向量累加起来,求和的差值。顺便问一下,这个简单的方法会受到字数的影响吗?因为一句话中的单词越多,总结的直方图就越多。 @zhfkt,很可能,是的。因此,您可能需要除以单词数或其他一些因素来尝试将其排除。无论哪种方式,任何像这样的启发式都会有严重的缺陷。 另见datascience.stackexchange.com/questions/23969/…【参考方案2】:

由于您使用的是 gensim,您可能应该使用它的 doc2vec 实现。 doc2vec 是 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

他们提出了一些这样的模型(变得越来越复杂),用于如何使用组合性来构建句子级表示。

2011 - unfolding recursive autoencoder(非常简单。如果有兴趣从这里开始)

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,但看起来很有希望!

tl;dr 是的,使用 gensim 的 doc2vec。但其他方法确实存在!

【讨论】:

您是否有更多关于如何使用预训练的 word2vec 值初始化 doc2vec 模型的信息?【参考方案3】:

如果您使用 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

【讨论】:

能否请您对 index2word_set 和 model.index2word 进行更多解释?谢谢。 请注意,计算“平均向量”与根本不计算一样是任意选择。 我很惊讶为什么这不是最佳答案,它工作得很好并且没有平均方法所具有的序列问题。 这是我一直在寻找的答案。解决了我的问题。感谢您的解决方案【参考方案4】:

您可以使用 Word Mover 的距离算法。这是easy description about 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)

P.s.:如果您遇到导入 pyemd 库的错误,您可以使用以下命令安装它:

pip install pyemd

【讨论】:

我以前使用过 WMD,它运行良好,但它会在大型语料库上窒息。试试 SoftCosineSimilarity。也可以在 gensim (twitter.com/gensim_py/status/963382840934195200) 中找到 WMD 不是很快,但是当你想查询语料库时。【参考方案5】:

一旦计算了两组词向量的总和,就应该取向量之间的余弦,而不是 diff。余弦可以通过将两个向量的点积归一化来计算。因此,字数不是一个因素。

【讨论】:

你能提供一些关于如何做到这一点的伪代码(我没有使用 gensim/python)【参考方案6】:

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

第 1 步:

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

第 2 步: 计算句子向量

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

使用SIF(smooth inverse frequency)计算句子向量的简单代码,文中提出的方法已给出here

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

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

【讨论】:

非常好的论文。注意:到 SIF 实现的链接需要编写 get_word_frequency() 方法,这可以通过使用 Python 的 Counter() 并返回带有键的字典轻松完成:唯一词 w,值:#w/#total doc len【参考方案7】:

documentation 中有一个函数可以获取单词列表并比较它们的相似性。

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。 n_similarity method in Gemsim word2vec 正是这样做的,它允许传递两组词进行比较。

【讨论】:

取向量的平均值与将它们相加来创建句子向量有什么区别? 不同之处在于所有句子的向量大小都是固定的 只要使用余弦相似度就没有区别。 @lechatpito 与矢量大小无关。向量是求和的,而不是串联的。【参考方案9】:

Word2Vec 的一些扩展旨在解决比较较长文本(如短语或句子)的问题。其中之一是paragraph2vec 或doc2vec。

“句子和文档的分布式表示” http://cs.stanford.edu/~quocle/paragraph_vector.pdf

http://rare-technologies.com/doc2vec-tutorial/

【讨论】:

值得一提的是所提出的算法是如何工作的。您基本上为每个话语添加一个唯一的“令牌”并计算 word2vec 向量。最后,您将获得语料库中每个单词的单词向量(前提是您要求所有单词,以及唯一的单词)。话语中的每个唯一“标记”都将代表该话语。关于论文中提出的结果存在一些争议,但那是另一回事了。【参考方案10】:

Gensim 为 paragraph embedding 实现了一个名为 Doc2Vec 的模型。

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

Doc2Vec Tutorial on the Lee Dataset Gensim Doc2Vec Tutorial on the IMDB Sentiment Dataset Doc2Vec to wikipedia articles

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

Finding similar documents with Word2Vec and 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

【讨论】:

【参考方案11】:

我已经尝试过之前答案提供的方法。它有效,但它的主要缺点是句子越长,相似度就越大(为了计算相似度,我使用任何两个句子的两个平均嵌入的余弦分数),因为单词越多,语义效果越积极将被添加到句子中。

我想我应该改变主意,改用 this paper 和 this 中研究的句子嵌入。

【讨论】:

【参考方案12】:

如果不使用 Word2Vec,我们还有其他模型可以使用 BERT 进行嵌入。 下面是参考链接 https://github.com/UKPLab/sentence-transformers

pip install -U sentence-transformers

from sentence_transformers import SentenceTransformer
import scipy.spatial

embedder = SentenceTransformer('bert-base-nli-mean-tokens')

# Corpus with example sentences
corpus = ['A man is eating a food.',
          'A man is eating a piece of bread.',
          'The girl is carrying a baby.',
          'A man is riding a horse.',
          'A woman is playing violin.',
          'Two men pushed carts through the woods.',
          'A man is riding a white horse on an enclosed ground.',
          'A monkey is playing drums.',
          'A cheetah is running behind its prey.'
          ]
corpus_embeddings = embedder.encode(corpus)

# Query sentences:
queries = ['A man is eating pasta.', 'Someone in a gorilla costume is playing a set of drums.', 'A cheetah chases prey on across a field.']
query_embeddings = embedder.encode(queries)

# Find the closest 5 sentences of the corpus for each query sentence based on cosine similarity
closest_n = 5
for query, query_embedding in zip(queries, query_embeddings):
    distances = scipy.spatial.distance.cdist([query_embedding], corpus_embeddings, "cosine")[0]

    results = zip(range(len(distances)), distances)
    results = sorted(results, key=lambda x: x[1])

    print("\n\n======================\n\n")
    print("Query:", query)
    print("\nTop 5 most similar sentences in corpus:")

    for idx, distance in results[0:closest_n]:
        print(corpus[idx].strip(), "(Score: %.4f)" % (1-distance))

其他链接 https://github.com/hanxiao/bert-as-service

【讨论】:

【参考方案13】:

Facebook 研究小组发布了一个名为 InferSent 的新解决方案 结果和代码发布在 Github 上,查看他们的 repo。这真是太棒了。我打算使用它。 https://github.com/facebookresearch/InferSent

他们的论文 https://arxiv.org/abs/1705.02364 抽象的: 许多现代 NLP 系统依赖于词嵌入,之前以无监督方式在大型语料库上进行训练,作为基本特征。然而,为更大的文本块(例如句子)获得嵌入的努力并没有那么成功。学习句子的无监督表示的几次尝试都没有达到令人满意的性能以被广泛采用。在本文中,我们展示了使用斯坦福自然语言推理数据集的监督数据训练的通用句子表示如何在广泛的传输任务上始终优于 SkipThought 向量等无监督方法。就像计算机视觉如何使用 ImageNet 来获取特征,然后可以将其转移到其他任务中一样,我们的工作倾向于表明自然语言推理对于将学习转移到其他 NLP 任务的适用性。我们的编码器是公开的。

【讨论】:

【参考方案14】:

你可以把一个句子的词向量加在一起。然后将两个句子向量的余弦相似度计算为两个句子的相似度。我认为这是最简单的方法。

【讨论】:

以上是关于如何使用 gensim 的 word2vec 模型与 python 计算句子相似度的主要内容,如果未能解决你的问题,请参考以下文章

更新 gensim word2vec 模型

用gensim学习word2vec

[Pytorch系列-57]:循环神经网络 - gensim.models.word2vec参数详解与构建词向量模型

确保 gensim 为相同数据的不同运行生成相同的 Word2Vec 模型

Gensim Word2Vec 从预训练模型中选择少量词向量

Python gensim库word2vec的使用