[机器学习与深度学习] - No.2 遗传算法原理及简单实现

2024-04-12 14:32

本文主要是介绍[机器学习与深度学习] - No.2 遗传算法原理及简单实现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

遗传算法(Genetic Algorithm),顾名思义,就是模拟物种进化的过程,遵循“物竞天择,适者生存”的原则,随机化搜索的算法。遗传算法模拟种群演化过程,经历“选择”,“基因交叉”,“变异”等过程。遗传算法不保证一定能得到解,如果有解也不保证找到的是最优解,但若干代以后,理想状态下,染色体的适应度可能达到近似最优的状态。

遗传算法的最大优点就是,我们不需要知道怎么去解决一个问题,获得最优解。你需要知道的仅仅是,怎么将解空间中的值进行编码,使得它能能被遗传算法机制所利用,遗传算法会为我们挑选出可能最优的解。

遗传算法组成如下:

  • 将染色体编码
  • 计算种群的适应度函数值
  • 进行选择,交叉,变异,获取新一代的染色体
  • 重复步骤2,直到迭代数或者适应度函数值收敛
1. 编码:

使用遗传算法首先就是要对问题的解,编码成一定形式,才能使用遗传算法。常用的二进制编码方式是二进制编码。例如:我们需要求解以下函数的在[0,9]上的极大值

y=x+10sin(5x)+7cos(4x)x[0,9] y = x + 10 ∗ s i n ( 5 ∗ x ) + 7 ∗ c o s ( 4 ∗ x ) x ∈ [ 0 , 9 ]

假设我们要求精度为0.0001,即小数点后四位,那么我们将[0,9]区间分为 (9 - 0 ) /0.0001 = 90000等分

我们使用二进制编码。我们知道3位二进制编码可以表示 231 2 3 − 1 个数字。由于 216<90000<217 2 16 < 90000 < 2 17 ,所以我们至少需要17位二进制编码来表示我们的解。每个解均被表示成17位的二进制数。

任意一个17位二进制数,可用如下公式解码到[0,9]

 x = 0 + decimal(chromosome)×(9-0)/(2^17-1)

一般化的解码公式:

x = lower_bound + decimal(chromosome)×(upper_bound-lower_bound)/(2^chromosome_size-1)
2. 适应度函数

用于评价某个染色体的适应度,用 f(x) f ( x ) 表示。有时需要区分染色体的适应度函数与问题的目标函数。适应度函数与目标函数是正相关的,可对目标函数作一些变形来得到适应度函数。但是,对于我们这种,求函数极大值的问题,可以将目标函数作为适应度函数。如果一个染色体,目标函数值大,那么它对应的适应度函数值也应该大。

3. 选择

按照一定的策略,选择一些染色体来产生下一代。轮盘法是一种 “比例选择”,也就是个体被选中的概率与其适应度函数值成正比。适应性分数最高的成员不一定能选入下一代,但是它有最大的概率被选中。
这里写图片描述
我们设 {p(i)|i=1..n} { p ( i ) | i = 1.. n } 为个体 i i 被选中的概率,那么显然 in(p(i))=1 。我们使用 S() S ( ) 集合来保存第n次选择的结果,轮盘赌算法的(伪代码)实现如下:

for i = 1:nr = rand()          %产生一个 [0 - 1] 之间的随机数ps = 0              %轮盘初始转动的位置,从0变化到1j = 1while ps < r        %终止条件为轮盘转动的位置超过色子位置ps = ps + p(j)  %轮盘转动j = j + 1ends(i) = j - 1        %轮盘停止时色子停留位置所指示的个体

举个例子:

染色体ABCDEF
种群适应度579121530
种群累计适应度51221334878
选中随机数区间[0 - 0.06][0.06 - 0.15][0.15-0.27][0.27 - 0.42][0.42 - 0.62 ][0.62 - 1]
被选中概率0.060.090.120.150.200.38

可以看出,种群适应度越高的染色体越容易被留下产生下一代

