【演化(进化)算法】遗传算法原理及python实现_搜索


文章目录

遗传算法

1 达尔文的自然选择学说

遗传算法是以达尔文的自然选择学说为基础发展起来的。自然选择学说包括以下3个方面:

(1)遗传:这是生物的普遍特征,亲代把生物信息交给子代,子代按照所得信息而发育、分化,因而子代总是和亲代具有相同或相似的性状。生物有了这个特征,物种才能稳定存在。

(2)变异:亲代和子代之间以及子代的不同个体之间总是有些差异,这种现象,称为变异。变异是随机发生的,变异的选择和积累是生命多样性的根源。

(3)生存斗争和适者生存:自然选择来自繁殖过剩和生存斗争。由于弱肉强食的生存斗争不断进行,其结果是适者生存,即具有适应性变异的个体被保留下来,不具有适应性变异的个体被淘汰,通过一代代的生存环境的选择作用,演变为新的适应环境的物种。

2 遗传算法概述

遗传算法(genetic algorithm,GA)是一种演化(进化)算法,用于求全局最优解。其基本原理是仿效生物界中的“物竞天(适应度函数)择、适者生存”的演化法则。遗传算法是把问题的参数编码为染色体,再利用迭代的方式进行选择(Selection)、交叉(Crossover)与变异(Mutation) 等运算来交换种群中染色体的信息产生新个体,根据适应度函数的值选择符合优化目标的染色体。

3 遗传算法相关概念

  • 编码(Coding):将一个待求解的问题的实际可行解从其解空间转换到遗传算法所能处理的搜索空间(即个体空间)的过程,就称为编码。如参数值 x = 3 x=3 x=3用二进制表示:0000011。(​​编码是为了交叉变异​​)

  • 解码(Decoding) :解码为编码的逆过程。

  • 染色体/个体(choromosome/Individual) :染色体通常是由一维的串结构数据来表示(如用二进制表示:0101001),串上各个位置对应基因的取值(如染色体0101001上的0和1)。(​​函数的一个解​​)

  • 种群(Population):一定数量的个体组成了种群( population)。种群中个体的数目称为种群大小。(​​函数的多个解​​)

  • 适应度(Fitness):在研究自然界中生物的遗传和进化现象时,生物学家使用适应度这个术语来度量某个物种对于生存环境的适应程度。对生存环境适应程度高的物种将获得更多繁殖机会,而对生存环境适应程度较低的物种,其繁殖的机会就相对较少,甚至逐渐灭绝。种群中各个个体对环境的适应程度叫做适应度。在遗传算法中,一般用适应度函数(Fitness function) 来衡量某一个体的适应度高低(​​适应度用来评价给定解好不好,适应度函数由目标函数组成,用它计算适应度​​)

  • 选择(Selection) 选择基于适者生存原理,是从一个旧种群中选择生命力强的个体产生新种群的过程。是根据适应度选择的。也就是说,具有高适应度的个体将自己的优良基因遗传给下一代。

  • 交叉操作(Crossover)
    选择操作能从旧种群中选择出优秀者,但不能创造新的染色体。而交叉模拟了生物进化过程中的繁殖现象,通过两个染色体的交换组合来产生新的优良品种。它的过程为:将群体内的各个个体随机搭配成对,对每一对个体,以某种概率(称为交叉概率)遵循某一种规则交换它们之间的部分染色体。例如单点交叉:选择一个交叉点,交换他们右边部分:
    【演化(进化)算法】遗传算法原理及python实现_迭代_02

  • 变异操作(Mutation):变异运算用来模拟生物在自然的遗传环境中由于各种偶然因素引起的基因突变,它以很小的概率(变异概率)随机地改变染色体上某些基因的值。例如在染色体以二进制编码的系统中,它可以随机地将染色体的某一个基因由1变为0,或由0变为1。若只有选择和交叉,而没有变异,则无法在初始基因组合以外的空间进行搜索,使进化过程在早期就陷入局部解而进入终止过程,从而影响解的质量。为了在尽可能大的空间中获得质量较高的优化解,必须采用变异操作。

