【发布时间】:2018-01-27 09:08:49
【问题描述】:
我有一个使用线性矩阵变换的“粒子传播”相对简单的例子。
我的粒子分布基本上是一组(“束”)5 维向量。它通常包含 100k 到 1M 个这样的向量。
这些向量中的每一个都必须乘以一个矩阵。
目前我想出的解决方案如下。
粒子是这样创建的,协方差矩阵在这里显示为对角线,但这是为了一个相对简单的例子:
# Edit: I now use np.random_intel linking to MKL for improved performances
d = np.random.multivariate_normal(
[0.0,
0.0,
0.0,
0.0,
0.0
],
np.array([
[1.0, 0.0, 0.0, 0.0, 0.0],
[0.0, 1.0, 0.0, 0.0, 0.0],
[0.0, 0.0, 1.0, 0.0, 0.0],
[0.0, 0.0, 0.0, 1.0, 0.0],
[0.0, 0.0, 0.0, 0.0, 0.1]
]),
int(1e5)
)
传播矩阵很简单
D = np.array([[1, 10, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 1]])
我对@987654323@ 的解决方案是
r = np.einsum('ij,kj->ik', d[:, 0:4], D)
(注意这里我滑动只获取向量的前四个坐标,但原因无关)。
有没有办法显着加快速度?
我对所有细节没有清晰的认识,但这里有一些想法:
-
einsum默认情况下不调用 BLAS,而是使用内部 SSE 优化,有没有办法用纯 BLAS 调用来表达我的问题,使其更快? - 显然是
einsum的optimize选项的最新版本,可以打开以在更广泛的情况下回退到BLAS 调用。我试过了,它不会改变执行时间。 - PyPy 和 numpy 会更好吗?
我测试了@Divakar 的建议,它确实更快(10M 粒子):
%%timeit
r = d[:, 0:4].dot(D.T)
# 541 ms ± 9.44 ms per loop (mean ± std. dev. of 7 runs, 1 loop each)
与我最初的相比
%%timeit -n 1 -r 1
r = np.einsum('ij,kj->ik', d[:, 0:4], D, optimize=True)
# 1.74 s ± 0 ns per loop (mean ± std. dev. of 1 run, 1 loop each)
可能会影响最终答案的直接相关问题:
如何处理“丢失”的粒子?
在逐个粒子矩阵相乘之后,我将检查一些坐标的上限,例如(r 是上一步的结果:
selected = (r[:, 0] < 0.1) & (r[:, 1] < 0.1)
ind = np.where(selected)
r[ind]
然后应用r[ind]的下一轮矩阵乘法。
有几件事我不清楚:
- 这是最有效的吗?
- 它不会创建太多副本吗?
- “保留”未选择的粒子(并无论如何将它们相乘),同时跟踪它们丢失的事实(通过掩码)不是更好吗?那是更多的乘法,但可以将所有内容都保留在一个对象中,无需进一步分配并保持所有内容对齐?
【问题讨论】:
-
使用
d[:, 0:4].dot(D.T)。 -
@Divakar:它确实改善了一些东西,但是一个因素〜4,谢谢!我将编辑我的问题以包含一个关于面具的子问题,因为这部分显然是微不足道的。
-
@CedricH。将矩阵
d放在 C 顺序 中,将D.T放在 fortran 顺序 中以进行更多优化。这应该使您的代码至少快 2 倍 :) -
@CedricH。因为轴 1 访问在 C 顺序 中很便宜,而轴 0 访问在 fortran 顺序 中更便宜,我们希望矩阵按适当的顺序排列!
-
@kmario23 谢谢,但不确定你的意思。我可以从 Divakar 的建议中进一步改进吗?
标签: python python-3.x numpy numpy-ndarray dot-product