精英(Elitist Strategy)选择:为了防止进化过程中产生的最优解被交叉和变异所破坏,可以将每一代中的最优解原封不动的复制到下一代中。

4. 交叉

我们从刚刚选择的种群中选出 2个染色体,同时生成其值在0到1之间一个随机数,然后根据此数据的值来确定两个染色体是否要进行杂交。一般设置杂交率为0.6,如果该随机数小与0.6,我们就对两个染色体进行杂交。交叉算子分为单点交叉、两点交叉等。在程序中我选择的是两点交叉,随机生成两个在[0,chromosome_length] 之间的随机数,作为基因杂交的位置,将该位置的两个基因进行交换,产生两个子染色体

杂交位置为(3,5)
杂交前
111222333
aaabbbccc
杂交后:
111bbb333
aaa222ccc

5. 变异

遗传算法中的变异运算,是指将个体染色体编码串中的某些位置上的基因值用该位置上其它等位基因来替换,从而形成以给新的个体。在我们程序的视线中,基因变异即将该位置的基因由0变为1,或由1变为0

我们选出任意一个染色体,同时生成其值在0到1之间一个随机数,然后根据此数据的值来确定该染色体是否要进行变异。变异率一般设为0.02

我们以上面提到的函数为例,编写程序,计算极大值:

函数图像:
这里写图片描述
程序运行截图:
这里写图片描述
可以看到,使用scipy计算得到的值为21.4475874266,遗传算法得到的值为21.4475874267,可以看作为近似最优

完整程序:

