【问题标题】:Why does the all the generated populations becomes the same?为什么所有生成的种群都变得相同?
【发布时间】:2020-07-10 07:52:31
【问题描述】:

所以我正在制作遗传算法,其中最适合的染色体是全为 [1,1,1,1...] 的染色体。我在调试时对下面的整个代码进行了编码,我发现交叉、突变、generate_population 有效,并且我浏览了其他函数的代码,它们都很有意义。但是每当我运行它时,我发现在第二代或第三代之后,我所有的其他代都一样。所以我的问题是为什么会发生这种情况,因为我浏览了我的代码,每一代都应该有一个不同的“更健康”的人群,每个人群之间应该有多样性。任何帮助将非常感激。 result image

import random

def generate_population(size):
    population = []
    for i in range(size):
        individual = []
        for g in range(64):
          x = random.randrange(0,2)
          individual.append(x)
        population.append(individual)
    return population

def fitnessFunc(individual):
  fit = 0
  for i in individual:
    if i == 1:
      fit += 1
    else:
      fit = fit 
  return fit


def choice_by_roulette(sorted_population, fitness_sum):
    offset = 0
    normalized_fitness_sum = fitness_sum
    lowest_fitness = fitnessFunc(sorted_population[0])
    if lowest_fitness < 0:
        offset = lowest_fitness
        normalized_fitness_sum += offset * len(sorted_population)

    draw = random.uniform(0, 1)

    accumulated = 0
    for individual in sorted_population:
        fitness = fitnessFunc(individual)+offset
        probability = fitness / normalized_fitness_sum
        accumulated += probability

        if draw <= accumulated:
            return individual

def sort_population_by_fitness(population):
    return sorted(population, key=fitnessFunc)

def crossover(individual_a, individual_b):
  for i in range(64):
    pop = random.randint(1,2)
    if pop == 1:
      individual_a[i] = individual_b[i]
    else:
      individual_a = individual_a
  return individual_a

def mutate(individual):
    rand = random.randrange(0,10)
    if rand == 5:
      if individual[rand]==1:
        individual[rand]=0
      else:
        individual[rand]=1
    return individual

def make_next_generation(previous_population):
    next_generation = []
    sorted_by_fitness_population = sort_population_by_fitness(previous_population)
    population_size = len(previous_population)
    fitness_sum = sum(fitnessFunc(individual) for individual in population)
    for i in range(population_size):
        choice = choice_by_roulette(sorted_by_fitness_population, fitness_sum)
        schoice = choice_by_roulette(sorted_by_fitness_population, fitness_sum)
        if choice != None:
          first_choice = choice
        if schoice != None:
          second_choice = schoice
        individual = crossover(first_choice, second_choice)
        individual = mutate(individual)
        next_generation.append(individual)
    return next_generation


population = generate_population(size=10)
generations = 1000

i = 1
while True:
    print(f" GENERATION {i}")

    for individual in population:
        print(individual, fitnessFunc(individual))
    if i == generations:
        break
    i += 1
    population = make_next_generation(population)

best_individual = sort_population_by_fitness(population)[-1]
print(" FINAL RESULT")
print(best_individual, fitnessFunc(best_individual))

【问题讨论】:

  • 看起来您期望函数参数和返回值是副本。这不是 Python 的工作方式。见nedbatchelder.com/text/names.html
  • 您所指的代码中是否有特定的行?
  • 例如,crossover 变异并返回其输入之一。
  • 你能说得更具体点吗?究竟是什么问题?
  • 是的,我添加了1000代后的结果图。正如你所看到的,人口中的所有个体都是一样的,这不应该是一样的,所有宝贵的人口也是一样的

标签: python genetic-algorithm genetic-programming


【解决方案1】:

我发现了三个主要问题。将尝试对其进行分解,但由于我不完全确定预期的结果是什么,因此很难确定应该如何完成。

问题 1:交叉函数在创建下一代时“就地”改变父代。这意味着约 25% 的亲本基因会向其他亲本基因组突变。

问题 2:make_next_generation() 不能确保将两个不同的人发送到交叉点。这有时会导致个人原封不动地传给下一代。

问题 3:mutate(individual) 仅影响 64 个基因中的一个。(不是故意猜测)

import random


def generate_individual():
    # Use list comprehensions
    return [random.randrange(0,2) for g in range(64)]


