Spacy,两个句子之间奇怪的相似之处

23

我已经下载了en_core_web_lg模型,并试图找出两个句子之间的相似性:

nlp = spacy.load('en_core_web_lg')

search_doc = nlp("This was very strange argument between american and british person")

main_doc = nlp("He was from Japan, but a true English gentleman in my eyes, and another one of the reasons as to why I liked going to school.")

print(main_doc.similarity(search_doc))

这会返回非常奇怪的值:

0.9066019751888448

这两个句子不应该相似度达到90%,它们的含义非常不同。

为什么会出现这种情况?我需要添加一些额外的词汇来使相似度结果更合理吗?

5个回答

35

Spacy通过对单词嵌入进行平均来构建句子嵌入。由于在普通句子中有很多无意义的词(称为停用词),因此结果较差。您可以像这样删除它们:

search_doc = nlp("This was very strange argument between american and british person")
main_doc = nlp("He was from Japan, but a true English gentleman in my eyes, and another one of the reasons as to why I liked going to school.")

search_doc_no_stop_words = nlp(' '.join([str(t) for t in search_doc if not t.is_stop]))
main_doc_no_stop_words = nlp(' '.join([str(t) for t in main_doc if not t.is_stop]))

print(search_doc_no_stop_words.similarity(main_doc_no_stop_words))

或者只保留名词,因为它们包含了最多的信息:
doc_nouns = nlp(' '.join([str(t) for t in doc if t.pos_ in ['NOUN', 'PROPN']]))

8
从阅读这篇及其他文章后,我明白了一个误解:在文档相似性计算中停用词是被去除的。这个特定答案很好,因为它专注于实际内容,同时减少噪声词并加快相似度计算。 - demongolem

16
Spacy文档中有关向量相似性的解释概括如下:
每个单词都有一个向量表示,由上下文嵌入(Word2Vec)学习而来,这些语料库已在文档中详细说明。
现在,整个句子的单词嵌入仅是所有不同单词的平均值。如果您现在有很多语义上处于相同区域的单词(例如像“he”、“was”、“this”等填充词),并且额外的词汇“抵消”,那么你可能会得到与你的情况相似的相似度。
问题是如何处理此问题:从我的角度来看,您可以提出更复杂的相似度度量。由于search_docmain_doc具有其他信息,例如原始句子,因此您可以通过长度差异惩罚修改向量,或者尝试比较句子的较短片段,并计算成对相似性(然后再次询问哪些部分进行比较) 。
目前,遗憾的是没有简单的方法可以解决这个问题。

清晰的方式要么是拥有更有意义的向量表示,要么只通过有意义的单词来判断相似性(请参见下面的答案)。 - Sergey Bushmanov

14

正如其他人指出的那样,您可能希望使用通用句子编码器(Universal Sentence Encoder)或 Infersent。

对于通用句子编码器,您可以安装预构建的 SpaCy 模型,管理 TFHub 的封装,这样您只需使用 pip 安装软件包即可使向量和相似性按预期工作。

您可以遵循此存储库的说明(我是作者)https://github.com/MartinoMensio/spacy-universal-sentence-encoder-tfhub

  1. 安装模型:pip install https://github.com/MartinoMensio/spacy-universal-sentence-encoder/releases/download/v0.4.3/en_use_md-0.4.3.tar.gz#en_use_md-0.4.3

  2. 加载并使用该模型

import spacy
# this loads the wrapper
nlp = spacy.load('en_use_md')

# your sentences
search_doc = nlp("This was very strange argument between american and british person")

main_doc = nlp("He was from Japan, but a true English gentleman in my eyes, and another one of the reasons as to why I liked going to school.")

print(main_doc.similarity(search_doc))
# this will print 0.310783598221594


2
请公开您是所提到软件包的作者(尽管这似乎很明显)。 - m02ph3u5
感谢 @m02ph3u5,我已经添加了提及。 - Martino Mensio
当使用这样的模型时,我是否仍然应该删除停用词,还是它们作为必要上下文的一部分被使用? - benino
@benino,您不需要删除停用词或进行词形还原。通用句子编码器可以直接处理您未经处理的文本。 - Martino Mensio
嗨@MartinoMensio,感谢您的回复。我该如何从磁盘加载'en_use_md'?我的服务器没有连接到互联网。有什么解决方法吗?1.要下载哪个文件,然后如何使用spacy.load...?谢谢。 - Droid-Bird
对@Droid-Bird的晚回复:https://spacy.io/usage/saving-loading 基本上,spacy包安装只是将数据二进制文件下载到site-packages目录中的磁盘中。您可以手动下载此目录的内容:https://github.com/MartinoMensio/spacy-universal-sentence-encoder/tree/master/spacy_universal_sentence_encoder,将其复制到服务器并使用nlp.from_disk("/path")。 - guignol

6

现在,在spaCy官网上可以使用通用句子编码器: https://spacy.io/universe/project/spacy-universal-sentence-encoder

1. 安装:

pip install spacy-universal-sentence-encoder

2. 代码示例:

import spacy_universal_sentence_encoder
# load one of the models: ['en_use_md', 'en_use_lg', 'xx_use_md', 'xx_use_lg']
nlp = spacy_universal_sentence_encoder.load_model('en_use_lg')
# get two documents
doc_1 = nlp('Hi there, how are you?')
doc_2 = nlp('Hello there, how are you doing today?')
# use the similarity method that is based on the vectors, on Doc, Span or Token
print(doc_1.similarity(doc_2[0:7]))

虽然这个链接可能回答了问题,但最好在此处包含答案的基本部分并提供参考链接。如果链接页面更改,仅有链接的答案可能会失效。-【来自审查】 - Rich

5
如@dennlinger所指出的,Spacy的句子嵌入只是单独获取所有单词向量嵌入的平均值。因此,如果您有一个包含否定词汇如"好"和"坏" 的句子,它们的向量可能会相互抵消,导致上下文嵌入效果不佳。如果您的用例特定于获取句子嵌入,则应尝试下面的SOTA方法。
  1. 谷歌通用句子编码器: https://tfhub.dev/google/universal-sentence-encoder/2

  2. Facebook的Infersent编码器:https://github.com/facebookresearch/InferSent

我已经尝试了这两种嵌入方法,并且在大多数情况下都能给出良好的结果,并将词嵌入用作构建句子嵌入的基础。祝你好运!

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