【问题标题】:How to reduce time taken For loop in python?如何减少python中for循环的时间?
【发布时间】:2013-10-21 04:40:15
【问题描述】:

我有以下 python 代码:

H1 = [[0.04,0.03,0.01,0.002],[0.02,0.04,0.001,0.5]]
H2 = [[0.06,0.02,0.02,0.004],[0.8,0.09,0.6,0.1]]    

D1 = [0.01,0.02,0.1,0.01]    
D2 = [0.1,0.3,0.01,0.4] 

Tp = np.sum(D1)    
Tn = np.sum(D2) 

T = []    
append2 = T.append   
E = []    
append3 = E.append   

for h1,h2 in itertools.izip(H1,H2) 
    Err = []    
    append1 = Err.append
    for v in h1:    

        L1 = [1 if i>=v else 0 for i in h1]    
        L2 = [1 if i>=v else 0 for i in h2]    

        Sp = np.dot(D1,L1)     
        Sn = np.dot(D2,L2)    

        err = min(Sp+Tn-Sn, Sn+Tp-Sp)    
        append1(err)  

    b = np.argmin(Err)    
    append2(h1[b])    
    append3(Err[b])

这只是一个示例代码。我需要运行内部 for 循环大约 20,000 次(这里它只运行两次)。但是内部 for 循环需要很长时间,因此使用起来不切实际。 在行分析器中,它显示行 Sp = np.dot(D1,L1)Sn = np.dot(D2,L2)b = np.argmin(Err) 是最耗时的。 如何减少上述代码所花费的时间。

任何帮助将不胜感激。

谢谢!

【问题讨论】:

  • 你的硬件是什么?根据计算机的不同,您可以使用多处理(因为代码是可并行化的)甚至 CUDA 进行 GPU 计算。
  • @georgesl:嗨,我已经在使用多处理了。我需要为 5 个不同的 H1 和 H2 运行上面的代码。我的 H1 再次包含近 20,000 个列表,即外部 for 循环运行 20,000 次。
  • 使用 CUDA,速度很快 我认为它有那些 python 绑定

标签: python list for-loop numpy append


【解决方案1】:

如果您使用带有 numpy 数组而不是列表的 numpy 函数,您可以获得相当大的速度提升。大多数 numpy 函数会在内部将列表转换为数组,这会增加运行时的大量开销。这是一个简单的例子:

In [16]: a = range(10)

In [17]: b = range(10)

In [18]: aa = np.array(a)

In [19]: bb = np.array(b)

In [20]: %timeit np.dot(a, b)
10000 loops, best of 3: 54 us per loop

In [21]: %timeit np.dot(aa, bb)
100000 loops, best of 3: 3.4 us per loop

numpy.dot 在这种情况下使用数组调用时运行速度快 16 倍。此外,当您使用 numpy 数组时,您将能够简化一些代码,这也应该有助于它运行得更快。例如,如果h1 是一个数组,那么L1 = [1 if i>=v else 0 for i in h1] 可以写成h1 > v,它返回一个数组并且应该运行得更快。贝娄我已经继续并用数组替换了您的列表,这样您就可以看到它的样子。

import numpy as np

H1 = np.array([[0.04,0.03,0.01,0.002],[0.02,0.04,0.001,0.5]])
H2 = np.array([[0.06,0.02,0.02,0.004],[0.8,0.09,0.6,0.1]])

D1 = np.array([0.01,0.02,0.1,0.01])
D2 = np.array([0.1,0.3,0.01,0.4])

Tp = np.sum(D1)    
Tn = np.sum(D2) 

T = np.zeros(H1.shape[0])
E = np.zeros(H1.shape[0])

for i in range(len(H1)):
    h1 = H1[i]
    h2 = H2[i]
    Err = np.zeros(len(h1))

    for j in range(len(h1)):    
        v = h1[j]

        L1 = h1 > v
        L2 = h2 > v

        Sp = np.dot(D1, L1)     
        Sn = np.dot(D2, L2)    

        err = min(Sp+Tn-Sn, Sn+Tp-Sp)    
        Err[j] = err

    b = np.argmin(Err)
    T[i] = h1[b]
    E[i] = Err[b]

一旦您对 numpy 数组更加熟悉,您可能希望考虑使用 broadcasting 来表达至少您的内部循环。对于某些应用程序,使用广播可能比 python 循环更有效。祝你好运,希望对你有所帮助。

