【优化算法】Python实现面向对象的遗传算法

news2024/11/15 4:44:34

遗传算法

遗传算法(Genetic Algorithm)属于智能优化算法的一种,本质上是模拟自然界中种群的演化来寻求问题的最优解。与之相似的还有模拟退火、粒子群、蚁群等算法。

在具体介绍遗传算法之前,我们先来了解一些知识🧀

DNA: 携带有合成RNA和蛋白质所必需的遗传信息的生物大分子,是生物体发育和正常运作必不可少的生物大分子。一般情况下,是以双螺旋结构存在。

现实中的DNA由碱基、脱氧核糖和磷酸双分子层组成,两条脱氧核苷酸链通过碱基间的氢键形成的碱基对相连,形成稳定的结构。碱基由四种,腺嘌呤,鸟嘌呤,胸腺嘧啶,胞嘧啶。

那么如何在计算机中对DNA进行编码?也不需要想的过于复杂,我们只需要表达每个位置上的碱基就行啦!譬如,一条DNA链可以写作:012332100123;每个数字对应一个碱基的映射。为了提高运行速度,我们将其以二进制的形式进行简化表达,即一条DNA链可以看做一串二进制文本:01011101010

个体与种群

我们将每条DNA链看做一个个体,实际上,也就是问题的一个解。譬如,我们寻找映射 F ( X , Y ) F(X,Y) F(X,Y)的最优解,其中一个可能的值 X = 6 X=6 X=6,便是一个个体。

种群就是个体的集合。注意,同一种群内部发生信息交换,不同种群之间不会发生信息交换。例如, X X X的全集不会与 Y Y Y的全集发生交换,DNA交换只会发生在 X X X集合或 Y Y Y集合内部。

遗传: 生物的DNA来自于父母,一般情况下由父亲提供X或Y染色体,母亲提供X染色体

假设现在有两个个体:

1001   0001 1001\ 0001 1001 0001 00011001 0001 1001 00011001 分别作为父亲和母亲,生下了一个新的个体,那么该个体的DNA将由父亲和母亲来决定,例如前四位由父亲提供,后四位由母亲提供,那么该子代个体就是:

1001   1001 1001\ 1001 1001 1001

变异: 在DNA的某个位置发生了变化

因为是二进制表达的DNA,那么所谓的变化就是某一位由0到1,或是由1到0

自然选择: 优胜劣汰,将会选择更加适宜的个体

个体的适宜度,实际上也就是满足函数最优解的值。适宜度越高,该个体在下一轮的自然选择中越容易存活,从而保留自身的DNA。


下面我们将来说一下如何实现一个GA算法~

一、创建属于我们的种群

在一切的开始,我们为种群制定一个规则,比方说这个种群的大小,DNA链的长度~

Population_Nums=200 # 种群有200个个体
DNA_Size=16 # DNA链的长度
Range=[[-3,3],[-3,3]] # 自变量的值域范围

# 初始化
import numpy as np 
# pop维度为(n,Population_Nums,DNA_Size)
# 其中n表示有几个种群,也就是自变量
pop=np.random.randint(0,2,size=(len(Range),Population_Nums,DNA_Size))

种群的数量呢决定了收敛的速度,但是也有可能因此陷入局部最优解,并降低运行速度(注意跟收敛速度的区别)

DNA链的长度实际上决定了精度。这句话如何理解呢?我们要来看如何将一串DNA转译成我们需要的信息~

假设有一串DNA链: 10101 10101 10101,我们的映射函数为 F ( X , Y ) F(X,Y) F(X,Y),其中 X X X的值域为 [ − 5 , 5 ] [-5,5] [5,5],想一想如何进行转译呢?

