使用spaCy将单词向量映射到最相似/最接近的单词

时间:2019-02-15 21:43:22

标签: nlp spacy

我正在使用spaCy作为主题建模解决方案的一部分,并且遇到一种情况,我需要将派生的单词向量映射到单词向量词汇中的“最接近”或“最相似”的单词。

我看到gensim有一个函数(WordEmbeddingsKeyedVectors.similar_by_vector)进行计算,但是我想知道spaCy是否具有类似的功能,可以将矢量映射到其词汇表(nlp.vocab)中的单词?

4 个答案:

答案 0 :(得分:3)

是的,spacy有一个API方法可以做到这一点,就像KeyedVectors.similar_by_vector:

GET stackoverflow/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "multi_match": {
            "fields": [
              "search_field_full"
            ],
            "fuzziness": "AUTO:4,7",
            "max_expansions": 500,
            "minimum_should_match": 2,
            "operator": "or",
            "query": "Star Trek",
            "type": "best_fields"
          }
        }
      ],
      "should": [
        {
          "multi_match": {
            "fields": [
              "search_field_full.raw^30"
            ],
            "fuzziness": 0,
            "operator": "or",
            "query": "Star Trek",
            "type": "best_fields"
          }
        },
        {
          "multi_match": {
            "fields": [
              "search_field_full.raw^20"
            ],
            "fuzziness": 1,
            "operator": "or",
            "query": "Star Trek",
            "type": "best_fields"
          }
        }
      ]
    }
  }
}

(这些单词在import numpy as np import spacy nlp = spacy.load("en_core_web_lg") your_word = "king" ms = nlp.vocab.vectors.most_similar( np.asarray([nlp.vocab.vectors[nlp.vocab.strings[your_word]]]), n=10) words = [nlp.vocab.strings[w] for w in ms[0][0]] distances = ms[2] print(words) ['King', 'KIng', 'king', 'KING', 'kings', 'KINGS', 'Kings', 'PRINCE', 'Prince', 'prince'] 中未正确归一化,但是您可以使用其他模型并观察到更具有代表性的输出)。

答案 1 :(得分:1)

经过一些实验,我发现了一个scikit函数(在scikit.spatial.distance中为cdist),该函数在向量空间中找到一个与输入向量有关的“接近”向量。

# Imports
from scipy.spatial import distance
import spaCy

# Load the spacy vocabulary
nlp = spacy.load("en_core_web_lg")

# Format the input vector for use in the distance function
# In this case we will artificially create a word vector from a real word ("frog")
# but any derived word vector could be used
input_word = "frog"
p = np.array([nlp.vocab[input_word].vector])

# Format the vocabulary for use in the distance function
ids = [x for x in nlp.vocab.vectors.keys()]
vectors = [nlp.vocab.vectors[x] for x in ids]
vectors = np.array(vectors)

# *** Find the closest word below ***
closest_index = distance.cdist(p, vectors).argmin()
word_id = ids[closest_index]
output_word = nlp.vocab[word_id].text
# output_word is identical, or very close, to the input word

答案 2 :(得分:1)

对此答案要谨慎。传统上,单词相似度(在gensim,spacy和nltk中)使用余弦相似度,而默认情况下,scipy的cdist使用欧几里得距离。您可以得到余弦距离,它与相似度不同,但它们是相关的。要复制gensim的计算,请将您的cdist调用更改为以下内容:

distance.cdist(p, vectors, metric='cosine').argmin()

但是,您还应该注意scipy测量的余弦距离是从余弦相似度“向后”的“余弦距离”,其中“ cosine dist” = 1-cos x(x是矢量之间的角度),因此要匹配/复制gensim数,您必须从一中减去答案(当然,请使用MAX参数-相似的向量更接近1)。这是一个非常细微的差异,但会引起很多混乱。

相似向量的相似性应大(接近1),而距离应小(接近零)。

余弦相似度可以为负(表示向量具有相反的方向),但它们的距离为正(视距离而定)。

来源: https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.distance.cdist.html

https://tedboy.github.io/nlps/generated/generated/gensim.models.Word2Vec.n_similarity.html#gensim.models.Word2Vec.n_similarity

在spacy中也要进行相似处理的是以下内容:

import spacy
nlp = spacy.load("en_core_web_md")
x = nlp("man")
y = nlp("king")
print(x.similarity(y))
print(x.similarity(x))

答案 3 :(得分:0)

这是一个 similarity search 的示例,其特征向量维度为 300(32 位浮点数为 1.2kB)。

您可以将词向量存储在几何数据结构 sklearn.neighbors.BallTree 中,以显着加快搜索速度,同时避免与 k-d 树相关的高维损失(当维数超过 ~100 时没有加速)。如果您需要避免加载 spaCy,这些可以很容易地被腌制和取消腌制并保存在内存中。实现细节见下文。 Demosource


线性搜索的其他答案有效(如果您的任何向量为零,我会注意到在使用余弦相似度时要小心),但对于大型词汇表会很慢。 spaCyen_core_web_lg 库有大约 680k 字词向量。由于每个字通常只有几个字节,因此这可能会导致占用几 GB 的内存。

我们可以使用使我们的搜索不区分大小写,并使用 word frequency table 删除不常用的单词(从 v3.0 开始,spaCy 已内置表,但您现在必须单独加载它们)以将词汇量缩减为约 10 万字。但是,搜索仍然是线性的,可能需要几秒钟,这可能是不可接受的。

libraries 可以快速进行相似性搜索,但它们安装起来非常麻烦且复杂,并且适用于 MB 或 GB 数量级的特征向量,GPU 加速等等。

我们也可能不希望每次应用程序运行时总是加载整个 spaCy 词汇表,因此我们根据需要选择/取消选择词汇表。

import spacy, numpy, pickle
import sklearn.neighbors as nbs

#load spaCy
nlp=spacy.load("en_core_web_lg")

#load lexeme probability table
lookups = load_lookups("en", ["lexeme_prob"])
nlp.vocab.lookups.add_table("lexeme_prob", lookups.get_table("lexeme_prob"))

#get lowercase words above frequency threshold with vectors, min_prob=-20
words = [word for word in nlp.vocab.strings if nlp.vocab.has_vector(word) and word.islower() and nlp.vocab[word].prob >= -18]
wordvecs = numpy.array([nlp.vocab.get_vector(word) for word in words])  #get wordvectors
tree = nbs.BallTree(wordvecs)  #create the balltree
dict = dict(zip(words,wordvecs))  #create word:vector dict

修剪词汇后,我们可以pickle words、dict和balltree,在需要的时候加载,而不必再次加载spaCy:

#pickle/unpickle the balltree if you don't want to load spaCy
with open('balltree.pkl', 'wb') as f:
        pickle.dump(tree,f,protocol=pickle.HIGHEST_PROTOCOL)
#...
#load wordvector balltree from pickle file
with open('./balltree.pkl','rb') as f:
    tree = pickle.load(f)

给定一个词,获取它的词向量,在树中搜索最接近词的索引,然后用字典查找该词:

#get wordvector and lookup nearest words
def nearest_words(word):
    #get vectors for all words
        try:
            vec = to_vec[word]
        #if word is not in vocab, set to zero vector
        except KeyError:
            vec = numpy.zeros(300)

    #perform nearest neighbor search of wordvector vocabulary
    dist, ind = tree.query([vec],10)

    #lookup nearest words using indices from tree
    near_words = [vocab[i] for i in ind[0]]

    return near_words