【讨论】:

    【解决方案2】:

    您需要将数据保存在 ndarray 类型中。当您对列表执行 numpy 操作时,它每次都必须构造一个新数组。我修改了您的代码以运行可变次数,并且发现它对于 10000 次迭代也是 ~1s。将数据类型更改为 ndarrays 将其减少了大约两倍,我认为仍有一些改进(第一个版本有一个错误,使其执行速度过快)

    import itertools
    import numpy as np
    N = 10000
    H1 = [np.array([0.04,0.03,0.01,0.002])] * N
    H2 = [np.array([0.06,0.02,0.02,0.004])] * N
    
    D1 = np.array([0.01,0.02,0.1,0.01]    )
    D2 = np.array([0.1,0.3,0.01,0.4] )
    
    Tp = np.sum(D1)    
    Tn = np.sum(D2) 
    
    T = []    
    append2 = T.append   
    E = []    
    append3 = E.append   
    
    for h1,h2 in itertools.izip(H1,H2):
        Err = []    
        append1 = Err.append
        for v in h1:
    
            #L1 = [1 if i>=v else 0 for i in h1]    
            #L2 = [1 if i>=v else 0 for i in h2]    
            L1 = h1 > v
            L2 = h2 > v
            Sp = np.dot(D1,L1)     
            Sn = np.dot(D2,L2)    
    
            err = min(Sp+Tn-Sn, Sn+Tp-Sp)    
            append1(err)  
    
        b = np.argmin(Err)    
        append2(h1[b])    
        append3(Err[b])
    

    【讨论】:

    • 谢谢,这项工作并减少了充足的时间。你说,还有一些改进。那是什么?我仍然需要减少一点时间因素。
    • 我看到的另一种加快速度的方法是使用广播数组操作来删除内部循环。例如h1 > h1.reshape(len(h1,1)) 将并行计算所有L1 向量(我认为)。点积和 np.min 可以在您想要的任何轴上运行。
    • 谢谢!我也要去试试这个。
    【解决方案3】:

    你的列表理解中有一些容易实现的目标:

    L1 = [1 if i>=v else 0 for i in h1]
    L2 = [1 if i>=v else 0 for i in h2]
    

    上面可以写成:

    L1 = [i>=v for i in h1]
    L2 = [i>=v for i in h2]
    

    因为布尔值是整数的子类,TrueFalse 已经是 1 和 0,只是穿着花哨的衣服。

    err = min(Sp+Tn-Sn, Sn+Tp-Sp)    
    append1(err)  
    

    你可以结合上面两行来避免变量赋值和访问。

    如果将代码放在函数中,所有局部变量的使用都会稍微快一些。此外,您使用的任何全局函数或方法(例如minnp.dot)都可以使用默认参数在函数签名中转换为局部变量。 np.dot 是一个特别慢的调用(除了操作本身需要多长时间),因为它涉及属性查找。这将类似于您已经使用列表 append 方法进行的优化。

    现在我想这一切都不会真正影响性能,因为您的问题似乎真的是“我怎样才能让 NumPy 更快?” (其他人对你来说是最重要的)但它们可能会产生一些影响并且值得做。

    【讨论】:

      【解决方案4】:

      如果我正确理解了指令np.dot() 对两个维度 1 列表的作用,那么在我看来,以下代码应该与您的代码相同。
      请你测试一下它的速度好吗?

      它的原理是使用索引而不是列表的元素,并利用列表的特性定义为函数的默认值

      H1 = [[0.04,0.03,0.01,0.002],[0.02,0.04,0.001,0.5]]
      H2 = [[0.06,0.02,0.02,0.004],[0.8,0.09,0.6,0.1]]    
      
      D1 = [0.01,0.02,0.1,0.01]    
      D2 = [0.1,0.3,0.01,0.4] 
      
      Tp = np.sum(D1)    
      Tn = np.sum(D2) 
      
      T,E = [],[]    
      append2 = T.append      
      append3 = E.append 
      
      ONE,TWO = [],[]
      
      def zoui(v, ONE=ONE,TWO=TWO,
               D1=D1,D2=D2,Tp=Tp,Tn=Tn,tu0123 = (0,1,2,3)):
          diff =  sum(D1[i] if ONE[i]>=v else 0 for i in tu0123)\
                 -sum(D2[i] if TWO[i]>=v else 0 for i in tu0123)
          #or maybe
          #diff =  sum(D1[i] * ONE[i]>=v for i in tu0123)\
          #       -sum(D2[i] * TWO[i]>=v for i in tu0123)
      
          return min(Tn+diff,Tp-diff)
      
      for n in xrange(len(H1)):
          ONE[:] = H1[n]
          TWO[:] = H2[n]
          Err = map(zoui,ONE)
          b = np.argmin(Err)    
          append2(ONE[b])    
          append3(Err[b])
      

      【讨论】:

        猜你喜欢
        • 2019-10-07
        • 2018-03-27
        • 2016-07-04
        • 2016-10-13
        • 2022-01-04
        • 1970-01-01
        • 2021-11-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多