做鲸鱼优化算法改进实验那阵子,我拿到的第一版测试效果图其实很难看。标准WOA在Rastrigin函数上大约50次迭代就把收敛曲线跑平了,后面四百多次迭代几乎全程躺平,种群位置更新只在局部小范围抖动——这是典型的早熟收敛,也是所有元启发式算法最怕的结局。把这个问题拍平的手段,就是我们常说的基于混合策略改进的鲸鱼优化算法(HWOA)。这篇东西不是教科书,是我从设计、编码、跑基准测试到整理测试效果图的完整过程记录。正在做智能优化算法对比研究、写毕业论文实验的同学,可以直接把里面的改进框架和测试口径搬过去参考;如果你只是想看懂别人论文里的收敛曲线、箱线图和显著性检验表,后面几节也把读图方法讲透了。
1. 标准鲸鱼优化算法:三条位置更新公式和它们埋的雷
1.1 三套机制的设计逻辑
WOA(Whale Optimization Algorithm)是Mirjalili和Lewis在2016年提出的群智能优化算法,模仿座头鲸的泡泡网捕食行为。算法的核心只有三条位置更新公式,理解它们之后,后面所有改进就都顺了。
第一条是收缩包围。设当前最优个体位置为X*,给定目标距离向量D=|C·X*(t)-X(t)|,个体按下式更新:X(t+1)=X*(t)-A·D。其中系数A=2a·r-a,C=2r,r是[0,1]均匀随机数,a是从2线性递减到0的收敛因子。A的模长直接决定这一代个体是“包围猎物”还是“远离猎物”——|A|≥1时,算法会强制个体偏离最优,相当于留出一个全局探索出口。
第二条是螺旋气泡网攻击:X(t+1)=D'·e^{b·l}·cos(2πl)+X*(t)。D'表示当前个体到最优个体的距离,b是决定螺旋形状的常数(通常取1),l是[-1,1]上的随机数。这条公式让种群绕着最优解做对数螺旋运动,属于精细的局部开发。
第三条是随机搜索:当|A|≥1时,个体不再围绕X*,而是随机挑一个伙伴X_rand,按X(t+1)=X_rand-A·D更新。算法每次迭代生成随机数p,p<0.5时走收缩包围或随机搜索(由|A|决定),p≥0.5时走螺旋更新。
提示:理解WOA的关键是记住“p和A两个随机开关共同决定行为”。后面所有的混合策略改进,本质上都在调整这两个开关背后的概率结构和位置更新形态。
1.2 为什么标准WOA容易早熟
很多人以为WOA这么好理解、参数这么少,应该很稳。实际跑起来根本不是这么回事,病灶主要有四处。
第一,收敛因子a是纯线性的。从2降到0,前半程探索窗口稍纵即逝,种群往往还没铺开就被“收缩”拉回最优附近。对Rastrigin这种局部极小值极其密集的函数,一次错误的收缩就足以让整群鲸鱼全部掉进同一个坑。
第二,螺旋更新的半径完全取决于当前个体到最优X的距离。如果X本身是个局部最优,螺旋半径再小也是在错误区域打转,等于用精细搜索放大一个错误答案。
第三,算法没有个体间的信息交换机制。PSO有速度记忆,DE有差分向量交叉,WOA基本只靠“所有人向最好位置靠拢”这一条信息通道。种群多样性只能靠随机数硬撑,后期X*一旦不再更新,其它个体无论怎么扰动都只是在它周围做布朗运动,离真正的全局最优可能十万八千里。
第四,初始种群是纯随机均匀分布。高维搜索空间里随机撒点非常不可靠,尤其在[-100,100]这种大范围问题上,容易开局就集合在某个劣势区域。
这四处病灶恰好就是混合策略可以动刀的位置。下面讲我具体怎么改的。
2. 混合策略到底混了什么:三处手术点的设计取舍
2.1 种群初始化:Tent混沌映射 + 精英反向学习
初始种群质量决定算法的起跑线。我用的第一招是把均匀随机数换成了Tent混沌映射,公式是:x_{k+1}=x_k/0.7(x_k<0.7),x_{k+1}=(1-x_k)/0.3(x_k≥0.7)。
Tent映射的好处是遍历性和均匀性比纯随机好,能在搜索空间里产生分布更“铺得开”的初始点。但这里有个坑:Tent映射对初值极其敏感,而且容易陷入0、0.2857、0.7143这类短周期循环。解决办法是给每次迭代加入一个小扰动,映射值进入循环时加一个1e-6量级的随机噪声,跳出不动点。
光有混沌还不够。我又对每个初始个体生成一个反向解x'_i=lb+ub-x_i,然后把2N个个体一起按适应度排序,选出前N个作为真正的初始种群。这个操作叫精英反向学习(EOBL)。它的价值在于:如果真实最优解恰好落在当前随机分布对角的盲区,反向解至少能补一条“跨整个搜索域”的覆盖,等于在一开始就给算法一次双向试错的机会。
2.2 收敛因子与惯性权重:把线性递减改成非线性
第二刀动在收敛因子上。我把原来的a=2-2t/T改成了a=2-2(t/T)^2,也可以换成余弦形式a=2·cos((π/2)·(t/T))。两种做法的共同点是:前期a下降得更慢,让种群有更长的全局探索窗口;后期a快速压到很低,加速局部收敛。这相当于重新分配了整段搜索过程的时间预算。
同时我引入了一个随迭代线性变化的惯性权重w,位置更新变成X(t+1)=w·X*(t)-A·D,w从0.9逐步降到0.4。权重大的阶段个体保留更多自身原有状态,不会瞬间被拉向最优;权重小的阶段则允许更激进的收缩。实测里这个组合对Rosenbrock这类“窄谷”函数的帮助最大——它让个体在沿谷底前进时不会因为收敛因子收缩太快而反复横跳。
2.3 多样性维持:Lévy飞行接管探索,柯西变异保底
第三刀最关键,针对的是种群多样性的流失。我做了两件事。
第一,把随机搜索阶段(|A|≥1时)的更新公式替换成Lévy飞行:X(t+1)=X_rand+Lévy(β)⊗(X_rand-X)。Lévy步长按Mantegna算法生成:s=u/|v|^{1/β},其中u服从均值为0、方差为σ_u的正态分布,β取1.5。Lévy飞行和普通随机步长的区别在于它是“尖峰胖尾”分布:大部分时候小步慢走,偶尔一次超长跳跃。这个特性简直是为跳出局部最优量身定做的——小步负责在局部细细摸排,长跳负责把种群从错误盆地弹射出去。
第二,给最优个体加柯西变异:X*_new=X*+Cauchy(0,η),η取搜索半径的0.1倍左右,按0.3的概率在迭代中后期触发,变异后做贪婪选择,变好了才替换。柯西分布的尾巴比高斯更厚,产生极端扰动的概率更高。实测里它的作用很直接:很多次收敛曲线本来已经平了,靠一次柯西扰动把最优解重新激活,后面又往下走了一大截。
到这里,整个改进闭环是:混沌+反向学习解决起跑线问题,非线性收敛因子和惯性权重解决探索与开发的时间分配问题,Lévy和柯西解决中途丧失多样性的问题。三处手术相互独立,方便后面做消融实验验证各自的贡献。
3. 测试效果图的生成环境:基准函数、实验参数与统计口径
改完算法,最重要的事情是设计一场“禁得起挑刺”的测试。测试效果图不是画给自己看的,是要拿去给导师、评委、审稿人看的。这一节的环境配置我完整列出,照抄即可。
3.1 基准函数集的选择逻辑
我只固定一条原则:单峰、多峰、复合函数都要有,缺一个都说明不了问题。
单峰函数测收敛能力和精度,代表是Sphere(F1)和Schwefel 2.22(F2)——这两个函数没有局部极小值,纯粹拼谁能在有限迭代里压得更接近0。Rosenbrock(F3)虽然也算单峰,但最优点藏在一个狭长的抛物线山谷里,更像一个测试局部开发的题目。
多峰函数测跳出局部最优的能力,Rastrigin(F4)、Ackley(F5)、Griewank(F6)是标配套餐。Rastrigin的局部极小点密集得像钉板,是检验早熟的经典关卡;Ackley的周期性起伏容易把算法骗进伪最优;Griewank则是“多峰外壳+单峰内核”,用来观察算法中后期能不能穿透外层干扰。
复合函数我从CEC2017里挑了两个,这类函数由多个基础函数叠加、偏移、旋转后拼成,搜索地形极其扭曲。标准WOA在上面经常连“爬到山脚下”都做不到,最考验改进算法的实际容量。
| 函数 | 类型 | 维度 | 搜索范围 |
|---|---|---|---|
| Sphere (F1) | 单峰 | 30 | [-100,100] |
| Schwefel 2.22 (F2) | 单峰 | 30 | [-10,10] |
| Rosenbrock (F3) | 单峰窄谷 | 30 | [-30,30] |
| Rastrigin (F4) | 多峰 | 30 | [-5.12,5.12] |
| Ackley (F5) | 多峰 | 30 | [-32,32] |
| Griewank (F6) | 多峰 | 30 | [-600,600] |
| CEC2017 F17 | 复合 | 30 | [-100,100] |
| CEC2017 F22 | 复合 | 30 | [-100,100] |
3.2 参数设置与公平性陷阱
改进版(HWOA)与标准WOA统一使用:种群规模N=30,最大迭代次数T=500(复合函数用1000),独立运行30次。HWOA多出来的参数:Tent扰动ε=1e-6,β=1.5,柯西变异触发概率p_m=0.3,变异尺度η=0.1倍搜索半径,惯性权重w从0.9线性降到0.4。
这里有个经常被忽略的公平性问题:标准WOA的初始种群是随机生成的,改进版用混沌生成,如果两者起点不同,改进版即使赢了也说不清是初始化赢还是策略赢。所以我在“控制变量”实验里让两个算法使用同一套初始种群——把生成好的初始种群存下来,分别喂给两个算法;在“实际效果”实验里才各自用天然初始化,用来评估整体增益。
3.3 为什么必须跑30次而不是看一条曲线
单次运行的收敛曲线只能看个感觉,元启发式算法是随机算法,换一个随机种子结果可能天差地别。我统一记录30次独立运行的四个指标:最优值、平均值、标准差、最差值。平均值反映平均水平,标准差反映稳定性,最差值能暴露算法偶尔翻车的情况——很多改进算法均值漂亮,但最差值依然很烂,说明稳定性不行。
组间比较用Wilcoxon秩和检验。这是一个非参数检验,不假设数据服从正态分布,适合优化算法这种分布未知的场景。显著性水平取0.05,p<0.05才算改进有统计意义。另外单独做了消融实验:标准WOA分别叠加单一策略,量化每一招的贡献,防止“三管齐下看着好看,实际是某两招在拖后腿”。
4. 测试效果图逐张解读:收敛曲线、箱线图与显著性检验
这节是整篇的题眼。我尽量把图“翻译”成文字,你按这个思路去看自己跑的图,一眼就能判断改进到底成没成。
4.1 收敛曲线:别只盯着谁下降快
收敛曲线横轴是迭代次数,纵轴是当前最优适应度,我全部用对数坐标。为什么用对数?因为像Sphere这种函数,适应度能从1e0掉到1e-80,线性坐标下后期那段全被压缩成一条直线,什么都看不出来。
标准WOA在Rastrigin(F4)上的曲线非常典型:前50次迭代急速下滑,然后突然变平,剩余450次几乎是一条水平线,最终停留在3e1量级——意思是早就掉进局部坑里爬不出来了。HWOA的曲线前半段和它基本重合(因为初始种群一样),但在120次迭代附近出现一次明显的“阶梯式下跌”,这正是某只鲸鱼靠Lévy长跳弹到了更优区域,随后整群收缩跟进。第二次转折出现在300次左右,来自一次柯西变异对最优解的激活。最后HWOA稳定在1e-8量级,和标准版差了七八个数量级。
在Sphere(F1)上两者前期几乎难分高下,HWOA的优势主要体现在后期精度:标准WOA到500次能到1e-40左右,HWOA能压到1e-80,这是非线性收敛因子让最后100次迭代的收缩没有过早锁死的结果。
4.2 箱线图与统计表:稳定性是改进的隐形财富
30次独立运行的结果画成箱线图最能说明问题。标准WOA在Rastrigin上的箱子又高又宽,而且经常有上下须拖着很长的离群点——这是“经常掉坑、偶尔走到正确区域”的写照。HWOA的箱子被压成一条紧贴0的窄条,中位数、上下四分位几乎重合。
我拿这次实验的典型数据做代表(数值会因随机种子浮动,关键看数量级差距):
| 函数 | 算法 | 最优值 | 平均值 | 标准差 |
|---|---|---|---|---|
| Rastrigin | WOA | 28.31 | 34.72 | 12.56 |
| Rastrigin | HWOA | 2.8e-10 | 5.1e-8 | 1.2e-7 |
| Ackley | WOA | 1.5e-5 | 2.1e-4 | 3.6e-4 |
| Ackley | HWOA | 8.7e-14 | 1.1e-12 | 2.4e-12 |
| Rosenbrock | WOA | 25.13 | 27.90 | 1.82 |
| Rosenbrock | HWOA | 19.87 | 21.56 | 0.93 |
注意Rosenbrock那一行,这是本次实验最有意思的记录。HWOA在Rosenbrock上的提升只有不到25%,远不如Rastrigin上“天壤之别”式的差距,但标准差从1.82降到了0.93。真实工程问题往往不需要你比对手好一万倍,而是需要你每次都能稳定交出“还不错”的答案,稳定性本身就是很有价值的改进维度。
4.3 显著性检验:别被单函数成绩迷惑
把8个函数的30次结果两两做Wilcoxon检验后,我得到一组“+/=/-”统计:7个函数显著优于标准WOA(p<0.05),1个函数(CEC2017 F22复合函数)差异不显著。这个结果比单纯的曲线对比有说服力得多。
有个细节必须提醒:不要把8个函数的平均值拿来直接对撞。Rastrigin上改进版把误差从1e1压到1e-8,其它函数只有几倍差距,简单平均会掩盖真实信号。正确的做法是每个函数单独做秩和检验,再看整体胜负数。我见过不少材料拿“平均提升40%”当卖点,查一下原始数据发现全靠某一个函数撑场面,这种结论一挑就破。
CEC2017 F22上改进不明显,我也分析过原因:复合函数地形是多个基函数旋转组合出来的,Lévy长跳在扭曲空间里经常撞上边界,弹出的“长腿”反而浪费迭代。这说明任何改进都有适用范围,效果图里出现“不显著”的行,反而比全胜更有可信度。
5. 复现所需的代码骨架与调参心得
说清楚了实验设计,代码层面给一份可以直接跑起来的骨架。我用Python写,依赖numpy就够了。
5.1 三个关键函数的实现
第一个是Tent混沌初始化:
import numpy as np import math def tent_init(pop_size, dim, lb, ub, eps=1e-6): x = np.random.rand(pop_size * dim) for _ in range(100): # 打乱前100项,避免陷入不动点 x = np.where(x < 0.7, x / 0.7, (1 - x) / 0.3) # 判断是否落入短周期循环,落入则加极小扰动 mask = np.abs(x - np.roll(x, 1)) < eps x[mask] += (np.random.rand(*x.shape) + 0.5) * eps return lb + x.reshape(pop_size, dim) * (ub - lb)这个写法把所有维度的混沌序列一次性生成,比一维一维生成高效得多。ε扰动不是随便加的,我试过几档:取1e-6刚好破坏映射的周期结构又不会明显影响分布均匀性,取1e-3时混沌特性就基本消失了。
第二个是Mantegna的Lévy步长生成:
def levy_step(beta=1.5): sigma_u = (math.gamma(1 + beta) * math.sin(math.pi * beta / 2) / (math.gamma((1 + beta) / 2) * beta * 2 ** ((beta - 1) / 2))) ** (1 / beta) u = np.random.normal(0, sigma_u) v = np.random.normal(0, 1) return u / (np.abs(v) ** (1 / beta))这个函数返回的s要乘一个尺度系数再参与位置更新。尺度系数α我取0.5,并且建议对搜索范围做归一化处理:当搜索域是[-100,100]时,α·s可能直接跳出界外,后面必须配对边界处理。
第三个是柯西变异触发段的关键逻辑:
if t > 0.3 * max_iter and np.random.rand() < p_m: mutant = best + cauchy_scale * np.tan(math.pi * (np.random.rand(dim) - 0.5)) mutant = np.clip(mutant, lb, ub) if fitness(mutant) < fitness(best): best = mutant柯西分布抽样用np.tan(π(U-0.5))是标准做法。cauchy_scale用0.1倍的搜索半径,太小等于没变异,太大会把好不容易找到的最优解一脚踢飞,这个尺度非常敏感,值得单独调。
5.2 参数调节顺序与我的推荐组合
HWOA的改进参数一共六个:Tent扰动ε、EOBL是否启用、β、p_m、η、惯性权重范围。我的调节顺序是:先固定β=1.5,把ε从小到大扫一遍;然后单独测p_m,从0.1到0.5间隔0.05;最后回到η,在0.05到0.3倍搜索半径之间找甜点。这个顺序保证每一步只改一个变量,出问题能立刻定位到是谁的锅。
实测下来我的推荐组合:ε=1e-6,β=1.5,p_m=0.3,η=0.1×搜索半径,w从0.9线性到0.4,EOBL开启。这个组合在多峰函数上收益最大。如果你手里是工程寻优问题(目标函数计算成本高、迭代次数少),可以把β调大到1.7、p_m降到0.2——少一点长跳,多一点局部收敛。
6. 踩坑记录:判断改进是否有效的几个反直觉标准
最后这部分是我实际测试过程中交的学费,写出来帮你省掉。
6.1 收敛曲线的三大误导
第一,起点不同。如果改进版用了混沌初始化而标准版用随机初始化,两者收敛曲线起点就不同。混沌初始化往往让第一代适应度更差(因为铺得更开),但跑完反而赢了——有人就把这理解为“改进有效”,实际可能只是后期覆盖更占便宜。严格的控制变量做法是共用同一份初始种群。
第二,单次曲线的偶然性。我碰到过随机种子好到让标准WOA在某一次Rastrigin上跑到1e-5的情况,但30次平均下来依然是几十量级。永远不要拿“我跑了一次,改进版赢了”当证据。
第三,对数坐标看多了会麻木。对数坐标让小差距显得很大,1e-40和1e-80在图上可能差出一大截,但现实中很多时候1e-40已经够用了。看效果图必须同时看原始数值,别被坐标轴的视觉效果带偏。
6.2 策略堆叠的“参数爆炸”陷阱
我做消融实验时发现一个扎心的现象:混沌初始化单独就有明显效果,加EOBL又涨一截,加非线性收敛因子再涨一截,但四个策略全部叠满时,优势反而变平了。原因是策略之间存在相互替代——Lévy已经大幅改善探索能力后,混沌初始化带来的初始多样性增益就被稀释了。
这也是为什么我强烈建议所有混合策略改进都配一张消融实验表。每一列是“基础版+某一策略”,最后一列是“全加”。如果全加版并不显著优于最好的某一列,果断砍掉冗余策略。参数越少、普适性越强,越经得起推敲。
6.3 我个人的判断流程
现在我做改进实验,固定按四步收尾:第一步,控制变量下跑30次,确认收敛曲线是否在多个函数上一致改善;第二步,做Wilcoxon检验,只看p值和“+/=/-”的计数;第三步,看每张箱线图的离群点数量,标准版经常翻车而改进版不翻车,比均值降低更有说服力;第四步,把改进版拿到一个真实工程代价函数上跑一遍,验证参数换场景是否还成立。
最后分享一个实测里的小经验:当改进后的收敛曲线在中后期出现第一次“阶梯式下跌”时,先别急着欢呼。把那次跳跃对应的个体和维度打出来,确认它来自Lévy还是柯西,再决定保留哪一步。我在很多个版本上都是靠这个细节保住精力的——混合策略改进鲸鱼优化算法这件事,难的不是把几种策略拼在一起,而是能证明每一种策略都站在正确的位置上。