遗传算法实践详解(deap框架初体验)

  • OneMax问题介绍
  • 遗传算法实践
    • 遗传算法要素配置
    • 遗传算法解的进化
    • 算法运行

OneMax问题介绍

OneMax问题是一个简单的优化任务,通常作为遗传算法Hello World
OneMax任务是查找给定长度的二进制串,最大化该二进制串中数字的总和。例如,对于长度为5的OneMax问题,10010的数字总和为2,01110的数字总和为3。
显然,此问题的最优解为每位数字均为1的二进制串。但是对于遗传算法而言,其并不具备此知识,因此需要使用其遗传算子寻找最优解。算法的目标是在合理的时间内找到最优解,或者至少找到一个近似最优解

遗传算法实践

在进行实践前,应首先明确遗传算法中所用到的要素定义。

  1. 选择染色体
    由于OneMax问题涉及二进制串,因此每个个体的染色体直接利用代表候选解的二进制串表示是一个自然的选择。在Python中,将其实现为仅包含0/1整数值的列表。染色体的长度与OneMax问题的规模匹配。例如,对于规模为5的OneMax问题,个体10010由列表[1,0,0,1,0]表示。
  2. 适应度的计算
    由于要最大化该二进制串中数字的总和,同时由于每个个体都由0/1整数值列表表示,因此适合度可以设计为列表中元素的总和,例如:sum([1,0,0,1,0])= 2。
  3. 选择遗传算子
    选择遗传算子并没有统一的标准,通常可以尝试几种选择方案,找到最适合的方案。其中选择算子通常可以处理任何染色体类型,但是交叉和突变算子通常需要匹配使用的染色体类型,否则可能会产生无效的染色体。
    选择算子:此处选用锦标赛选择。
    交叉算子:此处选用单点交叉。
    突变算子:此处选用位翻转突变。
  4. 设定停止条件
    限制繁衍的代际数通常是一个不错的停止条件,它可以确保算法不会永远运行。
    另外,由于我们知道了OneMax问题的最佳解决方案(一个全为1的二进制串,也就是说其适应度等于代表个体的列表长度),因此可以将其用作另一个停止条件。
    Note. 但是,对于现实世界中的多数问题而言,通常不存在这种可以公式化精确定义的先验知识。

遗传算法要素配置

在开始实际的遗传算法流程之前,需要根据上述要素的设计利用代码实现:

  1. 首先导入所用包:
from deap import base
from deap import creator
from deap import tools
import random
import matplotlib.pyplot as plt
  1. 接下来,声明一些常量,这些常量用于设置OneMax问题的参数并控制遗传算法的行为:
#超参数声明
ONE_MAX_LENGTH = 100    #length of bit string to be optimized
POPULATION_SIZE = 200   #number of individuals in population
P_CROSSOVER = 0.9       #probability for crossover
P_MUTATION = 0.1        #probability for mutating an individual
MAX_GENERATION = 50     #max number of generations for stopping condition
  1. 接下来,使用Toolbox类创建zeroOrOne操作,该操作用于自定义random.randomint(a,b)函数。通过将参数a和b固定为值0和1,当在调用此运算时时,zeroOrOne运算符将随机返回0或1。以下代码段,然后使用它来注册:
toolbox = base.Toolbox()#定义toolbox变量
toolbox.register("zeroOrOne",random.randint,0,1)#注册zeroOrOne运算
  1. 接下来,需要创建Fitness类。由于这里只有一个目标——最大化数字总和,因此选择FitnessMax策略,使用具有单个正权重的权重元组:
creator.create("FitnessMax",base.Fitness,weights=(1.0,))
  1. deap中,通常使用“Individual”的类来代表种群中的每个个体。使用creator工具创建该类,使用列表作为基类,用于表示个体的染色体。并为该类增加Fitness属性,该属性初始化为步骤4中定义的FitnessMax类:
