遗传算法入门&python实现

遗传算法是一种比较有(xuan)效(xue)的最优化的方法,下面贴上python的实现代码。本文在具体的概念上将不作涉及,而是尝试讲解遗传算法怎么与具体的问题相结合。

我们以在一个整数区间内寻找一个函数的最大值作为例子,代码中的函数是随便写的。

首先需要确定的是我们的适应度函数到底是什么,即如何衡量种群中每个个体的生存能力。在这里,适应度函数即是我们要求解的函数,每个个体代入所得的函数值即为其生存能力。

#适应度函数
def sol(x):
    return 10+np.exp(x)/1000-4*(x-23)**2

之后把需要确定的一些超参数定义好

同时这里也要想好我们所求解问题与个体基因信息的关系到底是怎样的。比如我们现在求的是[0,31]整数区间内该函数的最大值,那么种群中每个个体所代表的就应该是该区间内的一个整数,并通过代入到sol(x)中计算其适应度。进一步地,我们可以用5位二进制数的形式表示该区间内的所有整数,那么每个个体的基因就可以表示为一个长度为5的0,1数组。在其他更为实际的问题中,这种相对直观并且便利的对应关系更加难找一些。

#用二进制编码存储每一代的信息,遗传算法的一些超参数
iter_num=100  #终止的迭代数
indv_num=10  #每一代个体数量
indv_info=5  #每个个体的基因长度
individuals=getfirstgeneration(indv_num,indv_info) #存储当前种群的基因信息
parents_num=2  #选择父母的对数,小于总数一半
cross_prob=0.8  #交叉概率
mute_prob=0.03  #变异概率

其中getfirstgeneration函数是我专门写的一个用于生成a*b的随机0,1矩阵的函数,作为第一批种群基因信息的初始化

def getfirstgeneration(a,b):
    r=np.random.random((a,b))
    for i in range(a):
        for j in range(b):
            r[i,j]=round(r[i,j])
    return r

同时为了解决二进制数与十进制整数对应的问题,我还写了这个函数,也不知道python到底有没有类似的简单一点的库函数。

def bec2int(x):
    return sum([x[i]*(2**(len(x)-i-1)) for i in range(len(x))]) 

那么好了,现在可以开始进行我们的模拟遗传了

for generation in range(1,iter_num+1):

第一步:计算每个个体的适应度

 fit_func=[]
    for i in range(indv_num):
        fit_func.append(sol(bec2int(individuals[i,:])))

第二步:采用轮盘赌算法选择父母,这里还有其他多种算法

 tempmin=min(fit_func)
    for i in range(indv_num):
        fit_func[i]-=tempmin   
    fit_prob=[]
    for i in range(indv_num):
        fit_prob.append(sum(fit_func[0:i+1])/sum(fit_func))
    parents=np.zeros((2*parents_num,indv_info))
    for i in range(2*parents_num):
        choose=np.random.random()
        if choosefit_prob[j] and choose

轮盘赌算法其实很好理解,在百度上很容易查到相关资料。但在这里的实现我还不太熟悉,因为对于之前定义的适应度函数不能保证其为恒正,所以这里多了一步操作,即将所有个体的适应度减去当中的最小值重新进行运算,这样适应度最小的个体将变为0,基本保证不会被选为父母。但是在具体实验中我遇到了一些问题,这个之后再介绍。

第三步:开始确定下一代种群中的个体,首先储存需要被保留的较强的个体,即淘汰较弱的个体,感觉这个比较好实现一些,不知道有没有什么别的方法

 child=np.zeros((indv_num,indv_info))
    nowat=0
    stay_num=indv_num-2*parents_num
    for i in range(stay_num):
        for j in range(indv_num):
            if fit_func[j]==max(fit_func):
                child[nowat]=individuals[j,:]
                fit_func[j]=0
                nowat+=1
                break

stay_num即需要留下的数量,总数减去父母随繁殖后代的数量。nowat用于记录该确定第几个子代。

第四步:由选择的父母中产生同样数量的子代,用于填补之前所淘汰的个体。这里的原理采取的是单点交叉法,随机产生一个0到4的数字x,然后组合一对父母基因序列中的0:x和x:4。

 for i in range(parents_num):
        temptry=np.random.random()
        if temptry

第五步:最后考虑子代的变异,在变异概率的范围之内,随机选取基因序列中的一点反转。

 for i in range(stay_num,indv_num):
        temptry=np.random.random()
        if(temptry

最终child就是已经确定好的下一个种群,赋值给individuals,开始下一轮的模拟遗传循环

    individuals=child

最后,循环终止于迭代数达到iter_num。当然,终止条件可以设为其他,如出现个体达到你想要的要求。在最后的个体中选取最优的方案。

fit_func=[]
for i in range(indv_num):
    fit_func.append(sol(bec2real(individuals[i,:])))
print(max(fit_func))

其中的max(fit_func)即为所求函数sol(x)的最大值。

比如,我们要求解这个函数:113-23*x+4*(x-23)**2-2*(x-12)**3+0.2*(x-2)**4。经过100次迭代运算后,可以观察到整个种群的平均适应度,即sum(fit_func)/indv_num,是在增大的,而取到最大值的个体早已被种群所包含,并且被一直保留了下来。

遗传算法入门&python实现_第1张图片

接下来谈所遇到的问题:在求解带有三角函数的周期函数中,总会碰到的一个情况就是,在某个迭代时刻内,所有个体的适应度函数是一样的,因此在进行适应度函数负值的纠正时,会导致fit_func中所有的东西都为0,我不得不多加了 if tempmin>max(fit_func):  这个判断。

在求解函数最大值这个问题中,用遗传算法建模还是比较简单的,之后会尝试做一些与神经网络相关的遗传算法优化。




你可能感兴趣的:(AI)