【问题标题】:Avoiding numpy loops while calculating intersections在计算交叉点时避免 numpy 循环
【发布时间】:2016-03-20 05:03:46
【问题描述】:

我想加快处理r 射线和n 球体的以下计算。这是我到目前为止得到的:

# shape of mu1 and mu2 is (r, n)
# shape of rays is (r, 3)
# note that intersections has 2n columns because for every sphere one can
# get up to two intersections (secant, tangent, no intersection)
intersections = np.empty((r, 2*n, 3))
for col in range(n):
    intersections[:, col, :] = rays * mu1[:, col][:, np.newaxis]
    intersections[:, col + n, :] = rays * mu2[:, col][:, np.newaxis]

# [...]

# calculate euclidean distance from the center of gravity (0,0,0)
distances = np.empty((r, 2 * n))
for col in range(n):
    distances[:, col] = np.linalg.norm(intersections[:, col], axis=1)
    distances[:, col + n] = np.linalg.norm(intersections[:, col + n], axis=1)

我尝试通过避免for-Loops 来加快速度,但无法弄清楚如何正确广播数组,因此我只需要一个函数调用。非常感谢任何帮助。

【问题讨论】:

    标签: python arrays performance numpy vectorization


    【解决方案1】:

    这是使用broadcasting的矢量化方式-

    intersections = np.hstack((mu1,mu2))[...,None]*rays[:,None,:]
    distances = np.sqrt((intersections**2).sum(2))
    

    最后一步可以替换为np.einsum,就像这样 -

    distances = np.sqrt(np.einsum('ijk,ijk->ij',intersections,intersections))
    

    或者用np.einsum 替换几乎整个东西,换一种矢量化的方式,就像这样 -

    mu = np.hstack((mu1,mu2))
    distances = np.sqrt(np.einsum('ij,ij,ik,ik->ij',mu,mu,rays,rays))
    

    运行时测试和验证输出 -

    def original_app(mu1,mu2,rays):
        intersections = np.empty((r, 2*n, 3))
        for col in range(n):
            intersections[:, col, :] = rays * mu1[:, col][:, np.newaxis]
            intersections[:, col + n, :] = rays * mu2[:, col][:, np.newaxis]
    
        distances = np.empty((r, 2 * n))
        for col in range(n):
            distances[:, col] = np.linalg.norm(intersections[:, col], axis=1)
            distances[:, col + n] = np.linalg.norm(intersections[:, col + n], axis=1)
        return distances                    
    
    def vectorized_app1(mu1,mu2,rays):
        intersections = np.hstack((mu1,mu2))[...,None]*rays[:,None,:]
        return np.sqrt((intersections**2).sum(2))
    
    def vectorized_app2(mu1,mu2,rays):
        intersections = np.hstack((mu1,mu2))[...,None]*rays[:,None,:]
        return np.sqrt(np.einsum('ijk,ijk->ij',intersections,intersections))
    
    def vectorized_app3(mu1,mu2,rays):
        mu = np.hstack((mu1,mu2))
        return np.sqrt(np.einsum('ij,ij,ik,ik->ij',mu,mu,rays,rays))
    

    时间安排 -

    In [101]: # Inputs
         ...: r = 1000
         ...: n = 1000
         ...: mu1 = np.random.rand(r, n)
         ...: mu2 = np.random.rand(r, n)
         ...: rays = np.random.rand(r, 3)
    
    
    In [102]: np.allclose(original_app(mu1,mu2,rays),vectorized_app1(mu1,mu2,rays))
    Out[102]: True
    
    In [103]: np.allclose(original_app(mu1,mu2,rays),vectorized_app2(mu1,mu2,rays))
    Out[103]: True
    
    In [104]: np.allclose(original_app(mu1,mu2,rays),vectorized_app3(mu1,mu2,rays))
    Out[104]: True
    
    In [105]: %timeit original_app(mu1,mu2,rays)
         ...: %timeit vectorized_app1(mu1,mu2,rays)
         ...: %timeit vectorized_app2(mu1,mu2,rays)
         ...: %timeit vectorized_app3(mu1,mu2,rays)
         ...: 
    1 loops, best of 3: 306 ms per loop
    1 loops, best of 3: 215 ms per loop
    10 loops, best of 3: 140 ms per loop
    10 loops, best of 3: 136 ms per loop
    

    【讨论】:

    • 非常感谢!我采用了np.einsum 方法,整个程序的速度提高了近 2 倍
    • @rldw 非常好,很高兴为您提供帮助!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-09-28
    • 1970-01-01
    相关资源
    最近更新 更多