为了方便计算和模拟遗传变异,我们采用了二进制作为个体DNA。而我们想要的结果是十进制,那就需要先将二进制转为十进制! 1 ∗ 2 4 + 0 ∗ 2 3 + 1 ∗ 2 2 + 0 ∗ 2 1 + 1 ∗ 2 0 = 21 1*2^{4}+0*2^3+1*2^2+0*2^1+1*2^0=21 124+023+122+021+120=21,这样就来到了十进制。对于种群的基因,只需要除以一个最大值,即 11111 11111 11111,或者说 2 n 2^n 2n,就可以压缩到区间 [ 0 , 1 ] [0,1] [0,1],然后再通过区间匹配到实际值域区间中。

这段写成代码的话,可以是这样:

def decoding(pop):
    deList=[]
    for idx,i in enumerate(pop):
        deList.append(i.dot(2**np.arange(DNA_Size))/float(2**DNA_Size)*(Range[idx][1]-Range[idx][0])+Range[idx][0])
    return deList

好啦,那么现在我们就需要评估一个个体的适宜度,这也是自然选择中最重要的部分。适宜度越大的个体,越容易在下一轮的选择中存活。

假设函数为:

def F(x,y):
    return 3*(1-x)**2*np.exp(-(x**2)-(y+1)**2)-10*(x/5-x**3-y**5)*np.exp(-x**2-y**2)-1/3**np.exp(-(x+1)**2-y**2)

假设优化目标是求这个函数的极大值,那么我们的适宜度就应该是个体DNA转为十进制编码后,带入函数的结果,这个结果越大,说明适宜度越高。

def fitnetss(pop):
    deList=decoding(pop)
    pred=F(*deList)
    return (pred-pred.min())+1e-3

后面这个1e-3的实际含义是,让每一个个体都有机会,而不是绝对肯定或绝对否定哪个个体。


二、遗传和变异

在遗传部分,我们设置了一个参数,用来控制遗传发生的比例。毕竟有些个体并没有后代~

在变异部分,我们同样也有一个较小的参数,用来控制变异发生的可能性。

def mutation(pop,rate=1e-3):
    # 变异将随机发生
    for i in pop:
        if np.random.rand()<rate:
            # 随机一个个体发生变异
            i[np.random.randint(0,DNA_Size)]^=1

变异的作用是跳出局部最优解,下面是进行变异的三次结果:

(x,y):  -0.03668099860435703 1.499994903802568
(x,y):  -0.013274610833800438 1.6933678801875045
(x,y):  0.05119961805341333 1.4999723732455

而下面是不进行变异的三次结果:

(x,y):  -0.027307929236169315 1.5981612562037268
(x,y):  0.18948037561657305 1.4062327388663727
(x,y):  -0.0962074456338553 1.4998157322296937

可以发现,发生了变异后,结果稳定在[0,1.5],而不是陷入部分最优解。

遗传过程的算法可以描述如下:

  • 遍历种群中的每个个体,并将该个体A作为父母个体
  • 有一定概率该个体可以随机跟种群中的其他个体B发生基因交换(甚至包括它自己,但这对结果并没有影响,只是降低了遗传概率)
  • 发生基因交换时,随机选择DNA的断点,断点前半部分由个体A提供,后半段由个体B提供
def crossover(pop,rate=0.7):
    # 注意这里只与自身种群发生变化
    new_pop=[]
    for idx in pop.shape[0]:
        children=[]
        for father in pop[idx]:
            if np.random.rand()<rate:
                child=father
                mother=pop[idx][np.random.randn(0,Population_Nums)]
                # 随机选择发生互换的碱基对
                choicePoint=np.random.randn(0,DNA_Size)
                child[choicePoint:]=mother[choicePoint:]
             # 发生变异
            children.append(mutation(child))
      
    return chidren

三、自然选择

这部分将会根据个体的适宜度分配权值,决定该个体基因出现在下一轮概率。

def select(pop,fitness):
    pop_s=[]
    for i in pop:
        pop_s.append(i[np.random.choice(np.array(Population_Nums),size=Population_Nums,replace=True,p=(fitness)/(fitness.sum()))])
    return pop_s

