Python 如何对给定为矩阵行的特定指数集的均值计算进行矢量化?

Python 如何对给定为矩阵行的特定指数集的均值计算进行矢量化?,python,numpy,vectorization,pytorch,Python,Numpy,Vectorization,Pytorch,我在pytorch中对一些代码进行矢量化时遇到问题。 numpy解决方案也会有所帮助,但pytorch解决方案会更好。 我将交替使用数组和张量 我面临的问题是: 给定大小为(n,X)的二维浮点数组X,以及大小为(n,n)的布尔二维数组a,计算X中按a中的行索引的行的平均值。 问题是A中的行包含数量可变的True索引 示例(numpy): 这个例子是可行的,但这个公式有三个问题: 对于较大的A和X,for循环速度较慢。我需要循环数万个索引 A[i]可能不包含True索引。这导致np.mean(np

我在pytorch中对一些代码进行矢量化时遇到问题。 numpy解决方案也会有所帮助,但pytorch解决方案会更好。 我将交替使用
数组
张量

我面临的问题是:

给定大小为(n,X)的二维浮点数组
X
,以及大小为(n,n)的布尔二维数组
a
,计算
X
中按
a
中的行索引的行的平均值。 问题是
A
中的行包含数量可变的
True
索引

示例(numpy):

这个例子是可行的,但这个公式有三个问题:

  • 对于较大的
    A
    X
    ,for循环速度较慢。我需要循环数万个索引

  • A[i]
    可能不包含
    True
    索引。这导致
    np.mean(np.array([])
    ,即
    NaN
    。我希望它改为0

  • 在pytorch中以这种方式实现它会在反向传播通过该函数的过程中导致SIGFPE(浮点错误)。原因是未选择任何内容

  • 我现在使用的解决方法是(另请参见下面的代码):

    • A
      的对角线元素设置为
      True
      ,以便始终至少有一个元素可供选择
    • 所有选定元素的总和,从该总和中减去
      X
      中的值(对角线在开始时保证为
      False
      ),然后除以
      True
      元素的数量-每行至少1个
    这是可行的,在pytorch中是可微的,并且不会产生
    NaN
    ,但我仍然需要一个覆盖所有索引的循环。 我怎样才能摆脱这个循环

    这是我当前的pytorch代码:

     import torch
     A = torch.from_numpy(A).bytes()
     X = torch.from_numpy(X)
     A[np.diag_indices(len(A)] = 1  # Set the diagonal to 1
     means = [(X[A[i]].sum(dim=0) - X[i]) / torch.clamp(A[i].sum() - 1, min=1.)  # Compute the mean safely
              for i in range(len(A))]  # Get rid of the loop somehow
     means = torch.stack(means)
    

    我不介意你的版本看起来是否完全不同,只要它是可微的并产生相同的结果。

    我们可以利用
    矩阵乘法
    -

    c = A.sum(1,keepdims=True)
    means_np = np.where(c==0,0,A.dot(X)/c)
    
    我们可以通过将
    A
    转换为
    float32
    dtype来进一步优化它,如果它还不是这样,并且精度损失还可以,如下所示-

    In [57]: np.random.seed(0)
    
    In [58]: A = np.random.randint(0,2,(1000,1000))
    
    In [59]: X = np.random.rand(1000,1000).astype(np.float32)
    
    In [60]: %timeit A.dot(X)
    10 loops, best of 3: 27 ms per loop
    
    In [61]: %timeit A.astype(np.float32).dot(X)
    100 loops, best of 3: 10.2 ms per loop
    
    In [62]: np.allclose(A.dot(X), A.astype(np.float32).dot(X))
    Out[62]: True
    
    因此,使用
    A.astype(np.float32).dot(X)
    替换
    A.dot(X)

    或者,为了解决行和为零的情况,这要求我们使用
    np。其中
    ,我们可以将任何非零值赋值,比如
    1
    c
    ,然后简单地除以它,如下所示-

    c = A.sum(1,keepdims=True)
    c[c==0] = 1
    means_np = A.dot(X)/c
    

    这也避免了在那些零行和的情况下,我们从
    np.where
    得到的警告。

    Nice!转换为pytorch很容易:将
    keepdim
    重命名为
    keepdim
    ,用torch等价物和
    torch替换函数。其中
    需要
    torch.Tensor([0])
    而不是标量0。如果需要梯度,则必须指定计数。否则,由于被零除,向后更新将产生NaN。分割
    焊炬夹(计数,最小值=1)。
    工作
    c = A.sum(1,keepdims=True)
    c[c==0] = 1
    means_np = A.dot(X)/c