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

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

例如:。

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

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

124127 次浏览

这实际上是一个非常具有挑战性的问题。计算句子的相似度需要建立一个句子的语法模型,理解对应的结构(例如“ he walk to the store last”and“ last,he walk to the store”) ,不仅要在代词和动词中找到相似度,还要在专有名词中找到相似度,在大量真实的文本例子中找到统计上的共现/关系,等等。

你可以尝试的最简单的事情——尽管我不知道这样做效果如何,也肯定不会给你最佳的结果——就是首先删除所有的“停止”单词(像“ The”,“ an”等词,这些词对句子没有太大的意义) ,然后对两个句子中的单词运行 word2vec,对一个句子中的向量进行求和,对另一个句子中的向量进行求和,然后找出求和之间的差异。通过总结它们,而不是做一个明智的差异,你至少不会受到语序的影响。也就是说,这将在很多方面失败,而且无论如何都不是一个好的解决方案(尽管这个问题的好的解决方案几乎总是涉及一些 NLP、机器学习和其他聪明的方法)。

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

一旦你计算了两组单词向量的和,你应该取向量之间的余弦,而不是差异。余弦可以通过取两个向量的点乘归一化来计算。因此,单词计数不是一个因素。

我正在使用下面的方法,它工作得很好。 你首先需要运行一个 POSTagger,然后过滤你的句子,去掉停止词(行列式,连词,...)。我推荐 TextBlob APTagger。 然后你通过取句子中每个单词向量的平均值来构建一个单词2vec。通过允许传递两组单词进行比较,Gemsim word2vec 中的 n _ 相似性方法完全可以做到这一点。

由于您使用的是 gensim,因此您可能应该使用它的 doc2vec 实现。Doc2vec 是 word2vec 到短语、句子和文档级别的扩展。这是一个非常简单的扩展,在这里描述

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

Gensim 很不错,因为它直观、快速、灵活。最棒的是,你可以从官方 word2vec 页面中获取预先训练好的单词嵌入,并且 Gensim 的 Doc2Vec 模型的 syn0层被暴露出来,这样你就可以用这些高质量的向量来播种单词嵌入了!

GoogleNews-Vector-negative300.bin.gz (在 谷歌代码中链接)

我认为 gensim 绝对是在向量空间中嵌入句子的最简单的工具(到目前为止对我来说也是最好的)。

除了 Le & Mikolov 在上述论文中提出的句子向量转换技术之外,还存在其他的句子向量转换技术。来自斯坦福大学的 Socher 和 Manning 无疑是这一领域最著名的两位研究者。他们的工作是基于句子的构成-语义学原则,来自:

1. semantics of the words


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

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

2011-展开递归自动编码器(非常简单,如果感兴趣请从这里开始)

2012-矩阵向量神经网络

2013-神经张量网络神经张量网络

2015-树 LSTM

他的论文都可以在 socher.org 上找到。有些型号是可以买到的,但我还是推荐 Gensim 的 doc2vec。首先,2011年的 URAE 并不是特别强大。此外,它还预先训练了适合解释新闻数据的权重。他提供的代码不允许您重新训练网络。你也不能交换不同的单词向量,所以你只能使用2011年从 Turian 嵌入到 word2vec 之前的单词向量。这些向量当然不是 word2vec 或 GloVe 的水平。

还没有与树 LSTM 工作,但它似乎非常有前途!

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

Word2Vec 有一些扩展,旨在解决比较较长的文本片段(如短语或句子)的问题。其中之一是第2vec 段或 doc2vec。

句子和文件的分布式表示 Http://cs.stanford.edu/~quocle/paragraph_vector.pdf

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

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

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

我认为我应该改变我的想法,使用句子嵌入而不是作为研究的 这张纸这个

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

第一步:

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

第二步: 计算句子向量

在此之前,计算句子之间的语义相似度是很困难的,但是最近,一篇名为“一个简单但是很难打败的句子基线”的论文发表了 提出了一种简单的方法,即计算句子中词向量的加权平均数,然后去除第一个主成分上的平均向量投影。这里一个单词 w 的权重是一个参数 a/(a + p (w)) ,而 p (w)是一个被称为平滑逆频率的(估计的)单词频率。这种方法的性能要好得多。译注:

本文给出了利用平滑逆频率法计算句子向量的简单代码 给你

第三步: 使用 sklearn 余弦相似度加载两个向量对句子进行相似度计算。

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

你可以使用字移动的距离算法。这里是一个 大规模杀伤性武器的简单描述

#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

有一个来自 文件的函数,它获取一个单词列表并比较它们的相似性。

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


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

Facebook 研究小组发布了一个新的解决方案,叫做“地狱发送” 结果和代码发布在 Github 上,检查他们的回购。这是非常可怕的。我打算使用它。 Https://github.com/facebookresearch/infersent

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

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

IPython 笔记本提供了不同的教程:

另一种方法依赖于 Word2Vec字移动距离(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

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

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