引言

遗传算法在我看来是一种调参的时候可以考虑的算法,是一种可以找到全局最优参数的一种方法,当需要调参的数据范围很大的时候,穷举法显然不是一个很好的选择!这里通过一个简单的例子将遗传算法进行实现,以小见大。

介绍

遗传算法通过模拟自然界生物的优胜劣汰进化现象,把需要求解的问题抽象为一个遗传进化问题,把搜索空间映射为遗传空间,把可能的解编码成一个向量(染色体),而向量中的每一个元素则成为基因,通过不断计算各个染色体的适应值,选择最好的染色体,不断选择进化,不断接近我们需要的最优解。

我后面会首先介绍遗传算法的基本运算步骤,并且会解释如何取抽象我们的问题到遗传算法本身中。

遗传算法基本步骤

  1. 选择运算
  2. 交换操作
  3. 变异

选择操作

意义: 从旧的种群中选择适应度高的染色体,为以后的染色体交换,变异产生新的染色体做准备。
这一步是优胜劣汰的具体实现,而选择的方式也有很多,比如:轮转法,精英法等。
具体是个什么意思呢?就是说比如现在你创造了一群人,那么你需要一个方式来决定他们达到什么条件才能够生存下去,如果你设定在团队中前1/n力气大的才能够生存下去,那么你的方式就叫做精英法,只有力气大才能够生存;而如果你设定力气越大,那么生存下去的几率就越大,而不是绝对的力气大才能够生存下去,那么这种就叫做轮转法,相当于你在转转盘选择,占的面积多的(力气大的)自然被选择生存下去的几率就越大!

这个例子的力气大,我们称为适应度函数的目标,你可以决定长得高为适应度函数的目标,那么长得高的自然就是我们后面决定他们是否生存下去的重要指标,而适应度函数就是用已有条件(染色体)评估他力气到底有多大的函数。而这里的一群人就是指的染色体的个数,你可以设定10个人,你也可以设定100个人,你设定的人数越多,那么 染色体也就更加丰富,当然带来的是计算的复杂度。

因此这里我们需要确定的参数:染色体个数 适应度函数 选择策略

还是刚才的例子,我们使用轮转法做一个选择操作:
假定我们设定一群人具体为6人,则染色体我们有6个(假设定这样,不要问为什么一个人就一个染色体…),并且我们通过力气大来决定他们是否生存,力气越大生存下去的几率就越大!

这里染色体的组成是由人的特性组成:如是男是女,是胖是瘦之类的,按照某种映射关系,转换为了当前的二进制。
而适应度函数这里假设也根据某种计算,根据染色体的二进制组成,可以得到该染色体各种特性影响下的力气值。

序号 染色体 适应度值 累计 换算为百分比
1 110100 20 20+0 20/67=0.298=29.8%
2 010010 05 20+5=25 5/67=0.074=7.4%
3 001100 12 25+12=37 12/67=0.179=17.9%
4 110011 30 37+30=67 30/67=0.447=44.7%

解释一下为什么要有累计和百分比这两项,这个主要是为了轮转法编程实现考虑的。如下:


让后续轮转能够依据适应度的面积来选择染色体
因此选择的方式如下:
随机产生一个数N,数的范围是0<N<适应度函数累计最大值(这里是67),然后选择累计值大于等于N的那个染色体,比如随机数为35,那么选择的染色体就是3号,因为3号的累计为37>35,如果随机数是44,那么选择的染色体就是4号。通过这种方式,来选择染色体,直到达到你设定的初始种群大小。


交换操作

交换操作模拟染色体交换,将染色体上面的基因按照一定的规则进行交换,达到染色体创新的目的。
方法:单点交叉,多点交叉。
比如这里用简单的单点交叉,并且位置选择为1/2节点。
被选择染色体:
父染色体1:110 | 100
父染色体2:010 | 010
上面黄色标出来的位置即为交换位置,然后交换:
子染色体1: 110 | 010
子染色体2: 010 | 100

变异

变异操作模拟生物基因的突变.在染色体二进制编码中变异的操作为,1变成0;或0变成1。
突变产生染色体的多样性,避免进化中早期成熟,陷入局部极值点,突变的概率很低。
这个就根据你自己的来定

遗传算法的简单实现

这里我们来实现找到一个函数的最大值。
函数为:y=1/2*x+sin(3x),范围为x∈[1-9]

