Python 实战遗传算法——遗传算法导论

受查尔斯·达尔文自然进化理论的启发,一种用于问题求解的最迷人的技术就是被恰当地命名为"进化计算"的算法家族。在这个家族中,最突出且被广泛使用的分支就是遗传算法。本章将开启你掌握这一极其强大且极其简单技术的旅程。

在本章中,我们将介绍遗传算法及其与达尔文进化的类比,然后深入讲解它们的基本运作原理和理论基础。接着,我们会比较遗传算法与传统算法的差异,并讨论遗传算法的优势、局限性及其应用场景。最后,我们将回顾一些使用遗传算法可能带来益处的案例。

在本章的导读中,我们将涵盖以下主题:

  • 什么是遗传算法?
  • 遗传算法背后的理论
  • 遗传算法与传统算法的区别
  • 遗传算法的优势与局限性
  • 何时使用遗传算法

什么是遗传算法?

遗传算法是一类受自然进化原理启发的搜索算法。通过模拟自然选择和繁殖过程,遗传算法可以为涉及搜索、优化和学习的各种问题产生高质量的解决方案。同时,它们与自然进化的类比使遗传算法能够克服传统搜索和优化算法面临的一些障碍,尤其是针对参数众多且数学表达复杂的问题。

在本节的剩余部分,我们将回顾遗传算法的基本思想,以及它们与自然界进化过程的类比。

达尔文进化

遗传算法实现了自然界中达尔文进化的简化版本。达尔文进化理论的基本原则可以总结如下:

  1. 变异原则:属于同一群体的个体在特征(属性)上可能存在差异。因此,个体在某种程度上彼此不同,例如在行为或外观上。
  2. 遗传原则:某些特征会被稳定地传递给后代。因此,后代与父母相比,更像父母而非无关个体。
  3. 选择原则:群体通常在其环境中为资源而竞争。那些特征更适应环境的个体更有可能生存,并产生更多后代。

换句话说,进化维持着一个个体群体,这些个体彼此有所差异。适应环境更好的个体有更高的生存、繁殖和将特征传递给下一代的机会。随着世代更替,物种会变得更加适应其环境和所面临的挑战。

进化的一个重要推动因素是交叉(重组) ------后代由父母特征的组合生成。交叉有助于维持群体的多样性,并随着时间的推移将更优的特征组合起来。此外,突变------特征的随机变化------也可能在进化中发挥作用,通过引入变化偶尔带来突破性进步。

遗传算法的类比

遗传算法旨在为给定问题寻找最优解,而达尔文进化维持的是个体群体。遗传算法维持一个候选解的群体,称为个体(individuals)。这些候选解通过迭代评估,用于生成新一代解。更擅长解决问题的个体有更大机会被选中,并将其优良特性传递给下一代候选解。这样,随着世代更替,候选解在解决问题上会越来越好。

在接下来的章节中,我们将描述遗传算法的各个组成部分,这些组成部分使得遗传算法能够模拟达尔文进化的过程。

基因型(Genotype)

在自然界中,繁殖、复制和突变是通过基因型(genotype) 实现的------一组被组合成染色体的基因。如果两个个体繁殖产生后代,后代的每条染色体都将携带来自父母双方的基因组合。模仿这一概念,在遗传算法中,每个个体由表示一组基因的染色体表示。例如,染色体可以用二进制字符串表示,其中每一位代表一个基因。

群体(Population)

在任意时刻,遗传算法都会维护一个个体群体------即针对当前问题的一组候选解。由于每个个体都由某条染色体表示,因此这个个体群体可以看作是这些染色体的集合:

群体持续代表当前代,当当前代被新一代取代时,群体也随之进化。

适应度函数(Fitness Function)

在算法的每次迭代中,个体都会通过适应度函数(也称目标函数)进行评估。这个函数是我们希望优化的目标,或者是我们试图解决的问题。

