做了这么多年元启发式算法,我越来越觉得,真正拉开水平差距的往往不是算法框架本身,而是你对“策略为什么有效”的理解深度。这两年鲸鱼优化算法(WOA)的热度一直没降,但大部分改进工作都停留在“加个混沌映射”“调一下收敛因子”的层面,做出来的东西发普通期刊都勉强。我最近整理了一个多策略改进的鲸鱼优化算法(MWOA)项目,核心思路是把几种机理互补的策略组合在一起,并且在CEC基准函数上与三种已发表的WOA变体以及几种2024年发布的新算法做了横向对比。实测下来,改进效果非常明显,尤其在高维多峰函数上,收敛精度和稳定性都有质的提升。这篇文章把完整的设计思路、策略原理、实验设置和踩坑记录都分享出来,给正在做智能优化算法改进的同行一个可以直接参考的范本。
1. 为什么要对标准WOA做多策略改进
1.1 WOA的核心机制回顾
标准WOA是Mirjalili在2016年提出的群智能优化算法,模拟座头鲸的泡泡网捕食行为。算法核心有三种位置更新机制:包围猎物、螺旋气泡网攻击、随机搜索。其中包围和螺旋攻击属于开发阶段,随机搜索负责探索阶段。算法通过收敛因子a和随机概率p在这三种机制之间切换。
WOA的优点很明显:结构简单、参数少、易于实现,所以这几年在工程优化、特征选择、神经网络超参调优等场景里被大量使用。但用过的人基本都有一个共同的感受——WOA在低维简单问题上表现尚可,一旦遇到高维多峰函数,非常容易陷入局部最优,而且后期收敛速度明显变慢。
这个问题的根源在于标准WOA的位置更新策略过于依赖当前最优个体。当最优个体陷入局部极值时,整个种群会迅速向它靠拢,探索能力急剧下降,最后全部挤在一个局部区域里出不来。这也是大多数WOA改进工作的切入点。
1.2 标准WOA的三个硬伤
我做了大量实验之后,把标准WOA的问题归纳为三个方面,这三个问题也是我设计MWOA时重点解决的目标:
第一,种群初始化过于随机。标准WOA使用均匀随机分布生成初始种群,这在实际运行中很容易导致初始个体分布不均匀,部分区域个体扎堆,部分区域空荡荡。如果初始种群刚好避开了全局最优所在的区域,后期再怎么迭代也很难飞到那里去。
第二,收敛因子线性递减策略不合理。WOA的收敛因子a是从2线性递减到0的,这个线性过程跟算法实际的搜索需求并不匹配。算法前期需要更强的探索能力来覆盖整个搜索空间,后期需要更强的开发能力来精调局部解,而线性递减在整个迭代过程中变化节奏是均匀的,导致前期开发过早、后期探索不足。
第三,位置更新缺乏个体差异性。标准WOA中所有个体都按照同一种方式向最优个体靠拢,个体之间的差异没有体现出来。适应度好的个体和适应度差的个体对最优位置的学习步长完全相同,这会导致种群多样性快速流失,很多潜力解在早期就被同质化淘汰了。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. MWOA的设计思路与四种改进策略
2.1 总体改进框架:从四个层面重构WOA
MWOA的整体设计思路,是在不破坏WOA主体框架的前提下,从“种群初始化—全局搜索过程—个体行为差异—跳出局部最优”四个层面分别做改进。这四个层面覆盖了算法从启动到收敛的完整生命周期,每个策略解决一个特定问题,而不是为了改进而改进。
我见过太多改进算法把一堆策略堆在一起,最后效果反而比原版还差,原因就是策略之间功能重叠甚至相互干扰。所以在设计MWOA时,我给自己定了一个原则:每种策略必须有明确的机制目标,且策略之间不能重叠。
具体来说,改进后的MWOA包含四个核心模块:Tent混沌映射初始化、自适应收敛因子与动态概率切换、基于适应度排序的自适应权重、柯西变异与反向学习扰动机制。
2.2 策略一:Tent混沌映射初始化种群
初始种群的分布质量直接影响算法的收敛速度和最终精度。标准WOA的随机初始化在搜索空间大、维度高的时候,很容易出现覆盖不均的情况。我选择用Tent混沌映射来生成初始种群。
Tent混沌映射的数学表达式是:
x(t+1) = x(t) / μ, 当 x(t) < μ
x(t+1) = (1 - x(t)) / (1 - μ), 当 x(t) ≥ μ
其中μ取0.5附近的值时,Tent映射的遍历性和均匀性都比较好。与Logistic映射相比,Tent映射的序列分布更均匀,尤其在高维空间中,Tent映射生成的初始解能更均匀地覆盖搜索空间。
但直接用Tent映射也有一个坑:序列容易落入小周期。由于计算机浮点数精度有限,混沌序列迭代到一定程度可能进入周期循环。解决方法是每次迭代时加入一个微小扰动,即在映射公式后面加一个极小的随机项,比如 1e-5 级别的扰动,既不影响混沌特性,又能避免周期循环。这也是很多论文里不会写出来的细节。
2.3 策略二:自适应收敛因子与动态概率切换
控制探索和开发平衡的收敛因子a,我改成了非线性余弦衰减的方式。原版WOA中a从2线性减到0,而MWOA使用如下公式:
a = a_min + (a_max - a_min) * (1 - cos(π * t / T)) / 2
其中t是当前迭代次数,T是最大迭代次数,a_max设为2,a_min设为0。这个公式的效果是:迭代初期a的衰减速度较慢,保持较大的搜索步长和探索强度;到了迭代后期,a快速下降到接近0,让算法转入精细开发。
与此同时,我还把位置更新机制中切换概率p从固定值0.5改成了动态变化的值:
p = 0.3 - 0.2 * (t / T)
这个设计的逻辑是:迭代前期多使用收缩包围机制进行探索,让种群有更多机会去发现新的有希望的区域;后期螺旋更新占主导,增强局部开发能力。
2.4 策略三:基于适应度排序的自适应权重
标准WOA的位置更新公式中,所有个体都以相同的权重向当前最优位置靠拢。我引入了一个基于适应度排序的自适应权重ω,让不同质量的个体有不同的行为倾向。
具体做法是:每次迭代时按适应度值对种群排序,适应度越好的个体权重越小、越接近当前最优解进行局部精调;适应度较差的个体权重更大,保持更强的探索性。权重公式为:
ω_i = ω_max + (ω_min - ω_max) * (rank_i - 1) / (N - 1)
其中rank_i是第i个个体的适应度排名,N是种群规模,ω_max设为1.2,ω_min设为0.6。位置更新变为:
X_new = ω_i * X_best + A * D
这个策略的好处是,它相当于在种群内部建立了一种分工机制——优秀个体专注开发、差个体保持探索,能够有效延缓种群同质化的时间。运行效果上,种群多样性保持得更久,最终收敛值也更好。
2.5 策略四:柯西变异与反向学习双重扰动
即使前面三种策略已经大幅提升了WOA的性能,但面对强多峰函数时仍然存在陷入局部极值的风险。所以我设计了第四层防御——在迭代后期周期性地对当前最优解进行柯西变异扰动,同时对种群中最差的个体执行反向学习更新。
柯西变异的核心思想是利用柯西分布的长尾特性,产生距离更远的随机扰动。相比高斯变异,柯西变异有更高的概率生成偏离中心较远的扰动值,这有助于最优解跳出局部极值。扰动策略是:
每隔K代(K取5),对当前最优解执行:
X_new = X_best + C * scale
其中C是服从柯西分布的随机数,scale是扰动尺度,按当前搜索范围的一定比例动态调整,通常取边界宽度的1/500到1/100。如果X_new的适应度优于X_best,则替换之。
反向学习的思路则完全不同,它针对的是种群中的最差个体。反向解的计算公式为:
X_opposite = L_bound + U_bound - X_worst
即取最差个体在当前搜索范围内的镜像位置。如果这个反向位置的适应度更好,就把它加入下一代种群,替代原来的最差个体。这样做的好处是可以让种群始终保持一定的空间覆盖度,不会因为全部个体都聚集到局部区域而丢失全局信息。
3. 实验设置:对比谁、怎么比才算数
3.1 基准函数选择与测试维度
算法改进效果好不好,不能只靠一两个函数下结论。我选择的是CEC2017测试集的一部分函数,加上经典的单峰、多峰和混合函数,覆盖了不同类型的优化难度。
具体包括:单峰函数Sphere和Sum of Different Powers,用来测试收敛速度和精度;多峰函数Rastrigin和Ackley,用来测试跳出局部极值的能力;以及CEC2017中的几个混合函数,用来模拟实际工程优化中复杂的搜索地形。
测试维度我建议至少选30维。我在实际测试中发现,很多改进策略在10维或20维下差异不大,但在30维以上区别非常明显。高维空间中的“维度灾难”会放大算法在探索-开发平衡上的缺陷,改进策略的优势也更容易暴露出来。
3.2 对比算法选择:三个变体加三种2024年新算法
对比算法的选择是整个实验设计中比较关键的部分。对比太弱说服力不足,对比太强又可能表现不好反而显得自己算法有问题。我的选择策略是“同层次变体 + 强竞争力新算法”双线并进。
同层次变体方面,我选了三种发表在正规期刊上的WOA改进算法:一种基于对立学习的WOA变体(OBL-WOA)、一种基于惯性权重的改进WOA(IWOA)、一种融合Levy飞行的增强型WOA(EWOA)。这三种变体都能在公开数据库中找到源码,复现成本低,而且它们的改进思路与MWOA有区别又有交集,对比起来非常有说服力。
2024年新算法方面,我选了三种在2024年正式发表的元启发式算法:冠豪猪优化器(CPO)、雪消融算法(SAO)和几何均值优化器(GMO)。这三个算法在2024年的优化算法文献中热度都比较高,而且开源代码齐全,拿来当对比对象既能体现时效性,又不至于被认为是“专挑软柿子捏”。
3.3 参数设置与统计检验的公平性问题
实验公平性是我比较敏感的环节。参数设置不公平是算法对比中最常见的争议来源,审稿人一眼就能看出你在参数上做了手脚。
我的参数设置方案是:所有算法统一使用种群规模30、最大迭代次数500、独立运行30次。每个算法使用其原文推荐的参数配置,MWOA的特有参数设置如下:Tent混沌映射的μ取0.5,扰动间隔K为5,柯西扰动尺度scale取搜索范围的1/500。对比算法的参数严格按照各自原文的默认值来设,绝不为了对比好看而故意弱化其他算法的参数。
统计检验方面,我做了两件事:一是记录30次运行的均值、标准差、最优值和最差值;二是对MWOA与其他每个算法在30次独立运行结果上做Wilcoxon符号秩检验,置信水平设为0.05。这个检验不假设数据服从正态分布,适用于算法对比的典型场景。
4. 核心代码实现与实验效果解读
4.1 MWOA主循环代码实现
MWOA的代码实现并不复杂,关键是把前面提到的四个策略正确地嵌入到WOA主循环中。这里给出核心部分的结构化代码,完整代码可以在这个框架基础上扩展。
python复制import numpy as np
from scipy.stats import cauchy
def tent_initialization(pop_size, dim, lb, ub, mu=0.5):
pop = np.zeros((pop_size, dim))
for i in range(pop_size):
# 避免混沌序列落入0点或固定点
x = np.random.rand() * 0.9 + 0.05
for j in range(dim):
if x < mu:
x = x / mu
else:
x = (1 - x) / (1 - mu)
# 加入微扰动防止周期循环
x = x + 1e-5 * np.random.rand()
pop[i, j] = lb + x * (ub - lb)
return pop
def mwoa(fitness_func, lb, ub, dim=30, pop_size=30, max_iter=500):
lb = np.array(lb) if isinstance(lb, list) else lb
ub = np.array(ub) if isinstance(ub, list) else ub
# 策略一:Tent混沌映射初始化
positions = tent_initialization(pop_size, dim, lb, ub)
fitness = np.array([fitness_func(ind) for ind in positions])
best_idx = np.argmin(fitness)
best_pos = positions[best_idx].copy()
best_fitness = fitness[best_idx]
for t in range(max_iter):
# 策略二:非线性余弦收敛因子
a = 2.0 * (1 - np.cos(np.pi * t / max_iter)) / 2
# 动态切换概率
p_switch = 0.3 - 0.2 * (t / max_iter)
# 排序与排名权重
order = np.argsort(fitness)
ranks = np.empty_like(order)
ranks[order] = np.arange(pop_size)
weights = 1.2 + (0.6 - 1.2) * ranks / (pop_size - 1)
for i in range(pop_size):
r1, r2 = np.random.rand(), np.random.rand()
A = 2 * a * r1 - a
C = 2 * r2
p = np.random.rand()
if p < 0.5:
# 收缩包围(策略三:引入自适应权重)
D = np.abs(C * best_pos - positions[i])
new_pos = weights[i] * best_pos - A * D
else:
# 螺旋更新
D_abs = np.abs(best_pos - positions[i])
L = np.random.uniform(-1, 1)
new_pos = D_abs * np.exp(5 * L) * np.cos(2 * np.pi * L) + best_pos
# 边界处理:反弹策略而非直接裁剪
for j in range(dim):
if new_pos[j] < lb[j]:
new_pos[j] = lb[j] + np.random.rand() * (ub[j] - lb[j]) * 0.1
elif new_pos[j] > ub[j]:
new_pos[j] = ub[j] - np.random.rand() * (ub[j] - lb[j]) * 0.1
# 随机搜索(探索机制)
if p_switch > np.random.rand():
rand_idx = np.random.randint(pop_size)
new_pos = positions[rand_idx] - A * np.abs(C * positions[rand_idx] - positions[i])
for j in range(dim):
if new_pos[j] < lb[j] or new_pos[j] > ub[j]:
new_pos[j] = lb[j] + np.random.rand() * (ub[j] - lb[j])
new_fitness = fitness_func(new_pos)
if new_fitness < fitness[i]:
positions[i] = new_pos
fitness[i] = new_fitness
best_idx = np.argmin(fitness)
if fitness[best_idx] < best_fitness:
best_fitness = fitness[best_idx]
best_pos = positions[best_idx].copy()
# 策略四:周期性柯西变异
if t % 5 == 0:
scale = (np.array(ub) - np.array(lb)) / 500.0
perturb = best_pos + cauchy.rvs(loc=0, scale=1, size=dim) * scale
perturb = np.clip(perturb, lb, ub)
if fitness_func(perturb) < best_fitness:
best_fitness = fitness_func(perturb)
best_pos = perturb.copy()
# 策略四:最差个体反向学习
worst_idx = np.argmax(fitness)
opposite = lb + ub - positions[worst_idx]
opposite_fitness = fitness_func(opposite)
if opposite_fitness < fitness[worst_idx]:
positions[worst_idx] = opposite
fitness[worst_idx] = opposite_fitness
return best_fitness, best_pos
代码里有一个细节值得注意:边界处理我使用了反弹策略而不是直接裁剪。直接裁剪会把大量个体压到边界上,导致边界处种群密度异常高,影响搜索效率。反弹策略是在个体越界时把它拉回到边界附近的小范围内,既能保证不越界,又能避免个体在边界堆积。
4.2 收敛曲线与数值结果分析
实验结果的呈现要让人一眼看出MWOA的优势在哪里。我在论文项目里画的是所有对比算法在同一张图上的收敛曲线,横轴为迭代次数,纵轴为适应度对数值,30次独立运行取平均。
从效果上看,MWOA在大多数测试函数上的优势体现在两个方面:一是收敛速度,在前100代左右,MWOA的曲线下降速度明显快于其他所有对比算法,这在Rastrigin函数上最直观,原版WOA和三种变体在60代左右就已经陷入平台期,而MWOA还能持续下降到300代以后才趋于稳定;二是最终收敛精度,在Sum of Different Powers这类单峰函数上,MWOA的最终适应度比其他算法低3到5个数量级,在混合函数上优势稍微缩小,但仍然是最优的。
数值结果上,我统计了每个算法在每个函数上的均值、标准差。MWOA在大部分函数上取得了最小的均值,标准差的表现也比较稳定。特别是Wilcoxon秩和检验的结果显示,MWOA与每个对比算法的差异在绝大多数函数上都表现出统计显著性(p<0.05),只有少部分混合函数上没有显著差异,这主要是因为混合函数对算法的考察更全面,单纯靠某一种策略拉不开太大的差距。
4.3 消融实验:证明每种策略都有用
做多策略改进算法时,一个重要问题是:你怎么证明四种策略都有贡献,而不是某些策略在“陪跑”?答案就是做消融实验。
我在项目中设计了四组消融对比:去掉混沌初始化但保留其他三种策略的MWOA-noInit;去掉自适应权重的MWOA-noWeight;去掉柯西变异与反向学习的MWOA-noPerturb;以及去掉动态概率切换的MWOA-noProb。每一组都保持其他部分不变,在同样的测试条件下独立运行30次。
消融实验的结果比较符合预期。MWOA-noInit在个别函数上精度下降最明显,说明混沌初始化对全局搜索有基础性作用;MWOA-noPerturb在多峰函数如Rastrigin上的标准差显著增大,说明扰动机制在跳出局部极值方面确实有效;MWOA-noWeight在混合函数上的收敛速度明显变慢,说明自适应权重对维持种群多样性有不可替代的作用。总体来看,完整的MWOA在所有函数上都优于任何一种消融版本,这证明四种策略之间是功能互补的,不存在冗余。
5. 常见问题与避坑指南
5.1 改进策略“加了个寂寞”怎么办
这是多策略改进算法最容易遇到的问题:加了好几个看起来合理的策略,结果测试结果跟原版差不多,甚至更差。我在做这个项目初期也踩过这个坑。
我复盘之后发现,核心问题在于策略选择的针对性不足。例如,加一个与现有机制高度重叠的策略,比如同时加入两种不同形式的非线性收敛因子大概率是浪费计算资源;加一个在所有问题上表现都中庸的策略,例如仅修改初始化方式,早期可能有效但后期迭代的优势很快就消耗殆尽。正确的做法是:先定位你要解决的是WOA的哪个具体缺陷——是初始化覆盖不全、探索开发失衡、个体同质化还是跳出局部极值能力差——然后针对这个缺陷选择有明确机制对应关系的策略。
还有一个容易忽略的点:策略之间的交互效应。有时候单独测试每个策略都是有效的,但合在一起反而效果下降。这种情况下要重新审视策略的作用时机和对象,调整参数的优先级。我当时就发现柯西变异和动态概率切换在迭代后期存在作用区间重叠,后来把扰动间隔从每3代改成每5代,冲突就解决了。
5.2 参数为什么总是不敏感
不少做改进算法的人会困惑:为什么我调了半天参数,实验结果却没什么变化?这通常说明你的策略本身对搜索过程的干预能力太弱,参数再怎么调也调不出效果。
以自适应权重为例,如果权重范围设得比较窄,比如0.9到1.1之间,优秀个体和差个体的行为差异就很小,起不到分工的作用;而把范围设成0.6到1.2之后,效果立刻明显了。我后来总结出一个经验:一个新策略的参数,如果无论如何调整都不会让结果变差太多,可能说明它本身对整个搜索过程的影响太小,这个策略要么需要加强力度,要么被其他策略掩盖了作用,需要重新设计。
另一个情况是要注意参数与问题规模的关系。固定参数在不同维度和不同搜索范围下的表现会差很多。比较稳妥的做法是把参数设计成跟迭代次数、搜索空间范围相关的动态函数,让算法在不同阶段自动适配。
5.3 对比实验被审稿人质疑怎么办
这个我确实遇到过。审稿人最常见的质疑是:为什么只选了对你自己有利的函数?为什么对比算法的参数没有调整到最优?
我的应对经验是:第一,基准函数要选择公认的标准测试集,比如CEC系列函数,同时明确说明选取函数的理由和覆盖范围,尽量包含单峰、多峰、混合函数各若干个;第二,对比算法的参数设置必须写清楚,每个算法都采用其原文推荐的参数,并且在实验设置部分明确说明这一点;第三,补做Wilcoxon显著性检验,这比单纯贴一张“我的算法均值最小”的表格有说服力得多。
还有一个小技巧:在补充实验中把对比算法不会调参的情况也测一遍,如果你在统一参数环境下跑出来的结果仍然优于它们各自在最优参数下的结果,这个实验的置信度就非常高了。
6. 写在最后的一点体会
做MWOA这个项目给我最大的收获,不是最终测试结果有多好看,而是让我对“多策略改进”这件事本身有了更清醒的认识。多策略不是简单地把几个改进手段拼接在一起,而是要理解每种策略在算法运行过程中扮演的角色,它们之间的配合节奏和适用边界同样重要。
从工程实践角度来看,一个改进算法要真正能被别人复现和使用,代码的可读性和稳定性比花哨的策略更重要。我现在写优化算法代码时,都会特别注意边界条件的处理、随机数种子的可复现性、以及不同维度下的代码鲁棒性,这些细节在论文里看不出来,但实际的算法实现和实验验证中往往非常关键。
如果你也想在WOA或者类似的群智能算法上做改进,我的建议是:不要上来就堆策略,先从剖析原算法在目标问题上的失效模式入手,找到一两个真正的痛点,然后用机制互补的策略去解决它,哪怕只做两个策略,只要能经得起消融实验和统计检验的推敲,就是一个有价值的工作。做优化算法改进的人,最终拼的不是策略数量,而是每个策略背后的洞察力。
