在spaCy的预训练模型中列出最相似的单词列表

15

使用Gensim,我可以在训练了自己的模型后,使用model.wv.most_similar('cat', topn=5)来获取与向量空间中cat最接近的5个单词的列表。例如:

from gensim.models import Word2Vec
model = Word2Vec.load('mymodel.model')

In: model.wv.most_similar('cat', topn=5)
Out: ('kitten', .99)
     ('dog', .98)
     ...

根据文档,使用spaCy,我可以进行以下操作:

import spacy

nlp = spacy.load('en_core_web_md')
tokens = nlp(u'dog cat banana')

for token1 in tokens:
    for token2 in tokens:
        print(token1.text, token2.text, token1.similarity(token2))

该函数可为指定字符串中的标记提供相似度。但是,尽管查看了文档并进行搜索,我仍然无法确定是否有一种类似于gensim的方法可以列出以nlp = spacy.load('en_core_web_lg')nlp = spacy.load('en_vectors_web_lg')预加载模型的所有相似单词。这可行吗?

3个回答

15

我使用了Andy的方法,它可以正常运行但速度比较慢。为了解决这个问题,我采用了以下方法。

SpaCy在后端使用余弦相似度来计算.similarity。因此,我决定用优化后的方法替换word.similarity(w)。 优化的方法是cosine_similarity_numba(w.vector, word.vector),如下所示,它使用Numba库加速计算。您应该将most_similar方法中的第12行替换为下面的代码。

by_similarity = sorted(queries, key=lambda w: cosine_similarity_numba(w.vector, word.vector), reverse=True)

这种方法变得快了2-3倍,对我来说非常重要。

from numba import jit

@jit(nopython=True)
def cosine_similarity_numba(u:np.ndarray, v:np.ndarray):
    assert(u.shape[0] == v.shape[0])
    uv = 0
    uu = 0
    vv = 0
    for i in range(u.shape[0]):
        uv += u[i]*v[i]
        uu += u[i]*u[i]
        vv += v[i]*v[i]
    cos_theta = 1
    if uu != 0 and vv != 0:
        cos_theta = uv/np.sqrt(uu*vv)
    return cos_theta

我在这篇文章中更详细地解释了它:如何在SpaCy中构建一个快速的“最相似单词”方法


7

这个功能不是开箱即用的。然而,基于这个问题 (https://github.com/explosion/spaCy/issues/276),以下代码可以让它按照你的需求工作。

import spacy
import numpy as np
nlp = spacy.load('en_core_web_lg')

def most_similar(word, topn=5):
  word = nlp.vocab[str(word)]
  queries = [
      w for w in word.vocab 
      if w.is_lower == word.is_lower and w.prob >= -15 and np.count_nonzero(w.vector)
  ]

  by_similarity = sorted(queries, key=lambda w: word.similarity(w), reverse=True)
  return [(w.lower_,w.similarity(word)) for w in by_similarity[:topn+1] if w.lower_ != word.lower_]

most_similar("dog", topn=3)

2
你应该遍历nlp.vocab中的所有单词而不是word.vocab,对吗? - user_1177868
1
@Romain,我尝试过这个,但效果非常差。你能发布一下dog的topn=3 most_similar吗? - JJoao
1
确实。这个例子的结果是: [('she', 0.42), ('when', 0.41), ('he', 0.39)],这完全荒谬,不幸的是在许多网站上都有类似的例子。不过,感谢提供相关问题链接的点赞。 - Wtower

3
以下是获取最相似单词列表的性能检测结果。在某种程度上,这是一个极端情况,因为模型既没有w.prob也没有w.cluster来缩小搜索空间。我使用了四种方法:上述两种方法、SpaCy的most_similar和Gensim的most_similar。
def spacy_most_similar(word, topn=10):
  ms = nlp_ru.vocab.vectors.most_similar(
      nlp_ru(word).vector.reshape(1,nlp_ru(word).vector.shape[0]), n=topn)
  words = [nlp_ru.vocab.strings[w] for w in ms[0][0]]
  distances = ms[2]
  return words, distances

def spacy_similarity(word, topn=10):
  word = nlp_ru.vocab[str(word)]
  queries = [
      w for w in word.vocab if w.is_lower == word.is_lower and np.count_nonzero(w.vector)
  ]
  by_similarity = sorted(queries, key=lambda w: w.similarity(word), reverse=True)
  return [(w.lower_,w.similarity(word)) for w in by_similarity[:topn+1] if w.lower_ != word.lower_]

def spacy_similarity_numba(word, topn=10):
  word = nlp_ru.vocab[str(word)]
  queries = [
      w for w in word.vocab if w.is_lower == word.is_lower and np.count_nonzero(w.vector)
  ]
  by_similarity = sorted(queries, key=lambda w: cosine_similarity_numba(w.vector, word.vector), reverse=True)
  return [(w.lower_,w.similarity(word)) for w in by_similarity[:topn+1] if w.lower_ != word.lower_]

from numba import jit


@jit(nopython=True)
def cosine_similarity_numba(u:np.ndarray, v:np.ndarray):
    assert(u.shape[0] == v.shape[0])
    uv = 0
    uu = 0
    vv = 0
    for i in range(u.shape[0]):
        uv += u[i]*v[i]
        uu += u[i]*u[i]
        vv += v[i]*v[i]
    cos_theta = 1
    if uu != 0 and vv != 0:
        cos_theta = uv/np.sqrt(uu*vv)
    return cos_theta

以下是时间统计结果:

from time import time
import timeit, functools
from timeit import default_timer as timer
print(nlp_ru.vocab.vectors.shape)
arr = ("дерево")
print(f'Gensim most_similar: {timeit.Timer(functools.partial(wv.most_similar, arr)).timeit(1)}')
print(f'Spacy most_similar: {timeit.Timer(functools.partial(spacy_most_similar, arr)).timeit(1)}')
print(f'Spacy cosine_similarity_numba: {timeit.Timer(functools.partial(spacy_similarity_numba, arr)).timeit(1)}')
print(f'Spacy similarity: {timeit.Timer(functools.partial(spacy_similarity, arr)).timeit(1)}')

(1239964, 100)
Gensim most_similar: 0.06437033399993197
Spacy most_similar: 0.4855721250000897
Spacy cosine_similarity_numba: 13.404324778000046
Spacy similarity: 60.58928110700003

所有方法返回的结果都是相同的。如您所见,与其他技术相比,Gensim非常快速。而且您甚至不需要缩小搜索范围。所有测量均在CPU上完成。嵌入取自此处 http://panchenko.me/data/dsl-backup/w2v-ru/all.norm-sz100-w10-cb0-it1-min100.w2v


我尝试了这个spacy_similarity,但X.vocab只返回了300个元素,并且结果非常糟糕。 - JJoao

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