那些获得更高适应度分数的个体代表更优的解,更可能被选中进行繁殖,并在下一代中得到表现。随着时间推移,解的质量会不断提升,适应度值增加,当找到一个满意的解时,算法即可终止。

选择(Selection)

在计算完群体中每个个体的适应度后,会进行选择过程,以确定哪些个体能够繁殖并产生形成下一代的后代。

这个选择过程基于个体的适应度分数。适应度高的个体更可能被选中,将其遗传信息传递给下一代。

适应度低的个体仍有可能被选中,但概率较低。这样,它们的遗传信息不会被完全排除,从而维持群体的遗传多样性。

交叉(Crossover)

为了创建一对新的个体,通常会从当前代选择两个父本,并将它们染色体的部分片段互换(交叉)以生成两个新的染色体,代表后代。这个操作称为交叉或重组(crossover/recombination):

变异(Mutation)

变异操作的目的是更新群体,为染色体引入新的模式,并周期性地、随机地鼓励在解空间的未知区域进行搜索。

变异通常表现为基因的随机变化。变异通过对一个或多个染色体值进行随机修改来实现,例如在二进制字符串中翻转某一位:

现在,让我们来看一下遗传算法背后的理论。

遗传算法背后的理论

遗传算法的基本假设(building-block hypothesis)是:问题的最优解是由小的构建块组成的,随着我们将更多的这些构建块组合在一起,就能逐步接近最优解。

在群体中,包含一些期望构建块的个体会通过其较高的评分被识别出来。选择(selection)和交叉(crossover)的反复操作会产生更优的个体,将这些构建块传递到下一代,同时可能将它们与其他成功的构建块结合。这种过程产生了"遗传压力",从而引导群体逐渐拥有更多包含形成最优解的构建块的个体。

因此,每一代都会比上一代更优,并包含更多接近最优解的个体。

例如,考虑一个由四位二进制字符串组成的群体,我们的目标是找到数字总和最大的字符串。这就是所谓的 OneMax 问题,本书后面会详细讨论。在这种情况下,数字 1 出现在四个可能位置中的任意一个都可以视为一个良好的构建块。随着算法的进行,它会识别出包含这些构建块的解并将它们组合在一起。每一代中,包含 1 的个体会越来越多,最终得到字符串 1111,它包含了所有期望的构建块。

这个过程可以通过下图进行说明:

图 1.5 展示了两个对该问题是良好解的个体(每个都有三个 1)在交叉操作中将双方的期望构建块结合时,会生成一个最优解的后代(四个 1 位------即右侧的后代)。

模式定理(Schema Theorem)

构建块假设的更正式表达是 Holland 的模式定理(Schema Theorem),也称为遗传算法的基本定理。

该定理涉及 模式(schemata,schema 的复数) ,即染色体中可以找到的模式或模板。每个模式代表染色体的一个子集,这些染色体在某些方面具有相似性。

例如,如果长度为四的二进制字符串代表染色体集合,模式 1*01 表示所有左起第一位是 1、右起两位是 01、中间第二位可以是 1 或 0(* 表示通配符)的染色体。

对于每个模式,我们可以分配两个度量指标:

  • Order(阶) :固定位的数量(非通配符)
  • Defining length(定义长度) :最远两个固定位之间的距离

下表给出了几个四位二进制模式及其度量示例:

Schema Order Defining Length
1101 4 3
1*01 3 3
*101 3 2
11 2 2
**01 2 1
1*** 1 0
**** 0 0

表 1.1:四位二进制模式及其对应度量

群体中的每条染色体对应多个模式,就像一个字符串可以匹配多个正则表达式一样。例如,染色体 1101 对应表中出现的每个模式,因为它匹配每个模式表示的模板。如果该染色体得分较高,它和它所代表的所有模式都更可能在选择操作中存活。随着染色体与其他染色体交叉或发生变异,某些模式会存活,而其他模式会消失。低阶、短定义长度的模式更容易存活。