可以看到该函数会出现多个极大值,也就是我们常说的局部最优点。
因此根据步骤来我们首先要确定他的适应度函数,种群大小以及选择策略。
适应度函数: 因为我们这里是求函数的最大值,因此这里适应度函数的目标就是求最大值,而适应度函数就是函数本身!
种群大小: 这里设置为100
选择策略: 轮转法
确定为以上值后,我们要建立函数x值对应的二进制编码:
注意
解释一下为什么要转换为二进制进行操作:
二级制相对于十进制有更大的搜索范围以及更多的图式,并且操作也更加简单。

我们如果设定求解精度为小数点后4位,则我们需要多少位的二进制才能够表示我们呢?
给出如下公式:
其中l为二进制的位数,m为精度
2l>=(Umax−Umin)∗10m2^l >= (U_{max} - U_{min})*10^m2l>=(Umax​−Umin​)∗10m
因此如果需要小数点后四位,我们至少需要17位的二进制
217=131072>=(9−1)∗104=800002^{17}=131072 >= (9 - 1)*10^4=80000217=131072>=(9−1)∗104=80000
而通过这种表达,我们也可以算出它的精度是多少
这里给出精度计算公式:
精度QQ=(Umax−Umin)/2l−1Q = (U_{max} - U_{min})/2^{l} - 1Q=(Umax​−Umin​)/2l−1
精度结果
Q=(9−1)/217−1=6.103515625e−05Q = (9 - 1)/2^{17} - 1 = 6.103515625e-05Q=(9−1)/217−1=6.103515625e−05
这个精度越小当然越好一些,但是带来的则是庞大的二进制位数,计算起来就很麻烦。

因此我们需要如下步骤来进行我们的算法:

结果分析

这是在计算适应度的时候的一个dataframe,因为适应度函数计算出来有负数的,因此这里采用的做法,是将整体往上抬高(都加上负数最大那个数的绝对值),这是处理适应度函数为负数的一个方法。


这是最后程序选出来的最优点(图片上红色的点),可以看到结果是非常不错的。

下图是种群平均适应随着遗传代数的增加,其值的变化,可以看见在25代左右其实已经开始收敛了。

程序源码

