遗传算法详解

这篇具有很好参考价值的文章主要介绍了遗传算法详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1、遗传算法简介

  遗传算法(Genetic Algorithm,GA)最早是由美国的 John holland于20世纪70年代提出,该算法是用于解决最优化问题的一种搜索算法。它是模拟达尔文生物进化论的自然选择和遗传学机理的生物进化过程的计算模型,通过数学的方式,利用计算机仿真运算,将问题的求解过程转换成类似生物进化中的染色体基因的交叉、变异等过程。其本质是一种高效、并行、全局搜索的方法,能在搜索过程中自动获取和积累有关搜索空间的知识,并自适应地控制搜索过程以求得最佳解。

2、问题引入

  遗传算法是用来解决最优化问题的,下面以求一个二元函数在 x∈[−3,3],y∈[−3,3] 范围里的最大值为例子来详细讲解遗传算法的每一步。选取的二元函数为:

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)

  将函数可视化,可视化代码和运行结果如下:

%matplotlib notebook  # 为使结果图能旋转,本次使用的编辑器为jupyter notebook
import numpy as np 
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif']=['SimHei']
plt.rcParams["axes.unicode_minus"] = False

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)

x = np.arange(-3, 3, 0.01) # 可视化的 x 坐标范围
y = np.arange(-3, 3, 0.01) # 可视化的 y 坐标范围

# 生成 x-y 平面采样网格点,方便可视化
X,Y = np.meshgrid(x,y)
Z = F(X,Y) # 计算网格点上的函数值

ax = plt.axes(projection='3d')
ax.plot_surface(X,Y,Z,cmap='rainbow') # 3D 曲面图
ax.view_init(60, -30)
ax.set_xlabel('x')
ax.set_ylabel('y')
plt.show()

遗传算法详解
   可以很容易发现函数在这个局部的最大值大概在当x ≈ 0 , y ≈ 1.5 时,函数值取得最大值,而这里的 x , y 的取值就是我们最后想要得到的结果。

3、相关概念介绍以及实现代码

   为了更能方便理解遗传算法,这里需要引进几组概念。

基因型(genotype):性状染色体的内部表现;
表现型(phenotype):染色体决定的性状的外部表现,或者说,根据基因型形成的个体的外部表现;
进化(evolution):种群逐渐适应生存环境,品质不断得到改良。生物的进化是以种群的形式进行的。
适应度(fitness):度量某个物种对于生存环境的适应程度。
选择(selection): 以一定的概率从种群中选择若干个个体。一般,选择过程是一种基于适应度的优胜劣汰的过程。
复制(reproduction):细胞分裂时,遗传物质DNA通过复制而转移到新产生的细胞中,新细胞就继承了旧细胞的基因。
交叉(crossover):两个染色体的某一相同位置处DNA被切断,前后两串分别交叉组合形成两个新的染色体。也称基因重组或杂交;
变异(mutation):复制时可能(很小的概率)产生某些复制差错,变异产生新的染色体,表现出新的性状。
编码(coding):DNA中遗传信息在一个长链上按一定的模式排列。遗传编码可看作从表现型到基因型的映射。
解码(decoding):基因型到表现型的映射。
个体(individual):指染色体带有特征的实体;
种群(population):个体的集合,该集合内个体数称为种群的大小

3.1 种群和个体

   遗传算法是受进化理论启发而形成的。进化是由种群为单位的,种群在生物学上是指在一定空间范围内同时生活着的同种生物的全部个体。显然要想理解种群的概念,又先得理解个体的概念。在遗传算法里,个体通常为某个问题的一个解,并且该解在计算机中被编码为一个向量。例如,上面的例子中要求最大值,所以该问题的解为一组可能的 ( x , y ) 的取值。比如( x = 2.1 , y = 0.8 ) , ( x = − 1.5 , y = 2.3 ) . . . 就是求最大值问题的一个可能解,也就是遗传算法里的个体,把这样的一组一组的可能解的集合就叫做种群。比如在这个问题中设置100个这样的 x , y 的可能的取值对,那么这100个个体就构成了种群。

3.2 编码、解码与染色体

   在上面个体概念里提到个体(也就是一组可能解)在计算机程序中被编码为一个向量表示,而在我们这个问题中,个体是x , y 的取值,是两个实数,所以问题就可以转化为如何将实数编码为一个向量表示,编码是为了方便后续操作(交叉和变异)。在计算机中,将不同的实数用二进制串来表示就完成了编码,解码即为编码的逆行为。将个体(可能解)编码后的二进制串叫做染色体,染色体(或者有人叫DNA)就是个体(可能解)的二进制编码表示。
  遗传算法中每一条染色体,对应着遗传算法的一个解决方案,一般用适应性函数(fitness function)来衡量这个解决方案的优劣。所以从一个基因组到其解的适应度形成一个映射,可以把遗传算法的过程看作是一个在多元函数里面求最优解的过程。

