Ненормализованный результат расстояния Word Mover с SpaCy - PullRequest
1 голос
/ 30 июня 2019

Я пытаюсь использовать дистанционную библиотеку 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). В файле readme результат для этого недоступен, поэтому я не уверен, что мой результат неверен или масштаб для этого измерения отличается.

1 Ответ

3 голосов
/ 01 июля 2019

Краткий ответ: не интерпретируйте его . Используйте это так: чем меньше расстояние, тем больше предложений. Для практически всех практических применений (например, KNN) этого достаточно.

Теперь длинный ответ: расстояние перемещения слов (читать статья ) определяется как средневзвешенное расстояние между наиболее подходящими парами "безостановочных" слов. Поэтому, если вы хотите нормализовать ее в (0, 1), вам нужно разделить эту наилучшую сумму на худшую.

Проблема в том, что в spacy векторы слов не нормализованы (проверьте это, напечатав [sum(t.vector**2) for t in doc1]). Поэтому максимальное расстояние между ними не ограничено. И если вы их нормализуете, то новое ОМУ не будет эквивалентно исходному ОМУ (то есть по-разному будет сортировать пары текстов). Следовательно, не существует очевидного способа нормализовать исходные расстояния между ОМУ и пространством, которые вы продемонстрировали.

Теперь давайте представим, что векторы слов нормализованы по единицам. Если это так, то максимальное расстояние между двумя словами - это диаметр единичной сферы (то есть 2). Максимальное средневзвешенное значение для многих 2 равно 2. Поэтому вам нужно разделить расстояние между текстами на 2, чтобы оно полностью нормализовалось.

Вы можете встроить нормализацию вектора слов в расчет ОМУ, унаследовав используемый класс:

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

P.S. Вы можете видеть, что даже между двумя очень не связанными текстами это нормализованное расстояние намного меньше 1. Это потому, что в действительности векторы слов не охватывают всю единичную сферу - вместо этого большинство из них сгруппированы на нескольких «континентах» на ней. Следовательно, расстояние даже между очень разными текстами будет обычно меньше 1.

...