4 遗传算法与求解优化问题

优化问题建模:

(1)确定待优化的目标函数。

(2)确定目标函数的变量参数以及各种约束条件,即确定出遗传算法中的个体和问题的解空间。

遗传算法初始化:

(3)选择编码和解码策略,把参数集合转换到染色体串型结构空间;

(4)定义适应度函数,用于计算适应值;

(5)确定遗传策略,包括选择、交叉、变异操作并确定交叉概率、变异概率、种群大小和最大迭代次数等遗传参数;

遗传算法流程:

(6)随机产生初始化种群;

(7)计算种群中的个体适应度;

(8)判断种群性能是否满足某一指标,或者已达到最大迭代次数,满足则输出最优值,不满足则运用选择、交叉和变异算子作用于种群,形成下一代种群并返回第(7)步。

【演化(进化)算法】遗传算法原理及python实现_搜索_03

5 例子

优化问题建模:

(1)确定待优化的目标函数。

F ( x , y ) = 3 ( − x + 1 ) 2 e − x 2 − ( y + 1 ) 2 − ( − 10 x 3 + 2 x − 10 y 5 ) e − x 2 − y 2 − 3 − e − y 2 − ( x + 1 ) 2 F(x,y)=3 \left(- x + 1\right)^{2} e^{- x^{2} - \left(y + 1\right)^{2}} - \left(- 10 x^{3} + 2 x - 10 y^{5}\right) e^{- x^{2} - y^{2}} - 3^{- e^{- y^{2} - \left(x + 1\right)^{2}}} F(x,y)=3(−x+1)2e−x2−(y+1)2−(−10x3+2x−10y5)e−x2−y2−3−e−y2−(x+1)2

(2)确定目标函数的变量参数以及各种约束条件,即确定出遗传算法中的个体和问题的解空间,得到优化模型。

个体为 ( x , y ) (x,y) (x,y),约束条件为 x , y ∈ [ − 3 , 3 ] x,y\in[-3,3] x,y∈[−3,3]。

得到优化模型:

max ⁡ F ( x , y ) = 3 ( − x + 1 ) 2 e − x 2 − ( y + 1 ) 2 − ( − 10 x 3 + 2 x − 10 y 5 ) e − x 2 − y 2 − 3 − e − y 2 − ( x + 1 ) 2 \max F(x,y)=3 \left(- x + 1\right)^{2} e^{- x^{2} - \left(y + 1\right)^{2}} - \left(- 10 x^{3} + 2 x - 10 y^{5}\right) e^{- x^{2} - y^{2}} - 3^{- e^{- y^{2} - \left(x + 1\right)^{2}}} maxF(x,y)=3(−x+1)2e−x2−(y+1)2−(−10x3+2x−10y5)e−x2−y2−3−e−y2−(x+1)2

s.t. x , y ∈ [ − 3 , 3 ] \text{s.t.}\qquad x,y\in[-3,3] s.t.x,y∈[−3,3]

待优化模型可视化如下:

【演化(进化)算法】遗传算法原理及python实现_迭代_04

遗传算法初始化:

(3)选择编码和解码策略,把参数集合转换到染色体串型结构空间;

– 编码:

常用的编码方式有:二进制编码、浮点数编码等。这里采用二进制编码,将变量参数用二进制字符串表示,二进制编码的长度由所求精度确定。然后将所有变量参数量的二进制编码串连在一起,构成一个染色体。

