用Python计算KL发散


22

我对此很陌生,不能说我对背后的理论概念有完整的了解。我正在尝试计算Python中几个点列表之间的KL散度。我正在使用http://scikit-learn.org/stable/modules/generation/sklearn.metrics.mutual_info_score.html尝试执行此操作。我遇到的问题是,任何两个数字列表(其1.3862943611611198906)返回的值都是相同的。我有一种感觉,我在这里犯了某种理论上的错误,但无法发现。

values1 = [1.346112,1.337432,1.246655]
values2 = [1.033836,1.082015,1.117323]
metrics.mutual_info_score(values1,values2)

这是我正在运行的示例-只是我对任何2个输入都得到相同的输出。任何建议/帮助将不胜感激!


吉隆坡(KL)是指库尔巴克-莱布尔(Kullback-Leibler)分歧吗?
Dawny33

是的,就是这样!
南达(Nanda)2015年

通过跑步sklearn.metrics.mutual_info_score([1.346112,1.337432,1.246655], [1.033836,1.082015,1.117323]),我获得了价值1.0986122886681096
Dawny33

抱歉,我将value1用作[1,1.346112,1.337432,1.246655],将value2用作value2作为[1,1.033836,1.082015,1.117323],因此使用了差值。
南达

Answers:


18

首先,sklearn.metrics.mutual_info_score实现用于评估聚类结果的互信息,而不是纯粹的 Kullback-Leibler散度!

这等于联合分布的Kullback-Leibler散度与边际的乘积分布。

KL散度(以及任何其他此类度量)期望输入数据的总和为1。否则,它们不是适当的概率分布。如果您的数据总和不为1,则通常不适合使用KL散度!(在某些情况下,例如,如果缺少数据,则总和小于1可能是允许的。)

另请注意,通常使用以2为底的对数。这只会产生一个恒定的差异比例因子,但是以2为底的对数更易于解释,并且具有更直观的比例(0到1而不是0到log2 = 0.69314 ...,以位而不是nat的形式测量信息)。

> sklearn.metrics.mutual_info_score([0,1],[1,0])
0.69314718055994529

我们可以清楚地看到,sklearn的MI结果使用自然对数而不是log2进行缩放。如上所述,这是一个不幸的选择。

不幸的是,Kullback-Leibler的分歧很脆弱。在上面的示例中,定义不明确:KL([0,1],[1,0])导致被零除,并趋于无穷大。它也是不对称的


注意,当scipy.stats.entropy使用时,它将把概率归一化。来自文档(scipy.github.io/devdocs/generation/scipy.stats.entropy.html):“如果pk和qk的总和不为1,则此例程将对其进行标准化。”
Itamar Mushkin

15

如果输入两个表示概率分布的向量p和q,Scipy的熵函数将计算KL散度。如果两个向量不是pdf,它将首先进行标准化。

相互信息 KL Divergence 相关但不相同

“此加权互信息是加权KL-散度的一种形式,已知它对某些输入取负值,并且在某些示例中,加权互信息也取负值”


6

我不确定ScikitLearn的实现,但这是Python中KL散度的快速实现:

import numpy as np

def KL(a, b):
    a = np.asarray(a, dtype=np.float)
    b = np.asarray(b, dtype=np.float)

    return np.sum(np.where(a != 0, a * np.log(a / b), 0))


values1 = [1.346112,1.337432,1.246655]
values2 = [1.033836,1.082015,1.117323]

print KL(values1, values2)

输出: 0.775279624079

一些库中可能存在实现冲突,因此请确保在使用前阅读其文档。


1
我也尝试过,但是返回的负值,我认为这不是有效值。然后进行了一些研究,使我得到了这个结果mathoverflow.net/questions/43849/…,其中讨论了输入必须是概率分布的方式。猜猜那是我犯错的地方。
南达

@Nanda感谢您的链接。我的回报0.775279624079是您的输入,sklearn指标是回报1.3862943611198906。还是困惑!但是,好像将那些根据qn进行的值检查包括在内,应该在脚本中进行:)
Dawny33

1
我知道你的意思!我尝试了3个不同的函数来获取3个不同的值,它们之间唯一的共同之处是结果“感觉”不正确。输入值绝对是一个逻辑错误,因此请完全改变我的方法!
南达(Nanda)2015年

@Nanda Ahh,现在很清楚:)感谢您的解释
Dawny33

2

该技巧避免了条件代码,因此可以提供更好的性能。

import numpy as np

def KL(P,Q):
""" Epsilon is used here to avoid conditional code for
checking that neither P nor Q is equal to 0. """
     epsilon = 0.00001

     # You may want to instead make copies to avoid changing the np arrays.
     P = P+epsilon
     Q = Q+epsilon

     divergence = np.sum(P*np.log(P/Q))
     return divergence

# Should be normalized though
values1 = np.asarray([1.346112,1.337432,1.246655])
values2 = np.asarray([1.033836,1.082015,1.117323])

# Note slight difference in the final result compared to Dawny33
print KL(values1, values2) # 0.775278939433

好招!我很想知道这与时间基准上的其他解决方案相比如何。
肯定是

0

考虑来自分布的以下三个样本。

values1 = np.asarray([1.3,1.3,1.2])
values2 = np.asarray([1.0,1.1,1.1])
values3 = np.array([1.8,0.7,1.7])

显然,值1和值2更接近,因此我们希望与值3 surprise相比,度量或熵的度量值会更低。

from scipy.stats import entropy
print("\nIndividual Entropy\n")
print(entropy(values1))
print(entropy(values2))
print(entropy(values3))

print("\nPairwise Kullback Leibler divergence\n")
print(entropy(values1, qk=values2))
print(entropy(values1, qk=values3))
print(entropy(values2, qk=values3))

我们看到以下输出:

Individual Entropy

1.097913446793334
1.0976250611902076
1.0278436769863724 #<--- this one had the lowest, but doesn't mean much.

Pairwise Kullback Leibler divergence

0.002533297351606588
0.09053972625203921 #<-- makes sense
0.09397968199352116 #<-- makes sense

我们认为这是有道理的,因为value1和values3之间的值以及value 2和value 3之间的变化比value1到value 2更具变化性。这是我对理解KL-D及其可利用的软件包的验证。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.