Вычисление расстояния Kullback-Leibler (KL) между текстовыми документами с использованием numpy

Моя цель - вычислить расстояние KL между следующими текстовыми документами:

1)The boy is having a lad relationship
2)The boy is having a boy relationship
3)It is a lovely day in NY

В первую очередь я вектурировал документы, чтобы легко применить numpy

1)[1,1,1,1,1,1,1]
2)[1,2,1,1,1,2,1]
3)[1,1,1,1,1,1,1]

Затем я применил следующий код для вычисления расстояния KL между текстами:

import numpy as np
import math
from math import log

v=[[1,1,1,1,1,1,1],[1,2,1,1,1,2,1],[1,1,1,1,1,1,1]]
c=v[0]
def kl(p, q):
    p = np.asarray(p, dtype=np.float)
    q = np.asarray(q, dtype=np.float)
    return np.sum(np.where(p != 0,(p-q) * np.log10(p / q), 0))
for x in v:
    KL=kl(x,c)
    print KL

Вот результат приведенного выше кода: [0.0, 0.602059991328, 0.0]. Тексты 1 и 3 совершенно разные, но расстояние между ними равно 0, а тексты 1 и 2, которые сильно связаны, имеют расстояние 0.602059991328. Это неверно.

Есть ли у кого-то представление о том, что я не делаю правильно в отношении KL? Большое спасибо за ваши предложения.

Ответ 1

Хотя я ненавижу добавить еще один ответ, здесь есть два момента. Во-первых, как указал Хайме в комментариях, расхождение KL (или расстояние - они, согласно следующей документации, одинаковы) предназначено для измерения разницы между распределениями вероятностей. Это означает, что в основном то, что вы передаете функции, должно быть два типа массива, элементы каждой из которых равны 1.

Во-вторых, scipy, по-видимому, реализует это, и схема именования больше связана с полем теории информации. Функция - "энтропия":

scipy.stats.entropy(pk, qk=None, base=None)

http://docs.scipy.org/doc/scipy-dev/reference/generated/scipy.stats.entropy.html

Из документов:

Если qk не является None, тогда вычислите относительную энтропию (также известную как Дифференциация Кулбека-Лейблера или расстояние Кулбака-Лейблера) S = сумма (pk * log (pk/qk), ось = 0).

Бонус этой функции также заключается в том, что она нормализует векторы, которые вы передаете, если они не суммируются до 1 (хотя это означает, что вы должны быть осторожны с передаваемыми массивами, то есть, как они построены из данных).

Надеюсь, что это поможет, и, по крайней мере, библиотека предоставляет его, поэтому не нужно кодировать свои собственные.

Ответ 2

После небольшого количества поисковых запросов, чтобы понять концепцию KL, я думаю, что ваша проблема связана с векторизации: вы сравниваете количество появления разных слов. Вы должны либо связать свой индекс столбца с одним словом, либо использовать словарь:

#  The boy is having a lad relationship It lovely day in NY
1)[1   1   1  1      1 1   1            0  0      0   0  0]
2)[1   2   1  1      1 0   1            0  0      0   0  0]
3)[0   0   1  0      1 0   0            1  1      1   1  1]

Затем вы можете использовать свою функцию kl.

Для автоматической векторизации словаря см. Как подсчитать частоту элементов в списке? (collections.Counter именно то, что вам нужно). Затем вы можете перебрать объединение ключей словарей, чтобы вычислить расстояние KL. ​​

Ответ 3

Потенциальная проблема может быть в вашем NP-определении KL. Прочтите страницу wikipedia для формулы: http://en.wikipedia.org/wiki/Kullback%E2%80%93Leibler_divergence

Обратите внимание, что вы умножаете (p-q) на результат журнала. В соответствии с формулой KL это должно быть только p:

 return np.sum(np.where(p != 0,(p) * np.log10(p / q), 0))

Это может помочь...