【问题标题】:Computation of Kullback-Leibler (KL) distance between text-documents using numpy使用 numpy 计算文本文档之间的 Kullback-Leibler (KL) 距离
【发布时间】:2013-08-22 12:10:03
【问题描述】:

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

【问题讨论】:

  • 嗯,v[0]==v[2],因此在 kl 函数中 p-q 为 0,则总和为 0。“向量化文档”是什么意思?您的向量 1 和 3 相等。
  • @J.Martinot_Lagarde 感谢您的观察。这里的向量化意味着对文档中的每个单词进行频率计数,并使用这些值来表示文档。这里的问题是如何表示每个文档,以便可以使用 KL 准确计算两个文档之间的距离。

标签: python-2.7 numpy distance


【解决方案1】:

虽然我讨厌添加另一个答案,但这里有两点。首先,正如 Jaime 在 cmets 中指出的那样,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 = sum(pk * 对数(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 函数了。

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

    【讨论】:

    • 那行不通...根据wikipedia:“仅当 P 和 Q 总和为 1 并且 Q(i)=0 意味着 P( i)=0。”不过,不知道该怎么做。
    • 对。我发现的最有用的文章是staff.science.uva.nl/~tsagias/?p=185。他们计算词汇的交集而不是并集,并在词汇表差异太大时添加“workaroud”。最后还有代码。无论如何,问题在于这里的“矢量化”部分。
    • 谢谢@J.Martinot-Lagarde,我去看看这篇文章。
    • 另一种处理文档之间词汇差异的方法是为每个单词添加一个小的概率/频率,因此没有一个单词的概率为零。这在机器学习中是相当标准的,并且可能比忽略它们更好(例如:如果两个文档有一个共同的词,但有很多不同,那么当您考虑词汇的交集时,它们被认为是相同的!)
    • 链接失效了,我找到了一个cahed版本web.archive.org/web/20130508191111/http://staff.science.uva.nl/…
    【解决方案3】:

    您对 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))
    

    这可能会有所帮助...

    【讨论】:

    • 您拥有的公式用于非对称 KL 散度。看看对称KL散度,你会更了解我。
    • 我理解对称 KL 的必要性,但我相信你所做的不会给你它。有关版本,请查看 Jensen-Shannon 散度:en.wikipedia.org/wiki/Jensen%E2%80%93Shannon_divergence
    • 我已经有了 Jensen-Shannon 分歧。我什至回答了一个关于堆栈溢出的 JS 分歧的问题。除了 JS 散度,还有其他对称版本的 KL 散度。
    猜你喜欢
    • 2016-05-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-22
    • 2017-04-21
    • 2012-04-17
    • 1970-01-01
    相关资源
    最近更新 更多