# -*- encoding: utf-8 -*-
'''
@Description: 遗传算法的一个例子
@Date     :2021/10/27 14:57:16
@Author      :willpower
@version      :V1.0
'''
#%%
import numpy as np
from numpy.lib.function_base import average
import pandas  as pd
import matplotlib.pyplot as plt
import random
zhongqun_cnt = 100 # 种群染色体个数
p_jiaohuan = 60    # 交换的概率为百分之60
p_bianyi = 10      # 变异的概率为百分之10
x_min = 1          # 自变量最低为1
x_max = 9          # 自变量最高为9
rotate_cnt = 200  # 遗传代数
#%%
# 定义函数表达式
def calc(x):return 1/2*x+np.sin(3*x)
#%%
# 定义一个映射函数 将规定范围的 10进制映射为2进制
# (不是简单的映射,而是相同拉伸) 这是一个比例问题
def dec2bin(x):return bin(int((2**17)*((x-1)/8)))
# 和上述函数相反
def bin2dec(x):x1 = int(x, 2)return x1/(2**17)*8+1
#%%
# 首先展示整个图形
x = np.linspace(x_min,x_max,100)
y = calc(x)
plt.plot(x, y)
plt.legend()
plt.show()
# %%
# 0-1
# 2^17-9
#随机选择初始轮转法需要使用的zhongqun_cnt个基因
init_population = np.random.uniform(x_min,x_max,zhongqun_cnt)
# %%
# 适应度函数
def fitness(x):return calc(x)
#%%# 选择函数,通过轮转法选出zhongqun_cnt个初始种群
# 输入为随机的上一代,输出为轮转法选出来的基因(10进制)
def select(x):data = pd.DataFrame(x, columns=['ori_data']) data['fitness'] = fitness(data['ori_data'])# 因为适应度有负数,因此我整体往上移动,让其都为正(往上移动最低的那个负数即可)data['fitness_processed'] = data['fitness'] + abs(data['fitness'].min())  # 添加累计 , 注意左闭右开data['leiji'] = [sum(data['fitness_processed'][0:i]) for i in range(1, data.shape[0] + 1 )]print(data) # 开始选择,随机产生范围为0-(zhongqun_cnt-1)号的累计数值的随机数,取# 大于等于随机数的累计数所对应的编号,这样取zhongqun_cnt个select_dict = []while len(select_dict) < data.shape[0]:suijishu = np.random.uniform(data['leiji'][0], data['leiji'][data.shape[0] - 1], 1)[0]print(suijishu)xuanzeshu = data[data['leiji'] >= suijishu]['ori_data'].iloc[0]print(xuanzeshu)select_dict.append(xuanzeshu)return select_dict# 基因交换操作, 这里采用单点交换,因为二进制为17位
# 因此这里随机交换二基因的中间
# 输入为待交换的两个基因,输出为交换完成的两个基因(2进制输入)
def exchangge(x, y, div = 0.5):weishu = len(x) - 2# 减去2是因为0b会占用2个位置percent = int(div * weishu)# 开始组合x1 = x[:2+percent] + y[-(weishu-percent):-1] + y[-1]# print(x1)y1 = y[:2+percent] + x[-(weishu-percent):-1] + x[-1]# print(y1)return x1, y1# 基因变异操作,这里采用随机让第最低取反
# 输入为二进制
#%%
def variation(x, y):x1 = x[:-1] + str((int(x[-1]) + 1)%2)print(x1)y1 = y[:-1] + str((int(y[-1]) + 1)%2)print(y1)return x1, y1# 概率执行函数
def random_run(probability):"""以probability%的概率执行func(*args)"""list = []for i in range(probability):list.append(1)#list中放入probability个1for x in range(100 - probability):list.append(0)#剩下的位置放入0a = random.choice(list)#随机抽取一个return a
#%%
# 主函数
# 确定初始种群
selected = select(init_population)
# 开始进入优胜劣汰30轮
average_genes = []
average_genes.append(fitness(average(selected)))
while rotate_cnt:rotate_cnt -= 1# 概率交换index_x = random.randint(0, 19)index_y = random.randint(0, 19)# 如果抽中了,则进行交换if random_run(p_jiaohuan):x1, y1 = exchangge(dec2bin(selected[index_x]), dec2bin(selected[index_y]))selected[index_x], selected[index_y] = bin2dec(x1), bin2dec(y1)# 概率变异(如果抽中了,则进行变异)index_x = random.randint(0, 19)index_y = random.randint(0, 19)if random_run(p_bianyi):x2, y2 = variation(dec2bin(selected[index_x]), dec2bin(selected[index_y]))  selected[index_x], selected[index_y] = bin2dec(x2), bin2dec(y2)# 再次选择selected = select(selected)average_genes.append(fitness(average(selected)))
#%%
plt.plot(list(range(1, len(average_genes) + 1)),average_genes)# %%
# 将最佳点的位置打印出来看一下
x = np.linspace(x_min,x_max,100)
y = calc(x)
plt.plot(x, y)
plt.plot(average(selected), fitness(average(selected)), marker='o', color='red')
plt.legend()
plt.show()

结语

可能描述的不是很清晰,主要是因为遗传算法可变的东西实在太多了,选择方法、交换方法以及变异方法等等。因此没有绝对的最好,只有你对问题本身的理解越深,你抽象此问题到遗传算法上面表现才越好!还有就是,只有你认真下来将遗传算法简单的实现了,你才会发现它核心的原理其实并不难理解,因此如果要设计此算法,还是动手写一下比较好。

参考

如何通俗易懂地解释遗传算法?有什么例子?
Python:以指定概率执行某个函数。

