一个例子"入坑"布谷鸟算法(附完整py代码)
布谷鸟是比较新的启发式最优化算法,但其与传统的遗传算法,退火算法等相比,被证明收敛速度更快,计算效率更高!
本文诞生的缘由
由于布谷鸟算法比较新,所以国内外的网上对于该算法的介绍都比较少,虽然算法整体的思想看起来简单,但真正落实到实践时往往发现有些细节还是不甚清楚。令人尴尬的是,我搜索了国内外对于布谷鸟算法的教程,这些教程恰恰点到算法思想上就止住了,并且给出的代码千篇一律地还是Xin-She Yang(布谷鸟算法提出者之一)给出的matlab代码1,对初学者很不友好,于是我就打算写一篇教程,通过详细地举一个例子来带大家入门布谷鸟算法,并且我写了份python代码(可直接运行),需要者可在下面的github链接中自取。
布谷鸟算法思想简介
布谷鸟算法的启发当然来自于布谷鸟,因为布谷鸟这种鸟很有意思,生出来的孩子自己不养,直接被扔到其他鸟的鸟巢中去了,但有时候,这些布谷鸟蛋会被被寄宿的那些鸟妈妈发现,然后就被抛弃,有时候,这些宿主会直接放弃整个鸟巢寻找新住处。然而道高一尺魔高一丈,有些品种的布谷鸟生下来的布谷鸟蛋的颜色能和去寄宿的鸟的鸟蛋颜色很相似,并且布谷鸟的破壳时间往往比那些宿主的鸟蛋早,这样,一旦小布谷鸟破壳,它就会将一些鸟蛋扔出鸟巢去以求获得更多的食物,并且,小布谷鸟能模拟宿主鸟孩子的叫声来骗取更多的食物!简单来说,就是如何更高效地去骗吃骗喝。
图1 寄宿到其他鸟的布谷鸟蛋
这时,心急的朋友可能会问,这和最优化有啥蛋关系呢?如果光让我看到这种自然现象我也很难和最优化联系起来,但有些细心的人做到了。我们现在来简单概括一下他们是咋联系起来的。
- 他们假设最初在可行域内随机生成一组点(布谷鸟)
- 计算这些点的适应值(鸟的健康程度),并记录下最健康鸟的位置及其适应值
- 然后通过某种方式更新这些点的位置(布谷鸟找其他鸟的鸟巢下蛋)
- 这些寄宿到其他鸟的布谷鸟蛋有一定几率被抛弃,这时布谷鸟需要找新的位置来下新的布谷鸟蛋,没被发现的布谷鸟蛋就保持原样(也就是保持每次迭代的点的总数不变)
- 这些布谷鸟蛋成功被孵化然后长大,原有的布谷鸟则会死去,现在评估新的这些点的适应值(新长大的布谷鸟的健康程度),若比原有记录下最好适应值要好则更新最好适应值
- 新的这批布谷鸟从第3步继续迭代,直至满足迭代次数或精度要求
一头雾水?别怕,后面一个例子就懂了!
更新位置的方式
细心的朋友在看上面算法步骤时可能会问,以某种方式更新位置,是啥子方式呢?更新位置的算法同样很重要,它也会关系到最后算法的收敛速度,在上述的算法步骤中,我们在第3步和第4步都需要更新点的位置,现在就来解答这个问题。其实这些更新点的方式说的直白些其实很简单,就是让这些点乱走,当然它有个洋气的名字(random walk),乱走的方式也很简单粗暴,随机生成一个方向和长度,叠加到原有点上就成了新的点,但是需要指出的是,这个随机生成的方向和长度都是有讲究的,有研究发现通过莱维飞行(Levy Flight)的方式能比较有效地去寻找全局最优解而不至于陷入局部最优解中,并且它和布谷鸟算法配合达到了相当不错的效果,接下来就是解答什么是莱维飞行了。
莱维飞行
在介绍莱维飞行相关公式之前,我们先来简单说一下它背后的思想,莱维飞行是由较长时间的短步长和较短时间的长步长组成,说起来很拗口,我们先来看一下它的演示图吧:
图2 莱维飞行动画
从上面的动画中我们可以看到,点大多数时间都只有小距离移动,偶尔会有大距离移动的情况。这和自然界中大多数动物觅食方式方式类似,也就是找到一块区域后细致的查找猎物,如果没找到,就换一片区域找。
那么,该如何实现这种移动方式呢?我们现在假设我们就是捕食者,需要捕猎去了,但尚不清楚猎物在哪,那好,我们先随机选个方向(服从均匀分布,因为在不知道任何信息前提下,对于我们来说各个方向存在猎物的可能性相等),接下来就得确定要走多远了,根据上面的思想,Levy分布要求大概率落在值比较小的地方,而小概率落在值比较大的地方,Mantegna就提出了近似满足这种分布的计算公式(levy分布很难满足,有兴趣的小伙伴可以查阅相关资料):
s=u∣v∣1βs=\frac{u}{\left | v \right |^{\frac{1}{\beta }}}s=∣v∣β1u
(说明:u服从N(0,σu)N(0,\sigma _{u})N(0,σu)正态分布,v服从N(0,σv)N(0,\sigma _{v})N(0,σv)正态分布,而σu={Γ(1+β)sin(πβ2)Γ[(1+β)2β⋅2(β−1)2]}1β\sigma _{u} = \left \{ \frac{\Gamma (1+\beta )\sin (\frac{\pi\beta }{2})}{\Gamma \left [ \frac{(1+\beta )}{2}\beta \cdot2^{\frac{(\beta -1)}{2}} \right ]}\right \}^{\frac{1}{\beta }}σu=⎩⎨⎧Γ[2(1+β)β⋅22(β−1)]Γ(1+β)sin(2πβ)⎭⎬⎫β1,σv=1\sigma _{v}=1σv=1,其中Γ(z)=∫0∞tz−1etdt\Gamma (z)=\int_{0}^{\infty }\frac{t^{z-1}}{e^{t}}dtΓ(z)=∫0∞ettz−1dt,β\betaβ取值最好落在(1,2)上)。上面的公式乍一看十分复杂,但幸运的是,无论是matlab还是python,关于正态分布和Γ\GammaΓ函数都有现成的函数,我们做伸手党即可。而这个公式咋来的便是数学家的事情了,我也不懂啊(ಥ_ಥ),但我们可以编个小程序直观地感受到其生成的步长确实短步长多,长步长少(可见上面的动画)。概括地来讲,实现莱维飞行,一要通过均匀概率分布生成一个方向,二要确认步长!
局部随机行走
我们再来介绍一种随机行走的方法(random walk),但这种方法只适用于局部,很容易陷入局部最优解,优点是比较稳定,因为是局部走嘛。它的计算公式如下:
xit+1=xit+αs⨂H(pa−ϵ)⨂(xjt−xkt)x_{i}^{t+1}=x_{i}^{t}+\alpha s\bigotimes H(p_{a}-\epsilon )\bigotimes (x_{j}^{t}-x_{k}^{t})xit+1=xit+αs⨂H(pa−ϵ)⨂(xjt−xkt)
(说明:xitx_{i}^{t}xit是第i个点t时刻的位置,α\alphaα是步长系数,也就是让步长增量和你问题的规模相匹配,举个例子,你的问题的规模是个位数级别的,你肯定不希望步长增量是十位数级别或者毫米数级别的,这就需要你适当缩小或扩大步长了,H是跃迁函数,也就是x大于等于1时数值为1,否则为0,⨂\bigotimes⨂代表点乘,xjtx_{j}^{t}xjt和xktx_{k}^{t}xkt是t时刻任意选取的两个点)。
相对于莱维飞行的随机性,局部随机行走时已有一定的方向性了,其最大程度地利用已有点的位置信息,新产生的点便是这些已有点的“折中”。
抛出个栗子
看完上面一些理论知识,你可能更晕了,那么,接下来吃下这颗栗子,你可能便会豁然开朗了。
我们要优化的问题是这样的:
3(1−x)2⋅e−x2−(y+1)2−10(x5−x3−y5)⋅e−x2−y2−e−(x+1)2−y233(1-x)^{2}\cdot e^{-x^{2}-(y+1)^{2}}-10(\frac{x}{5}-x^{3}-y^{5})\cdot e^{-x^{2}-y^{2}}-\frac{e^{-(x+1)^{2}-y^2}}{3}3(1−x)2⋅e−x2−(y+1)2−10(5x−x3−y5)⋅e−x2−y2−3e−(x+1)2−y2其中x和y都落在(-3,3)上,求其最大值。
为了简化起见,我们这里只取5个点。
第一步、我们现在先在可行域内随机生成5个点:
第二步、计算它们的适应度(适应值函数我们就取为上面那个要优化的函数):
我们从中挑出最好的并记录下来,我们可以看到,最好的适应值为下标4对应的点,为7.648。
第三步、利用莱维飞行更新点,莱维飞行公式中的β\betaβ我们取为1.5,剩下的把公式敲上去获得步长,但是,这样生成的步长的规格是大于问题的规模的,后面我们需要调参,但这里为了简便我就把我后来调出来的结果摆出来,莱维飞行的步长需要乘以系数0.4才能让莱维飞行不至于走的太放飞自我,也不至于走的太畏畏缩缩(多数情况下,系数取为0.1和0.01)。这里我们还用到了一个小技巧,我们当然是希望上一轮产生的最好点能在下一轮保留下来,所以我们可以在步长前乘以个系数(xi上一轮−xbest上一轮)(x_{i}^{上一轮}-x_{best}^{上一轮})(xi上一轮−xbest上一轮),这里的iii就是步长对应的点下标。当然,有了步长还不够,还得有个步长方向,这个方向我们就用0-1均匀概率分布产生,步长方向(矢量)点乘步长(矢量)就是步长增量。这是莱特飞行更新后的结果:
第四步、接下来,一些布谷鸟蛋可能会被宿主发现并被抛弃,这个抛弃的概率PaP_{a}Pa我们设置为0.25,当有布谷鸟蛋被抛弃时,我们需要寻找新的寄宿地点,而寻找新的寄宿地点的算法就是上面提到的局部随机行走算法,我们可以把α\alphaα这个系数定为1,步长s我们也取为1,后面两个随机选取的点xjt,xktx_{j}^{t},x_{k}^{t}xjt,xkt,我们通过均匀概率分布选出,这里我们不使用跃迁函数H。更新后的结果如下:
第五步、计算这些新点的适应值,并挑出最大值和记录的最好点的适应值比较,若该轮更新得到的最大值更好,则将该轮得到的最大点设为最好点并记录下来。遗憾的是,这里,该轮点的最大值没有比记录的最好点好,所以我们不用更新最好点的值。
第六步、回到上面的第三步,继续循环直至满足迭代次数要求或精度要求。
补充:由于随机行走(莱维飞行是随机行走的一种特殊形式)可能会超出我们的可行域范围,我们可以利用简单的边界条件,即当点超过下边界时,则用下边界的值代替,同理,当点超过上边界时,则用上边界的值代替。
一些参数的建议
布谷鸟算法提出者之一的Xin-She Yang教授试了多组参数,发现种群规模在15到40,布谷鸟蛋被发现的概率pap_{a}pa取0.25时1 2,对于大多数优化问题已足够,并且结果和分析也表明收敛速度在一定程度上对所使用的参数不敏感。这意味着对于任何问题都不需要进行仔细地调整。至于迭代次数,我在实验中发现100次的迭代次数已能很好地找到最优解,但迭代次数往往取决你问题的规模,并没有一组所谓的万能参数。
完整的python实现
在这份代码中,我写了详细的注释,每个函数的功能和输入输出参数都有详细的说明,如果对上面的一些讲解还有些疑惑,可以直接查看代码的相关部分,我想应该很快就能茅塞顿开了。下载地址如下:https://github.com/SJ2050SJ/Optimization_Algorithms(如果对你有帮助的话不要忘点个star哦 ! (^_ ^)
运行结果
上面那个优化函数的图像在这篇文章中可以看到遗传算法(白话文版)。在这次测试中,我们选择种群规模为25,迭代次数为100。由精确分析可知,上述优化问题在可行域内的最大值为8.1062,在[−0.0093,1.5814]T[-0.0093,1.5814]^{T}[−0.0093,1.5814]T上取到,可以看到,我们优化的结果已经很接近精确答案了,并并且每次优化出来的结果很稳定!
参考文献
一个例子"入坑"布谷鸟算法(附完整py代码)的更多相关文章
- 音频降噪算法 附完整C代码
降噪是音频图像算法中的必不可少的. 目的肯定是让图片或语音 更加自然平滑,简而言之,美化. 图像算法和音频算法 都有其共通点. 图像是偏向 空间 处理,例如图片中的某个区域. 图像很多时候是以二维数据 ...
- mser 最大稳定极值区域(文字区域定位)算法 附完整C代码
mser 的全称:Maximally Stable Extremal Regions 第一次听说这个算法时,是来自当时部门的一个同事, 提及到他的项目用它来做文字区域的定位,对这个算法做了一些优化. ...
- 基于RNN的音频降噪算法 (附完整C代码)
前几天无意间看到一个项目rnnoise. 项目地址: https://github.com/xiph/rnnoise 基于RNN的音频降噪算法. 采用的是 GRU/LSTM 模型. 阅读下训练代码,可 ...
- 自动曝光修复算法 附完整C代码
众所周知, 图像方面的3A算法有: AF自动对焦(Automatic Focus)自动对焦即调节摄像头焦距自动得到清晰的图像的过程 AE自动曝光(Automatic Exposure)自动曝光的是为了 ...
- 基于傅里叶变换的音频重采样算法 (附完整c代码)
前面有提到音频采样算法: WebRTC 音频采样算法 附完整C++示例代码 简洁明了的插值音频重采样算法例子 (附完整C代码) 近段时间有不少朋友给我写过邮件,说了一些他们使用的情况和问题. 坦白讲, ...
- 音频自动增益 与 静音检测 算法 附完整C代码
前面分享过一个算法<音频增益响度分析 ReplayGain 附完整C代码示例> 主要用于评估一定长度音频的音量强度, 而分析之后,很多类似的需求,肯定是做音频增益,提高音量诸如此类做法. ...
- 音频自动增益 与 静音检测 算法 附完整C代码【转】
转自:https://www.cnblogs.com/cpuimage/p/8908551.html 前面分享过一个算法<音频增益响度分析 ReplayGain 附完整C代码示例> 主要用 ...
- 磨皮美颜算法 附完整C代码
前言 2017年底时候写了这篇<集 降噪 美颜 虚化 增强 为一体的极速图像润色算法 附Demo程序> 这也算是学习过程中比较有成就感的一个算法. 自2015年做算法开始到今天,还有个把月 ...
- 图片文档倾斜矫正算法 附完整c代码
2年前在学习图像算法的时候看到一个文档倾斜矫正的算法. 也就是说能将一些文档图像进行旋转矫正, 当然这个算法一般用于一些文档扫描软件做后处理 或者用于ocr 文字识别做前处理. 相关的关键词: 抗倾斜 ...
随机推荐
- istio 常见的 10 个异常
总结使用 istio 常见的10个异常: Service 端口命名约束 流控规则下发顺序问题 请求中断分析 sidecar 和 user container 启动顺序 Ingress Gateway ...
- Node.js 从零开发 web server博客项目[安全]
web server博客项目 Node.js 从零开发 web server博客项目[项目介绍] Node.js 从零开发 web server博客项目[接口] Node.js 从零开发 web se ...
- iOS多线程之超实用理论+demo演示(可下载)
目录 背景简介 GCD.OperationQueue 对比 核心理念 区别 GCD 队列 串行队列(Serial Queues) 并发队列(Concurrent Queues) 串行.并发队列对比图 ...
- 有关java反射的几个小方法的作用和区别
1.Class类中 getXXX()和getDeclaredXXX()的作用和区别: 前者获取某个类的所有公共(public)的字段(or方法or构造函数),包括父类.后者获取所有的字段(or方法or ...
- 刷题[网鼎杯 2020 朱雀组]phpweb
解题思路 打开是一个蛮有意思的背景,众生皆懒狗,是自己没错了.源代码看一看,啥都没有.抓个包 诶,一看到func和p两个参数,想到了call_user_func(). 尝试着把date改成system ...
- Ubuntu18.04安装常用软件指南
安装中文版火狐浏览器 第一步:先卸载:sudo apt-get remove firefox第二步:安装:sudo apt-get install firefox第三步:设置成中文:sudo apt- ...
- linux与linux间,互相拷贝文件
直接使用scp命令 和远程Linux主机 进行文件的拷贝 1.可以将远程Linux系统上的文件拷贝到本地计算机 2.也可以将本地计算机上的文件拷贝到远程Linux系统上. 比如:我们要拷贝 ...
- shiro入门学习--使用MD5和salt进行加密|练气后期
写在前面 在上一篇文章<Shiro入门学习---使用自定义Realm完成认证|练气中期>当中,我们学会了使用自定义Realm实现shiro数据源的切换,我们可以切换成从关系数据库如MySQ ...
- Flutter沉浸式状态栏/AppBar导航栏/仿咸鱼底部凸起导航
Flutter中如何实现沉浸式透明Statusbar状态栏效果? 如下图:状态栏是指android手机顶部显示手机状态信息的位置.android 自4.4开始新加入透明状态栏功能,状态栏可以自定义颜色 ...
- tuple的增删改查
dict = {"k1": "v1", "k2": "v2", "k3": "v3&quo ...