【问题标题】:Vectorizing NumPy covariance for 3D array向量化 3D 数组的 NumPy 协方差
【发布时间】:2017-03-16 15:22:40
【问题描述】:

我有一个形状为 (t, n1, n2) 的 3D numpy 数组:

x = np.random.rand(10, 2, 4)

我需要计算另一个 3D 数组 y,其形状为 (t, n1, n1),这样:

y[0] = np.cov(x[0,:,:])

...等沿第一个轴的所有切片。

所以,一个循环的实现将是:

y = np.zeros((10,2,2))
for i in np.arange(x.shape[0]):
    y[i] = np.cov(x[i, :, :])

有什么方法可以将其向量化,以便我可以一次性计算所有协方差矩阵?我试着做:

x1 = x.swapaxes(1, 2)
y = np.dot(x, x1)

但是没有用。

【问题讨论】:

    标签: python numpy multidimensional-array vectorization covariance


    【解决方案1】:

    侵入numpy.cov source code 并尝试使用默认参数。事实证明,np.cov(x[i,:,:]) 很简单:

    N = x.shape[2]
    m = x[i,:,:]
    m -= np.sum(m, axis=1, keepdims=True) / N
    cov = np.dot(m, m.T)  /(N - 1)
    

    因此,任务是对这个循环进行矢量化,该循环将遍历 i 并一次性处理来自 x 的所有数据。同样,我们可以在第三步使用broadcasting。对于最后一步,我们沿着第一轴的所有切片执行sum-reduction。这可以使用np.einsum 以矢量化方式有效实现。于是,最终的实现就到了这个地步——

    N = x.shape[2]
    m1 = x - x.sum(2,keepdims=1)/N
    y_out = np.einsum('ijk,ilk->ijl',m1,m1) /(N - 1)
    

    运行时测试

    In [155]: def original_app(x):
         ...:     n = x.shape[0]
         ...:     y = np.zeros((n,2,2))
         ...:     for i in np.arange(x.shape[0]):
         ...:         y[i]=np.cov(x[i,:,:])
         ...:     return y
         ...: 
         ...: def proposed_app(x):
         ...:     N = x.shape[2]
         ...:     m1 = x - x.sum(2,keepdims=1)/N
         ...:     out = np.einsum('ijk,ilk->ijl',m1,m1)  / (N - 1)
         ...:     return out
         ...: 
    
    In [156]: # Setup inputs
         ...: n = 10000
         ...: x = np.random.rand(n,2,4)
         ...: 
    
    In [157]: np.allclose(original_app(x),proposed_app(x))
    Out[157]: True  # Results verified
    
    In [158]: %timeit original_app(x)
    1 loops, best of 3: 610 ms per loop
    
    In [159]: %timeit proposed_app(x)
    100 loops, best of 3: 6.32 ms per loop
    

    那里有巨大的加速!

    【讨论】:

    • 感谢 Divakar,现在详细研究它以了解 einsum。
    • 我刚刚在您的代码中将 N/(N**2-N) 更改为 1/(N-1) 以符合标准公式并易于理解
    • @Divakar,您能否更详细地解释一下这一步:m1 = x - x.sum(2,keepdims=1)/N?更改.sum 轴,似乎不会影响最终答案...
    • 另一种方式,不如 einsum 方式快:out = (m1 @ m1.transpose(0,2,1)) / (N - 1),它遵循 sample covariance matrix 的定义。
    猜你喜欢
    • 1970-01-01
    • 2019-09-11
    • 1970-01-01
    • 1970-01-01
    • 2016-01-10
    • 2015-02-21
    • 1970-01-01
    • 2021-12-07
    • 1970-01-01
    相关资源
    最近更新 更多