加快大阵列之间的numpy总结

时间:2016-10-07 17:42:54

标签: python arrays numpy

我有一个代码在numpy数组上运行操作。 虽然线性代数运算似乎很快,但我现在发现了一个不同问题的瓶颈:两个不同数组的总和。 在下面的示例中,WE3T1是两个1000X1000X1000数组。 首先,我使用numpy操作计算WE3,然后对这些数组求​​和。

import numpy as np
import scipy as sp
import time
N = 100
n = 1000
X = np.random.uniform(size = (N,n))
wE = np.mean(X,0)
wE3 = np.einsum('i,j,k->ijk', wE, wE, wE) #22 secs
T1 = np.random.uniform(size = (n,n,n))
a = wE3 + T1 #115 secs

wE3的计算需要22秒,而WE3T1之间的加法需要115秒。

为什么这些数组的总和比WE3的计算慢得多?它们应该或多或少具有相同的复杂性。

有没有办法加快代码的速度?

3 个答案:

答案 0 :(得分:1)

np.einsum('i,j,k->ijk', wE, wE, wE)部分没有进行任何总和减少,基本上只是广播元素乘法。所以,我们可以用这样的东西代替它 -

wE[:,None,None] * wE[:,None] * wE

运行时测试 -

In [9]: # Setup inputs at 1/5th of original dataset sizes
   ...: N = 20
   ...: n = 200
   ...: X = np.random.uniform(size = (N,n))
   ...: wE = np.mean(X,0)
   ...: 

In [10]: %timeit np.einsum('i,j,k->ijk', wE, wE, wE)
10 loops, best of 3: 45.7 ms per loop

In [11]: %timeit wE[:,None,None] * wE[:,None] * wE
10 loops, best of 3: 26.1 ms per loop

接下来,我们有wE3 + T1,其中T1 = np.random.uniform(size = (n,n,n))看起来并没有大的帮助,因为无论如何我们必须创建T1然后#39} ; s只是元素添加。我们似乎可以使用np.add让我们将结果写回其中一个数组:wE3T1。我们假设我们选择T1,如果可以修改的话。我想这会带来轻微的内存效率,因为我们不会在工作空间中添加另一个变量。

因此,我们可以做 -

np.add(wE3,T1,out=T1)

运行时测试 -

In [58]: def func1(wE3):
    ...:     T1 = np.random.uniform(size = (n,n,n))
    ...:     return wE3 + T1
    ...: 
    ...: def func2(wE3):
    ...:     T1 = np.random.uniform(size = (n,n,n))
    ...:     np.add(wE3,T1,out=T1)
    ...:     return T1
    ...: 

In [59]: # Setup inputs at 1/4th of original dataset sizes
    ...: N = 25
    ...: n = 250
    ...: X = np.random.uniform(size = (N,n))
    ...: wE = np.mean(X,0)
    ...: wE3 = np.einsum('i,j,k->ijk', wE, wE, wE)
    ...: 

In [60]: %timeit func1(wE3)
1 loops, best of 3: 390 ms per loop

In [61]: %timeit func2(wE3)
1 loops, best of 3: 363 ms per loop

使用@Aaron's suggestion,我们可以使用循环并假设将结果写回wE3是可以的,我们可以这样做 -

wE3 = wE[:,None,None] * wE[:,None] * wE
for x in wE3: 
    np.add(x, np.random.uniform(size = (n,n)), out=x)

最终结果

因此,回顾所有建议的改进,最后运行时测试结果是 -

In [97]: def func1(wE):
    ...:     wE3 = np.einsum('i,j,k->ijk', wE, wE, wE)
    ...:     T1 = np.random.uniform(size = (n,n,n))
    ...:     return wE3 + T1
    ...: 
    ...: def func2(wE):
    ...:     wE3 = wE[:,None,None] * wE[:,None] * wE
    ...:     for x in wE3: 
    ...:         np.add(x, np.random.uniform(size = (n,n)), out=x)
    ...:     return wE3
    ...: 

In [98]: # Setup inputs at 1/3rd of original dataset sizes
    ...: N = 33
    ...: n = 330
    ...: X = np.random.uniform(size = (N,n))
    ...: wE = np.mean(X,0)
    ...: 

In [99]: %timeit func1(wE)
1 loops, best of 3: 1.09 s per loop

In [100]: %timeit func2(wE)
1 loops, best of 3: 879 ms per loop

答案 1 :(得分:1)

  

是否有任何已知的原因导致这些数组的总和比WE3的计算慢?

数组wE3T1a每个都需要8千兆字节的内存。您的物理内存可能已用完,swap memory访问会导致您的性能下降。

  

有没有办法加快代码的速度?

获得更多物理内存(即RAM)。

如果无法做到这一点,请查看您要对这些阵列执行的操作,并查看是否可以批量工作,以便处理批处理时所需的总内存保持在物理内存的限制范围内。

答案 2 :(得分:0)

你应该真的使用Numba的Jit(只是及时编译器)。它是一条纯粹的numpy管道,非常适合Numba。

您只需将上面的代码放入一个函数中,然后将@jit decorater放在顶部。它获得了接近Cython的加速。

但是,正如其他人所指出的那样,您似乎正在尝试使用对于本地计算机来说太大的数据,并且numba无法解决您的问题

相关问题