用 Python 从零开始实现简单遗传算法

本文介绍了遗传算法的基本概念和工作原理,并通过Python代码展示了如何从零开始实现遗传算法,包括OneMax问题的解决和应用于连续函数优化。遗传算法是一种受进化启发的随机优化算法,通过遗传表示、适应度、重组和突变等算子进行全局搜索。文中还给出了具体的Python实现代码和示例。
摘要由CSDN通过智能技术生成

遗传算法是一种随机全局优化算法。连同人工神经网络,它可能是最流行和广为人知的生物学启发算法之一。该算法是一种进化算法,它通过自然选择,具有二进制表示形式和基于遗传重组和遗传突变的简单算子,来执行受进化生物学理论启发的优化过程。

在本教程中,您将发现遗传算法优化算法。完成本教程后,您将知道:

  • 遗传算法是一种受进化启发的随机优化算法。

  • 如何在Python中从头开始实现遗传算法。

  • 如何将遗传算法应用于连续目标函数。

教程概述

本教程分为四个部分。他们是:

  • 遗传算法

  • 从零开始的遗传算法

  • OneMax的遗传算法

  • 连续函数优化的遗传算法

遗传算法

遗传算法是一种随机全局搜索优化算法。它受到自然选择进化生物学理论的启发。具体来说,是将对遗传学的理解与理论相结合的新综合方法。

该算法使用遗传表示(位串),适应度(功能评估),基因重组(位串交叉)和突变(翻转位)的类似物。该算法的工作原理是首先创建固定大小的随机位串。重复算法的主循环固定次数的迭代,或者直到在给定迭代次数的最佳解决方案中看不到进一步的改善为止。该算法的一次迭代就像是进化的一代。

首先,使用目标函数评估总体位串(候选解决方案)。每个候选解决方案的目标函数评估被视为解决方案的适用性,可以将其最小化或最大化。然后,根据他们的健康状况选择父母。给定的候选解决方案可以用作父级零次或多次。一种简单有效的选择方法包括从总体中随机抽取k个候选者,并从适应性最好的组中选择成员。这就是所谓的锦标赛选择,其中k是一个超参数,并设置为诸如3的值。这种简单的方法模拟了成本更高的适应度成比例的选择方案。

父母被用作生成下一代候选点的基础,并且人口中的每个职位都需要一个父母。

然后将父母配对,并用来创建两个孩子。使用交叉算子执行重组。这涉及在位串上选择一个随机的分割点,然后创建一个子对象,该子对象的位从第一个父级到分割点直至从第二个父级到字符串的末尾。然后,为第二个孩子倒转此过程。

例如,两个父母:

parent1 = 00000

parent2 = 11111

可能会导致两个交叉孩子:

子1 = 00011

孩童2 = 11100

这称为单点交叉,并且操作员还有许多其他变体。

交叉概率是对每对父母概率应用的,这意味着在某些情况下,父母的副本将作为孩子而不是重组算子。交叉由设置为较大值(例如80%或90%)的超参数控制。变异涉及在已创建的子候选解决方案中翻转位。通常,将突变率设置为1 / L,其中L是位串的长度。

例如,如果问题使用具有20位的位串,则良好的默认突变率将是(1/20)= 0.05或5%的概率。

这定义了简单的遗传算法过程。这是一个很大的研究领域,并且对该算法进行了许多扩展。

现在我们已经熟悉了简单的遗传算法过程,下面让我们看一下如何从头开始实现它。

从零开始的遗传算法

在本节中,我们将开发遗传算法的实现。第一步是创建随机位串。我们可以使用布尔值True和False,字符串值“ 0”和“1”,或者整数值0和1。在这种情况下,我们将使用整数值。我们可以使用randint()函数生成一个范围内的整数值数组,并且可以将范围指定为从0开始且小于2的值,例如 0或1。为了简化起见,我们还将候选解决方案表示为列表而不是NumPy数组。可以如下创建初始的随机位串填充,其中“n_pop”是控制填充大小的超参数,“n_bits”是定义单个候选解决方案中位数的超参数:

# initial population of random bitstring
pop = [randint(0, 2, n_bits).tolist() for _ in range(n_pop)]

接下来,我们可以枚举固定数量的算法迭代,在这种情况下,该迭代由名为“ n_iter”的超参数控制。

...
# enumerate generations
 for gen in range(n_iter):
 ...

算法迭代的第一步是评估所有候选解。

我们将使用一个名为Objective()的函数作为通用目标函数,并对其进行调用以获取适合度得分,我们将其最小化。

# evaluate all candidates in the population
scores = [objective(c) for c in pop]

然后,我们可以选择将用于创建子代的父代。

锦标赛选择过程可以实现为一种函数,该函数可以获取总体并返回一个选定的父级。使用默认参数将k值固定为3,但是您可以根据需要尝试使用不同的值。

# tournament selection
def selection(pop, scores, k=3):
 # first random selection
 selection_ix = randint(len(pop))
 for ix in randint(0, len(pop), k-1):
 # check if better (e.g. perform a tournament)
 if scores[ix] < scores[selection_ix]:
 selection_ix = ix
 return pop[selection_ix]

然后,我们可以为总体中的每个位置调用一次此函数,以创建父母列表。

# select parents
selected = [selection(pop, scores) for _ in range(n_pop)]

然后,我们可以创建下一代。

这首先需要执行交叉的功能。此功能将占用两个父级和交叉率。交叉率是一个超参数,它确定是否执行交叉,如果不进行交叉,则将父级复制到下一代。这是一个概率,通常具有接近1.0的较大值。

下面的crossover()函数使用范围为[0,1]的随机数来实现交叉以确定是否执行了交叉,然后如果要进行交叉则选择有效的分割点。

# crossover two parents to create two children
def crossover(p1, p2, r_cross):
 # children are copies of parents by default
 c1, c2 = p1.copy(), p2.copy()
 # check for recombination
 if
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值