四、基于面向对象的遗传算法

现在,我们就要将这些东西封装成一个类啦,提高复用性和稳定性。

首先是构造函数,就是先写入一些常量。

class GA(object):

    def __init__(self,popN=2000,DNA_Size=16,Epochs=500,crossRate=0.8,mutationRate=0.005):
        self.popN=popN # 种群数量
        self.DNA_Size=DNA_Size # DNA长度
        self.Epochs=Epochs # 迭代次数
        self.crossRate=crossRate # 交叉遗传概率
        self.mutationRate=mutationRate  # 变异概率
        self.Range=None # 输入数据的值域
        # 例如:[[-3,3],[2,5],[1,9]] 这表示第一个变量的值域是[-3,3],第二个是[2,5]
        
        self.plot_=[] # 保留每轮的最优值
        self.bestScore=None # 最佳得分
        self.best=None # 最佳个体

然后,需要提供一个输入函数的接口:

   def fit_function(self,f,range):
        self.f=f
        self.Range=range
        # 初始化种群
        self.pop=np.random.randint(0,2,size=(len(range),self.popN,self.DNA_Size))
        self.plot_=[]

解码方法:

   def decoding(self):
        deList = []
        for idx, i in enumerate(self.pop):
            deList.append(
                i.dot(2 ** np.arange(self.DNA_Size)) / float(2 ** self.DNA_Size) * (self.Range[idx][1] - self.Range[idx][0]) + self.Range[idx][
                    0])
        return deList

适应值:

    def fitness(self):
        deList=self.decoding()
        pred=self.f(*deList)
        return (pred-pred.min())+1e-3

变异:

    def mutation(self,pop):
        if np.random.rand()<self.mutationRate:
            pop[np.random.randint(0,self.DNA_Size)]^=1
        return pop

交叉遗传:

    def crossover(self):
        for idx in range(self.pop.shape[0]):
            for _,father in enumerate(self.pop[idx]):
                child = father
                
                if np.random.rand()<self.crossRate:
                    mother=self.pop[idx][np.random.randint(0,self.popN)]
                    crossPoint=np.random.randint(0,self.DNA_Size)
                    child[crossPoint:]=mother[crossPoint:]
                    
                self.pop[idx][_]=self.mutation(child)

自然选择:

    def select(self,fitness):
        pops=[]
        for i in self.pop:
            pops.append(i[np.random.choice(self.popN,size=self.popN,replace=True,p=fitness/(fitness.sum()))])
        return pops

打印信息:

    def getInfo(self):
        print('最优参数为: ',[i for i in self.best])
        print("最优结果为: ",self.bestScore)

提供一个训练接口和绘图接口供使用者调用:

    def train(self,plot=False):
        for _ in range(self.Epochs):
            self.crossover() # 交叉变异

            f=self.fitness() # 计算适宜度
                       
            max_fit = np.argmax(f) # 获取最大适宜度下标
            k=[(i[max_fit].dot(2**np.arange(self.DNA_Size))/float(2**self.DNA_Size))*(self.Range[idx][1]-self.Range[idx][0])+self.Range[idx][0] for idx,i in enumerate(self.pop)] # 获取最佳个体的十进制值
            bs=self.f(*k) # 计算该值的适宜度
            # 请注意,适宜度并不代表函数结果,适宜度是一个相对的值
            
            # 记录全局最优结果
            if self.bestScore==None or bs>self.bestScore:
                self.bestScore=bs
                self.best=k
      
 			self.pop = np.array(self.select(f)) # 自然选择
        
            if plot:
                self.plot_.append(bs)

        self.getInfo()

    def plot(self):
        if self.plot_==[]:
            pass
        plt.plot([i for i in range(self.Epochs)],self.plot_)
        plt.xlabel("Epochs")
        plt.ylabel("BestValue")
        plt.show()

最终的结果如下:

在这里插入图片描述

可以看到在25个Epoch左右就开始收敛了。


完整代码

