首页 > 解决方案 > 计算矩阵行和向量行之间的 KL 散度

问题描述

我有一个矩阵(numpy 2d 数组),其中每一行都是有效的概率分布。我有另一个向量(numpy 1d 数组),也是一个概率分布。我需要计算矩阵的每一行和向量之间的 KL 散度。是否可以在不使用 for 循环的情况下做到这一点?

这个问题问的是同样的事情,但没有一个答案能解决我的问题。其中之一建议使用我想避免的 for 循环,因为我有大量数据。另一个答案在 tensorflow 中提供了一个解决方案,但我想要 numpy 数组。

scipy.stats.entropy计算 2 个向量之间的 KL 散度,但是当其中一个是矩阵时,我不知道如何使用它。

标签: pythonnumpyscipy

解决方案


实际上,该函数scipy.stats.entropy可以进行矢量化计算,但是您必须适当地重塑参数才能使其工作。当输入是二维数组时,entropy期望保存概率向量。在p是二维和q一维的情况下,必须添加一个平凡的维度q以使参数与广播兼容。

这是一个例子。一、进口:

In [10]: import numpy as np                                                     

In [11]: from scipy.stats import entropy                                        

创建一个二维p,其行是概率向量,以及一个一维概率向量q

In [12]: np.random.seed(8675309)                                                

In [13]: p = np.random.rand(3, 5)                                               

In [14]: p /= p.sum(axis=1, keepdims=True)                                      

In [15]: q = np.random.rand(5)                                                  

In [16]: q /= q.sum()                                                           

In [17]: p                                                                      
Out[17]: 
array([[0.32085531, 0.29660176, 0.14113073, 0.07988999, 0.1615222 ],
       [0.05870513, 0.15367858, 0.29585406, 0.01298657, 0.47877566],
       [0.1914319 , 0.29324935, 0.1093297 , 0.17710131, 0.22888774]])

In [18]: q                                                                      
Out[18]: array([0.06804561, 0.35392387, 0.29008139, 0.04580467, 0.24214446])

为了与矢量化结果进行比较,这里是使用 Python 循环计算的结果。

In [19]: [entropy(t, q) for t in p]                                             
Out[19]: [0.32253909299531597, 0.17897138916539493, 0.2627905326857023]

为了进行entropy向量化计算,第一个参数的必须是概率向量,所以我们将转置p. 然后,为了q与 兼容p.T,我们将其重塑为形状为 (5, 1) 的二维数组(即它包含一列):

In [20]: entropy(p.T, q.reshape(-1, 1))                                         
Out[20]: array([0.32253909, 0.17897139, 0.26279053])

注意:用作第二个参数很诱人q.T,但这不起作用。在 NumPy 中,转置操作只交换现有维度的长度——它从不创建新维度。所以一维数组的转置就是它本身。即,q.T与 的形状相同q


此答案的旧版本如下...

您可以使用scipy.special.kl_divscipy.special.rel_entr来执行此操作。这是一个例子。

In [17]: import numpy as np 
    ...: from scipy.stats import entropy 
    ...: from scipy.special import kl_div, rel_entr 

p以和q为例 。p具有形状 (3, 5);行是概率分布。q是一个长度为 5 的一维数组。

In [18]: np.random.seed(8675309) 
    ...: p = np.random.rand(3, 5) 
    ...: p /= p.sum(axis=1, keepdims=True) 
    ...: q = np.random.rand(5) 
    ...: q /= q.sum()

这是您想要的计算,使用 Python 循环和scipy.stats.entropy. 我将其包含在此处,因此可以将结果与下面的矢量化计算进行比较。

In [19]: [entropy(t, q) for t in p]                                                                                                          
Out[19]: [0.32253909299531597, 0.17897138916539493, 0.2627905326857023]

我们构造了p和,使得每个概率向量之和为 1。在这种情况下,上述结果也可以用或q进行向量化计算。(我建议. 添加和减去最终会在总和中抵消的附加项,因此它所做的工作比必要的要多。)这些函数仅计算计算的逐点部分;你必须对结果求和才能得到实际的熵或散度。scipy.special.rel_entrscipy.special.kl_divrel_entrkl_div

In [20]: rel_entr(p, q).sum(axis=1)                                                                              
Out[20]: array([0.32253909, 0.17897139, 0.26279053])

In [21]: kl_div(p, q).sum(axis=1)                                                                                
Out[21]: array([0.32253909, 0.17897139, 0.26279053])

推荐阅读