LinJM @HQU
谈及遗传算法,我首先想到的就是孟德尔的豌豆实验。当然,遗传算法实质上并不能用豌豆实验说明,豌豆实验探讨了分离定律和自由组合定律,而遗传算法所借鉴的并不是这两个定律。遗传算法,简单的讲,就是达尔文的适者生存的原理,当新结果的适应度比原来的适应度高,那么这个结果就保存下来,并遗传给下一代,就是把好的留下来(这个“好的”,“怎么好”,就是我们根据具体情况具体定义的)当然,这里面不仅仅是把好的结果留下来,同时还借鉴了遗传进化里面的染色体交叉和变异的想法。闲话说完,那么咱们就来看看遗传算法比较正式的说法是什么:
遗传算法是计算机科学人工智能领域中用于解决最优化的一种搜索启发式算法,是进化算法的一种。这种启发式通常用来生成有用的解决方案来优化和搜索问题。进化算法最初是借鉴了进化生物学中的一些现象而发展起来的,这些现象包括遗传、突变、自然选择以及杂交等。[1]
Genetic algorithms are a part of evolutionary computing, which is a rapidly growing area of artificial intelligence.As you can guess, genetic algorithms are inspired by Darwin's theory about evolution. Simply said, solution to a problem solved by genetic algorithms is evolved.
——————————————————————————————————————————————
对遗传算法的定义有了初步的了解后,咱们再看一下遗传算法的基本算法
begin initialize theta, P_co, P_mut, L, N-位染色体 do 确定每个染色体的适应度f_i, i=1,…,L 染色体排序 do 选择得分最高的两个染色体 if Rand[0,1) < P_co then 交叉一对随机选择的位 else 以概率P_mut改变每一位;删除父染色体 until N个子代被创建 until 任何染色体的得分都超过theta return 最高适应度的染色体 end
其中,P_co是染色体的交叉率,P_mut是染色体的变异率,theta是适应度的阈值。
总结来说:遗传算法的基本步骤如下:
1) 在一定编码方案下,随机产生一个初始种群;
2) 用相应的解码方法,将编码后的个体转换成问题空间的决策变量,并求得个体的适应值;
3) 按照个体适应值的大小,从种群中选出适应值较大的一些个体构成交配池;
4) 由交叉和变异这两个遗传算子对交配池中的个体进行操作,并形成新一代的种群;
5) 反复执行步骤2~4 ,直至满足收敛判据为止。
使用遗传算法需要决定的运行参数有:编码串长度、种群大小、交叉和变异概率。编码串长度由优化问题所要求的求解精度决定。种群大小表示种群中所含个体的数量,种群较小时,可提高遗传算法的运算速度,但却降低了群体的多样性,可能找不出最优解;种群较大时,又会增加计算量,使遗传算法的运行效率降低。一般取种群数目为20~100。交叉概率控制着交叉操作的频率,由于交叉操作是遗传算法中产生新个体的主要方法,所以交叉概率通常应取较大值;但若过大的话,又可能破坏群体的优良模式。一般取0.4~0.99。变异概率也是影响新个体产生的一个因素,变异概率小,产生新个体少;变异概率太大,又会使遗传算法变成随机搜索。一般取变异概率为0.0001~0.1。遗传算法常采用的收敛判据有:规定遗传代数;连续几次得到的最优个体的适应值没有变化或变化很小等。
编码:
遗传算法不对优化问题的实际决策变量进行操作,所以应用遗传算法首要的问题是通过编码将决策变量表示成串结构数据。这里我们采用最常用的二进制编码方案,即用二进制数构成的符号串来表示一个个体,用下面的encoding 函数来实现编码并产生初始种群:
function [bin2gen ,bits] =encoding(min2var ,max2var ,scale2var ,popsize) bits = ceil (log2((max2var2min2var)./scale2var) ) ; bin2gen = randint (popsize ,sum(bits) ) ;
在上面的代码中, 首先根据各决策变量的下界(min2var)、上界(max2var)及其搜索精度scale2var来确定表示各决策变量的二进制串的长度bits,然后随机产生一个种群大小为popsize的初始种群bin2gen。编码后的实际搜索精度为scale2dec = (max2var2min2var) / (2^bits-1) ,该精度会在解码时用到。
解码
编码后的个体构成的种群bin2gen 必须经过解码,以转换成原问题空间的决策变量构成的种群var2gen ,方能计算相应的适应值。我们用下面的代码实现。
function [ var2gen ,fitness ] = decoding (funname ,bin2gen ,bits ,min2var ,max2var) num2var = length(bits); popsize = size (bin2gen ,1); scale2dec = (max2var2min2var)./(2.^bits-1); bits = cumsum(bits); bits = [0 bits] ; for i = 1 :num2var bin2var{i} = bin2gen( : ,bits(i) + 1 :bits(i + 1) ) ; var{i} = sum(ones (popsize ,1)*2.^(size(bin2var{i},2)-1:-1:0).*bin2var{i} ,2).*scale2dec (i) + min2var (i) ; end var2gen = [var{1 , :} ] ; for i = 1 :popsize fitness (i) = eval ( [funname ,’(var2gen(i , :) ) ’]) ; end
解码函数的关键在于先由二进制数求得对应的十进制数D ,并根据下式求得实际决策变量值X:
X= D ×scale2dec + min2var
选择
选择过程是利用解码后求得的各个体适应值大小,淘汰一些较差的个体而选出一些比较优良的个体,以进行下一步的交叉和变异操作。选择算子的程序如下:
function [ evo2gen ,best2indiv ,max2fitness ] = selection ( old2gen ,fit2 ness) popsize = length(fitness) ; [max2fitness ,index1 ] = max (fitness) ;
[min2fitness , index2 ] =min(fitness) ; best2indiv = old2gen(index1 , :) ; index = [1 :popsize ] ; index(index1) = 0 ; index(index2) = 0 ; index = nonzeros(index) ; evo2gen = old2gen(index , :) ; evo2fitness = fitness(index , :) ; evo2popsize = popsize22 ; ps = evo2fitness/ sum(evo2fitness) ; pscum= cumsum(ps) ; r = rand(1 ,evo2popsize) ; selected = sum( pscum*ones (1 ,evo2popsize) < ones (evo2pop-size ,1)*r) + 1 ; evo2gen = evo2gen(selected , :) ;
在该算子中,采用了最优保存策略和比例选择法相结合的思路,即首先找出当前群体中适应值最高和最低的个体,将最佳个体best2indiv 保留并用其替换掉最差个体。为保证当前最佳个体不被交叉、变异操作所被坏,允许其不参与交叉和变异而直接进入下一代。然后将剩下的个体evo2gen 按比例选择法进行操作。所谓比例选择法,也叫赌轮算法,是指个体被选中的概率与该个体的适应值大小成正比。将这两种方法相结合的目的是:在遗传操作中,不仅能不断提高群体的平均适应值,而且能保证最佳个体的适应值不减小。
交叉
下面采用单点交叉的方法来实现交叉算子,即按选择概率PC 在两两配对的个体编码串cpairs 中随机设置一个交叉点cpoints ,然后在该点相互交换两个配对个体的部分基因,从而形成两个新的个体。交叉算子的程序如下:
function new_gen = crossover (old_gen ,pc) [ nouse ,mating] = sort (rand(size (old_gen ,1) ,1) ) ; mat_gen = old_gen(mating,:) ; pairs = size (mat_gen ,1) / 2 ; bits = size (mat_gen ,2) ; cpairs = rand(pairs ,1) < pc ; cpoints = randint (pairs ,1 ,[1 ,bits]) ; cpoints = cpairs.*cpoints ; for i = 1 :pairs new_gen( [2*i-1 2*i],:) = [mat_gen([2*i-1 2*i] ,1 :cpoints(i)) mat2gen([2*i 2*i-1 ] ,cpoints (i) + 1 :bits) ] ; end
变异
对于二进制的基因串而言,变异操作就是按照变 异概率pm随机选择变异点mpoints ,在变异点处将其位 取反即可。变异算子的实现过程如下:
function new2gen = mutation (old2gen ,pm) mpoints = find(rand(size (old2gen) ) < pm) ; new2gen = old2gen ; new2gen(mpoints) = 12old2gen(mpoints) ;
从上面的代码可以发现,其实遗传算法并不复杂,总共也就那么几步,因此,关键的是我们如何理解GA并应用于实际问题中。
以下是遗传算法的一些要点:
——————————————————————————————————————
——适应度是为当前问题预先定义的数字度量,比如:
- 如果学习任务是在给定一个未知函数的输入输出训练样例后逼近这个函数,适应度可被定义为假设在训练数据上的精度
- 如果是学习下国际象棋的策略,适应度可被定义为该个体在当前群体中与其他个体对弈的获胜率
——————————————————————————————————————
——算法迭代更新一个假设池,这个假设池称为群体
——在每一次迭代中,根据适应度评估群体中的所有成员,然后用概率方法选取适应度最高的个体产生新一代群体
——在被选中的个体中,一部分保持原样地进入下一代群体,其他被用作产生后代个体的基础,其中应用交叉和变异这样的遗传方法
——————————————————————————————————————
复制(replication):直接从当前群体中选择
交叉(crossover):在选中的个体中进行交叉操作
变异(mutation):在新群体上进行变异操作
——————————————————————————————————————
——————————————————————————————————————