creator.create("Individual", list, fitness=creator.FitnessMax)
  1. 接下来,注册individualCreator操作,该操作创建Individual类的实例,并利用步骤1中自定义的zeroOrOne操作随机填充0/1。注册individualCreator操作使用基类initRepeat操作,并使用以下参数对基类进行实例化:
    (1) 将creator.Individual类作为放置结果对象的容器类型
    (2) zeroOrOne操作是生成对象的函数
    (3) 常量ONE_MAX_LENGTH作为要生成的对象数目
    由于zeroOrOne运算符生成的对象是0/1的随机数,因此,所得的individualCreator运算符将使用100个随机生成的0或1填充单个实例:
toolbox.register("individualCreator",tools.initRepeat,creator.Individual,toolbox.zeroOrOne,ONE_MAX_LENGTH)
  1. 最后,注册用于创建个体列表的populationCreator操作。该定义使用带有以下参数的initRepeat基类操作
    (1) 将列表类作为容器类型
    (2) 用于生成列表中对象的函数——personalCreator运算符
    这里没有传入initRepeat的最后一个参数——要生成的对象数量。这意味着,当使用populationCreator操作时,需要指定此参数用于确定创建的个体数:
toolbox.register("populationCreator",tools.initRepeat,list,toolbox.individualCreator)
  1. 为了简化适应度(在deap中称为evaluation)的计算,首先定义一个独立的函数,该函数接受Individual类的实例并返回其适应度。
    这里定义oneMaxFitness函数,用于计算个体中1的数量。
def oneMaxFitness(individual):return sum(individual),#deap中的适用度表示为元组,因此,当返回单个值时,需要用逗号将其声明为元组。
  1. 接下来,将evaluate运算符定义为oneMaxfitness()函数的别名。使用evaluate别名来计算适应度是deap的一种约定:
toolbox.register("evaluate",oneMaxFitness)
  1. 遗传算子通常是通过对tools模块中的现有函数进行别名命名,并根据需要设置参数值来创建的。根据上节设计的要素创建遗传算子。
toolbox.register("select",tools.selTournament,tournsize=3)
toolbox.register("mate",tools.cxOnePoint)
# mutFlipBit函数遍历个体的所有特征,并且对于每个特征值,
# 都将使用indpb参数值作为翻转(应用not运算符)该特征值的概率。
# 该值与突变概率无关,后者由P_MUTATION常数设置。
# 突变概率用于确定是否为种群中的给定个体调用mutFlipBit函数
toolbox.register("mutate",tools.mutFlipBit,indpb=1.0/ONE_MAX_LENGTH)

遗传算法解的进化

遗传流程如以下步骤所示:

  1. 通过使用之前定义的populationCreator操作创建初始种群,并以POPULATION_SIZE常量作为该操作的参数。并初始化generationCounter变量,用于判断代际数:
population = toolbox.populationCreator(n=POPULATION_SIZE)
generationCounter = 0
  1. 为了计算初始种群中每个个体的适应度,使用map()函数将evaluate操作应用于种群中的每个个体。由于evaluate操作是oneMaxFitness()函数的别名,因此,迭代的结果由每个个体的计算出的适应度元组组成。 得到结果后将其转换为元组列表:
