python - Word Mover 距离与 SpaCy 的非标准化结果

标签 python nlp spacy

我正在尝试将 Fast Word Mover 的距离库与 SpaCy 结合使用,对于 GitHub 中的相同示例

import spacy
import wmd
nlp = spacy.load('en_core_web_md')
nlp.add_pipe(wmd.WMD.SpacySimilarityHook(nlp), last=True)

doc1 = nlp("Politician speaks to the media in Illinois.")
doc2 = nlp("The president greets the press in Chicago.")
print(doc1.similarity(doc2))

结果是:

6.070106029510498

我不知道如何解释它,因为通常距离是标准化的(0 到 1)。在自述文件中,此结果不可用,因此我不确定我的结果是否错误或此测量的比例不同。

最佳答案

一个简短的回答:不要解释它。就像这样使用它:距离越小,句子越相似。对于几乎所有实际应用(例如 KNN),这就足够了。

现在长答案:单词移动距离(阅读 the paper )定义为“不间断”单词的最佳匹配对之间距离的加权平均值。所以如果你想把它归一化为 (0, 1),你需要用这个最好的总和除以它的最坏情况。

问题是在 spacy 中词向量没有标准化(通过打印 [sum(t.vector**2) for t in doc1] 检查)。因此,它们之间的最大距离是无限的。如果你对它们进行规范化,新的 WMD 将不会等同于原始的 WMD(即它会以不同的方式对文本对进行排序)。因此,没有明显的方法可以标准化您演示的原始 spacy-WMD 距离。

现在让我们假设词向量是单位归一化的。如果是这样,那么两个单词之间的最大距离就是一个单位球体的直径(即2)。而很多2的最大加权平均还是2。所以需要将文本之间的距离除以2,使其完全归一化。

您可以通过继承您使用的类,将词向量归一化构建到 WMD 计算中:

import wmd
import numpy
import libwmdrelax

class NormalizedWMDHook(wmd.WMD.SpacySimilarityHook):
    def compute_similarity(self, doc1, doc2):
        """
        Calculates the similarity between two spaCy documents. Extracts the
        nBOW from them and evaluates the WMD.

        :return: The calculated similarity.
        :rtype: float.
        """
        doc1 = self._convert_document(doc1)
        doc2 = self._convert_document(doc2)
        vocabulary = {
            w: i for i, w in enumerate(sorted(set(doc1).union(doc2)))}
        w1 = self._generate_weights(doc1, vocabulary)
        w2 = self._generate_weights(doc2, vocabulary)
        evec = numpy.zeros((len(vocabulary), self.nlp.vocab.vectors_length),
                           dtype=numpy.float32)
        for w, i in vocabulary.items():
            v = self.nlp.vocab[w].vector                                      # MODIFIED
            evec[i] = v / (sum(v**2)**0.5)                                    # MODIFIED
        evec_sqr = (evec * evec).sum(axis=1)
        dists = evec_sqr - 2 * evec.dot(evec.T) + evec_sqr[:, numpy.newaxis]
        dists[dists < 0] = 0
        dists = numpy.sqrt(dists)
        return libwmdrelax.emd(w1, w2, dists) / 2                             # MODIFIED

现在您可以确定您的距离已正确归一化:

import spacy
nlp = spacy.load('en_core_web_md')
nlp.add_pipe(NormalizedWMDHook(nlp), last=True)
doc1 = nlp("Politician speaks to the media in Illinois.")
doc2 = nlp("The president greets the press in Chicago.")
print(doc1.similarity(doc2))
print(doc1.similarity(doc1))
print(doc1.similarity(nlp("President speaks to the media in Illinois.")))
print(doc1.similarity(nlp("some irrelevant bullshit")))
print(doc1.similarity(nlp("JDL")))

现在的结果是

0.469503253698349
0.0
0.12690649926662445
0.6037049889564514
0.7507566213607788

附言你可以看到,即使在两个非常不相关的文本之间,这个归一化距离也远小于 1。这是因为实际上词向量并没有覆盖整个单位球体——相反,它们中的大部分都聚集在它上面的几个“大陆”上。因此,即使是非常不同的文本之间的距离通常也小于 1。

关于python - Word Mover 距离与 SpaCy 的非标准化结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56822056/

相关文章:

python - 如何训练以行号为特征的 spaCy 模型?

python - 如何将 namedtuple 转换为元组

python - 使用 joblib.load 从磁盘读取 xgboost 模型时出现类型错误

machine-learning - 大型多类 NLP 分类的数据和样本量不平衡

nlp - 在斯坦福 CoreNLP 中提取实体之间的关系

python - 如何取消对 spacy.tokens.token.Token 的标记?

python - spaCy NLP 管道操作顺序

python - Pandas - 移动平均线 - 使用当前行的先前 X 条目的值

python - Tensorflow:如何在调用 tf.reduce_sum 时忽略数组的一部分

python - AllenNLP 阅读理解结果在 UI Demo 和 Python Library 中不同