Python自定义列表构建优化

我在Python中编写一个遗传算法,但我的操作符(MMX)在处理拥有300万权重的个体时(每个个体是一个包含3,000,000个元素的列表)执行时间过长(10秒)。

这是操作符的代码:

def calc_gen(maxel, minel, rec1, rec2, phiC):    g = maxel - minel    phi = 0    if g > phiC:        # Recta 2        phi = rec2[0] * g + rec2[1]    elif g < phiC:        # Recta 1        phi = rec1[0] * g + rec1[1]    #确保不超出范围:    maxv = min(1, maxel - phi)    minv = max(0, minel + phi)    gen1 = random.uniform(minv, maxv)  # 保存第一个子代的基因    # 如果C是中心,A是我们已有的元素,B是A的对称点:C - A + C = B -> 2C - A = B    # C = (maxv + minv) / 2; 2C - A = B -> maxv + minv - A = B    # center = (maxv + minv) / 2    gen2 = maxv + minv - gen1    return gen1, gen2    #return gen1, maxv + minv - gen1def cxMMX(poblacion, rec1, rec2, phiC):    start = timer()    # 计算整个种群中每个基因的最大值和最小值    max_genes = numpy.amax(poblacion, axis=0).tolist()    min_genes = numpy.amin(poblacion, axis=0).tolist()    gis = timer()    hijo1 = Individual()    hijo2 = Individual()    # 同时迭代两个列表(zip)并使用其索引(enumerate)。这样我们可以在一个循环中同时创建子代    for i, (maxel, minel) in enumerate(zip(max_genes, min_genes)):        gen1, gen2 = calc_gen(maxel, minel, rec1, rec2, phiC)        hijo1.append(gen1)        hijo2.append(gen2)    end = timer()    #print("Tiempo Gi: %f Tiempo init: %f Tiempo calc gen: %f Tiempo mate total: %f" % (gis-start, init-gis, end-init, end-start))    return [hijo1, hijo2]

rec1, rec2和phiC是决定如何进行交叉的参数,你不需要关心它们。它们在整个算法中都保持相同的值。

poblacion是一个列表的列表,假设它的形状是[7,3000000]。Individual()是一个自定义类。它基本上是继承了“list”并添加了一些属性来存储适应度值。

分别使用numpy.amax和numpy.amin似乎是多余的工作。另外,可能有更Pythonic的方式来处理“calc_gen()”循环。

附注:“gen1”依赖于“gen2”:gen1在某个范围内随机获得,然后gen2通过寻找对称点获得。

附注2:关于MMX操作符的更详细解释可以在原始论文中找到,不过,你可以假设代码是正确的并且完成了它应该做的工作。DOI是https://doi.org/10.1007/3-540-44522-6_73

附注:enumerate()和i是从旧代码中遗留下来的,忘记删除了!

编辑:使用Dillon Davis的解决方案,时间减少了20%。这是一个非常干净的解决方案,可以与任何自定义列表构建函数一起使用,前提是你通过执行一个函数来获取列表的每个值:

def calc_gen_v2(maxel,minel, rec1m, rec1b, rec2m, rec2b, phiC):    g = maxel - minel    phi = 0    if g > phiC:        # Recta 2        phi = rec2m * g + rec2b    elif g < phiC:        # Recta 1        phi = rec1m * g + rec1b    #确保不超出范围:    maxv = min(1, maxel - phi)    minv = max(0, minel + phi)    gen1 = random.uniform(minv, maxv)  # 保存第一个子代的基因    # 如果C是中心,A是我们已有的元素,B是A的对称点:C - A + C = B -> 2C - A = B    # C = (maxv + minv) / 2; 2C - A = B -> maxv + minv - A = B    # center = (maxv + minv) / 2    gen2 = maxv + minv - gen1    return gen1, gen2def cxMMX_v3(poblacion, rec1, rec2, phiC):    start = timer()    # 计算整个种群中每个基因的最大值和最小值    max_genes = numpy.amax(poblacion, axis=0)    min_genes = numpy.amin(poblacion, axis=0)    gis = timer()    hijo1, hijo2 = map(Individual, numpy.vectorize(calc_gen_v2)(max_genes, min_genes, rec1[0], rec1[1], rec2[0], rec2[1], phiC))    end = timer()    #print("Tiempo Gi: %f Tiempo init: %f Tiempo calc gen: %f Tiempo mate total: %f" % (gis-start, init-gis, end-init, end-start))    return [hijo1, hijo2]

编辑2:如Dillon Davis所建议,我使用纯numpy实现了它,将时间减少到3.5秒!(节省了65%的时间)

def cxMMX_numpy(poblacion, rec1, rec2, phiC):    # 计算种群中每个基因的最大值和最小值    max_genes = numpy.amax(poblacion, axis=0)    min_genes = numpy.amin(poblacion, axis=0)    g_pop = numpy.subtract(max_genes, min_genes)    phi_pop = numpy.where(g_pop < phiC, numpy.multiply(g_pop, rec1[0]) + rec1[1], numpy.where(g_pop > phiC, numpy.multiply(g_pop, rec2[0]) + rec2[1], 0))    maxv = numpy.minimum(numpy.subtract(max_genes, phi_pop), 1)    minv = numpy.maximum(numpy.sum([min_genes, phi_pop], axis=0), 0)    hijo1 = numpy.random.uniform(low=minv, high=maxv, size=minv.size)    hijo2 = numpy.subtract(numpy.sum([maxv, minv], axis=0), hijo1)    return [Individual(hijo1), Individual(hijo2)]

注意:如果你想重用,Individual继承自list

注意:如果g=phiC,那么rec1[0] * g_pop + rec1[1]=0,总是如此,rec1[0]和rec1[1]保证了这一点!所以也许最好做数学运算而不是三重选项?


回答:

尝试用以下内容替换cxMMX()中的for循环:

hijo1, hijo2 = map(Individual, numpy.vectorize(calc_gen)(max_genes, min_genes, rec1, rec2, phiC))

并从你的numpy.amin()numpy.amax()中删除.tolist()

这将使你的calc_gen函数向量化,避免使用zip和多次.append()调用的函数开销,总体上应该会快很多。

编辑:

还可以考虑将calc_gen()直接转换为在numpy数组上工作。用numpy.random.uniform()替换random.uniform()的调用,用numpy.minimum()numpy.maximum()替换min()max(),然后完全消除for循环/映射+向量化。这最终将是最快的选项。

Related Posts

Keras Dense层输入未被展平

这是我的测试代码: from keras import…

无法将分类变量输入随机森林

我有10个分类变量和3个数值变量。我在分割后直接将它们…

如何在Keras中对每个输出应用Sigmoid函数?

这是我代码的一部分。 model = Sequenti…

如何选择类概率的最佳阈值?

我的神经网络输出是一个用于多标签分类的预测类概率表: …

在Keras中使用深度学习得到不同的结果

我按照一个教程使用Keras中的深度神经网络进行文本分…

‘MatMul’操作的输入’b’类型为float32,与参数’a’的类型float64不匹配

我写了一个简单的TensorFlow代码,但不断遇到T…

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注