fitnessValues = list(map(toolbox.evaluate,population)
  1. 由于fitnessValues的项分别与population(个体列表)中的项匹配,因此可以使用zip()函数将它们组合并为每个个体分配相应的适应度元组:
for individual,fitnessValue in zip(population,fitnessValues):individual.fitness.values = fitnessValue
  1. 接下来,由于适应度元组仅有一个值,因此从每个个体的适应度中提取第一个值以获取统计数据:
fitnessValues = [indivalual.fitness.values[0] for individual in population]
  1. 统计种群每一代的最大适应度和平均适应度。创建两个列表用于存储统计值:
maxFitnessValues = []
meanFitnessValues = []
  1. 遗传流程的主要准备工作已经完成,在循环时还需设置停止条件,通过限制代际数来设置一个停止条件,而通过检测是否达到了最佳解(所有二进制串位都为1)作为另一个停止条件:
while max(fitnessValues)<ONE_MAX_LENGTH and generationCounter<MAX_GENERATIONS:
  1. 接下来更新代际计数器:
 generationCounter = generationCounter + 1
  1. 遗传算法的核心是遗传运算符。第一个是selection运算符,使用先前利用toolbox.select定义的锦标赛选择。由于我们已经在定义运算符时设置了锦标赛大小,因此只需要将物种及其长度作为参数传递给选择运算符:
 offspring = toolbox.select(population,len(population))
  1. 被选择的个体被赋值给offspring变量,接下来将其克隆,以便我们可以应用遗传算子而不影响原始种群:
    Note. 尽管命名为offspring,但它们仍然是前一代的个体的克隆,我们仍然需要使用crossover运算符将它们配对以创建实际的后代。
 offspring = list(map(toolbox.clone,offspring)
  1. 下一个遗传算子是交叉。已经在上节中定义为toolbox.mate运算符,并且其仅仅是单点交叉的别名。使用Python切片将offspring列表中的每个偶数索引项与奇数索引项对作为双亲。然后,以P_CROSSOVER常数设置的交叉概率进行交叉。这将决定这对个体是会交叉或保持不变。最后,删除后代的适应度值,因为它们现有的适应度已经不再有效:
 for child1,child2 in zip(offspring[::2],offspring[1::2]):if random.random() < P_CROSSOVER:toolbox.mate(child1,child2)del child1.fitness.valuesdel child2.fitness.values
  1. 最后一个遗传运算符是突变,先前已注册为toolbox.mutate运算符,并设置为翻转位突变操作。遍历所有后代项,将以由突变概率常数P_MUTATION设置的概率应用变异算子。如果个体发生突变,我们确保删除其适应性值。由于该值可能继承自上一代,并且在突变后不再正确,需要重新计算:
 for mutant in offspring:if random.random() < P_MUTATION:toolbox.mutate(mutant)del mutant.fitness.values
  1. 没有交叉或变异的个体保持不变,因此,它们的现有适应度值(已在上一代中计算出)就无需再次计算。其余个体的适应度值为空。使用“Fitness”类的valid属性查找这些新个体,然后以与原始适应性值计算相同的方式为其计算新适应性:
 freshIndividuals = [ind for ind in offspring if not ind.fitness.valid]freshFitnessValues = list(map(toolbox.evaluate,freshIndividuals))for individual,fitnessValue in zip(freshIndividuals,freshFitnessValues):individual.fitness.values = fitnessValue
  1. 遗传算子全部完成后,就可以使用新的种群取代旧的种群了:
 population[:] = offspring
  1. 在继续下一轮循环之前,将使用与上述相同的方法统计当前的适应度值以更新统计信息:
 fitnessValues = [ind.fitness.values[0] for ind in population]
  1. 获得最大和平均适应度值,将它们的值添加到统计列表中:
 maxFitness = max(fitnessValues)meanFitness = sum(fitnessValues) / len(population)maxFitnessValues.append(maxFItness)meanFItnessValues.append(meanFItness)print(“- Generation {}: Max Fitness = {}, Avg Fitness = {}”.format(generationCounter,maxFitness, meanFitness)
  1. 此外,使用得到的最大适应度值来找到最佳个体的索引,并打印出该个体:
 best_index = fitnessValues.index(max(fitnessValues))print(“Best Individual = “, *population[best_index], “\n”)
  1. 满足停止条件并且遗传算法流程结束后,可以使用获取的统计信息,使用matplotlib库可视化算法执行过程中的统计信息,展示各代个体的最佳和平均适应度值的变化:
plt.plot(maxFitnessValues,color=’red’)
plt.plot(meanFitnessValues,color=’green’)
plt.xlabel(‘Generation’)
plt.ylabel(‘Max / Average Fitness’)
plt.title(‘Max and Average fitness over Generation’)
plt.show()

该部分完整代码如下:

def main():population = toolbox.populationCreator(n=POPULATION_SIZE)generationCounter = 0fitnessValues = list(map(toolbox.evaluate,population))for individual,fitnessValue in zip(population,fitnessValues):individual.fitness.values = fitnessValuefitnessValues = [individual.fitness.values[0] for individual in population]maxFitnessValues = []meanFitnessValues = []while max(fitnessValues) < ONE_MAX_LENGTH and generationCounter < MAX_GENERATION:generationCounter = generationCounter + 1offspring = toolbox.select(population,len(population))offspring = list(map(toolbox.clone,offspring))for child1,child2 in zip(offspring[::2],offspring[1::2]):if random.random() < P_CROSSOVER:toolbox.mate(child1,child2)del child1.fitness.valuesdel child2.fitness.valuesfor mutant in offspring:if random.random() < P_MUTATION:toolbox.mutate(mutant)del mutant.fitness.valuesfreshIndividuals = [ind for ind in offspring if not ind.fitness.valid]freshFitnessValues = list(map(toolbox.evaluate,freshIndividuals))for individual,fitnessValue in zip(freshIndividuals,freshFitnessValues):individual.fitness.values = fitnessValuepopulation[:] = offspringfitnessValues = [ind.fitness.values[0] for ind in population]maxFitnessValue = max(fitnessValues)meanFitnessValue = sum(fitnessValues) / len(population)maxFitnessValues.append(maxFitnessValue)meanFitnessValues.append(meanFitnessValue)print("- Generation {}: Max Fitness = {}, Avg Fitness = {}".format(generationCounter,maxFitnessValue,meanFitnessValue))best_index = fitnessValues.index(max(fitnessValues))print("Best Indivadual = ", *population[best_index],"\n")plt.plot(maxFitnessValues,color="red")plt.plot(meanFitnessValues,color="green")plt.xlabel("Generation")plt.ylabel("Max / Average Fitness")plt.title("Max and Average fitness over Generation")plt.show()

至此可以开始测试我们的遗传算法了,运行代码以验证其是否找到了OneMax问题的最优解。

算法运行

if __name__ == "__main__":main()

运行程序时,可以看到程序运行输出:

- Generation 27: Max Fitness = 99.0, Avg Fitness = 96.805
Best Indivadual =  1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 - Generation 28: Max Fitness = 99.0, Avg Fitness = 97.235
Best Indivadual =  1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 - Generation 29: Max Fitness = 99.0, Avg Fitness = 97.625
Best Indivadual =  1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 - Generation 30: Max Fitness = 100.0, Avg Fitness = 98.1
Best Indivadual =  1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1

可以看到30代后算法找到全1解,结果适应度为100,并停止了遗传流程。平均适应度开始时仅为53左右,结束时接近100。
绘制图形如下所示:

该图说明了最大适应度与平均适应度是如何随着代数的增加而逐步增加。

遗传算法实践详解(deap框架初体验)相关推荐

  1. 组件化实践详解(二)

    在上一篇文章<组件化实践详解(一)>中我们介绍了组件化实践的目标和实践步骤,本文继续说说关于组件化实践遇到的问题及思考. 1.组件内的架构设计 这条本来我是不想写的,但是很多组件化的文章里 ...

  2. 遗传算法(Python) #5 用DEAP框架解决OneMax问题

    遗传算法(Python) #5 用DEAP框架解决OneMax问题 遗传算法系列的第三期介绍了如何不用任何框架从零开始解决OneMax问题,第四期介绍了DEAP框架的基本用法.若读者对下文中定义或术语 ...

  3. 详解Spring框架的异步请求

    文章目录 详解Spring框架的异步请求 1.导入响应的jar包(gson) 2.前端请求 3.后端逻辑处理并返回结果 详解Spring框架的异步请求 在开发过程中有异步请求和同步请求之分. 同步请求 ...

  4. EKF SLAM Matlab仿真实践详解(附源码)

    EKF SLAM Matlab仿真实践详解(附源码) 为提供更好的阅读体验,详细内容及源码请移步https://github.com/Nrusher/EKF_SLAM 或 https://gitee. ...

  5. centos 安装mysql5.7_Zabbix 4.2.5 安装部署实践详解

    [导读]云计算背景下,无论是大数据.物联网还是边缘计算,规模化后大量的设备需要保证正常运行,在人员一定的情况下,就需要提高运行维护效率.同时随着智能化被应用在人们生活的方方面面,关联性也越来越紧密,即 ...

  6. ios无痕埋点_移动端无痕埋点实践详解(二)

    0x01 前言 在移动端无痕埋点实践详解(一)这篇文章大致总结了移动端无痕埋点的基本原理.主要介绍了什么是无痕埋点,无痕埋点的基础数据流程以及在Android系统上总体思路.这篇文章着重总结下无痕埋点 ...

  7. 详解工作流框架Activiti的服务架构和组件

    摘要:通过这篇文章,可以对工作流有一个基本的认识,为后续工作流框架Activiti的学习打下坚实的基础. 本文分享自华为云社区<BPMN工作流的基本概念!详解工作流框架Activiti的服务架构 ...

  8. 负载均衡原理与实践详解 第三篇 服务器负载均衡的基本概念-网络基础

    负载均衡原理与实践详解 第三篇 服务器负载均衡的基本概念-网络基础 系列文章: 负载均衡详解第一篇:负载均衡的需求 负载均衡详解第二篇:服务器负载均衡的基本概念-网络基础 负载均衡详解第三篇:服务器负 ...

  9. jsp漂亮的登录界面源码_【案例+源码】详解MVC框架模式及其应用

    案例+源码]详解MVC框架模式及其应用 写在开头: 首先我们需要知道,框架模式.模式.开发模式是三种不同的概念,但他们的目的都一样:解耦! 1.关于MVC框架模型 MVC是三个单词的缩写: M,Mod ...

  10. Zabbix 4.2.5 安装部署实践详解

    一.安装 1.安装CentOS操作系统,并配置网络 2.安装Zabbix官方源 rpm -ivh http://repo.zabbix.com/zabbix/4.2/rhel/7/x86_64/zab ...

最新文章

  1. Qt ffmpeg环境搭建
  2. Delphi 中的 XMLDocument 类详解(5) - 获取元素内容
  3. 原来博客园的日历是这样用的!
  4. 想理解Java的IO,不要从操作系统开始说起的都是耍流氓...
  5. 用户级别线程的切换切换到内核线程_【修炼内功】[JVM] 细说线程
  6. Android开发学习之TabView选项卡具体解释 -- 基于Android4.4
  7. python-day76--django-中间件
  8. offer上不写具体薪资合理吗_拿着OFFER当白菜?职场菜鸟不带这么玩的
  9. php 极光im注册用户,极光IM- 用户信息管理 - 极光文档
  10. 拜托!面试请不要再问我Spring Cloud底层原理
  11. 制作QQ背景音乐链接
  12. 阿里云被攻击的处理方法
  13. 二级域名解析配置方法
  14. JSP水电费管理系统myeclipse开发mysql数据库web结构java编程
  15. 洛谷P4043 费用流
  16. Java word转pdf Linux/windows跨平台 格式完美(利用命令行调用libreoffice)
  17. jq使用请求报405错误
  18. git color 让git有颜色
  19. 电视机尺寸一览表2022
  20. 计算机 北航 在线作业,北航计算机组成原理在线作业

热门文章

  1. SIM-MICRO-SIM- NANO SIM 区别
  2. OldSkoolVerb Plus for Mac(算法混响插件)
  3. 51单片机贪吃蛇程序
  4. 毕业设计之 ---- 基于机器视觉的图像拼接算法
  5. 火狐浏览器怎么打不开网页
  6. Hbase学习(四)---hbase 的下载,maven依赖,以及官网文档
  7. 【uni-app的ui组件】uni-ui如何安装使用教程
  8. 小白可以来看看磁盘与文件管理实验
  9. Visual Basic 基础知识
  10. 5.4 logistic回归分析