【发布时间】:2018-01-03 16:49:23
【问题描述】:
我正在用 numpy 处理一个简单的问题。我有两个矩阵列表——比如A,B——编码为形状分别为(n,p,q)和(n,q,r)的3D数组。
我想计算它们的逐元素点积,即 3D 数组 C 使得 C[i,j,l] = sum A[i,j,:] B[i,:,l]。这在数学上非常简单,但我必须遵循以下规则:
1) 我只能使用 numpy 函数(dot、tensordot、einsum 等):没有循环和 cie。这是因为我希望它可以在我的 gpu(使用 cupy)上工作,并且循环在它上面很糟糕。我希望在当前设备上进行所有操作。
2) 由于我的数据可能非常大,通常 A 和 B 已经占用了几十 Mb 的内存,我不想构建任何形状比 (n,p,q),(n,q,r),(n,p,r) 更大的项目(没有中间 4D数组必须存储)。
例如,我找到的解决方案 there ,即使用:
C = np.sum(np.transpose(A,(0,2,1)).reshape(n,p,q,1)*B.reshape(n,q,1,r),-3)
在数学上是正确的,但意味着中间创建了一个 (n,p,q,r) 数组,这对我的目的来说太大了。
我也遇到过类似的问题
C = np.einsum('ipq,iqr->ipr',A,B)
我不知道底层的操作和构造是什么,但它总是会导致内存错误。
另一方面,有点天真,比如:
C = np.array([A[i].dot(B[i]) for i in range(n)])
在内存方面似乎还可以,但在我的 gpu 上效率不高:列表似乎是在 CPU 上构建的,并且将其重新分配给 gpu 很慢(如果有一种对 cupy 友好的方式来编写它,它将是一个不错的解决方案!)
感谢您的帮助!
【问题讨论】:
-
n,p,q,r在您的实际使用案例中的典型值是多少? -
希望它可以与
n =~ 5000一起使用,并说p=q=r = 50(但越多越好)。例如,形状为(5000,50,50)的cp.array()在我的gpu 上占用112 Mb(超过2 Gb),这没关系,但如果我必须存储(5000,50,50,50)数组... -
我认为
np.einsum('ipq,iqr->ipr',A,B)最适合这些形状。它会导致内存错误吗?如果是这样,为什么不切块呢? -
实际上 gpu 上的
einsum确实很容易产生内存错误......即使使用n = 1000和p=q=r=50它也不起作用(而至少我提到的其他解决方案可以处理这么少的数据)。你把东西切成块是什么意思?不管怎样,我很确定它可以在不存储更大数组的情况下完成...我的意思是,np.dot不会创建(p,q,r)数组,那么我为什么要为元素操作这样做呢?... -
还有一件事是在初始化输出数组后使用传统循环
C,然后使用np.dot方法。