import numpy as np
import matplotlib.pyplot as plt


class GA(object):

    def __init__(self,popN=2000,DNA_Size=16,Epochs=500,crossRate=0.8,mutationRate=0.005):
        self.popN=popN
        self.DNA_Size=DNA_Size
        self.Epochs=Epochs
        self.crossRate=crossRate
        self.mutationRate=mutationRate
        self.Range=None
        self.plot_=[]
        self.bestScore=None
        self.best=None

    def fit_function(self,f,range):
        self.f=f
        self.Range=range
        self.pop=np.random.randint(0,2,size=(len(range),self.popN,self.DNA_Size))
        self.plot_=[]

    def decoding(self):
        deList = []
        for idx, i in enumerate(self.pop):
            deList.append(
                i.dot(2 ** np.arange(self.DNA_Size)) / float(2 ** self.DNA_Size) * (self.Range[idx][1] - self.Range[idx][0]) + self.Range[idx][
                    0])
        return deList

    def fitness(self):
        deList=self.decoding()
        pred=self.f(*deList)
        return (pred-pred.min())+1e-3

    def mutation(self,pop):
        if np.random.rand()<self.mutationRate:
            pop[np.random.randint(0,self.DNA_Size)]^=1
        return pop

    def crossover(self):
        for idx in range(self.pop.shape[0]):
            for _,father in enumerate(self.pop[idx]):
                child = father
                if np.random.rand()<self.crossRate:
                    mother=self.pop[idx][np.random.randint(0,self.popN)]
                    crossPoint=np.random.randint(0,self.DNA_Size)
                    child[crossPoint:]=mother[crossPoint:]
                self.pop[idx][_]=self.mutation(child)

    def select(self,fitness):
        pops=[]
        for i in self.pop:
            pops.append(i[np.random.choice(self.popN,size=self.popN,replace=True,p=fitness/(fitness.sum()))])
        return pops

    def getInfo(self):
        print('最优参数为: ',[i for i in self.best])
        print("最优结果为: ",self.bestScore)

    def train(self,plot=False):
        for _ in range(self.Epochs):
            self.crossover()

            f=self.fitness()
            max_fit = np.argmax(f)
            k=[(i[max_fit].dot(2**np.arange(self.DNA_Size))/float(2**self.DNA_Size))*(self.Range[idx][1]-self.Range[idx][0])+self.Range[idx][0] for idx,i in enumerate(self.pop)]
            bs=self.f(*k)
            if self.bestScore==None or bs>self.bestScore:
                self.bestScore=bs
                self.best=k
            self.pop = np.array(self.select(f))

            if plot:
                self.plot_.append(bs)

        self.getInfo()
        return self.best

    def plot(self):
        if self.plot_==[]:
            pass
        plt.plot([i for i in range(self.Epochs)],self.plot_)
        plt.xlabel("Epochs")
        plt.ylabel("BestValue")
        plt.show()




if __name__ == '__main__':
    ga=GA(popN=200,DNA_Size=16,Epochs=200)
    
    def F(x, y):
        return 3 * (1 - x) ** 2 * np.exp(-(x ** 2) - (y + 1) ** 2) - 10 * (x / 5 - x ** 3 - y ** 5) * np.exp(
            -x ** 2 - y ** 2) - 1 / 3 ** np.exp(-(x + 1) ** 2 - y ** 2)
    Range = [[-3, 3], [-3, 3]]
    
    ga.fit_function(F,Range)
    ga.train(True)
    ga.plot()


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/935948.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Acwing901】滑雪(记忆化搜索)题目讲解

题目描述 题目分析 样例解释 轨迹如下所示 状态表示 可以用f[i,j]表示从点&#xff08;i&#xff0c;j&#xff09;开始往下滑的最长的滑雪轨迹&#xff0c;那么最终答案就是遍历每一个点的f[i,j]&#xff0c;然后取最大值 状态计算 状态的转移也是非常的简单&#xff0c;…

