遗传算法之扇贝的进化

目录

  • 背景故事
  • 进化论
  • 遗传算法
  • 遗传算法过程
  • 代码实现
  • 完整代码

1. 背景故事

​扇贝的进化讲述了一个有趣的故事:
​从前在海岸边有一群扇贝在生活繁衍。它们自然是衣食不愁,连房子也有了着落。它们担忧的只有一件事:每隔一段时间,总有一个人来挖走它们之中的一部分。但扇贝们不知道的是,这人的家族图腾是Firefox的图标,所以他总是选择那些贝壳花纹长得比较不像Firefox图标的扇贝。
​这种状况持续了好几十万代。大家应该也猜到扇贝们身上发生什么事情了:它们的贝壳上都印着很像Firefox图标的图案。

2. 进化论

达尔文进化论很好的解释了上述扇贝的问题:自然选择。自然选择讲究的是物竞天择、适者生存。在扇贝生存的环境中,只有更像Firefox图标的扇贝才能容易活下来。根据遗传学来讲,活下来的扇贝的后代继承了更像像Firefox图标的基因,在基因表达后更像Firefox。

3. 遗传算法

基于自然选择和遗传定理,人们提出了一种新的算法--遗传算法。 ​遗传算法是模拟达尔文生物进化论的自然选择和遗传学机理的生物进化过程的计算模型,是一种通过模拟自然进化过程搜索最优解的方法。 ​其主要特点是直接对结构对象进行操作,不存在求导和函数连续性的限定;具有内在的隐并行性和更好的全局寻优能力;采用概率化的寻优方法,不需要确定的规则就能自动获取和指导优化的搜索空间,自适应地调整搜索方向。 ​遗传算法以一种群体中的所有个体为对象,并利用随机化技术指导对一个被编码的参数空间进行高效搜索。其中,选择、交叉和变异构成了遗传算法的遗传操作;参数编码、初始群体的设定、适应度函数的设计、遗传操作设计、控制参数设定五个要素组成了遗传算法的核心内容。

4. 遗传算法过程

