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 =
总和(pk * log(pk / qk),轴=0)。
在我们的例子中,我们针对所有行对每一行执行这些熵计算,执行求和以在这两个嵌套循环的每次迭代中获得一个标量。因此,输出数组的形状为(M,M),其中M 是输入数组中的行数。
现在,这里的问题是 stats.entropy() 将与 axis=0 相加,因此我们将为其提供两个版本的 distributions,这两个版本都会将 rowth-dimension 带到 axis=0 以减少它其他两个轴交错 - (M,1) 和 (1,M) 使用 broadcasting 为我们提供 (M,M) 形状的输出阵列。
因此,解决我们的案例的矢量化且更有效的方法是 -
from scipy import stats
kld = stats.entropy(distributions.T[:,:,None], distributions.T[:,None,:])
运行时测试和验证 -
In [15]: def entropy_loopy(distrib):
...: n = distrib.shape[0] #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] = stats.entropy(distrib[i, :], distrib[j, :])
...: return kld
...:
In [16]: distrib = np.random.randint(0,9,(100,100)) # Setup input
In [17]: out = stats.entropy(distrib.T[:,:,None], distrib.T[:,None,:])
In [18]: np.allclose(entropy_loopy(distrib),out) # Verify
Out[18]: True
In [19]: %timeit entropy_loopy(distrib)
1 loops, best of 3: 800 ms per loop
In [20]: %timeit stats.entropy(distrib.T[:,:,None], distrib.T[:,None,:])
10 loops, best of 3: 104 ms per loop