我想在python中将三个(或四个)大型2D数组进行元素逐个外部乘积(值将float32舍入为两位小数)。它们都具有相同数量的行“ n”,但是具有不同数量的列“ i”,“ j”,“ k”。
结果数组的形状应为(n,i * j * k)。然后,我想对结果的每一列求和,最后得到一维形状为(i * j * k)的一维数组。
np.shape(a) = (75466, 10)
np.shape(b) = (75466, 28)
np.shape(c) = (75466, 66)
np.shape(intermediate_result) = (75466, 18480)
np.shape(result) = (18480)
感谢ruankesi and divakar,我得到了一段有效的代码:
# Multiply first two matrices
first_multi = a[...,None] * b[:,None]
# could use np.einsum('ij,ik->ijk',a,b), which is slightly faster
ab_fills = first_multi.reshape(a.shape[0], a.shape[1]*b.shape[1])
# Multiply the result with the third matrix
second_multi = ab_fills[..., None] * c[:,None]
abc_fills = second_multi.reshape(ab_fills.shape[0], ab_fills.shape[1] * c.shape[1])
# Get the result: sum columns and get a 1D array of length 10*28*66 = 18 480
result = np.sum(abc_fills, axis = 0)
这大约需要3秒钟,但是我必须重复多次此操作,并且某些矩阵甚至更大(以行数计)。这是可以接受的,但是使其更快将是不错的。
例如,“ a”实际上包含0的70%。我尝试玩scipy csc_matrix,但实际上无法获得工作版本。 (要在此处获得按元素分类的外部乘积,我需要转换为3D矩阵,而scipy sparse_matrix不支持该矩阵)
如果我尝试同时使用第4个矩阵,则会遇到内存问题。
我想将这段代码转换为sparse_matrix可以节省大量内存,并且可以通过忽略众多0值来加快计算速度。
真的吗?如果可以,有人可以帮我吗?
当然,如果您有更好的实施建议,我也很感兴趣。我不需要任何中间结果,只需要最终的一维结果。
我停留在这部分代码上已经有几个星期了,我疯了!
谢谢!
方法1:
一种很好的衬板,但出人意料地比原始方法要慢(?)。
在我的测试数据集上,方法#1每个循环花费4.98 s±3.06 ms(optimize = True时无加速)
最初的分解方法每个循环耗时3.01 s±16.5 ms
方法2:
太好了,谢谢!多么令人印象深刻的加速!
每个循环62.6 ms±233 µs
关于numexpr,我尽量避免对外部模块的需求,并且我不打算使用多核/线程。这是一项“令人费解的”可并行化任务,需要分析成千上万个对象,我将在生产过程中将列表分散到可用的CPU上。我将尝试进行内存优化。
作为对numexpr的一个简短尝试,它限制了1个线程并执行1个乘法,没有numexpr的运行时间为40毫秒,而使用numexpr的运行时间为52毫秒。
再次感谢!
答案 0 :(得分:0)
方法1
我们可以使用np.einsum
一次性进行总和减少-
result = np.einsum('ij,ik,il->jkl',a,b,c).ravel()
另外,通过将其设置为optimize
以使用BLAS,在np.einsum
中使用True
标志。
方法2
我们可以使用broadcasting
来执行第一步,正如在发布的代码中也提到的那样,然后将张量矩阵乘法与np.tensordot
-
def broadcast_dot(a,b,c):
first_multi = a[...,None] * b[:,None]
return np.tensordot(first_multi,c, axes=(0,0)).ravel()
我们还可以使用numexpr
module来支持多核处理,并获得更高的内存效率来获得first_multi
。这为我们提供了一种经过修改的解决方案,例如-
import numexpr as ne
def numexpr_broadcast_dot(a,b,c):
first_multi = ne.evaluate('A*B',{'A':a[...,None],'B':b[:,None]})
return np.tensordot(first_multi,c, axes=(0,0)).ravel()
具有给定数据集大小的随机浮动数据的计时-
In [36]: %timeit np.einsum('ij,ik,il->jkl',a,b,c).ravel()
4.57 s ± 75.9 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
In [3]: %timeit broadcast_dot(a,b,c)
270 ms ± 103 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [4]: %timeit numexpr_broadcast_dot(a,b,c)
172 ms ± 63.8 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
仅凭numexpr
给人一种改善的感觉-
In [7]: %timeit a[...,None] * b[:,None]
80.4 ms ± 2.64 ms per loop (mean ± std. dev. of 7 runs, 10 loops each)
In [8]: %timeit ne.evaluate('A*B',{'A':a[...,None],'B':b[:,None]})
25.9 ms ± 191 µs per loop (mean ± std. dev. of 7 runs, 10 loops each)
在将此解决方案扩展到更多输入时,这应该是实质性的。