我必须计算数千个离散概率向量之间的Kullback-Leibler Divergence(KLD)。目前我使用的是以下代码,但对于我的目的而言,它太慢了。我想知道是否有更快的方法来计算KL发散?
import numpy as np
import scipy.stats as sc
#n is the number of data points
kld = np.zeros(n, n)
for i in range(0, n):
for j in range(0, n):
if(i != j):
kld[i, j] = sc.entropy(distributions[i, :], distributions[j, :])
答案 0 :(得分:11)
Scipy的stats.entropy
在其默认意义上邀请输入作为一维数组给我们一个标量,这是在列出的问题中完成的。在内部,此功能还允许broadcasting
,我们可以滥用在这里为矢量化解决方案。
来自docs
-
scipy.stats.entropy(pk,qk = None,base = None)
如果只有概率pk 给出,熵计算为S = -sum(pk * log(pk), 轴= 0)。
如果qk不是None,则计算Kullback-Leibler散度S = sum(pk * log(pk / qk),axis = 0)。
在我们的例子中,我们针对所有行对每一行进行这些熵计算,执行求和减少以在每次迭代时使用这两个嵌套循环具有标量。因此,输出数组的形状为(M,M)
,其中M
是输入数组中的行数。
现在,这里的问题是stats.entropy()
将与axis=0
相加,因此我们将为其提供两个版本的distributions
,这两个版本都会将行标题带到{沿着它减少{1}}和其他两个交错的轴 - axis=0
& (M,1)
使用(1,M)
为我们提供(M,M)
形状的输出数组。
因此,解决我们案例的矢量化和更有效的方法是 -
broadcasting
运行时测试和验证 -
from scipy import stats
kld = stats.entropy(distributions.T[:,:,None], distributions.T[:,None,:])