3.3 适应度和选择

   遗传算法是在得到一个种群后根据适者生存规则把优秀的个体保存下来,同时淘汰掉那些不适应环境的个体。那么,如何评价一个个体对环境的适应度?以求解上述函数最大值为例,这个衡量的标准比较容易制定:函数值的大小,即适应度函数直接返回函数值就行了。即直接用可能解(个体)对应的函数的函数值的大小来评估,这样可能解对应的函数值越大越有可能被保留下来。其求解函数最大值的python代码如下:

def get_fitness(pop): 
    x,y = translateDNA(pop)
	pred = F(x, y)
	return (pred - np.min(pred)) + 1e-3 #减去最小的适应度是为了防止适应度出现负数,通过这一步fitness的范围为[0, np.max(pred)-np.min(pred)],最后在加上一个很小的数防止出现为0的适应度

pred是将可能解带入函数F中得到的预测值,因为后面的选择过程需要根据个体适应度确定每个个体被保留下来的概率,而概率不能是负值,所以所以最后加上了一个很小的正数。有了求最大值的适应度函数,求最小值适应度函数也就容易了,python代码如下:

def get_fitness(pop): 
    x,y = translateDNA(pop)
	pred = F(x, y)
	return (pred - np.max(pred)) + 1e-3 

.
   有了评估的适应度函数,则可以根据适者生存法则将优秀者保留下来了。选择是根据新个体的适应度进行,但同时不意味着完全以适应度高低为导向(选择 k 个适应度最高的个体,容易陷入局部最优解,因为单纯选择适应度高的个体将可能导致算法快速收敛到局部最优解而非全局最优解,我们称之为早熟)作为折中,遗传算法依据原则:适应度越高,被选择的机会越高,而适应度低的,被选择的机会就低。常用的选择方法――轮盘赌(Roulette Wheel Selection)选择法。比如有5条染色体,他们所对应的适应度评分分别为:5,7,10,13,15,所以所以累计总适应度为:
遗传算法详解
则各个个体被选中的概率分别为:
遗传算法详解
可以想象一下,转动轮盘,轮盘停下来的时候,指针会随机地指向某一个个体所代表的区域,很明显,适应度评分越高的个体被选中的概率越大。

   遗传算法的选择在python中使用choice实现,np.random.choice函数是numpy库中用于生成随机样本的函数。它可以从一个给定的数组中随机抽取样本。该函数有三个必选参数: a、Size、p。其中a为数组,size为抽取样本的个数,p为每个元素被抽到的概率。还有其他可选参数如replace、weights、random_state等。代码如下:

def select(pop, fitness):    # nature selection wrt pop's fitness
    idx = np.random.choice(np.arange(POP_SIZE), size=POP_SIZE, replace=True,
                           p=(fitness)/(fitness.sum()) )
    return pop[idx]

其中,参数p描述了从np.arange(POP_SIZE)里选择每一个元素的概率,概率越高约有可能被选中,最后返回被选中的个体即可。

3.4 交叉、变异

   通过选择得到了当前看来“还不错的基因”,但是这并不是最好的基因,需要通过繁殖后代(包含有交叉+变异过程)来产生比当前更好的基因,但是繁殖后代并不能保证每个后代个体的基因都比上一代优秀,这时需要继续通过选择过程来让试应环境的个体保留下来,从而完成进化,不断迭代上面这个过程种群中的个体就会一步一步地进化。具体地繁殖后代过程包括交叉和变异两步。

   交叉: 二进制编码的基因交换过程非常类似高中生物中所讲的同源染色体的联会过程――随机把其中几个位于同一位置的编码进行交换,产生新的个体。每一个个体是由父亲和母亲两个个体繁殖产生,子代个体的DNA(二进制串)获得了一半父亲的DNA,一半母亲的DNA,但是这里的一半并不是真正的一半,这个位置叫做交配点,是随机产生的,可以是染色体的任意位置。
遗传算法详解

   变异: 通过交叉子代获得了一半来自父亲一半来自母亲的DNA,但是子代自身可能发生变异,使得其DNA即不来自父亲,也不来自母亲,在某个位置上发生随机改变,通常就是改变DNA的一个二进制位(0变到1,或者1变到0)。例如下面这串二进制编码:

101101001011001

经过基因突变后,可能变成以下这串新的编码:

