【发布时间】:2017-06-15 23:26:34
【问题描述】:
我正在尝试执行大型线性代数计算以将通用协方差矩阵KK_l_obs(形状(NL, NL))转换为缩小空间中的协方差矩阵映射Kmap_PC(形状(q, q, X, Y))。
有关如何为每个空间位置构造Kmap_PC 的信息保存在其他数组a、I0 和k_l_th 中。前两个有形状(X, Y),第三个有(nl, nl)。观察空间和缩小空间之间的转换由 eingenvectors E(形状 (q, nl))处理。注意NL > nl。
Kmap_PC 的空间元素计算为:
Kmap_PC[..., X, Y] = E.dot(
KK_l_obs[I0[X, Y]: I0[X, Y] + nl,
I0[X, Y]: I0[X, Y] + nl] / a_map[X, Y] + \
k_l_th).dot(E.T)
第一个点积中的位理论上可以直接使用np.einsum 计算,但会占用数百 GB 的内存。我现在正在做的是循环遍历Kmap_PC 的空间索引,这非常慢。我还可以使用 MPI 分配计算(这可能会加速 3-4 倍,因为我有 16 个内核可用)。
我想知道:
(a) 如果我可以更有效地进行计算——也许明确地将其分解为空间元素组;和
(b) 如果我可以改善这些计算的内存开销。
代码 sn-p
import numpy as np
np.random.seed(1)
X = 10
Y = 10
NL = 3000
nl = 1000
q = 7
a_map = 5. * np.random.rand(X, Y)
E = np.random.randn(q, nl)
# construct constant component
m1_ = .05 * np.random.rand(nl, nl)
k_l_th = m1_.dot(m1_)
# construct variable component
m2_ = np.random.rand(NL, NL)
KK_l_obs = m2_.dot(m2_.T)
# where to start in big cov
I0 = np.random.randint(0, NL - nl, (X, Y))
# the slow way
def looping():
K_PC = np.empty((q, q, X, Y))
inds = np.ndindex((X, Y))
for si in inds:
I0_ = I0[si[0], si[1]]
K_PC[..., si[0], si[1]] = E.dot(
KK_l_obs[I0_ : I0_ + nl, I0_ : I0_ + nl] / a_map[si[0], si[1]] + k_l_th).dot(E.T)
return K_PC
def veccalc():
nl_ = np.arange(nl)[..., None, None]
I, J = np.meshgrid(nl_, nl_)
K_s = KK_l_obs[I0[..., None, None] + J, I0[..., None, None] + I]
K_s = K_s / a_map[..., None, None] + k_l_th[None, None, ...]
print(K_s.nbytes)
K_PC = E @ K_s @ E.T
K_PC = np.moveaxis(K_PC, [0, 1], [-2, -1])
return K_PC
【问题讨论】:
-
主题行具有误导性,听起来您正在从多个
aranges或类似的东西创建一个数组。相反,这是一个很大的dot产品问题E.dot(A).dot(E.T)。我想看看einsum表达式,以及一个我可以通过简单的复制粘贴运行的小测试用例。光看你的描述很难掌握计算方法。 -
刚刚添加了一个循环实现的示例,并且数据维度相对较小。现在正在处理基于
einsum的示例 -
因此,使用这些数字,您可以进行 100 个涉及
(7,1000)@(1000,1000)@(1000,7) => (7,7)的双点乘积。如果我可以进行I0映射(同时处理索引和内存大小),那么最大的问题将是(7,1000)@(10,10,1000,1000)@(1000,7) -> (10,10,7,7) -
我已经处理了
I0映射。基本上,问题在于X, Y接近 70 左右;随着NL和nl接近3000 和4000(这更接近我的真正问题),中间矩阵K_s变得非常大。
标签: python performance numpy