【问题标题】:Vectorized sum-reduction with outer product - NumPy带外积的矢量化减和 - NumPy
【发布时间】:2018-06-05 20:50:53
【问题描述】:

我对 NumPy 比较陌生,经常读到应该避免编写循环。在许多情况下,我知道如何处理它,但目前我有以下代码:

p = np.arange(15).reshape(5,3)
w = np.random.rand(5)
A = np.sum(w[i] * np.outer(p[i], p[i]) for i in range(len(p)))

有人知道是否有办法避免内部 for 循环吗?

提前致谢!

【问题讨论】:

    标签: python numpy vectorization


    【解决方案1】:

    方法 #1: 使用 np.einsum -

    np.einsum('ij,ik,i->jk',p,p,w)
    

    方法#2:使用broadcasting + np.tensordot -

    np.tensordot(p[...,None]*p[:,None], w, axes=((0),(0)))
    

    方法#3:使用np.einsum + np.dot -

    np.einsum('ij,i->ji',p,w).dot(p)
    

    运行时测试

    设置#1:

    In [653]: p = np.random.rand(50,30)
    
    In [654]: w = np.random.rand(50)
    
    In [655]: %timeit np.einsum('ij,ik,i->jk',p,p,w)
    10000 loops, best of 3: 101 µs per loop
    
    In [656]: %timeit np.tensordot(p[...,None]*p[:,None], w, axes=((0),(0)))
    10000 loops, best of 3: 124 µs per loop
    
    In [657]: %timeit np.einsum('ij,i->ji',p,w).dot(p)
    100000 loops, best of 3: 9.07 µs per loop
    

    设置#2:

    In [658]: p = np.random.rand(500,300)
    
    In [659]: w = np.random.rand(500)
    
    In [660]: %timeit np.einsum('ij,ik,i->jk',p,p,w)
    10 loops, best of 3: 139 ms per loop
    
    In [661]: %timeit np.einsum('ij,i->ji',p,w).dot(p)
    1000 loops, best of 3: 1.01 ms per loop
    

    第三种方法只是把其他一切都搞砸了!

    为什么 Approach #3Approach #1 快 10-130 倍?

    np.einsum 是在 C 中实现的。在第一种方法中,这三个字符串在其字符串表示法中有 i,j,k,我们将有三个嵌套循环(当然是在 C 中) .那里有很多内存开销。

    使用第三种方法,我们只进入两个字符串ij,因此有两个嵌套循环(再次在 C 中),并且还利用基于 BLAS 的 matrix-multiplicationnp.dot。这两个因素促成了这一惊人的加速。

    【讨论】:

    • 哇,谢谢! np.einsum 的加速非常好! %timeit np.sum(w[i] * np.outer(p[i], p[i]) for i in range(len(p))) 76.8 µs ± 313 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)%timeit np.einsum('ij,ik,i->jk',p,p,w) 5.49 µs ± 43.5 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)%timeit np.tensordot(p[...,None]*p[:,None], w, axes=((0),(0))) 23.9 µs ± 510 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
    • @joe-92 增加了更大阵列的计时。
    • @Divakar,你对 einsum 和 dot 的组合比纯 einsum 更快有什么解释吗?
    • NumPy 1.14 似乎会在可能的情况下将 BLAS 用于einsum (github.com/numpy/numpy/releases)。也许这会提高第一种方法的速度。
    • @joe-92 好消息!我们只需要看看“如果可能”部分的表现如何。但确实很有前途。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-05-04
    • 1970-01-01
    • 2019-07-15
    • 2018-12-25
    • 2018-03-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多