001101011011001

   需要说明的是交叉和变异不是必然发生,而是有一定概率发生。先考虑交叉,最坏情况,交叉产生的子代的DNA都比父代要差(这样算法有可能朝着优化的反方向进行,不收敛),如果交叉是有一定概率不发生,那么就能保证子代有一部分基因和当前这一代基因水平一样;而变异本质上是让算法跳出局部最优解,如果变异时常发生,或发生概率太大,那么算法到了最优解时还会不稳定。交叉概率,范围一般是0.6~1,突变常数(又称为变异概率),通常是0.1或者更小。对于这两个概率,我们一般管叫“步长”。一般来说步长越大,开始时进化的速度会比较快,但是后来比较难收敛到精确的点上。而小步长却能较精确的收敛到一个点上。所以很多时候为了加快遗传算法的进化速度,而又能保证后期能够比较精确地收敛到最优解上面,会采取动态改变步长的方法。这个过程与前面介绍的模拟退火过程比较相类似。

交叉和变异的实现代码如下:

def crossover_and_mutation(pop, CROSSOVER_RATE = 0.8):
	new_pop = []
	for father in pop:		#遍历种群中的每一个个体,将该个体作为父亲
		child = father		#孩子先得到父亲的全部基因(这里我把一串二进制串的那些0,1称为基因)
		if np.random.rand() < CROSSOVER_RATE:			#产生子代时不是必然发生交叉,而是以一定的概率发生交叉
			mother = pop[np.random.randint(POP_SIZE)]	#再种群中选择另一个个体,并将该个体作为母亲
			cross_points = np.random.randint(low=0, high=DNA_SIZE*2)	#随机产生交叉的点
			child[cross_points:] = mother[cross_points:]		#孩子得到位于交叉点后的母亲的基因
		mutation(child)	#每个后代有一定的机率发生变异
		new_pop.append(child)

	return new_pop

def mutation(child, MUTATION_RATE=0.003):
	if np.random.rand() < MUTATION_RATE: 				#以MUTATION_RATE的概率进行变异
		mutate_point = np.random.randint(0, DNA_SIZE)	#随机产生一个实数,代表要变异基因的位置
		child[mutate_point] = child[mutate_point]^1 	#将变异点的二进制为反转

3.5 群体进化

   上述步骤即为遗传算法的核心模块,将这些模块在主函数中迭代起来,即实现种群进化,实现代码如下:

	pop = np.random.randint(2, size=(POP_SIZE, DNA_SIZE*2)) #生成种群 matrix (POP_SIZE, DNA_SIZE)
	for _ in range(N_GENERATIONS):	#种群迭代进化N_GENERATIONS代
		crossover_and_mutation(pop, CROSSOVER_RATE)	#种群通过交叉变异产生后代
		fitness = get_fitness(pop)	#对种群中的每个个体进行评估
		pop = select(pop, fitness) 	#选择生成新的种群

4、遗传算法流程

4.1 算法步骤

1.评估每条染色体所对应个体的适应度。
2.遵照适应度越高,选择概率越大的原则,从种群中选择两个个体作为父方和母方。
3.抽取父母双方的染色体,进行交叉,产生子代。
4.对子代的染色体进行变异。
5.重复2,3,4步骤,直到新种群的产生。

选择的作用:优胜劣汰,适者生存;
交叉的作用:保证种群的稳定性,朝着最优解的方向进化;
变异的作用:保证种群的多样性,避免交叉可能产生的局部收敛。

4.2 遗传算法流程图

遗传算法详解
文章来源地址https://www.toymoban.com/news/detail-451176.html

5、完整代码



import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D

DNA_SIZE = 24 #编码长度
POP_SIZE = 200 #种群中个体数量
CROSSOVER_RATE = 0.8 # 发生交叉的概率
MUTATION_RATE = 0.005 # 发生变异的概率
N_GENERATIONS = 50  # 迭代次数
X_BOUND = [-3, 3]
Y_BOUND = [-3, 3]


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)

#---------绘制函数3D图--------------
def plot_3d(ax):
    X = np.linspace(*X_BOUND, 100)
    Y = np.linspace(*Y_BOUND, 100)
    X,Y = np.meshgrid(X, Y)
    Z = F(X,Y)
    ax.plot_surface(X,Y,Z,rstride=1,cstride=1,cmap=cm.coolwarm)
    ax.set_zlim(-10,10)
    ax.set_xlabel('x')
    ax.set_ylabel('y')
    ax.set_zlabel('z')
    plt.pause(3)
    plt.show()