因此,模式定理指出,低阶、短定义长度且适应度高于平均值的模式在连续世代中的频率呈指数增长。换句话说,代表使解更优属性的小而简单的构建块会随着遗传算法的进行,在群体中越来越多地出现。

下一节,我们将讨论遗传算法与传统算法的差异。

与传统算法的差异

遗传算法与传统的搜索和优化算法(如基于梯度的算法)之间存在若干重要差异。主要区别包括:

  • 维护一个解的群体
  • 使用解的遗传表示
  • 利用适应度函数的结果
  • 表现出概率性行为

下面将详细描述这些特点。

基于群体

遗传搜索是在候选解(个体)群体上进行的,而不是单个候选解。在搜索的任何时刻,算法都会保留一组个体,形成当前世代。遗传算法的每次迭代会产生下一代个体。

相比之下,大多数其他搜索算法只维护单个解,并通过迭代修改它来寻找最优解。例如,梯度下降算法会迭代地沿着梯度的负方向移动当前解,从而寻找最陡下降方向。

遗传表示

遗传算法并不是直接操作候选解,而是操作其表示(或编码),通常称为 染色体。一个简单的染色体例子是固定长度的二进制字符串。

染色体允许我们执行交叉(crossover)和变异(mutation)等遗传操作。交叉通过交换两个父体的染色体部分实现,而变异通过修改染色体的部分位置实现。

使用遗传表示的一个副作用是将搜索与原问题域解耦。遗传算法并不知道染色体具体代表什么,也不试图去解释它们。

适应度函数

适应度函数表示我们希望解决的问题。遗传算法的目标是找到在计算该函数时得分最高的个体。

与许多传统搜索算法不同,遗传算法只考虑适应度函数的值,不依赖导数或其他信息。这使它们适合处理难以或无法进行数学求导的函数。

概率性行为

许多传统算法是确定性的,而遗传算法从一代进化到下一代的规则是概率性的

例如,在选择用于生成下一代的个体时,个体被选择的概率随其适应度增加,但仍存在随机因素。低适应度的个体仍可能被选中,只是概率较低。

变异操作同样是概率驱动的,通常发生概率较低,会随机改变染色体的一个或多个位置。

交叉操作也可能具有概率性。在某些遗传算法变体中,交叉仅在一定概率下发生。如果未发生交叉,两个父体将不变地复制到下一代。

尽管这个过程具有概率性,遗传算法的搜索并非完全随机,而是利用随机性将搜索引导至更可能获得更好结果的解空间区域。

接下来,我们将讨论遗传算法的优势。

遗传算法的优势

前面章节讨论的遗传算法独特特性,使其相较于传统搜索算法具有多项优势。主要优势包括:

  • 全局优化能力
  • 能够处理具有复杂数学表示的问题
  • 能够处理缺乏数学表示的问题
  • 对噪声具有鲁棒性
  • 支持并行和分布式处理
  • 适合连续学习

下面我们将逐一介绍这些优势。

全局优化

在许多情况下,优化问题存在局部极大值和极小值点;这些点比其周围的解更优,但并非整体最优解。

下图展示了全局最大值/最小值与局部最大值/最小值的区别:

大多数传统搜索和优化算法,尤其是基于梯度的算法,容易陷入局部最大值而无法找到全局最优解。这是因为在局部最大值附近,任何微小的改变都会导致评分下降。

而遗传算法对此现象的敏感性较低,更有可能找到全局最大值。这得益于使用了一整个人口的候选解而非单个解,以及交叉和变异操作,在许多情况下能够生成与先前解相距较远的候选解。只要我们能够维持种群的多样性并避免过早收敛(将在下一节中讨论),这一点就成立。

处理复杂问题

由于遗传算法只需要每个个体的适应度函数结果,而不关注适应度函数的其他特性(如导数),因此它们可以用于具有复杂数学表示的问题,或者处理难以或无法求导的函数。

