【问题标题】:Optimize operations with arrays in numpy在 numpy 中使用数组优化操作
【发布时间】:2014-01-16 22:50:46
【问题描述】:

我必须应用我写的一些数学公式 在python中为:

    for s in range(tdim):
        sum1 = 0.0
        for i in range(dim):
            for j in range(dim):
                sum1+=0.5*np.cos(theta[s]*(i-j))*
                eig1[i]*eig1[j]+eig2[i]+eig2[j])-0.5*np.sin(theta[s]*(i-j))*eig1[j]*eig2[i]-eig1[i]*eig2[j])

        PHi2.append(sum1)

现在,这是正确的,但显然效率低下,反之亦然:

for i in range(dim):
            for j in range(dim):
                PHi2 = 0.5*np.cos(theta*(i-j))*(eig1[i]*eig1[j]+eig2[i]+eig2[j])-0.5*np.sin(theta*(i-j))*(eig1[j]*eig2[i]-eig1[i]*eig2[j])

但是,第二个示例在 PHi2 的所有元素中给了我相同的数字,所以这个 更快,但答案是错误的。您如何才能更正确、更有效地做到这一点?

注意:eig1 和 eig2 具有相同的维度 d,theta 和 PHi2 具有相同的维度 D, 但是 d!=D。

【问题讨论】:

    标签: python optimization numpy numerical-computing


    【解决方案1】:

    您可以使用暴力广播方法,但您正在创建一个形状为(D, d, d) 的中间数组,如果您的数组甚至是中等大小,它可能会失控。此外,在使用没有改进的广播时,您需要从最内层循环重新计算大量计算,而您只需要执行一次。如果您首先计算 i - j 的所有可能值的必要参数并将它们相加,则可以在外循环中重用这些值,例如:

    def fast_ops(eig1, eig2, theta):
        d = len(eig1)
        d_arr = np.arange(d)
        i_j = d_arr[:, None] - d_arr[None, :]
        reidx = i_j + d - 1
        mult1 = eig1[:, None] * eig1[ None, :] + eig2[:, None] + eig2[None, :]
        mult2 = eig1[None, :] * eig2[:, None] - eig1[:, None] * eig2[None, :]
        mult1_reidx = np.bincount(reidx.ravel(), weights=mult1.ravel())
        mult2_reidx = np.bincount(reidx.ravel(), weights=mult2.ravel())
    
        angles = theta[:, None] * np.arange(1 - d, d)
    
        return 0.5 * (np.einsum('ij,j->i', np.cos(angles), mult1_reidx) -
                      np.einsum('ij,j->i', np.sin(angles), mult2_reidx))
    

    如果我们将 M4rtini 的代码重写为函数进行比较:

    def fast_ops1(eig1, eig2, theta):
        d = len(eig1)
        D = len(theta)
        s = np.array(range(D))[:, None, None]
        i = np.array(range(d))[:, None]
        j = np.array(range(d))
        ret = 0.5 * (np.cos(theta[s]*(i-j))*(eig1[i]*eig1[j]+eig2[i]+eig2[j]) -
                     np.sin(theta[s]*(i-j))*(eig1[j]*eig2[i]-eig1[i]*eig2[j]))
        return ret.sum(axis=(-1, -2))
    

    我们还编造了一些数据:

    d, D = 100, 200
    eig1 = np.random.rand(d)
    eig2 = np.random.rand(d)
    theta = np.random.rand(D)
    

    速度提升非常显着,在原始代码的 115 倍基础上提高了 80 倍,从而实现了惊人的 9000 倍加速:

    In [22]: np.allclose(fast_ops1(eig1, eig2, theta), fast_ops(eig1, eig2, theta))
    Out[22]: True
    
    In [23]: %timeit fast_ops1(eig1, eig2, theta)
    10 loops, best of 3: 145 ms per loop
    
    In [24]: %timeit fast_ops(eig1, eig2, theta)
    1000 loops, best of 3: 1.85 ms per loop
    

    【讨论】:

      【解决方案2】:

      这通过广播起作用。
      对于tdim = 200 and dim = 100.
      原版 14 秒。
      版本为 120 毫秒。

      s = np.array(range(tdim))[:, None, None]
      i = np.array(range(dim))[:, None]
      j = np.array(range(dim))
      PHi2 =(0.5*np.cos(theta[s]*(i-j))*(eig1[i]*eig1[j]+eig2[i]+eig2[j])-0.5*np.sin(theta[s]*(i-j))*(eig1[j]*eig2[i]-eig1[i]*eig2[j])).sum(axis=2).sum(axis=1)
      

      【讨论】:

        【解决方案3】:

        在第一段代码中,您有0.5*np.cos(theta[s]*(i-j))...,但在第二段代码中是0.5*np.cos(theta*(i-j))...。除非您对第二段代码的 theta 定义不同,否则这很可能是造成问题的原因。

        【讨论】:

        • 这是真的,因为在第二种情况下,我考虑的是整个名为 theta 的列表由乘法和余弦运算。因为我希望在计算 i 和 j 的总和时考虑 theta 的每个元素。
        猜你喜欢
        • 2021-11-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-12-13
        • 2018-02-18
        • 2021-03-03
        • 2013-08-10
        • 2018-09-06
        相关资源
        最近更新 更多