​遗传算法的大致过程可以参考下图: ![在这里插入图片描述](https://img-blog.csdnimg.cn/47c44d0d0d2e4af596618bd159d23e1b.webp#pic_center)

4.1 编码
编码的过程就是寻找一种对问题潜在解的数字化形式,即通过数字的形式定义问题的解。生物的性状是基因表的结果。
在扇贝的进化这一问题中,我们使用一个带颜色的三角形代表一个基因,多个三角形的融合代表生物的形状,即扇贝的外在图案。简单来说,我们通过n个三角形来拟合扇贝图案。编码就是使用数据结构来表示这n个三角形的形状和颜色。然后定义如何使用这n个三角形来拟合。

4.2 初始化种群
随机初始化一个种群,种群大小一般在20-200之间。这个种群作为第0代,种群中的不同个体有着不同的性状,和Firefox图标的相似度有大有小。

4.3 个体适应度
在生物上个体适应度指的是生物个体对环境的适应性度量。在扇贝的进化中指的是评估扇贝和理想中的个体(Firefox图标)的相似性。在评估相似性时,都是以基因表达后的扇贝个体进行比较,即通过比较扇贝图片和Firefox图标的相似性,相似性越高,则适应度越大。

4.4 选择
根据适应度函数来比较所有个体的适应性,通过一定的选择规则保留最优的个体,淘汰其他适应性低的个体。在扇贝的进化问题中,保留和Firefox图标最为相像的m个个体,淘汰其他的个体。

4.5 交叉
对保留的个体进行交配产生下一代,来保持种群的大小。通常通过交叉基因的方式来得到下一代。交叉方式分为以下三种:

  • 单点交叉:交换两个父体某一位置p的基因;
  • 多点交叉:交换两个父体位置(p, q)之间的基因;
  • 均匀交叉:两个父体各有一半基因组成新的个体基因。

4.6 变异
对于个体的基因,按照一定的概率进行随机改变。这个概率一般在0.001到0.1之间。变异的目的是为了通过随机改变来产生更接近理想目标的个体。

4.7 演化
对于初始种群,按照适应度淘汰劣势个体,父代交叉染色体后得到子代,子代经过变异后加入到种群中去,保持种群数量的稳定。经过几十万代的演化,种群中的个体会向目标个体靠近。

5. 代码实现

5.1 开发环境

  • 编程语言:Python
  • 第三方库:pillow8.2.0, numpy1.20.1

5.2 数据结构和函数实现
5.2.1 基因表示
​定义一个单独的类Gene表示基因,该基因用三角形表示,其中三角形的位置和大小使用三个顶点表示,然后使用颜色进行填充,颜色使用(R,G,B,a),其中a表示透明度,透明度在100左右三角形融合效果更好。

class Gene():'''使用三角形的位置和颜色表示基因A,B,C三个顶点的坐标以及颜色的RGBA值width和height表示目标图片的宽和高'''def __init__(self, width, height):self.A = (randint(0, width), randint(0, height))self.B = (randint(0, width), randint(0, height))self.C = (randint(0, width), randint(0, height))self.color = (randint(0, 255), randint(0, 255), randint(0, 255), randint(90, 110))

5.2.2 遗传算法GA
​定义一个遗传算法GA,分别使用函数来模拟生物遗传的染色体、个体、适应性函数、交叉、变异和迭代,其具体内容如下:
​定义了种群数量、个体的基因数量、基因变异概率、繁衍代数以及要观察的最优个体的代。

class GA():def __init__(self, ideal_path, generation_save, save_path, population_size = 20, gene_number = 100, generation_number = 100000, mutate_rate = 0.05):# 目标图片的RGBAself.ideal_individual = Image.open(ideal_path).convert('RGBA')# 目标图片的宽self.width = self.ideal_individual.width      # 目标图片的高self.height = self.ideal_individual.height    # 要保存最优个体的代的集合self.generation_save = generation_save   # 保存的位置self.save_path = save_path   # 种群数量大小self.population_size = population_size  # 基因数量self.gene_number = gene_number       # 基因变异概率self.mutate_rate = mutate_rate   # 繁衍的代数self.generation_number = generation_number                     

5.2.3 生成个体的所有基因

def get_genes(self):'''self.gene_number 表示染色体的基因数'''genes = []for i in range(0, self.gene_number):genes.append(Gene(self.width, self.height))return genes

5.2.4 生成个体
​在生成个体时,利用pillow第三方库的绘图工具ImageDraw.Draw进行绘制。分别绘制m个三角形,然后利用RGBA图像的RGB值和透明度A,进行图像的混合,这里使用alpha_composite函数对两张RGBA图片进行混合,最终得到个体。

def get_individual(self, genes):'''生成个体'''individual = Image.new("RGBA", size=(self.width, self.height))individual_draw = ImageDraw.Draw(individual)# 初始化画布,使用纯白填充individual_draw.polygon([(0, 0), (self.width, 0), (self.width, self.height), (0, self.height)], fill = (255, 255, 255, 255))# 对于每个三角形,先在空白画布上画下,再和之前的进行融合for gene in genes:gene_show = Image.new("RGBA", size=(self.width, self.height))gene_draw = ImageDraw.Draw(gene_show)gene_draw.polygon([gene.A, gene.B, gene.C],fill = gene.color)individual = Image.alpha_composite(individual, gene_show)return individual

5.2.5 适应性函数
通过计算个体和理想个体的图片RGB值的欧式距离代表个体和理想个体的相似性。将个体和理想个体的RGB值转化为一个一维数组,然后使用numpy计算,这里算的是欧式距离的平方(没有进行开方运算)。

def calculate_adapt_grades(self, individual):'''计算适应性函数通过计算图片的RGB值的欧式距离来比较相似性'''array1 = np.array(self.ideal_individual, dtype=np.float32)[:,:,:-1]array2 = np.array(individual, dtype=np.float32)[:,:,:-1]return np.sum(np.square(np.subtract(array1, array2)))

5.2.6 基因变异
​以一定概率对基因进行随机变异,变异概率通常在0.001-0.1之间。

def mutate(self, gene):'''对gene按照变异概率mutate_rate进行变异,得到变异mutate_gene'''if random() < self.mutate_rate:mutate_gene = Gene(self.width, self.height)return mutate_genereturn gene

5.2.7 染色体交叉
​对于两个父体,使用多点交叉,得到子代的染色体。这里随机两个数值,交换这两个位置之间的基因。

def cross(self, parent1, parent2):'''对两个父体进行多点交叉'''child = []random_a = randint(0, self.gene_number)random_b = randint(0, self.gene_number)start = min(random_a, random_b)end = max(random_a, random_b)for i in range(0, 100):if i > start and i < end:child.append(parent2[i])else:child.append(parent1[i])return child

5.2.8 迭代演化

def generate(self):# 1. 初始化种群parent_genes = []for i in range(self.population_size):parent_genes.append(self.get_genes())# 2. 计算适应度,挑选最优的两个作为父代,然后淘汰其他的个体adapt_grades = []for i in range(self.population_size):adapt_grades.append(self.calculate_adapt_grades(self.get_individual(parent_genes[i])))adapt_grades_sorted = sorted(adapt_grades)parent = []parent.append(parent_genes[adapt_grades.index(adapt_grades_sorted[0])])parent.append(parent_genes[adapt_grades.index(adapt_grades_sorted[1])])for generation in range(1, self.generation_number + 1):child_genes = []child_genes.append(parent[0])child_genes.append(parent[1])# 3. 父代进行交叉得到子代for i in range(self.population_size - 2):child_genes.append(self.cross(parent[0], parent[1]))# 4. 子代进行变异后加入到种群中for i in range(2, self.population_size):for j in range(self.gene_number):child_genes[i][j] = self.mutate(child_genes[i][j])adapt_grades = []for i in range(self.population_size):                                        adapt_grades.append(self.calculate_adapt_grades(self.get_individual(child_  genes[i])))                                                     adapt_grades_sorted = sorted(adapt_grades)parent[0] = child_genes[adapt_grades.index(adapt_grades_sorted[0])] parent[1] = child_genes[adapt_grades.index(adapt_grades_sorted[1])]# 每100代打印代数if generation % 100  == 0:print("%d", generation)# 对于generateion_save代打印其适应度,并将结果图片保存if generation in self.generation_save:     print('第%d代的adapt_grades:\t%d' %(generation,self.calculate_adapt_grades(self.get_individual(parent[0])))) save_img = self.get_individual(parent[0])save_img.save(os.path.join(self.save_path, '%d.png' % (generation)))

5.3 结果展示

  • 理想个体(Firefox图标)

  • 演化结果(下标表示代数)

完整代码

from PIL import Image, ImageDraw
from random import randint, random
import numpy as np
import osclass Gene():'''使用三角形的位置和颜色表示基因A,B,C三个顶点的坐标以及颜色的RGBA值width和height表示目标图片的宽和高'''def __init__(self, width, height):self.A = (randint(0, width), randint(0, height))self.B = (randint(0, width), randint(0, height))self.C = (randint(0, width), randint(0, height))self.color = (randint(0, 255), randint(0, 255), randint(0, 255), randint(90, 110))class GA():def __init__(self, ideal_path, generation_save, save_path, population_size = 20, gene_number = 100, generation_number = 100000, mutate_rate = 0.05):self.ideal_individual = Image.open(ideal_path)                  # 目标图片的RGBAself.width = self.ideal_individual.width                        # 目标图片的宽self.height = self.ideal_individual.height                      # 目标图片的高self.generation_save = generation_save                          # 要保存最优个体的代的集合self.save_path = save_path                                      # 保存的位置self.population_size = population_size                          # 种群数量大小self.gene_number = gene_number                                  # 染色体的基因数量self.mutate_rate = mutate_rate                                  # 基因变异概率self.generation_number = generation_number                      # 繁衍的代数def get_genes(self):'''生成染色体'''genes = []for i in range(0, self.gene_number):genes.append(Gene(self.width, self.height))return genesdef get_individual(self, genes):'''生成个体'''individual = Image.new("RGBA", size=(self.width, self.height))individual_draw = ImageDraw.Draw(individual)individual_draw.polygon([(0, 0), (self.width, 0), (self.width, self.height), (0, self.height)], fill = (255, 255, 255, 255))for gene in genes:gene_show = Image.new("RGBA", size=(self.width, self.height))gene_draw = ImageDraw.Draw(gene_show)gene_draw.polygon([gene.A, gene.B, gene.C],fill = gene.color)individual = Image.alpha_composite(individual, gene_show)return individualdef mutate(self, gene):'''对gene按照变异概率mutate_rate进行变异,得到变异mutate_gene'''if random() < self.mutate_rate:mutate_gene = Gene(self.width, self.height)return mutate_genereturn genedef calculate_adapt_grades(self, individual):array1 = np.array(self.ideal_individual, dtype=np.float32)[:,:,:-1]array2 = np.array(individual, dtype=np.float32)[:,:,:-1]return np.sum(np.square(np.subtract(array1, array2)))def cross(self, parent1, parent2):'''对两个父体进行多点交叉'''child = []random_a = randint(0, self.gene_number)random_b = randint(0, self.gene_number)start = min(random_a, random_b)end = max(random_a, random_b)for i in range(0, self.gene_number):if i > start and i < end:child.append(parent2[i])else:child.append(parent1[i])return childdef generate(self):parent_genes = []for i in range(self.population_size):parent_genes.append(self.get_genes())adapt_grades = []for i in range(self.population_size):adapt_grades.append(self.calculate_adapt_grades(self.get_individual(parent_genes[i])))adapt_grades_sorted = sorted(adapt_grades)parent = []parent.append(parent_genes[adapt_grades.index(adapt_grades_sorted[0])])  parent.append(parent_genes[adapt_grades.index(adapt_grades_sorted[1])])for generation in range(1, self.generation_number + 1):child_genes = []child_genes.append(parent[0])child_genes.append(parent[1])for i in range(self.population_size - 2):child_genes.append(self.cross(parent[0], parent[1]))for i in range(2, self.population_size):for j in range(self.gene_number):child_genes[i][j] = self.mutate(child_genes[i][j])adapt_grades = []for i in range(20):adapt_grades.append(self.calculate_adapt_grades(self.get_individual(child_genes[i])))adapt_grades_sorted = sorted(adapt_grades)parent[0] = child_genes[adapt_grades.index(adapt_grades_sorted[0])]   parent[1] = child_genes[adapt_grades.index(adapt_grades_sorted[1])]if generation % 100  == 0:print('第',generation, '代了')if generation in self.generation_save:     print('第%d代的adapt_grades:\t%d' %(generation, self.calculate_adapt_grades(self.get_individual(parent[0]))))save_img = self.get_individual(parent[0])save_img.save(os.path.join(self.save_path, '%d.png' % (generation)))if __name__ == '__main__':        # 运行主函数ideal_path = 'E:/test/qq.png'generation_save = [1, 50, 100, 200, 400, 1000, 2000, 4000, 10000, 20000, 50000, 100000]save_path = 'E:/test'ga = GA(ideal_path, generation_save, save_path)ga.generate()

遗传算法之扇贝的进化(python代码实现)相关推荐

  1. python求函数极值_python 遗传算法求函数极值的实现代码

    废话不多说,大家直接看代码吧! """遗传算法实现求函数极大值-Zjh""" import numpy as np import rando ...

  2. 数学建模——智能优化之遗传算法详解Python代码

    数学建模--智能优化之遗传算法详解Python代码 import numpy as np import matplotlib.pyplot as plt from matplotlib import ...

  3. 进化算法——蛙跳算法Python代码

    进蛙跳算法Python代码 蛙跳算法原理 Python代码 参考文献 蛙跳算法原理 假设种群个数为 c h r o m n u m chromnum chromnum , 分组个数为 g r o u ...

  4. 多目标遗传算法NSGAII求解环境经济调度(Python代码实现)

    目录 1 电力系统环境经济调度数学模型 2 算例--IEEE10节点 2.1 数据​ 2.2 Python代码学习 3 一点拓展知识 1 电力系统环境经济调度数学模型   2 算例--IEEE10节点 ...

  5. 基于遗传算法GA算法优化BP神经网络(Python代码实现)

    一. 概述 BP-GA算法的设计︰基于遗传算法的BP神经网络算法(以下简称BP-GA)就是在BP神经网络的学习过程中,将权重和阀值描述为染色体,并选取适宜的适应函数,然后进行GA迭代,直到某种意义上的 ...

  6. 一文读懂遗传算法工作原理(附Python实现)

    Datawhale干货 选自:AnalyticsVidhya,编译:机器之心 近日,Analyticsvidhya 上发表了一篇题为<Introduction to Genetic Algori ...

  7. 启发式算法Python代码库——scikit-opt

    一个封装了7种启发式算法的 Python 代码库--scikit-opt (差分进化算法.遗传算法.粒子群算法.模拟退火算法.蚁群算法.鱼群算法.免疫优化算法) scikit-opt应用代码 安装 p ...

  8. 基于遗传算法(GA)进化的小游戏

    在这篇文章中,我们模拟了一个包含生物体和食物的环境,生物体为了生存必须尽可能多的消耗食物.在模拟环境中,生物体将由一个简单的.完全连接的神经网络来控制.神经网络的输入是一种标准化值,从-1到+1之间, ...

  9. 深度学习中的正则化技术(附Python代码)

    作者:SHUBHAM JAIN 翻译:和中华 校对:丁楠雅 本文约3500字,建议阅读20分钟. 本文简单介绍了什么是正则化以及在深度学习任务中可以采用哪些正则化技术,并以keras代码具体讲解了一个 ...

最新文章

  1. php worker类,Workerman进阶之Worker类-id属性研究
  2. jqMobi + Android 试手
  3. 【Python CheckiO 题解】Digits Multiplication
  4. mysql中下杠怎么打_怎么打字母下方的短横杠?,下横杠怎么打
  5. rm linux 复制目录,linux学习(四)复制(cp)移动(mv)删除(rm)查找(find)文件、文件夹操作、软硬链接的区别...
  6. 阿里云异构计算产品是如何保障双11业务的
  7. windows配置samba客户端_怎样设置Samba文件服务器以使用Windows客户端
  8. 利用request库请求api
  9. POJ 1830.开关问题(高斯消元)
  10. Atitit.nosql api 标准化 以及nosql数据库的实现模型分类差异
  11. python 实现简单画板_Python图像处理之简单画板实现方法示例
  12. oracle文本类型字段,Oracle字符的5种类型的介绍
  13. 华为服务器基本功能介绍 及服务器BMC提供的基本功能
  14. win7下笔记本电脑给手机开热点
  15. 两个处理IP好用的Python库ipaddr和netaddr
  16. HDFS分布式文件系统的常用命令行操作
  17. Lua - 输出打印table表
  18. 荣耀发布开发者服务平台,智慧生态合作提速
  19. CPU与主板如何搭配---2(转)
  20. 想做游戏开发要深入c/c++还是c#?

热门文章

  1. ICML 2023截稿时间
  2. UHS-II文档学习
  3. 快应用JS自定义月相变化效果
  4. 笔记本电脑很久没用键盘不灵解决方案
  5. SparkStreaming实时数仓——日活
  6. JavaScript实现鼠标点击监听---弹出社会主义核心价值观(面向对象小练习)
  7. 软件需求分析(第九章)
  8. C++ | 大小写字母转换
  9. Visio2010中设置线为直线
  10. 城市物流管理系统的设计与实现