# -*- coding: utf-8 -*-
# @Author  :yenming
# @Time    :2018/4/10  16:10import numpy as np
import math
from scipy.optimize import fmin,fminbounddef obj_function(x):return x + 10 * math.sin(5*x) -7 * math.cos(4*x)#二进制转十进制
def bin2dec(bin):res = 0max_index = len(bin) - 1for i in range(len(bin)):if bin[i] == 1:res = res + 2**(max_index-i)return res#获取特定精度下,染色体长度
def get_encode_length(low_bound,up_bound,precision): # e.g 0,9,0.01divide = (up_bound - low_bound)/precisionfor i in range(10000):if 2**i < divide < 2**(i + 1):return i+1return -1#将二进制的染色体解码成[low,up]空间内的数
def decode_chromosome(low_bound,up_bound, length, chromosome):return low_bound + bin2dec(chromosome)*(up_bound - low_bound)/(2**length -1)#定义初始染色体
def intial_population(length,population_size):chromosomes = np.zeros((population_size,length),dtype=np.int8)for i in range(population_size):chromosomes[i] = np.random.randint(0,2,length)              #随机数[0,2)之间的整数return chromosomes##########hyperparameter##############
# f(x) = x + 10*sin(5x) -7*cos(4x)
population_size = 500   #种群大小
iselitist = True        #是否精英选择
generations = 1000      #演化多少代
low_bound = 0           #区间下界
up_bound = 9            #区间上界
precision = 0.0000001      #精度
chromosome_length = get_encode_length(low_bound,up_bound,precision)  #染色体长度
populations = intial_population(chromosome_length,population_size)   #初始种群
best_fit = 0            #获取到的最大的值
best_generation = 0     #获取到最大值的代数
best_chromosome = [0 for x in range(population_size)]               #获取到最大值的染色体
fitness_average = [0 for x in range(generations)]                   #种群平均适应度
cross_rate = 0.6                                                    #基因交叉率
mutate_rate = 0.01                                                  #基因变异率
#######################################计算种群中每个染色体的适应度函数值(在这个问题中,适应度函数就是目标函数)
def fitness(populations):fitness_val = [0 for x in range(population_size)]for i in range(population_size):fitness_val[i] = obj_function(decode_chromosome(low_bound,up_bound,chromosome_length,populations[i]))return fitness_val#对种群染色体根据适应度函数进行排序,适应度函数值高的在最后
def rank(fitness_val,populations,cur_generation):global best_fit,best_generation,best_chromosomeglobal fitness_averagefitness_sum = [0 for x in range(len(populations))]              #初始化种群累计适应度#population_size,length = populations.shapefor i in range(len(populations)):                               #冒泡排序按照种群适应度从小到大min_index = ifor j in range(i+1,population_size):if fitness_val[j] < fitness_val[min_index]:min_index = jif min_index!=i:tmp = fitness_val[i]fitness_val[i] = fitness_val[min_index]fitness_val[min_index] = tmptmp_list = np.zeros(chromosome_length)for k in range(chromosome_length):tmp_list[k] = populations[i][k]populations[i][k] = populations[min_index][k]populations[min_index][k] = tmp_list[k]#########种群适应度从小到大排序完毕#########for l in range(len(populations)):                               #获取种群累计适应度if l == 1:fitness_sum[l] = fitness_val[l]else:fitness_sum[l] = fitness_val[l] + fitness_val[l-1]fitness_average[cur_generation] = fitness_sum[-1]/population_size   #每一代的平均适应度,在这个算法程序中没有使用到,仅作记录if fitness_val[-1] > best_fit:                                  #更新最佳适应度及其对应的染色体best_fit = fitness_val[-1]best_generation = cur_generationfor m in range(chromosome_length):best_chromosome[m] = populations[-1][m]return fitness_sum#根据当前种群,按照轮盘法选择新一代染色体
def select(populations,fitness_sum,iselitist): #轮盘选择法,实现过程可看为二分查找population_new = np.zeros((population_size, chromosome_length), dtype=np.int8)for i in range(population_size):rnd = np.random.rand()*fitness_sum[-1]first = 0last = population_size-1mid = (first+last)//2idx = -1while first <= last:if rnd >fitness_sum[mid]:first = midelif rnd < fitness_sum[mid]:last = midelse:idx = midbreakif last - first == 1:idx = lastbreakmid = (first + last) // 2for j in range(chromosome_length):population_new[i][j] = populations[idx][j]if iselitist == True: #是否精英选择,精英选择强制保留最后一个染色体(适应度函数值最高)p = population_size - 1else:p = population_sizefor k in range(p):for l in range(chromosome_length):populations[k][l] = populations[k][l]#基因交叉
def crossover(populations):for i in range(0,population_size,2):rnd = np.random.rand()if rnd < cross_rate:rnd1 = int(math.floor(np.random.rand()*chromosome_length))rnd2 = int(math.floor(np.random.rand()*chromosome_length))else:continueif rnd1 <= rnd2:cross_position1 = rnd1   #这里我选择了一个基因片段,进行两点的交叉cross_position2 = rnd2else:cross_position1 = rnd2cross_position2 = rnd1for j in range(cross_position1,cross_position2):tmp = populations[i][j]populations[i][j] = populations[i+1][j]populations[i+1][j] = tmp
#基因变异
def mutation(populations):for i in range(population_size):rnd = np.random.rand()if rnd < mutate_rate:mutate_position = int(math.floor(np.random.rand()*chromosome_length))else:continuepopulations[i][mutate_position] = 1 - populations[i][mutate_position]#演化generations代
for g in range(generations):print("generation {} ".format(g))fitness_val = fitness(populations)fitness_sum = rank(fitness_val,populations,g)select(populations,fitness_sum,iselitist)crossover(populations)mutation(populations)print("best chromosome", best_chromosome)print("best_generation", best_generation)print("best_fit", best_fit)print("####################Done######################")
print("best chromosome",best_chromosome)
print("best_generation",best_generation)
print("best_fit",best_fit)print("####################Actual###################")def func(x):#使用fmincound 将函数取负,求最小值return -obj_function(x)min_global=fminbound(func,0,9)#这个区域的最小值
print("The actual max value",-func(min_global))
x = [0,1,2,3,4,5,6,7,8,9]
y = [i + 10 * np.sin(i*5) -7 * np.cos(i*4) for i in x]
import matplotlib.pyplot as plt
plt.plot(x,y)
plt.show()

