【发布时间】:2020-03-18 14:37:10
【问题描述】:
我有一个大小为 (N,M) 的二维数组 Y,例如:
N, M = 200, 100
Y = np.random.normal(0,1,(N,M))
对于每个 N,我想计算向量 (M,1) 与其转置的点积,它返回一个 (M,M) 矩阵。一种低效的方法是:
Y = Y[:,:,np.newaxis]
[Y[i,:,:] @ Y[i,:,:].T for i in range(N)]
这很慢:第二行的timeit返回
11.7 ms ± 1.39 ms per loop (mean ± std. dev. of 7 runs, 100 loops each)
我认为更好的方法是使用 einsum numpy 函数 (https://docs.scipy.org/doc/numpy/reference/generated/numpy.einsum.html):
np.einsum('ijk,imk->ijm', Y, Y, optimize=True)
(这意味着:对于每一行 i,创建一个 (j,k) 矩阵,其元素来自最后一维 m 上的点积)
这两种方法确实返回了完全相同的结果,但是这个新版本的运行时间令人失望(速度只有两倍多一点)
3.82 ms ± 146 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
由于第一种方法非常效率低下,因此人们期望通过使用矢量化 einsum 函数获得更多改进...您对此有解释吗?有没有更好的方法来做这个计算?
【问题讨论】:
-
没有发生总和减少。不要指望 einsum 有帮助。你可以简单地做:
Y*Y[:,None,:,0]. -
没有太多可能的加速(只有大约 2 倍,例如高效的 Numba 实现)。原因是这种计算完全受限于内存带宽。如果你之后做一些减少(例如总和),很容易有一个数量级或更多的可能。优化归约情况的示例:stackoverflow.com/a/58189944/4045774
标签: python performance numpy dot