使用numpy计算文本文档之间的Kullback-Leibler(KL)距离

时间:2013-08-22 12:10:03

标签: python-2.7 numpy distance

我的目标是计算以下文本文档之间的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的做法不对?非常感谢你的建议。

3 个答案:

答案 0 :(得分:30)

虽然我不想添加另一个答案,但这里有两点。首先,正如Jaime在评论中指出的那样,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,则计算相对熵(也称为   Kullback-Leibler发散或Kullback-Leibler距离)S =总和(pk *   log(pk / qk),axis = 0)。

这个函数的好处是它会将你传递的向量标准化,如果它们不总和为1(虽然这意味着你必须小心你传递的数组 - 即它们是如何从数据构造的)。

希望这会有所帮助,至少有一个库提供它,所以不必编写自己的代码。

答案 1 :(得分:1)

经过一些谷歌搜索以及KL概念,我认为你的问题是由于矢量化:你正在比较不同单词的出现次数。您应该将列indice链接到一个单词,或者使用词典:

#  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函数。

要自动向量化为字典,请参阅How to count the frequency of the elements in a list?collections.Counter正是您所需要的)。然后你可以遍历字典键的并集来计算KL距离。

答案 2 :(得分:0)

潜在的问题可能出在您对KL的NP定义中。阅读维基百科页面,了解公式: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))

这可能会有所帮助......