ip地址查询进行企业网络数据管理

在现代企业中&#xff0c;数据管理变得越来越重要。企业需要了解和控制其网络上的各种数据流动&#xff0c;以保护敏感信息并提高网络安全性。IP地址查询是一种常用的技术&#xff0c;可以帮助企业有效地管理网络数据&#xff0c;并识别潜在的威胁。 IP地址查询是通过查找特定I…

Unix时间戳

江科大学习记录 Unix时间戳 Unix 时间戳&#xff08;Unix Timestamp&#xff09;定义为从UTC/GMT的1970年1月1日0时0分0秒开始所经过的秒数&#xff0c;不考虑闰秒时间戳存储在一个秒计数器中&#xff0c;秒计数器为32位/64位的整型变量世界上所有时区的秒计数器相同&#xf…

无涯教程-Python机器学习 - Based on human supervision函数

Python机器学习 中的 Based on human s - 无涯教程网无涯教程网提供https://www.learnfk.com/python-machine-learning/machine-learning-with-python-based-on-human-supervision.html

〔017〕Stable Diffusion 之 常用模型推荐 篇

✨ 目录 &#x1f388; 模型网站&#x1f388; 仿真系列&#x1f388; 国风系列&#x1f388; 卡通动漫系列&#x1f388; 3D系列&#x1f388; 一些好用的lora模型 &#x1f388; 模型网站 由于现在大模型超级多&#xff0c;导致每种画风的模型太多&#xff0c;那么如何选择最…

Kubernetes对象深入学习之五:TypeMeta无效之谜

欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码)&#xff1a;https://github.com/zq2599/blog_demos 本篇概览 本文是《Kubernetes对象深入学习之五》系列的第五篇&#xff0c;从前文的分析也能看出&#xff0c;代表对象类型的schema.ObjectKind&#xff0c;于…

uview ui 1.x ActonSheet项太多,设置滚动

问题&#xff1a;ActionSheet滚动不了。 使用uview ui &#xff1a;u-action-sheet, 但是item太多&#xff0c;超出屏幕了&#xff0c; 查了一下文档&#xff0c;并没有设置滚动的地方。 官方文档&#xff1a;ActionSheet 操作菜单 | uView - 多平台快速开发的UI框架 - uni-a…

【C语言】程序环境预处理 -- 详解

一、程序的翻译环境和执行环境 在 ANSI C 的任何一种实现中&#xff0c;存在两个不同的环境。 翻译环境&#xff0c;在这个环境中源代码被转换为可执行的机器指令。执行环境&#xff0c;它用于实际执行代码。 1、翻译环境 组成一个程序的每个源文件通过编译过程分别转换成目标代…

如何让qt tableView每个item中个别字用不同颜色显示?

如何让qt tableView每个item中个别字用不同颜色显示&#xff1f; 从上面图片可以看到&#xff0c;Item为红色&#xff0c;数字5为黑色。 要实现在一个控件实现不同颜色&#xff0c;目前想到的只有QTextEdit 。有两种方法&#xff0c;第一种是代理&#xff0c;第二种是通过setI…

python+mysql+前后端分离国内职位数据分析(源码+文档+指导)

系统阐述的是使用国内python职位数据分析系统的设计与实现&#xff0c;对于Python、B/S结构、MySql进行了较为深入的学习与应用。主要针对系统的设计&#xff0c;描述&#xff0c;实现和分析与测试方面来表明开发的过程。开发中使用了 Flask框架和MySql数据库技术搭建系统的整体…

Linux(实操篇二)

Linux实操篇 Linux(实操篇二)1. 常用基本命令1.3 时间日期类1.3.1 date显示当前时间1.3.2 显示非当前时间1.3.3 date设置系统时间1.3.4 cal查看日历 1.4 用户管理命令1.4.1 useradd添加新用户1.4.2 passwd设置用户密码1.4.3 id查看用户是否存在1.4.4 cat /etc/passwd 查看创建了…

