我的目标是计算以下文本文档之间的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的做法不对?非常感谢你的建议。
答案 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))
这可能会有所帮助......