加速一些numpy操作的有效方法

时间:2016-07-08 00:33:31

标签: python numpy

我试图找到一个有效的代码而不是下面的代码(这只是我的代码的一部分),以提高速度:

for pr in some_list:
    Tp = T[partition[pr]].sum(0)
    Tpx = np.dot(Tp, xhat)
    hp = h[partition[[pr]].sum(0)
    up = (uk[partition[pr][:]].sum(0))/len(partition[pr])
    hpu = hpu + np.dot(hp.T, up)
    Tpu = Tpu + np.dot(Tp.T, up)

我至少有两个相似的代码块。正如你所看到的,我使用了三次花式索引(真的找不到另一种方式)。在我的算法中,我需要很快完成这部分,但现在还没有发生。我真的很感激任何建议。

谢谢大家。

最佳,

1 个答案:

答案 0 :(得分:3)

如果你的分区很少并且每个分区都有很多元素,你应该考虑交换对象的索引。沿着第二个维度对形状(30,1000)的数组进行求和应该比沿着第一个维度对形状(1000,30)的数组求和更快,因为在前一种情况下,您总是将相邻的内存块求和(即{{每个剩余索引的每个arr[k,:])1}}。因此,如果你将求和指数放在最后(并且在你使用时除去一些尾随的单一维度),你可能会加速。

作为hpaulj noted in a comment,不清楚你的循环是如何被矢量化的。但是,由于它的性能至关重要,您仍然可以尝试对某些工作进行矢量化。

我建议您为每个分区存储khpup(预分配后),然后在单个矢量化步骤中执行标量/矩阵产品。另请注意,Tp在您的示例中未使用,因此我在此省略了它(无论您使用它做什么,您都可以像其他示例一样):

Tpx

显然,关键球员是numpy.einsum。当然,如果part_len = len(some_list) # number of partitions, N Tpshape = (part_len,) + T.shape[1:] # (N,30,100) if T was (1000,30,100) hpshape = (part_len,) + h.shape[1:] # (N,30,1) if h was (1000,30,1) upshape = (part_len,) + uk.shape[1:] # (N,30,1) if uk was (1000,30,1) Tp = np.zeros(Tpshape) hp = np.zeros(hpshape) up = np.zeros(upshape) for ipr,pr in enumerate(some_list): Tp[ipr,:,:] = T[partition[pr]].sum(0) hp[ipr,:,:] = h[partition[[pr]].sum(0) up[ipr,:,:] = uk[partition[pr]].sum(0)/len(partition[pr]) # compute vectorized dot products: #Tpx unclear in original, omitted # sum over second index (dot), sum over first index (sum in loop) hpu = np.einsum('abc,abd->cd',hp,up) # shape (1,1) Tpu = np.einsum('abc,abd->cd',Tp,up) # shape (100,1) hpu在循环之前有一些先前值,则必须使用上面Tpu的结果递增这些值。

对于einsum,它执行任意维数组的求和和收缩。上面的模式,einsum,当应用于3d数组'abc,abd->cd'A时,将返回一个二维数组B,具有以下定义(数学伪代码):

C

对于给定的修正C(c,d) = sum_a sum_b A(a,b,c)*B(a,b,d) 求和指数,其内部是

a

如果保留sum_b A(a,b,c)*B(a,b,d) c索引,则与d相同。由于我们也将这些矩阵与np.dot(A(a,:,:).T,B(a,:,:))进行求和,我们应该完全按照你的循环版本执行,总计每个a贡献总和。