【python】python智能停车场数据分析(代码+数据集)【独一无二】

&#x1f449;博__主&#x1f448;&#xff1a;米码收割机 &#x1f449;技__能&#x1f448;&#xff1a;C/Python语言 &#x1f449;公众号&#x1f448;&#xff1a;测试开发自动化【获取源码商业合作】 &#x1f449;荣__誉&#x1f448;&#xff1a;阿里云博客专家博主、5…

TypeError: ‘set‘ object is not subscriptable

问题出现的背景&#xff1a;写了一个python脚本&#xff0c;在脚本里用到了 pyexcel_xlsx 这个包&#xff0c;这个包可以读取excel文件。在本地运行可以运行成功&#xff0c;在Linux服务器上面运行报这个错。两边python都是用到3.8版本的&#xff0c;pyexcel_xlsx 版本也相同…

2023-8-26 字符串哈希

题目链接&#xff1a;字符串哈希 #include <iostream>using namespace std;typedef unsigned long long ULL;const int N 100010, P 131;char str[N]; ULL h[N], p[N];ULL get(int l, int r) {return h[r] - h[l - 1] * p[r - l 1]; }int main() {int n, m;cin >…

3000-6000元优质全单吉他推荐,雅马哈LL16、卡马A1、VEAZEN费森S88和伊斯特曼E1D深度评测对比,哪一款会是你心目中的首选呢?

对于初学新手和进阶的朋友来说&#xff0c;可以入手一把性价比很不错的吉他当然是最理想的&#xff0c;对音质和手感有更高要求的&#xff0c;后期想要演出需求的&#xff0c;不妨考虑全单吉他。下面就给大家推荐四款市面上3000-6000元比较热门值得推荐的全单吉他系列品牌&…

分类预测 | MATLAB实现SSA-CNN-SVM基于麻雀算法优化卷积支持向量机分类预测

分类预测 | MATLAB实现SSA-CNN-SVM基于麻雀算法优化卷积支持向量机分类预测 目录 分类预测 | MATLAB实现SSA-CNN-SVM基于麻雀算法优化卷积支持向量机分类预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 MATLAB实现SSA-CNN-SVM基于麻雀算法优化卷积支持向量机分类预测…

从源码到原理剖析activity核心知识点

如何在onResume方法中获取到View的宽高&#xff1f; 有两种方式&#xff1a;post和addOnGlobalLayoutListener override fun onResume() {super.onResume()Log.e("onresume",tabBottom.width.toString()"--"tabBottom.height.toString())//view.post之所以…

Docker之私有仓库 RegistryHabor

目录 一、Docker私有仓库&#xff08;Registry&#xff09; 1.1 Registry的介绍 二、搭建本地私有仓库 2.1首先下载 registry 镜像 2.2在 daemon.json 文件中添加私有镜像仓库地址 2.3运行 registry 容器 2.4Docker容器的重启策略 2.5为镜像打标签 2.6上传到私有仓库 2…

SpringBoot+MyBatisPlus+MySql+vue2+elementUi的案例、java访问数据库服务、java提供接口服务

文章目录 前言后端关键代码前端关键代码完整代码 前言 1、项目不使用前后端分离。 2、在创建SpringBoot的时候要注意各个插件间的版本问题。 3、后端技术SpringBootMyBatisPlusMySql。 4、前端技术vue2elementUi。 后端关键代码 简单介绍 1、数据库名称ssm_db 2、表名称tbl_bo…

「MySQL-03」用户管理与给用户授权

目录 一、用户管理 1. 用户信息 2. 创建用户 3. 删除用户 4. 修改用户密码 二、给用户授权 0.MySQL数据库提供的权限列表 1. 给用户授权 2. 回收权限 一、用户管理 1. 用户信息 1.0 数据库mysql和user表 安装好 MySQL后&#xff0c;里面会有一个默认的数据库mysql里面有一个u…