#-----------获取适应度------------
def get_fitness(pop):
    x,y = translateDNA(pop)
    pred = F(x,y)
    return (pred - np.min(pred)) + 1e-3 # 减去最小的适应度是为了防止适应度出现负数,通过这一步fitness的范围为[0, np.max(pred)-np.min(pred)],最后在加上一个很小的数防止出现为0的适应度


#-------------解码---------------
def translateDNA(pop): #pop表示种群矩阵,一行表示一个二进制编码表示的DNA,矩阵的行数为种群数目
    x_pop = pop[:,1::2]#奇数列表示X
    y_pop = pop[:,::2] #偶数列表示y    
    # pop:(POP_SIZE,DNA_SIZE)*(DNA_SIZE,1) --> (POP_SIZE,1)完成解码
    # x_pop.dot(2**np.arange(DNA_SIZE)[::-1]) 矩阵乘法:x_pop*(2**np.arange(DNA_SIZE)[::-1])
    x = x_pop.dot(2**np.arange(DNA_SIZE)[::-1])/float(2**DNA_SIZE-1)*(X_BOUND[1]-X_BOUND[0])+X_BOUND[0]
    y = y_pop.dot(2**np.arange(DNA_SIZE)[::-1])/float(2**DNA_SIZE-1)*(Y_BOUND[1]-Y_BOUND[0])+Y_BOUND[0]
    return x,y

def crossover_and_mutation(pop, CROSSOVER_RATE = 0.8):
    new_pop = []
    for father in pop: #遍历种群中的每一个个体,将该个体作为父亲
        child = father #孩子先得到父亲的全部基因(这里我把一串二进制串的那些0,1称为基因)
        if np.random.rand() < CROSSOVER_RATE: #产生子代时不是必然发生交叉,而是以一定的概率发生交叉
            mother = pop[np.random.randint(POP_SIZE)] #再种群中选择另一个个体,并将该个体作为母亲
            cross_points = np.random.randint(low=0, high=DNA_SIZE*2) #随机产生交叉的点
            child[cross_points:] = mother[cross_points:] #孩子得到位于交叉点后的母亲的基因
        mutation(child) #每个后代有一定的机率发生变异
        new_pop.append(child)
    return new_pop

def mutation(child, MUTATION_RATE=0.003):
    if np.random.rand() < MUTATION_RATE:  #以MUTATION_RATE的概率进行变异 
        mutate_point = np.random.randint(0, DNA_SIZE*2) #随机产生一个实数,代表要变异基因的位置
        child[mutate_point] = child[mutate_point]^1  #将变异点的二进制为反转

#-------选择----------
def select(pop, fitness):    # nature selection wrt pop's fitness
    idx = np.random.choice(np.arange(POP_SIZE), size=POP_SIZE, replace=True,
                           p=(fitness)/(fitness.sum()) )
    
    ,,,
    介绍以下choice方法的参数:
    numpy.random.choice(a, size=None, replace=True, p=None)
    #从a(只要是ndarray都可以,但必须是一维的)中随机抽取数字,并组成指定大小(size)的数组
    #replace:True表示可以取相同数字,False表示不可以取相同数字
    #数组p:与数组a相对应,表示取数组a中每个元素的概率,默认为选取每个元素的概率相同。
    也就是说,从种群中根据适应度函数的大小为挑选概率,挑选POP_SIZE个元素作为下一代
    ,,,
    
    return pop[idx]


def print_info(pop):
    fitness = get_fitness(pop)
    max_fitness_index = np.argmax(fitness)
    print("max_fitness:", fitness[max_fitness_index])
    x,y = translateDNA(pop)
    print("最优的基因型:", pop[max_fitness_index])
    print("(x, y):", (x[max_fitness_index], y[max_fitness_index]))


if __name__ == "__main__":
    fig = plt.figure()
    ax = Axes3D(fig)
    plt.ion()#将画图模式改为交互模式,程序遇到plt.show不会暂停,而是继续执行
    plot_3d(ax)

    pop = np.random.randint(2, size=(POP_SIZE, DNA_SIZE*2)) #matrix (POP_SIZE, DNA_SIZE)
    for _ in range(N_GENERATIONS):#迭代N代
        x,y = translateDNA(pop)
        if 'sca' in locals():
            sca.remove()
        sca = ax.scatter(x, y, F(x,y), c='black', marker='o');plt.show();plt.pause(0.1)
        pop = np.array(crossover_and_mutation(pop, CROSSOVER_RATE))
        #F_values = F(translateDNA(pop)[0], translateDNA(pop)[1])#x, y --> Z matrix
        fitness = get_fitness(pop)
        pop = select(pop, fitness) #选择生成新的种群
    print_info(pop)
    plt.ioff()
    plot_3d(ax)

