目录
-
准备工作
import numpy as np
DNA_SIZE = 10 # DNA length
POP_SIZE = 100 # population size,种群中个体数目
CROSS_RATE = 0.8 # mating probability (DNA crossover),0.8的概率进行交叉配对
MUTATION_RATE = 0.003 # mutation probability,变异强度
N_GENERATIONS = 200 #迭代次数
X_BOUND = [0, 5] # x upper and lower bounds,指定x的取值范围
-
初始化种群
pop = np.random.randint(2, size=(POP_SIZE, DNA_SIZE)) # initialize the pop DNA
#生成一个元素大小在0-2之间但不包括2,1000行10列的矩阵
np.random.randint——————生成矩阵。举个例子:
a = np.random.randint(10, size = (5, 5))
b = np.random.randint(10, size = (5, 3))
print(a)
print(b)
输出结果:
[[5 8 6 3 7]
[4 9 2 7 8]
[6 2 6 6 4]
[0 8 0 6 6]
[5 1 4 6 2]]
[[2 4 7]
[9 5 2]
[9 5 4]
[3 4 2]
[1 5 9]]
-
解码、归一化以及相关知识(逐步解释)
def translateDNA(pop):
return pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) / float(2**DNA_SIZE-1) * X_BOUND[1]
#np.arange(DNA_SIZE) 从0开始,步长为1,输出[0 1 2 3 4 5 6 7 8 9],不包括10
#np.arange(DNA_SIZE)[::-1] 把[0 1 2 3 4 5 6 7 8 9]倒过来,得到[9 ... 0]
#2 ** np.arange(DNA_SIZE)[::-1] [512 256 128 64 32 16 8 4 2 1]
#pop.dot(2 ** np.arange(DNA_SIZE)[::-1])
# pop与[512 256 128 64 32 16 8 4 2 1]点乘,即:计算各个2进制编码的DNA转变为10进制表达
#X_BOUND[1] 5
#float(2**DNA_SIZE-1) * X_BOUND[1] 将上边转化得到DNA10进制表达式,归一化到0-5
a.dot(b) 与 np.dot(a,b)效果相同,都是实现点乘的功能。
np.arange————生成一个数组。举个例子:
x=np.arange(0,np.pi/2,0.1)
y=np.sin(x)
print(x)
print(y)
总代码
import numpy as np
import matplotlib.pyplot as plt
DNA_SIZE = 10 # DNA length
POP_SIZE = 100 # population size,种群中个体数目
CROSS_RATE = 0.8 # mating probability (DNA crossover),0.8的概率进行交叉配对
MUTATION_RATE = 0.003 # mutation probability,变异强度
N_GENERATIONS = 200 #迭代次数
X_BOUND = [0, 5] # x upper and lower bounds,指定x的取值范围
pop = np.random.randint(2, size=(POP_SIZE, DNA_SIZE)) # initialize the pop DNA
#生成一个元素大小在0-2之间但不包括2,1000行10列的矩阵
def translateDNA(pop):
return pop.dot(2 ** np.arange(DNA_SIZE)[::-1]) / float(2**DNA_SIZE-1) * X_BOUND[1]
#np.arange(DNA_SIZE) 从0开始,步长为1,输出[0 1 2 3 4 5 6 7 8 9],不包括10
#np.arange(DNA_SIZE)[::-1] 把[0 1 2 3 4 5 6 7 8 9]倒过来,得到[9 ... 0]
#2 ** np.arange(DNA_SIZE)[::-1] [512 256 128 64 32 16 8 4 2 1]
#pop.dot(2 ** np.arange(DNA_SIZE)[::-1])
# pop与[512 256 128 64 32 16 8 4 2 1]点乘,即:计算各个2进制编码的DNA转变为10进制表达
#X_BOUND[1] 5
#/float(2**DNA_SIZE-1) * X_BOUND[1] /(2^10-1) * 5,将10进制的DNA归一化到0-5(得到表现型)
def F(x):
return np.sin(10*x)*x + np.cos(2*x)*x # 需要求解的函数y=sin(10*x)*x + cos(2*x)*x
# 也相当于把之前的表现型转化为适应度了,然后再通过get_fitness()函数,将适应度转化的都大于0
def get_fitness(pred):
return pred + 1e-3 - np.min(pred)
# 这个是真正的适应度函数!!
# 调用函数的时候pred为F!!
# 经过这一波操作,get_fitness函数输出的值就非负了,后边用pred/max(pred)算每个pred的被选概率
# xe-y 表示有y个0构成的0.000……000x
# 1e-3 即0.001,加一个极小量,防止pred等于0
# pred 可能是随便打的,abcd都行,到时候调用这个函数的时候再往里填数
# np.min(pred) 取一个pred的最小值
def select(pop, fitness): # 选择函数,根据适应度确定每个DNA被选中的概率
idx = np.random.choice(np.arange(POP_SIZE), size=POP_SIZE, replace=True,p=fitness/fitness.sum())
# np.arange(POP_SIZE) 生成一个0-99的数列,arrange生成的是数组,randint生成的是矩阵
# size=100 从前边的数列里取100个数,每个数被选到的概率为p,之前对pred的非负处理在这里就显得很关键了
# replace 表示抽样后是否放回,这里为True表示有放回,那么一个数就有可能被选中多次
# p 就是选它的比例,按比例来选择适应度高的,也会保留一些适应度低的,因为也可能后面产生更好的变异
return pop[idx] #把生成的idx序列返回给pop
#繁衍,交叉父母的基因
def crossover(parent, pop): # mating process (genes crossover)
if np.random.rand() < CROSS_RATE: #np.random.rand() 随机生成一个0-1之间的数
i_ = np.random.randint(0, POP_SIZE, size=1) #给i_随机赋一个0-100间的数
cross_points = np.random.randint(0, 2, size=DNA_SIZE).astype(np.bool)
# np.random.randint(0, 2, size=DNA_SIZE) 得到一个长为10的随机数列,例如:[0 0 1 0 0 1 1 0 0 1]
# np.random.randint(0, 2, size=DNA_SIZE).astype(np.bool) 将得到的随机数列进行布尔化(非0用ture表示,0用false表示),得到[False False True False False True True False False True]
parent[cross_points] = pop[i_, cross_points]
#生成布尔数组可以组合应用多个布尔条件,使用&(和),|(或)之类的布尔算数运算符,python的关键字and和or在布尔型数组中无效
#parent[cross_points] 即在parent列表中取出cross_points为True地方的值!!!!parent列表在哪里生成了??
#【母亲是pop的i_索引行DNA,选出母亲对应在cross_points为TRUE的地方的值】赋给【父亲DNA对应在cross_points选出为TRUE的地方的值】。
return parent
#繁衍,有变异的基因会出现
#将某些 DNA 中的 0 变成 1, 1 变成 0
def mutate(child):
for point in range(DNA_SIZE):
if np.random.rand() < MUTATION_RATE:
child[point] = 1 if child[point] == 0 else 0
return child
plt.ion() # 开启图像交互模式
x = np.linspace(*X_BOUND, 200)
# linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None)
# X_BOUND = [0, 5],要产生200个样本点
# 把[0,5]分成200份,输出一个200个元素的数组。
plt.plot(x, F(x))
# plt.plot()是直接出图像,不需要plt.show()。
# plt.plot(x, y) = 输出一个由(x1,y1)...(x200,y200)组成的图像,即横坐标为x,纵坐标为y。
#接下来的就是遗传算法的主函数
for _ in range(N_GENERATIONS): #迭代次数200次
F_values = F(translateDNA(pop)) #将归一化到0-5的DNA传入到F函数
# 画图
if 'sca' in globals(): sca.remove() #remove():移除列表中某个值s
sca = plt.scatter(translateDNA(pop), F_values, s=200, lw=0, c='red', alpha=0.5)
#plt.scatter() 是一个画图函数,调用方式:plt.scatter(x轴,y轴,s=散点大小,lw=?,c=颜色,alpha: 散点的透明度)。即画一个F_values的函数图像
plt.pause(0.05) #plt.pause() 暂停模式,这里表示输出的每张图显示0.5秒
# GA part (evolution)
fitness = get_fitness(F_values) #计算适应度
print("Most fitted DNA: ", pop[np.argmax(fitness), :])
pop = select(pop, fitness) #这里根据fitness,选出一个新的population
pop_copy = pop.copy()# 备个份
for parent in pop: #这里parent为遍历pop,一次为其中一行,而这里的pop是从原pop中按适应度概率有放回的选出了POP_SIZE行
child = crossover(parent, pop_copy)#繁衍
child = mutate(child) #进行变异
parent[:] = child # 宝宝变大人,[:] 即让 child集合 = parent集合
plt.ioff() #关闭图像交互模式
plt.show() #在plt.show()之前一定不要忘了加plt.ioff(),如果不加,界面会一闪而过,并不会停留。