141

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

例如

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

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

4

14 回答 14

95

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

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

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

于 2014-03-02T16:18:16.347 回答
79

由于您使用的是 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 -展开递归自动编码器(非常简单。如果有兴趣,请从这里开始)

2012 -矩阵向量神经网络

2013 -神经张量网络

2015 -树 LSTM

他的论文都可以在 socher.org 上找到。其中一些模型可用,但我仍然推荐 gensim 的 doc2vec。一方面,2011 URAE 并不是特别强大。此外,它预训练了适合解释新闻数据的权重。他提供的代码不允许您重新训练网络。您也无法交换不同的词向量,因此您只能使用 Turian 的 2011 年 pre-word2vec 嵌入。这些向量肯定不在 word2vec 或 GloVe 的水平上。

尚未使用 Tree LSTM,但它似乎很有希望!

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

于 2015-07-14T20:54:53.443 回答
49

如果您使用 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
于 2016-01-29T19:09:01.070 回答
27

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

ps:如果你遇到导入pyemd库的错误,你可以使用以下命令安装它:

pip install pyemd
于 2017-10-02T11:25:32.657 回答
18

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

于 2014-05-05T01:35:35.960 回答
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())
于 2018-05-22T14:34:19.897 回答
16

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

步骤1:

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

第二步:计算句子向量

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

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

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

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

于 2017-06-06T07:02:15.030 回答
8

我正在使用以下方法,并且效果很好。您首先需要运行 POSTagger,然后过滤您的句子以去除停用词(行列式、连词等)。我推荐TextBlob APTagger。然后你通过取句子中每个词向量的平均值来构建一个 word2vec。Gemsim word2vec中的n_similarity 方法正是通过允许传递两组词进行比较来做到这一点的。

于 2014-10-08T14:29:01.880 回答
7

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

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

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

于 2015-08-24T22:56:03.550 回答
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

于 2019-01-23T15:29:01.360 回答
4

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

我想我应该改变主意,改用句子嵌入,如本文this中所研究的那样。

于 2016-11-18T07:07:04.613 回答
4

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

于 2020-01-08T06:38:49.770 回答
3

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

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

于 2018-09-14T03:12:30.300 回答
0

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

于 2020-10-26T08:55:06.680 回答