每次迭代后Python代码变慢

时间:2014-07-24 14:06:30

标签: python performance numpy

我有以下代码,它应该对数据向量进行一些操作并存储结果,我的问题是,当我首先运行此代码时,每次迭代(每个外部循环)大约需要12秒但是过了一段时间迭代的时间变得越来越长,每次迭代需要2分钟才能完成,我想知道我的代码有什么问题?这与内存大小和我保持的数组大小有关吗?如果是,我该如何解决?如果没有什么问题?

allclassifiers是2D向量:allclassifiers.shape = tuple: (1020, 1629)这意味着我应该循环1629次,每次对大小为1020的向量进行操作,每个向量是数组0或1。

最后,points_comb的大小约为400MB,points_comb.shape = tuple: (26538039, 2),我必须提到我正在使用4GB RAM的系统上运行代码。
valid_list是0和1的向量,大小为1020,valid_list.shape tuple: (1020,)

startTime = datetime.now()

for i in range(allclassifiers.shape[1]):
    for j in range(allclassifiers.shape[1]):
        rs_t = combine_crisp(valid_list, allclassifiers[:,i], allclassifiers[:,j], boolean_function)
        fpr_tmp, tpr_tmp = resp2pts(valid_list, rs_t)
        points_comb = np.vstack((points_comb,np.hstack((fpr_tmp, tpr_tmp))))
    endTime = datetime.now()
    executionTime = endTime - startTime
    print "Combination for classifier: " + str(i)+ ' ' + str(executionTime.seconds / 60) + " minutes and " + str((executionTime.seconds) - ((executionTime.seconds / 60)*60)) +" seconds"


def combine_crisp(lab, r_1, r_2, fun):

    rs = np.empty([len(lab), len(fun)])
    k = 0
    for b in fun:
        if b == 1:            #----------------> 'A AND B'
            r12 = np.logical_and(r_1, r_2)
        elif b == 2:            #----------------> 'NOT A AND B'
            r12 = np.logical_not(np.logical_and(r_1, r_2))
        elif b == 3:            #----------------> 'A AND NOT B'
            r12 = np.logical_and(r_1, np.logical_not (r_2))
        elif b == 4:            #----------------> 'A NAND B'
            r12 = np.logical_not( (np.logical_and(r_1, r_2)))
        elif b == 5:            #----------------> 'A OR B'
            r12 = np.logical_or(r_1, r_2)
        elif b == 6:            #----------------> 'NOT A OR B'; 'A IMP B'
            r12 = np.logical_not (np.logical_or(r_1, r_2))
        elif b == 7:            #----------------> 'A OR NOT B' ;'B IMP A'
            r12 = np.logical_or(r_1, np.logical_not (r_2))
        elif b == 8:            #----------------> 'A NOR B'
            r12 = np.logical_not( (np.logical_or(r_1, r_2)))
        elif b == 9:            #----------------> 'A XOR B'
            r12 = np.logical_xor(r_1, r_2)
        elif b == 10:            #----------------> 'A EQV B'
            r12 = np.logical_not (np.logical_xor(r_1, r_2))
        else:
            print('Unknown Boolean function')

        rs[:, k] = r12
        k = k + 1


    return rs

def resp2pts(lab, resp):
    lab = lab > 0
    resp = resp > 0
    P = sum(lab)
    N = sum(~lab)
    if resp.ndim == 1:
        num_pts = 1
        tp = sum(lab[resp])
        fp = sum(~lab[resp])
    else:
        num_pts = resp.shape[1]
        tp = np.empty([num_pts,1])
        fp = np.empty([num_pts,1])
        for i in np.arange(num_pts):
            tp[i] = np.sum( lab[resp[:,i]]) 
            fp[i] = np.sum( ~lab[resp[:,i]])
    tp = np.true_divide(tp,P)
    fp = np.true_divide(fp,N)
    return fp, tp

2 个答案:

答案 0 :(得分:2)

我会建议你做面对这样的问题时要做的第一件事。

使用分析器。

使用分析器,您可以找到(或至少更接近)问题所在的位置,原因是什么功能,以及应用程序如何使用内存。

所以,你可以从The python Profilers开始,Which Python memory profiler is recommended?上有一个非常有趣的问题

在可能的意见中,本指南的一个非常非常好的开始: A guide to analyzing Python performance

您可以学到的一件事是找出每个代码行使用的内存量!!!

enter image description here

祝你好运!

答案 1 :(得分:1)

我实际上没有测试,但我很确定减速是由于np.vstack的重复使用造成的。你不能真正附加到Numpy数组,所以如果你想增加大小,你必须先分配一个新数组,然后将旧数据和新数据复制到其中。这个过程需要时间:

A = np.random.rand(1e7)
%timeit A.copy()
10 loops, best of 3: 119 ms per loop

points_comb大小增加,因此制作副本的时间越来越长。

解决方案是谨慎附加到数组或使用Python列表(如果您事先不知道结果的大小)。

所以而不是:

result = np.empty(shape=(0, N))

for i in some_iterable:
    for j in range(X):
        temp = somefunction(i,j)
        result = np.vstack(result, temp)

你可以这样做:

result = list()

for i in some_iterable:
    for j in range(X):
        temp = somefunction(i,j)
        result.append(temp)

result_np = np.vstack(result)

该列表可能会占用大量内存。如果你知道循环之前结果的大小,你可以预先分配数组,并在内容可用时将内容复制到它:

result = np.empty(shape=(X**2, N))

for i in range(X):
    for j in range(X):
        temp = somefunction(i,j)
        result[i*j, :] = temp

现在你也在复制,但只有很小的块,所以这个速度相当快。最好的事情是以某种方式将你的作品矢量化(我不是说它甚至可能)。然后你可以说循环再见并做一些像:

result = some_vectorized_function(X)