def generate_population(size):
    # Same result, only less code
    return [generate_individual() for i in range(size)]

def fitnessFunc(individual):
  # Yes, this line does the same work.
  return sum(individual)
  # fit = 0
  # for i in individual:
  #   if i == 1:
  #     fit += 1
  # The following two lines did nothing
  #   else:
  #     fit = fit 
  # return fit

choice_by_roulette() 中存在一个真正的问题。不过,不知道它应该如何工作。在下面评论。

def choice_by_roulette(sorted_population, fitness_sum):
    offset = 0
    normalized_fitness_sum = fitness_sum
    lowest_fitness = fitnessFunc(sorted_population[0])
    # Lower than 0? fitnessFunc() will never return that.
    # Could it be lowest_fitness > 0?
    if lowest_fitness < 0:
        offset = lowest_fitness
        normalized_fitness_sum += offset * len(sorted_population)

    draw = random.random()

    accumulated = 0
    for individual in sorted_population:
        fitness = fitnessFunc(individual)+offset
        probability = fitness / normalized_fitness_sum
        accumulated += probability

        if draw <= accumulated:
            return individual

def sort_population_by_fitness(population):
    return sorted(population, key=fitnessFunc)

Crossover 不应该改变老个体(?)。由于相同的个体可能会被多次挑选,因此它们将向同一代中的另一个个体变异约 50%。这可能是您最大的问题。

def crossover(individual_a, individual_b):
  for i in range(64):
    pop = random.randint(1,2)
    if pop == 1:
      individual_a[i] = individual_b[i]
    else:
      individual_a = individual_a
  return individual_a

我的建议:

def crossover(individual_a, individual_b):
  return [random.choice(genes) for genes in zip(individual_a, individual_b)]

在这里您无需返回任何东西。你实际上是在原地变异个体。另外,只有基因 5 可以突变,但也许应该是这样?

def mutate(individual):
    rand = random.randrange(0,10)
    if rand == 5:
      individual[rand] = 1 - individual[rand]  # Toggle: 1-0=1, 1-1=0
      # if individual[rand]==1:
      #   individual[rand]=0
      # else:
      #   individual[rand]=1

def make_next_generation(previous_population):
    next_generation = []
    sorted_by_fitness_population = sort_population_by_fitness(previous_population)
    population_size = len(previous_population)
    fitness_sum = sum(fitnessFunc(individual) for individual in population)
    for i in range(population_size):
        choice = choice_by_roulette(sorted_by_fitness_population, fitness_sum)
        schoice = choice_by_roulette(sorted_by_fitness_population, fitness_sum)
        # This code will not work. first_choice will be user without being declared below if any of there are None.
        # if choice != None:
        #   first_choice = choice
        # if schoice != None:
        #   second_choice = schoice
        # choice and schoice will sometimes be the same individual. That will definitely diminish the genetic diversity.
        while choice == schoice:
            schoice = choice_by_roulette(sorted_by_fitness_population, fitness_sum)
        individual = crossover(choice, schoice)
        mutate(individual)
        next_generation.append(individual)
    return next_generation


population = generate_population(size=10)
generations = 1000

for i in range(1, generations+1):
    print(f" GENERATION {i}")
    for individual in population:
        print(individual, fitnessFunc(individual))
    population = make_next_generation(population)

best_individual = sort_population_by_fitness(population)[-1]
print(" FINAL RESULT")
print(best_individual, fitnessFunc(best_individual))

【讨论】:

  • 所以在我的交叉函数中应该通过两个父染色体来工作并生成孩子,它会从两个父母中随机选择一个基因。突变通过每 20 个左右的种群随机翻转子染色体中的一个基因来起作用。
  • choice_by_roulette 基本上就像一个饼,它给了具有最高适应度分数的染色体更大的部分,然后我们在饼图中随机选择一个点并选择相应的染色体作为父代。
  • 原始 crossover() 的问题在于它不仅生成了一个孩子,它还改变了一个父母 50% 的基因以匹配另一个父母。然后那个父母可能会再次被选中,但现在随着基因的改变,将这些基因传给另一个孩子,并可能传给另一位父母。
  • 对于choice_by_roulette(),我认为您应该将if minimum_fitness 更改为if minimum_fitness:
猜你喜欢
  • 2019-04-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-18
  • 1970-01-01
  • 2017-07-26
  • 2020-01-01
相关资源
最近更新 更多