遗传算法讲解与实现(python)相关推荐

  1. 遗传算法解决旅行商问题(Python版)

    完整代码点这里 遗传算法解决旅行商问题(Python版) 一.问题描述 TSP问题(Travelling Salesman Problem)即旅行商问题,又译为旅行推销员问题.货郎担问题,是数学领域中 ...

  2. 手把手讲解超详细python入门游戏项目‘打外星飞船’(二)

    手把手讲解超详细python入门游戏项目'打外星飞船'(二) 上次我们在(一)中创建了游戏的背景,现在我们这里将要实现用键盘控制飞船的移动.射击子弹,但是在此之前我们还有一个非常重要的部分–重构. 重 ...

  3. 手把手讲解超详细python入门游戏项目‘打外星飞船’(四)

    手把手讲解超详细python入门游戏项目'打外星飞船'(四) 在经过创立屏幕.飞船移动和设置子弹,我们这里开始设置外形人的创建和移动.我们这里主要的任务是:创建一众外星人让它们充满屏幕,让他们向下和两 ...

  4. 手把手讲解超详细python入门游戏项目‘打外星飞船’(五)

    手把手讲解超详细python入门游戏项目'打外星飞船'(五) 这是最后一个项目了,前面我们讲了整个游戏页面的控制.飞船.子弹.外星人的创建,这里我们讨论一下子弹射杀外星人和整个游戏的结束,我们这里的文 ...

  5. 手把手讲解超详细python入门游戏项目‘打外星飞船’(三)

    手把手讲解超详细python入门游戏项目'打外星飞船'(三) 第三部分我们讲解一下飞船需要射出子弹,那么子弹的部分是怎么操作呢?接下来我直接把项目的四个文件展示出来,以注释的形式在旁边讲解.因为有很多 ...

  6. 手把手讲解超详细python入门游戏项目‘打外星飞船’(一)

    手讲解超详细python入门游戏项目'打外星飞船'手把(一) 由于内容比较多,这里会分为五篇文章来讲解,从页面的创建.飞船控制.射击.外星人创建.射杀外星人五片来展开. 做一个窗口和设置响应用户 im ...

  7. 遗传算法解决TSP问题 Python实现【160行以内代码】

    简述 之前通过遗传算法(Genetic Algorithm )+C++实现解决TSP问题 写了一些基本的原理.并且给出了C++版本代码. 相比于近300行的C++程序,Python只用了160行就解决 ...

  8. 粒子群PSO优化算法学习笔记 及其python实现(附讲解如何使用python语言sko.PSO工具包)

    算法描述 粒子群算法思想来源于实际生活中鸟捕食的过程.假设在一个n维的空间中,有一群鸟(m只)在捕食,食物位于n维空间的某个点上,对于第i只鸟某一时刻来说,有两个向量描述,一个是鸟的位置向量,第二个是 ...

  9. python遗传算法_【机器学习】遗传算法(Genetic Algorithm)的Python实现

    本文章用Python实现了基本的优化遗传算法并用类进行了封装 一.遗传算法概述 遗传算法(Genetic Algorithm)是模拟达尔文生物进化论的自然选择和遗传学机理的生物进化过程的计算模型,是一 ...

  10. 遗传算法的概念和python实现

    遗传算法是一个非常经典的智能算法,主要用于解决优化问题.本文主要简单介绍一些原理,同时给出一个基于python实现的,用于解决实数内优化问题的模板. 本文参考: 原理:遗传算法入门详解 - 知乎 简单 ...

最新文章

  1. Keras神经网络集成技术
  2. Linux运维人员-服务器组成硬件基础
  3. 开源许可证 如何工作_开源许可证的工作方式以及如何将其添加到您的项目中...
  4. CVPR 2020录用率十年最低,商汤官宣62篇入选
  5. 一文学会动态规划解题技巧
  6. RNA和机器学习:多维生物标志物的合理设计
  7. c8800 mp4设置
  8. 软件架构设计的6大原则
  9. unity改变图片像素大小_类动森像素画反向转换 (xBR)
  10. python s d是什意思_python里d是什么意思
  11. 日志中每段代码执行时间的和不等于整段代码执行的总时间
  12. java里的日期时间
  13. Atitit 研发团队建设----福利法案--非物质福利与物质福利法案
  14. 小程序和H5 之间的通信
  15. 【VB】中CInt()、Fix()、Int()的区别
  16. 配对碱基链(C语言)
  17. python数据分析与挖掘实战(2)帕累托法则菜品盈利分析与相关性分析
  18. 单核CPU和多核CPU的理解
  19. ECharts学习--调色盘
  20. java 时区转换 转换成东八区 时间

热门文章

  1. vba mysql 3706_Excel、VBA与MySQL交互
  2. 使用C#与三菱PLC通讯
  3. ibm x5服务器系统重装,IBM 3850 X5 Server安装Windows 2003步骤说明
  4. 苹果cms V10模板/MXone Pro自适应影视电影网站模板
  5. 全是宝!20款优质高效的在线协作工具任你挑,就是这么强大!
  6. InnoDB存储引擎:锁
  7. ubuntu下安装tftp
  8. “OneNMP”-超高性价比的实用网管工具
  9. 网页爬虫:零基础用爬虫爬取网页内容
  10. 在谷歌chrome、Firefox等浏览器打开、编辑、保存微软Office、金山WPS文档