参考文章:
https://www.zhihu.com/question/23293449
http://blog.jobbole.com/110913/
https://blog.csdn.net/zzwu/article/details/561620

这篇关于[机器学习与深度学习] - No.2 遗传算法原理及简单实现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/897398

相关文章

python设置环境变量路径实现过程

《python设置环境变量路径实现过程》本文介绍设置Python路径的多种方法:临时设置(Windows用`set`,Linux/macOS用`export`)、永久设置(系统属性或shell配置文件... 目录设置python路径的方法临时设置环境变量(适用于当前会话)永久设置环境变量(Windows系统

深度解析Nginx日志分析与499状态码问题解决

《深度解析Nginx日志分析与499状态码问题解决》在Web服务器运维和性能优化过程中,Nginx日志是排查问题的重要依据,本文将围绕Nginx日志分析、499状态码的成因、排查方法及解决方案展开讨论... 目录前言1. Nginx日志基础1.1 Nginx日志存放位置1.2 Nginx日志格式2. 499

Python对接支付宝支付之使用AliPay实现的详细操作指南

《Python对接支付宝支付之使用AliPay实现的详细操作指南》支付宝没有提供PythonSDK,但是强大的github就有提供python-alipay-sdk,封装里很多复杂操作,使用这个我们就... 目录一、引言二、准备工作2.1 支付宝开放平台入驻与应用创建2.2 密钥生成与配置2.3 安装ali

Spring Security 单点登录与自动登录机制的实现原理

《SpringSecurity单点登录与自动登录机制的实现原理》本文探讨SpringSecurity实现单点登录(SSO)与自动登录机制,涵盖JWT跨系统认证、RememberMe持久化Token... 目录一、核心概念解析1.1 单点登录(SSO)1.2 自动登录(Remember Me)二、代码分析三、

PyCharm中配置PyQt的实现步骤

《PyCharm中配置PyQt的实现步骤》PyCharm是JetBrains推出的一款强大的PythonIDE,结合PyQt可以进行pythion高效开发桌面GUI应用程序,本文就来介绍一下PyCha... 目录1. 安装China编程PyQt1.PyQt 核心组件2. 基础 PyQt 应用程序结构3. 使用 Q

Python实现批量提取BLF文件时间戳

《Python实现批量提取BLF文件时间戳》BLF(BinaryLoggingFormat)作为Vector公司推出的CAN总线数据记录格式,被广泛用于存储车辆通信数据,本文将使用Python轻松提取... 目录一、为什么需要批量处理 BLF 文件二、核心代码解析:从文件遍历到数据导出1. 环境准备与依赖库

linux下shell脚本启动jar包实现过程

《linux下shell脚本启动jar包实现过程》确保APP_NAME和LOG_FILE位于目录内,首次启动前需手动创建log文件夹,否则报错,此为个人经验,供参考,欢迎支持脚本之家... 目录linux下shell脚本启动jar包样例1样例2总结linux下shell脚本启动jar包样例1#!/bin

go动态限制并发数量的实现示例

《go动态限制并发数量的实现示例》本文主要介绍了Go并发控制方法,通过带缓冲通道和第三方库实现并发数量限制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录带有缓冲大小的通道使用第三方库其他控制并发的方法因为go从语言层面支持并发,所以面试百分百会问到

Go语言并发之通知退出机制的实现

《Go语言并发之通知退出机制的实现》本文主要介绍了Go语言并发之通知退出机制的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1、通知退出机制1.1 进程/main函数退出1.2 通过channel退出1.3 通过cont

Python实现PDF按页分割的技术指南

《Python实现PDF按页分割的技术指南》PDF文件处理是日常工作中的常见需求,特别是当我们需要将大型PDF文档拆分为多个部分时,下面我们就来看看如何使用Python创建一个灵活的PDF分割工具吧... 目录需求分析技术方案工具选择安装依赖完整代码实现使用说明基本用法示例命令输出示例技术亮点实际应用场景扩