变量 x x x和 y y y的定义域都为 [ − 3 , 3 ] [-3,3] [−3,3],如果要求编码的精度为 1 0 − 5 10^{-5} 10−5,则需要将 [ − 3 , 3 ] [-3,3] [−3,3]划分成至少 ( 3 − ( − 3 ) / 1 0 − 5 = 600000 (3-(-3)/10^{-5}=600000 (3−(−3)/10−5=600000个等长的小区域,而每个小区域用一个二进制串染色体表示。设染色体的长度为 L L L,则 2 L = 600000 2^L=600000 2L=600000,即:

L = 向 上 取 整 ( log ⁡ 2 600000 ) = 20 L=向上取整(\log_2600000)=20 L=向上取整(log2​600000)=20

因此 x x x可以用二进制串 a 19 a 18 . . . a i . . . a 0 a_{19}a_{18}...a_i...a_0 a19​a18​...ai​...a0​表示,其中 a i ∈ { 0 , 1 } a_i\in\{0,1\} ai​∈{0,1}。同样的, y y y可表示为 a 39 a 38 . . . a i . . . a 20 a_{39}a_{38}...a_i...a_{20} a39​a38​...ai​...a20​表示,其中 a i ∈ { 0 , 1 } a_i\in\{0,1\} ai​∈{0,1}。

将 x , y x,y x,y编码并拼接起来。常见的编码方式为将两个参数对应的染色体拼接起来,如下:

( x , y ) → a 39 a 38 . . . a i . . . a 1 a 0 , a i ∈ { 0 , 1 } (x,y)\rightarrow a_{39}a_{38}...a_i...a_1a_0,a_i\in\{0,1\} (x,y)→a39a38...ai...a1a0,ai∈{0,1}

为了方便交叉的代码编写,本文采用交错拼接:

( x , y ) → a 39 a 19 a 37 a 18 . . . a i . . . a 20 a 0 (x,y)\rightarrow a_{39}a_{19}a_{37}a_{18}...a_i...a_{20}a_0 (x,y)→a39a19a37a18...ai...a20a0

这样仅需选择一个交叉点就能同时对 x , y x,y x,y对应的染色体进行交叉。

– 解码:

x , y x,y x,y的二进制解码公式为( L = 20 L=20 L=20; x , y ∈ [ − 3 , 3 ] : x,y\in [-3,3]: x,y∈[−3,3]: x r i g h t x_{right} xright为3, x l e f t x_{left} xleft为-3, y r i g h t y_{right} yright为3, y l e f t y_{left} yleft为-3):

k = ∑ i = 0 L − 1 a i ⋅ 2 i k=\sum_{i=0}^{L-1}a_i\cdot 2^i k=i=0∑L−1ai⋅2i

x ^ = ( x r i g h t − x l e f t ) ⋅ k 2 L − 1 + x l e f t \hat{x}=\frac{(x_{right}-x_{left})\cdot k}{2^L-1}+x_{left} x^=2L−1(xright−xleft)⋅k+xleft

k = ∑ i = L 2 L − 1 a i ⋅ 2 i k=\sum_{i=L}^{2L-1}a_i\cdot 2^i k=i=L∑2L−1ai⋅2i

y ^ = ( y r i g h t − y l e f t ) ⋅ k 2 L − 1 + y l e f t \hat{y}=\frac{(y_{right}-y_{left})\cdot k}{2^L-1}+y_{left} y^=2L−1(yright−yleft)⋅k+yleft

(4)定义适应度函数,用于计算适应值;

遗传算法的目标是最大化适应度,因此,如果我们我们要最大化目标函数,直接用目标函数作为适应度函数:

F ( x , y ) = 3 ( − x + 1 ) 2 e − x 2 − ( y + 1 ) 2 − ( − 10 x 3 + 2 x − 10 y 5 ) e − x 2 − y 2 − 3 − e − y 2 − ( x + 1 ) 2 F(x,y)=3 \left(- x + 1\right)^{2} e^{- x^{2} - \left(y + 1\right)^{2}} - \left(- 10 x^{3} + 2 x - 10 y^{5}\right) e^{- x^{2} - y^{2}} - 3^{- e^{- y^{2} - \left(x + 1\right)^{2}}} F(x,y)=3(−x+1)2e−x2−(y+1)2−(−10x3+2x−10y5)e−x2−y2−3−e−y2−(x+1)2

为了避免一些运算上的问题,我们可以将 F ( x , y ) F(x,y) F(x,y)减去当前种群中最小的适应度 F m i n F_{min} Fmin​是使得所有适应度都为正,即得到适应度函数为:

F i t n e s s = F ( x , y ) − F m i n + ϵ Fitness=F(x,y)-F_{min}+\epsilon Fitness=F(x,y)−Fmin​+ϵ

ϵ \epsilon ϵ取一个很小的数,加上它是为了防止出现0的适应度。

另外,如果我们要求目标函数的最小值,仅需要将上式取倒数:

F i t n e s s = 1 F ( x , y ) − F m i n + ϵ Fitness=\frac{1}{F(x,y)-F_{min}+\epsilon} Fitness=F(x,y)−Fmin+ϵ1

(5)确定交叉概率、变异概率、种群大小和最大迭代次数等遗传参数并确定遗传策略,包括选择、交叉、变异操作;

– 交叉概率: p c = 0.8 p_c=0.8 pc=0.8

– 变异概率: p m = 0.001 p_m=0.001 pm=0.001

– 种群大小: N = 200 N=200 N=200

– 最大迭代次数: G = 60 G=60 G=60

– 选择操作:

根据个体的适应度确定个体被选择到的概率,适应度越大,被选择到的概率也越大。若某个个体 pop ( i ) \text{pop}(i) pop(i),其适应度 f i f_i fi,则其被选择的概率表示为:

p i = f i ∑ i = 0 N − 1 f i p_i=\frac{f_i}{\sum_{i=0}^{N-1}f_i} pi=∑i=0N−1fifi

确定个体被选择概率后,可以选用的选择算法有:轮盘赌选择法、随机遍历抽样法、局部选择法、截断选择法和锦标赛选择法等。常用轮盘赌选择法

python有一个根据概率随机选择的函数​​np.random.choice()​​,我们可以根据个体被选择概率得到要被选择出来的个体的索引。

– 交叉操作:

对种群中的每个个体(父),从种群中随机选择一个个体(母),依概率 p c p_c pc对他们采用单点交叉(选择一个交叉点,将父交叉点以右的基因替换为母交叉点以右的基因)得到一个新个体(子)。

– 变异操作:

对每个个体,随机选择个体上的一个基因,依概率 p m p_m pm对其进行变异(0变为1,1变为0)。

遗传算法流程:

(6)随机产生初始化种群;

(7)计算种群中的个体适应度;

(8)判断是否已达到最大迭代次数,达到则输出最优值,还没达到则运用选择、交叉和变异算子作用于种群,形成下一代种群并返回第(7)步。

6 python实现

# https://blog.csdn.net/ha_ha_ha233/article/details/91364937
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D

NUM_PARAMETER = 2
CHOROMOSOME_SIZE = 19
POP_SIZE = 200
CROSSOVER_RATE = 0.8
MUTATION_RATE = 0.001
N_GENERATIONS = 60
X_BOUND = [-3, 3] # [x_left,x_right]
Y_BOUND = [-3, 3] # [y_left,y_right]

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)


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 = decoder(pop)
pred = F(x, y)
#fitness=(pred - np.min(pred)) + 1e-3 # 最大
fitness = 1/(pred - np.min(pred) + 1e-3) # 最小
return fitness


def decoder(pop):
x_pop = pop[:, 1::2] # 奇数列表示X
y_pop = pop[:, ::2] # 偶数列表示y

x = x_pop.dot(2 ** np.arange(CHOROMOSOME_SIZE)[::-1]) / float(2 ** CHOROMOSOME_SIZE - 1) * (X_BOUND[1] - X_BOUND[0]) + X_BOUND[0]
y = y_pop.dot(2 ** np.arange(CHOROMOSOME_SIZE)[::-1]) / float(2 ** CHOROMOSOME_SIZE - 1) * (Y_BOUND[1] - Y_BOUND[0]) + Y_BOUND[0]
return x, y


def crossover_and_mutation(pop):
new_pop = []
for father in pop: # 遍历种群中的每一个个体,将该个体作为父亲
child = father
if np.random.rand() < CROSSOVER_RATE: # 以一定的概率发生交叉
mother = pop[np.random.randint(POP_SIZE)] # 在种群中选择另一个个体,并将该个体作为母亲
cross_points = np.random.randint(low=0, high=CHOROMOSOME_SIZE * NUM_PARAMETER) # 随机产生交叉的点
child[cross_points:] = mother[cross_points:] # 孩子得到位于交叉点后的母亲的基因
mutation(child) # 逐个个体交叉变异可以提高程序运行效率
new_pop.append(child)

return new_pop


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


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


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


if __name__ == "__main__":
fig1 = plt.figure()
ax = Axes3D(fig1)
plt.ion() # 将画图模式改为交互模式,程序遇到plt.show不会暂停,而是继续执行
plot_3d(ax)
fitness_list=[]
pop = np.random.randint(2, size=(POP_SIZE, CHOROMOSOME_SIZE * NUM_PARAMETER))
for _ in range(N_GENERATIONS): # 迭代N代
x, y = decoder(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))
fitness = get_fitness(pop)
pop = select(pop, fitness)

print_info(pop)
plt.ioff()
plot_3d(ax)

求最大:

【演化(进化)算法】遗传算法原理及python实现_迭代_05

求最小:

【演化(进化)算法】遗传算法原理及python实现_迭代_06

7 遗传算法特点

(1)遗传算法是对参数的编码进行操作,而非对参数本身,这就是使得我们在优化计算过程中可以借鉴生物学中染色体和基因等概念,模仿自然界中生物的遗传和进化等机理。

(2)遗传算法同时使用多个搜索点的搜索信息。传统的优化方法往往是从解空间的一个初始点开始最优解的迭代搜索过程,单个搜索点所提供的信息不多,搜索效率不高,有时甚至使搜索过程局限于局部最优解而停滞不前。遗传算法从由很多个体组成的一个初始群体开始最优解的搜索过程,而不是从一个单一的个体开始搜索,这是遗传算法所特有的一种隐含并行性,因此遗传算法的搜索效率较高

(3)遗传算法直接以目标函数作为搜索信息。传统的优化算法不仅需要利用目标函数值,而且需要目标函数的导数值等辅助信息才能确定搜索方向。而遗传算法仅使用由目标函数值变换来的适应度函数值,就可以确定进一步的搜索方向和搜索范围,无须目标函数的导数值等其他一些辅助信息。因此,遗传算法可应用于目标函数无法求导数或导数不存在的函数的优化问题,以及组合优化问题等。而且,直接利用目标函数值或个体适应度,也可将搜索范围集中到适应度较高的部分搜索空间中,从而提高搜索效率。

(4)遗传算法使用概率搜索技术。许多传统的优化算法使用的是确定性搜索算法,一个搜索点到另一个搜索点的转移有确定的转移方法和转移关系,这种确定性的搜索方法有可能使得搜索无法达到最优点,因而限制了算法的使用范围。遗传算法的选择、交叉和变异等运算都是以一种概率的方式来进行的,因而遗传算法的搜索过程具有很好的灵活性。随着进化过程的进行,遗传算法新的群体会更多地产生出许多新的优良的个体。理论已经证明,遗传算法在一定条件下以概率1收敛于问题的最优解。

(5)遗传算法在解空间进行高效启发式搜索,而非盲目地穷举或完全随机搜索。

(6)遗传算法对于待寻优的函数基本无限制,它既不要求函数连续,也不要求函数可微,既可以是数学解析式所表示的显函数,又可以是映射矩阵甚至是神经网络的隐函数,因而应用范围较广。

(7)遗传算法具有并行计算的特点,因而可通过大规模并行计算来提高计算速度,适合大规模复杂问题的优化。

参考:

[1] 《智能控制:理论基础、算法设计与应用作者》 作者:刘金琨

十分感谢三连支持!最靓的公式送给最靓的你:

e i π + 1 = 0 e^{i\pi}+1=0 eiπ+1=0