到了这里,关于遗传算法详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【遗传算法简介】

    遗传算法是一种模拟达尔文生物进化论的自然选择以及遗传学机制的搜索算法,由 John Holland 在20世纪70年代提出。它们在各种搜索、优化和机器学习任务中已被广泛应用。 1. 编码 遗传算法的第一步是将问题的可能解表示为一种称为染色体(或基因)的数据结构。这个过程称

    2024年02月07日
    浏览(22)
  • 基本遗传算法(GA)详解

    遗传算法由John H.Holland教授提出,为一种全局优化算法。它模拟自然进化与遗传理论,通过将优化问题进行转移,从而成功避免了一般优化算法中需要过多考虑的动力学信息问题,在原理上突破了常规的优化算法框架,算法结构较简单、处理信息能力较强,具有很好的鲁棒性

    2024年02月04日
    浏览(55)
  • 遗传算法详解

      遗传算法(Genetic Algorithm,GA)最早是由美国的 John holland于20世纪70年代提出,该算法是用于 解决最优化问题 的一种搜索算法。它是模拟达尔文生物进化论的自然选择和遗传学机理的生物进化过程的计算模型,通过数学的方式,利用计算机仿真运算,将问题的求解过程转

    2024年02月05日
    浏览(67)
  • 详解遗传算法与生产作业调度

    🍎道阻且长,行则将至。🍓 根据遗传学的理论,生物的进化发展来源于三大动力:自然选择、遗传和突变。自然选择就是自然环境对不同表现型生物有不同的影响,使用适应度来度量这种影响,适应度较好的生物个体对环境亲和力较高,有较大的几率可以存活下来,而适应

    2023年04月23日
    浏览(32)
  • 数学建模——遗传算法步骤及程序详解

    遗传算法是一种基于生物染色体遗传时发生交叉、变异的原理,是一种通过模拟自然进化过程,对解集进行优化更新的算法,属于优化算法的一种。   遗传算法是基于生物染色体遗传时发生的原理,那么就要先了解一下生物学关于遗传的基本原理。假设种群为P(t),下一代种

    2024年02月06日
    浏览(36)
  • 【大道至简】机器学习算法之EM算法(Expectation Maximization Algorithm)详解(附代码)---通俗理解EM算法。

    ☕️ 本文来自专栏:大道至简之机器学习系列专栏 🍃本专栏往期文章:逻辑回归(Logistic Regression)详解(附代码)---大道至简之机器学习算法系列——非常通俗易懂!_尚拙谨言的博客-CSDN博客_逻辑回归代码 ❤️各位小伙伴们关注我的大道至简之机器学习系列专栏,一起学习各大

    2024年02月06日
    浏览(34)
  • 机器学习中四类进化算法的详解(遗传算法、差分进化算法、协同进化算法、分布估计算法)

    GA算法原理 首先我们来介绍进化算法的先驱遗传算法,遗传算法(Genetic Algorithm,简称GA)是一种最基本的进化算法,它是模拟达尔文生物进化理论的一种优化模型,最早由J.Holland教授于1975年提出。遗传算法中种群分每个个体都是解空间上的一个可行解,通过模拟生物的进化

    2024年02月09日
    浏览(32)
  • 推荐系统简介+算法详解+项目介绍

    1、推荐系统目的 信息过载 让用户更快更好的获取到自己需要的内容 内容更快更好的推送到喜欢它的用户手中 让网站(平台)更有效的保留用户资源 即好的推荐系统–让三方共赢 2、推荐系统的应用 个性化音乐、电影视频、社交网络、个性化阅读、证券理财、个性化旅游、

    2024年02月06日
    浏览(40)
  • 贪心算法(Greedy Algorithm)

    贪心算法(Greedy Algorithm)是一种解决优化问题的算法策略。在贪心算法中,每一步都会选择当前情况下最优的选择,而不考虑未来的后果。 贪心算法的基本思想是通过局部最优选择达到全局最优。它并不保证一定能得到全局最优解,但在某些情况下可以得到近似最优解或者

    2024年02月09日
    浏览(29)
  • 算法介绍 | 泛洪算法(Flood fill Algorithm)

    漫水填充算法、种子填充算法(Seed Fill) 用于确定连接到多维数组中给定节点的区域,可以用来标记或者分离图像的一部分,实现如Ps中自动选区功能。 顾名思义就像洪水漫过一样,把一块连通的区域填满。 当然水要能漫过需要满足一定的条件,可以理解为满足条件的地方

    2024年02月14日
    浏览(25)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包