使用numpy sparsey数组优化操作

时间:2017-10-14 14:24:03

标签: python numpy sparse-matrix

我正在使用python 3进行缓慢的numpy操作。

我有以下操作:

DocumentFile

其中

np.sum(np.log(X.T * b + a).T, 1)

我的问题是这个操作很慢(大约1.5秒),并且它在一个循环中,所以重复大约100次,这使我的代码的运行时间很长。

我想知道是否有更快的实现此功能。

也许有用的事实:X非常稀疏(只有0.08%的条目是非零的),但它是一个NumPy数组。

2 个答案:

答案 0 :(得分:2)

我们可以优化对数运算,这似乎是瓶颈,并且作为超越函数之一可以用numexpr module然后sum-reduce和NumPy加速,因为NumPy做得更好,因此给我们一个混合的,像这样 -

import numexpr as ne

def numexpr_app(X, a, b):
    XT = X.T
    return ne.evaluate('log(XT * b + a)').sum(0)

仔细观察广播业务:XT * b + a,我们看到有两个广播阶段,我们可以进一步优化。目的是看看是否可以将其缩减到一个阶段,这似乎可以通过某种划分来实现。这给了我们一个稍微修改过的版本,如下所示 -

def numexpr_app2(X, a, b):
    ab = (a/b)
    XT = X.T
    return np.log(b).sum() + ne.evaluate('log(ab + XT)').sum(0)

运行时测试和验证

原创方法 -

def numpy_app(X, a, b):
    return np.sum(np.log(X.T * b + a).T, 1)

计时 -

In [111]: # Setup inputs
     ...: density = 0.08/100 # 0.08 % sparse
     ...: m,n = 30000, 1000
     ...: X = scipy.sparse.rand(m,n,density=density,format="csr").toarray()
     ...: a = np.random.rand(n,1)
     ...: b = np.random.rand(n,1)
     ...: 

In [112]: out0 = numpy_app(X, a, b)
     ...: out1 = numexpr_app(X, a, b)
     ...: out2 = numexpr_app2(X, a, b)
     ...: print np.allclose(out0, out1)
     ...: print np.allclose(out0, out2)
     ...: 
True
True

In [114]: %timeit numpy_app(X, a, b)
1 loop, best of 3: 691 ms per loop

In [115]: %timeit numexpr_app(X, a, b)
10 loops, best of 3: 153 ms per loop

In [116]: %timeit numexpr_app2(X, a, b)
10 loops, best of 3: 149 ms per loop

只是为了证明开始时所说的观察log部分是原始NumPy方法的瓶颈,这里有时间 -

In [44]: %timeit np.log(X.T * b + a)
1 loop, best of 3: 682 ms per loop

改善意义重大 -

In [120]: XT = X.T

In [121]: %timeit ne.evaluate('log(XT * b + a)')
10 loops, best of 3: 142 ms per loop

答案 1 :(得分:1)

有点不清楚为什么你会np.sum(your_array.T, axis=1)代替np.sum(your_array, axis=0)

您可以使用scipy sparse matrix :(对X使用列压缩格式,以便X.T被行压缩,因为您乘以具有一行X.T形状的b)

X_sparse = scipy.sparse.csc_matrx(X)

并将X.T * b替换为:

X_sparse.T.multiply(b) 

但是,如果a不稀疏,它将无法帮助你。

这些是我为此操作获得的加速:

In [16]: %timeit X_sparse.T.multiply(b)
The slowest run took 10.80 times longer than the fastest. This could mean that an intermediate result is being cached.
1000 loops, best of 3: 374 µs per loop

In [17]: %timeit X.T * b
10 loops, best of 3: 44.5 ms per loop

使用:

import numpy as np
from scipy import sparse

X = np.random.randn(30000, 1000)
a = np.random.randn(1000, 1)
b = np.random.randn(1000, 1)
X[X < 3] = 0
print(np.sum(X != 0))
X_sparse = sparse.csc_matrix(X)