遗传算法擅长处理的其他复杂情况包括:具有大量参数的问题,以及参数类型混合的问题------例如连续参数和离散参数的组合。

处理缺乏数学表示的问题

遗传算法也可以用于完全缺乏数学表示的问题。一个特别有趣的例子是,当适应度评分基于人工评价时。例如,我们希望找到一个网站上最吸引人的配色方案。我们可以尝试不同的颜色组合,并让用户对网站的吸引力进行评分。遗传算法可以在这个基于意见的评分作为适应度函数结果的情况下,搜索得分最高的组合。即使适应度函数没有数学表示,也无法直接从给定颜色组合计算分数,遗传算法仍然可以正常运行。

正如下一章所示,遗传算法甚至可以处理每个个体得分无法直接获得的情况,只要我们能比较两个个体并判断哪个更优。例如,在模拟赛车中,机器学习算法控制汽车行驶时,可以使用遗传算法通过让不同版本的算法互相竞争,从而优化和调优算法。

对噪声的鲁棒性

一些问题存在噪声行为,即即使输入参数相似,每次测量输出值也可能有所不同。例如,当使用传感器输出数据,或者当评分基于人工评价时,就可能出现这种情况。

虽然这种行为会干扰许多传统搜索算法,遗传算法通常具有鲁棒性,因为它通过不断地重组和重新评估个体来减轻噪声影响。

并行化

遗传算法非常适合并行化和分布式处理。适应度的计算对于每个个体是独立的,这意味着种群中的所有个体都可以同时进行评估。

此外,选择、交叉和变异操作也可以在个体及个体对之间同时进行。

这使得遗传算法成为分布式和云端实现的天然候选方法。

持续学习

在自然界中,进化从不停歇。随着环境条件变化,种群会不断适应。同样,遗传算法可以在不断变化的环境中持续运行,并且在任意时间点都可以获取当前最优解并使用。

为了有效实施,这种环境变化需要相对于遗传算法的世代更新速度较慢。

在介绍了遗传算法的优势之后,我们接下来来看其局限性。

遗传算法的局限性

为了充分发挥遗传算法的优势,我们需要了解其局限性和潜在的陷阱。遗传算法的局限性主要包括以下几个方面:

  • 需要特殊定义
  • 需要调节超参数
  • 计算操作开销大
  • 存在过早收敛的风险
  • 无法保证一定找到最优解

下面我们将逐一说明。

特殊定义

在将遗传算法应用到某个具体问题时,需要为其创建合适的表示方式------定义适应度函数和染色体结构,以及适用于该问题的选择、交叉和变异操作。这往往具有一定的挑战性并且耗时较长。

幸运的是,遗传算法已经被应用于无数不同类型的问题,其中许多定义已经标准化。本书涵盖了多种现实问题及其使用遗传算法的解决方法。在遇到新问题时,可以参考这些经验。

超参数调节

遗传算法的行为受一组超参数控制,例如种群规模和变异率。在具体应用中,没有固定规则告诉我们如何选择这些值。

不过,这种情况几乎适用于所有搜索和优化算法。通过阅读本书中的示例并进行一定的实验,你将能够对这些值做出合理的选择。

计算开销大

操作(可能较大的)种群以及遗传算法的重复性操作可能会非常耗费计算资源,同时在得到良好结果之前也可能耗时较长。

这些问题可以通过合理选择超参数、实施并行处理,以及在某些情况下缓存中间结果来缓解。

过早收敛

如果某个个体的适应度远高于种群其他个体,它可能被过度复制,从而占据整个种群。这可能导致遗传算法过早陷入局部最大值,而无法找到全局最优解。

为了防止这种情况,保持种群多样性非常重要。保持多样性的各种方法将在下一章讨论。

无保证最优解

使用遗传算法并不能保证一定能找到问题的全局最大值。

不过,对于几乎所有搜索和优化算法来说,除非是针对特定问题类型的解析解,否则都无法保证全局最优。

