【问题标题】:Is there a way to aviod picking two same parents in genetic aglorithm?有没有办法避免在遗传算法中选择两个相同的父母?
【发布时间】:2021-11-04 19:12:57
【问题描述】:

我正在 GA 中选择两个父母,问题是当我实现“锦标赛选择”函数时,它总是返回两个相同的父母。

初始种群为:

num_emitter_coefficients = 782
sol_per_pop_1=1
sol_per_pop_2=3
sol_per_pop=sol_per_pop_1+sol_per_pop_2   

pop_size_1 = [sol_per_pop_1, num_emitter_coefficients]
pop_size_2 = [sol_per_pop_2, num_emitter_coefficients]

initial_population_1 = np.random.uniform(low=0.0, high=0.0, size=pop_size_1)
initial_population_2 = np.random.uniform(low=0.0, high=0.0, size=pop_size_2)

for row in initial_population_2:
    locations_used_in_this_row = 0
    while locations_used_in_this_row != 5:
        column = np.random.randint(num_emitter_coefficients)
        if row[column] == 0.0:
            row[column] = np.random.rand()*10
            locations_used_in_this_row += 1

population=np.vstack([initial_population_1, initial_population_2])
print('Population: ',population)

假设每个人的分数是:

[44,56,63,34]

在我的项目中,每个人的得分越高,每个人的适应度就越低。 选择父母的功能如下图:

def tournament_selection(population, scores, k=4):
    first_pick = np.random.randint(0,len(population))  
    for second_pick in np.random.randint(0,len(population),k-1):
        if scores[second_pick] < scores[first_pick]:
            winner = second_pick
        else:
            winner = first_pick
    return population[winner,:]

当我实现这个功能时,

for i in range(0, len(population),2):
    parent_1 = tournament_selection(population, scores)
    parent_2 = tournament_selection(population, scores)

它总是返回两个相同的父母。 你能告诉我如何避免这个问题吗?

也许如果我可以从人口列表中排除“获胜者”,可以解决选择两个相同父母的问题,但我不知道如何用代码实现,请你给我一些想法?提前非常感谢。

【问题讨论】:

  • 您在 for 循环中重复设置“winner”(丢弃前一个值),但在循环后仅读取一次(使用其最后一个值)。

标签: python genetic-algorithm


【解决方案1】:

我认为列表理解是您在没有更多信息的情况下的答案(我知道基因分析会变得很麻烦)。

我在您的函数开头添加了列表理解,以从您的主列表中过滤掉以前的获胜者。请记住,.index() 只会选择列表中的第一个匹配项,因此如果您的列表很长且可能存在重复项,则需要另一种方法

import numpy as np

num_emitter_coefficients = 782
sol_per_pop_1=1
sol_per_pop_2=3
sol_per_pop=sol_per_pop_1+sol_per_pop_2   

pop_size_1 = [sol_per_pop_1, num_emitter_coefficients]
pop_size_2 = [sol_per_pop_2, num_emitter_coefficients]

initial_population_1 = np.random.uniform(low=0.0, high=0.0, size=pop_size_1)
initial_population_2 = np.random.uniform(low=0.0, high=0.0, size=pop_size_2)

for row in initial_population_2:
    locations_used_in_this_row = 0
    while locations_used_in_this_row != 5:
        column = np.random.randint(num_emitter_coefficients)
        if row[column] == 0.0:
            row[column] = np.random.rand()*10
            locations_used_in_this_row += 1

population=np.vstack([initial_population_1, initial_population_2]).tolist()

prevwinner = []

scores = [44,56,63,34]

def tournament_selection(population, scores, k=4):
    # filter previous winners out
    selectable_parents = [nonwinner for nonwinner in population if nonwinner not in prevwinner]
    if len(selectable_parents) > 1:
        first_pick, second_pick = np.random.randint(len(selectable_parents), size=2)
        first_pick_score = scores[population.index(selectable_parents[first_pick])]
        while second_pick == first_pick:
            second_pick = np.random.randint(0,len(selectable_parents))
        second_pick_score = scores[population.index(selectable_parents[second_pick])]
        if second_pick_score < first_pick_score:
            winner = second_pick
            loser = first_pick
        else:
            winner = first_pick
            loser = second_pick

        # append winner to prevwinner list
        prevwinner.append(population[population.index(selectable_parents[winner])])
        prevwinner.append(population[population.index(selectable_parents[loser])])
        return population[population.index(selectable_parents[winner])], population[population.index(selectable_parents[loser])]
    else: 
        # there is one parent left
        return selectable_parents[0]

for i in range(0, len(population),2):
    parent_1, parent_2 = tournament_selection(population, scores)
    print('parent_1: ', np.unique(np.array(parent_1)))
    print('parent_2: ', np.unique(np.array(parent_2)))

【讨论】:

  • 感谢您的回复。但是当我运行你的代码时,它返回了一个错误:AttributeError: 'numpy.ndarray' object has no attribute 'index'。你能告诉我如何解决它吗?
  • 代码在我的最后工作正常,我建议您的代码使用我的列表的 np 数组。您可以使用 np.array.tolist() 将 numpy 数组转换为列表。 @alvas 下面的答案是获得两个父母的一种更简洁的方法,您可以用他们的答案替换我代码中的父母选择行,但从“selectable_parents”列表中选择。
  • 我使用 numpy 创建了人口,也许 AttributeError 是由它引起的。我听从了你的建议,并尝试使用代码将人口数组转换为列表:first_pick_score=scores[population.tolist().index(selected_pa​​rents[first_pick])],但它返回了一个 ValueError:The truth value of an array with more than一个元素不明确。使用 a.any() 或 a.all()。所以我将代码更改为 first_pick_score=scores[population.all().tolist().index(selected_pa​​rents[first_pick])],它返回了一个 AttributeError: 'bool' object has no attribute 'index'。你能给我一些想法吗?
  • 您需要将等效的 populationscores 数组转换为列表。 (这也是您发布完整示例的原因)
  • 感谢您的耐心等待! score 是一个列表,而 population 是一个数组。我使用 population_1=population.tolist() 将人口数组转换为列表,但它总是返回 ValueError:具有多个元素的数组的真值是不明确的。使用 a.any() 或 a.all()
【解决方案2】:

您应该使用np.random.choicereplace=False,而不是从总体中随机选择两次。

但看似 numpy 中的 random.choice 仅支持一维数组,因此您必须使用 np.random.randint 中的 size 参数。见Numpy: Get random set of rows from 2D array

import numpy as np

population= [[0., 1., 0., 0., 0., 0.],
             [0., 0., 0., 0., 1., 0.],
             [0., 0., 1., 0., 0., 0.],
             [0., 0., 0., 1., 0., 0.]]

parent_1, parent_2 = np.random.randint(len(population), size=2)

【讨论】:

  • 感谢您的回复,能否请您指定选择两个父母的代码?我是 GA 新手,我没有完全理解您的代码。
  • size=2 指定您要选择的父母数量。 len(population) 是总数。行数。
  • 所以你的意思是我不需要锦标赛选择功能,只需使用 np.random.randint 和 size=2 直接选择两个父母?
猜你喜欢
  • 1970-01-01
  • 2020-04-14
  • 2012-12-10
  • 2019-05-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多