【问题标题】:list comprehension convolution列表理解卷积
【发布时间】:2018-03-25 12:13:53
【问题描述】:

我有一个这样的工作代码,但它相当慢。

def halfconvolution(g,w,dz):
    convo=np.zeros_like(g)
    for i in range(0,len(g)):
        sum=0
        for j in range(0,i):
            sum+=g[j]*w[(i-j)]*dz
        convo[i] = -sum
    return convo

我正在尝试将其转换为列表理解,但我很挣扎。
我试过了:

convo=[-g*w[i-j] for i in g for j in w]

【问题讨论】:

  • 为什么您认为列表理解会“更快”?不要为了减少代码行数而混淆循环
  • 您应该在理解中使用for i in range(len(g))for j in range(i),就像在循环中一样。不要忘记测量,看看你是否真的获得了更好的性能。
  • @cricket_007 出于某种原因,我认为列表理解更快.. 我计时了,实际上它更慢。关于加速这样的代码有什么建议吗?
  • 您正在使用 Numpy,但执行循环以返回一个数组...您应该一次对整个列使用 矢量化操作,而不是索引列并更新单个单元格

标签: python list list-comprehension


【解决方案1】:

我不确定这是否会提高性能,但正如你所问的那样,这是一个列表理解

convo = [-sum(g[j] * w[i - j] * dz for j in range(0, i)) for i in range(0, len(g))]

使用 NumPy 的更快实现:

# make the matrices square
g = np.repeat(g, g.shape[0]).reshape(g.shape[0], g.shape[0], order='F')
w = np.repeat(w, w.shape[0]).reshape(w.shape[0], w.shape[0], order='F')

# take the lower half of g
g = np.tril(g, k=-1)

# shift each column by its index number
# see: https://stackoverflow.com/questions/20360675/roll-rows-of-a-matrix-independently
rows_w, column_indices_w = np.ogrid[:w.shape[0], :w.shape[1]]
shift = np.arange(w.shape[0])
shift[shift < 0] += w.shape[1]
w = w[rows_w, column_indices_w - shift[:,np.newaxis]].T

convo = np.sum(g * w, axis=1) * dz

为了让它工作,它需要 w 和 g 的大小相同,否则我确信可以找到解决方法。

我希望这对您来说是一个更可接受的加速?始终尝试将您的逻辑/问题重写为向量/矩阵乘法。

【讨论】:

  • 您可以在 sum 函数中松开 [],因为它接受生成器函数
  • 这行得通,谢谢。我很惊讶,但这个结果比原来的要慢。我的印象是列表理解比 for 循环快?
  • 根据我的经验,它们的速度通常差不多。如果代码对性能至关重要,最好用 NumPy 重写它。
  • 我添加了一个“纯”numpy 实现。看看它是否适合您的需求。
【解决方案2】:

内层循环可以用sum函数代替(不要用同名变量覆盖)

然后你将外循环附加到那个的末尾

 [-sum(g[j]*w[i-j]*dz for j in range(i)) for i in range(len(g))]

【讨论】:

    【解决方案3】:

    不要出于性能原因使用列表推导

    使用

    • Numba
    • 赛通
    • 矢量化 Numpy 操作

    Numba

    import numba as nb
    import numpy as np
    import time
    
    @nb.njit(fastmath=True)
    def halfconvolution(g,w,dz):
        convo=np.empty(g.shape[0],dtype=g.dtype)
        for i in range(g.shape[0]):
            sum=0.
            for j in range(0,i):
                sum+=g[j]*w[(i-j)]*dz
            convo[i] = -sum
        return convo
    
    g=np.random.rand(1000)
    w=np.random.rand(1000)
    dz=0.15
    
    t1=time.time()
    for i in range(1000):
      #res=halfconvolution(g,w,dz)
      res=[-sum(g[j]*w[i-j]*dz for j in range(i)) for i in range(len(g))]
    
    print(time.time()-t1)
    print("Done")
    

    性能

    List Comprehension: 0.27s per iteration 
    Numba Version: 0.6ms per iteration
    

    所以这两个版本之间有 500 倍的系数。如果您不想一次在多个数组上调用此函数,您也可以轻松地并行化此问题,并且您应该至少可以加快另一个“核心数”。

    【讨论】:

      猜你喜欢
      • 2017-03-24
      • 2021-02-14
      • 2016-04-22
      • 1970-01-01
      • 2018-06-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-30
      相关资源
      最近更新 更多