通常情况下,合理使用遗传算法可以在合理时间内获得较优解。

接下来,我们来看几个遗传算法的实际应用案例。

遗传算法的应用案例

基于前面章节的内容,遗传算法最适合用于以下类型的问题:

  1. 具有复杂数学表示的问题:由于遗传算法只需要适应度函数的结果,它们可以用于目标函数难以或无法求导的问题(如计划与调度问题)、参数数量较多的问题(如图像重建),以及参数类型混合的问题(如超参数优化)。
  2. 没有数学表示的问题:只要能获得评分值,或者可以比较两个解的优劣,遗传算法就可以使用,而无需数学表示。例如,在解决强化学习任务或优化深度学习模型架构时,这种特性非常有用。
  3. 涉及噪声环境的问题:遗传算法能够适应数据可能不一致的情况,例如来自传感器输出的信息或基于人类评分的数据。例如,根据客户反馈和使用模式选择网站的最佳配色方案。
  4. 涉及随时间变化环境的问题:遗传算法可以通过持续创建新一代个体来适应环境的缓慢变化。延续前面的网站配色方案例子,客户的喜好可能会随时尚趋势而变化。另一方面,如果问题已有已知且专业的解决方法,使用现有的传统或解析方法通常更高效。

至此,本章内容结束。

总结

在本章中,我们首先介绍了遗传算法及其与达尔文进化的类比,并讲解了其基本操作原理,包括种群的使用、基因型、适应度函数以及选择、交叉和变异等遗传操作。

随后,我们讲解了遗传算法的理论基础,通过构建块假设和模式定理说明了遗传算法如何通过组合优秀的小构建块来生成最优解。

接着,我们讨论了遗传算法与传统算法的差异,例如维护解的种群以及使用解的遗传表示。

然后,我们介绍了遗传算法的优势,包括全局优化能力、处理复杂或不存在数学表示的问题、对噪声的适应能力;以及其局限性,包括需要特殊定义、超参数调节以及过早收敛的风险。

最后,我们概述了遗传算法适用的场景,例如数学复杂问题以及在噪声或不断变化的环境中的优化任务。

在下一章中,我们将深入探讨遗传算法的关键组件和实现细节,为后续章节中使用遗传算法编码解决各种问题做好准备。

延伸阅读

有关本章内容的更多信息,请参考 Amita Kapoor 于 2019 年 1 月出版的《Hands-On Artificial Intelligence for IoT》一书中的 Introduction to Genetic Algorithms ,可访问链接:subscription.packtpub.com/book/big_da...

相关推荐
max5006004 分钟前
北京大学MuMo多模态肿瘤分类模型复现与迁移学习
人工智能·python·机器学习·分类·数据挖掘·迁移学习
2501_924877356 分钟前
智慧零售漏扫率↓79%!陌讯多模态融合算法在智能收银与货架管理的实战解析
大数据·人工智能·算法·目标检测·边缘计算·零售
修一呀16 分钟前
[后端快速搭建]基于 Django+DeepSeek API 快速搭建智能问答后端
后端·python·django
WSSWWWSSW22 分钟前
Seaborn数据可视化实战:Seaborn数据可视化实战入门
python·信息可视化·数据挖掘·数据分析·matplotlib·seaborn
xz2024102****31 分钟前
吴恩达机器学习作业二:线性可分逻辑回归
人工智能·机器学习·逻辑回归
爱编程的鱼1 小时前
C# 数组&C# 多维数组
数据结构·算法·c#
小石1 小时前
Python 装饰器核心知识点:无参装饰器构建、带参装饰器扩展及函数与类实现差异
python
前端开发工程师请求出战1 小时前
MCP — 让AI变得更“百搭”
算法
巴厘猫1 小时前
从 Manim 中提取表格 / 坐标系并转 GIF:实用方案与核心代码
python·音视频开发
老歌老听老掉牙2 小时前
Pandas DataFrame 列数操作完全指南
python·pandas