遗传算法的Python实现
- 遗传算法的基本原理
- 优化目标
- 种群建模
- 选择算子
- 交叉算子
- 变异算子
- 个体建模
- 染色体的编码
- 算法控制
- 基本过程
- 保留上一代最优个体
- 算法分析
- 目标函数
- 程序中所用的分析方法
- 参数的影响
- 说明
- 需要注意的问题
- 致谢
- 源码下载
遗传算法是一种启发式算法,在优化问题中应用非常之广泛。由于是一种启发式算法,除了用于优化问题外,也有人将之应用到分类、聚类等问题的求解中,取得了非常好的效果。算法设计和参数选择对遗传算法来说非常重要,为此,笔者对遗传算法进行了实现,并比较了不同参数对问题求解的影响。
遗传算法的基本原理
遗传算法主要是受自然界“优胜劣汰、适者生存”启发而设计出来的一种算法,体现的思想是:如果每次都保留适应生存环境的个体,并使之繁衍生息,将以较大概率得到优质群体。需要厘清以下概念:
- 种群:个体的集合,可认为是全部可行解的一个子集(个人认为种群还应该包括各类算子、规则的定义);
- 个体 :代表着可行解(并不一定是最优解);
- 染色体:由基因组成,实质上是自变量的编码方式,在个体的本质特征;
- 选择:留下适应度高的个体,淘汰适应度低的个体;
- 交叉:两个父代染色体之间进行交叉,可以产生下一代(通常是一对);
- 变异:复制过程中可能会产生偏差,是保证种群多样性的重要操作。
优化目标
此程序主要是针对形如以下的函数优化问题。
种群建模
种群既要包含个体集合,也要设置一系列的算子,以使对每个个体是一视同仁的。因此,种群类Population主要有以下属性:
- size:种群的规模
- individuals:个体的集合
- p_cross:交叉概率
- p_exchange:交换概率
- p_mutate:变异概率
另外,还有一些用于优化计算过程的中间量。
种群还需要有一定的方法: - init_pop:初始化种群
- get_fitness:获取适应度值
- get_all_fitness:获取所有个体的适应度值
- get_best_indv:获取本轮最优个体
- selection:选择算子
- cross:交叉算子
- mutate:变异算子
种群采用的以下三个算子,在实现的过程中参考了gaft工具箱,很易懂,可以直接看代码。
选择算子
采用轮盘赌选择方法选择出一个父本(father),再随便选择一个母本(mother)。
交叉算子
均匀交叉。两个小步骤:一是当随机数小于交叉概率时,再进行交叉操作;二是对于父本和母本中每个基因座,如果随机数大于交换概率,则交换,反之则不交换。
变异算子
采用均匀多点变异方法。对每个基因座均判断,若随机数小于变异概率值,再对基因座取反。
个体建模
个体是基因型、表现型的综合体,并且每个个体都应当对应一个适应度值。因此,类Individual包含有三个属性:
- chromosome:染色体
- decode:解码值(实际值)
- fitness:适应度
怎样由解码值得到适应度值?直接将解码值代入种群的get_fitness函数即可。为什么不将该函数设计到Individual内呢?直观的看,个体是无法决定自身适应度的,在种群内必须有一个统一的规则。
染色体的编码
染色体编码是设计遗传算法的重要内容,一种好的编码不仅能使算法更易实现,还可以让各类操作算子效果更好。
采用二进制编码的益处是更加符合“积木块”假设,可以让好的模式传承下去。
采用浮点数编码的益处是可以有效减少计算量,尤其是对于大型问题,进行科学的浮点数编码,会使性价比更高。
本文采用二进制编码,由于目的是处理具有多个变量的函数优化问题,因此,将染色体建模为多重列表,相应变量依次对应子列表。即:
由于每个自变量的编码长度由取值范围和精度决定,因此,每个子列表的长度也不相同。
这样进行构造的益处是比较形象,容易理解,代价则是处理起来相对麻烦一些。
另外,也可以连接每个自变量的编码成为一个长列表,但是,我认为这样不够清晰。
算法控制
基本过程
选择:从源种群中选择适当的父本和母本(假如种群的规模是,由选择出ceil(
)对父本和母本)
交叉:采用交叉算子对父本和母本进行交叉,生成两个child
变异:对每个child进行变异操作
可见,如果为偶数,由经过以上操作后的种群规模是不变的,即保持规模为
保留上一代最优个体
在实验的过程中发现,强制保留每一代的最优个体到下一代是极为必要的,翻阅了几本书籍,也明确应当保留最优个体,有的文献将之称为精英保留。至于替换哪一个个体,我采用的是随机选择一个个体进行替换
算法分析
目标函数
设置两个目标函数:
其中,。此函数在(1,1)处,取得最大值0
其中,。此函数在(0,0)处,取得最大值0,且此函数有多个局部极值,在求解时极为困难
在测试中发现,对于这两个函数,遗传算法均能在极大的概率上得到最优值或者极其逼近最优值。
程序中所用的分析方法
遗传算法最经常用最优值的收敛曲线来进行分析,对于低维的情况,还可以绘出峰值图,更加直观。
参数的影响
参数对遗传算法的效果影响甚巨,比如,我发现对于大多数问题来说,交叉概率设为0.8的时候,比设为0.9的时候效果要更好一些。
另外,很多时候可以让算法收敛到“最高峰”上,但是,有时候经过几百代也无法到达“峰尖”,这是以后需要研究的问题。
说明
需要注意的问题
在复制个体的时候,一定要严格区分什么时候直接赋值,什么时候采用deepcopy,否则可能会掉到坑里。
致谢
参考了很多GAFT工具箱的内容,特此说明。
源码下载
本文所用源码已上传到遗传算法的python实现。文中和程序中不免有很多不合理、不科学的地方,恳请批评指正。
[相关参考]
[1]: https://github.com/PytLab/gaft [2]: http://python.jobbole.com/88069/