【问题标题】:Efficient product of 1D array and 3D array along one dimension - NumPy一维数组和 3D 数组的高效乘积 - NumPy
【发布时间】:2024-05-02 18:20:01
【问题描述】:

我有两个 numpy 数组:

  • 一个名为 t 的形状为 (70L,) 的一维数组,其元素名为 let s say ti
  • 一个名为 I 的 3D 数组,形状为 (70L, 1024L, 1024L),每个元素称为 Ii。因此,Ii 的尺寸为 (1024L, 1024L)

我想沿第一个维度制作两个数组的乘积,即:

tI = t1*I1,t2*I2,...,tN*IN

比如再得到一个新的维数(70L,1024L,1024L)的数组,然后沿第一个维求和,得到一个维数为(1024L,1024L)的数组:

tsum = t1*I1 + t2*I2 + ... +tN*IN

目前我对执行以下操作感到满意:

tI = np.asarray([t[i]*I[i,:,:] for i in range(t.shape[0])])
tsum = np.sum(tI,axis=0)

但它会有点慢,因为我的数组的尺寸正在增加。 我想知道是否存在针对特定任务更优化的 numpy 或 scipy 函数?

在此先感谢您提供任何链接或信息。

格雷格

【问题讨论】:

    标签: python arrays performance numpy multiplication


    【解决方案1】:

    你可以使用np.tensordot -

    np.tensordot(t,I, axes=([0],[0]))
    

    你也可以使用np.einsum -

    np.einsum('i,ijk->jk',t,I)
    

    运行时测试和输出验证 -

    In [21]: def original_app(t,I):
        ...:     tI = np.asarray([t[i]*I[i,:,:] for i in range(t.shape[0])])
        ...:     tsum = np.sum(tI,axis=0)
        ...:     return tsum
        ...: 
    
    In [22]: # Inputs with random elements
        ...: t = np.random.rand(70,)
        ...: I = np.random.rand(70,1024,1024)
        ...: 
    
    In [23]: np.allclose(original_app(t,I),np.tensordot(t,I, axes=([0],[0])))
    Out[23]: True
    
    In [24]: np.allclose(original_app(t,I),np.einsum('i,ijk->jk',t,I))
    Out[24]: True
    
    In [25]: %timeit np.tensordot(t,I, axes=([0],[0]))
    1 loops, best of 3: 110 ms per loop
    
    In [26]: %timeit np.einsum('i,ijk->jk',t,I)
    1 loops, best of 3: 201 ms per loop
    

    【讨论】:

    • 对我来说tensordot 实际上更快。
    • @egpbos 您是否尝试过使用相同的数据大小?我认为这可能是 NumPy 在安装过程中的编译方式。对于不同的硬件,它的性能可能不同,但主要是猜测。
    • 是的,我刚刚复制了你的测试代码。对我来说,tensordot 始终比einsum 快约 35%。 In [65]: %timeit np.einsum('i,ijk->jk',t,I) 10 loops, best of 3: 106 ms per loop In [66]: %timeit np.tensordot(t,I, axes=([0],[0])) 10 loops, best of 3: 65.9 ms per loop
    • @egpbos 嗯,有趣的结果。我猜硬件必须在其中发挥重要作用。
    • 嘿,谢谢。 np.tensordot 为我做到了,对我来说它比 einsum 快
    【解决方案2】:

    Divakar 给出了最好(最有效)的答案。为了完整起见,另一种方法是使用Numpy's broadcasting capabilities

    (t[:,np.newaxis,np.newaxis]*I).sum(axis=0)
    

    通过向t 添加两个轴,广播成为可能,并且可以使用常规的 Numpy 操作,这对某些人来说可能更具可读性。

    【讨论】:

    • broadcasting 也是 NumPy 的一个很棒的功能!喜欢那个。
    最近更新 更多