做多目标优化这一块,最耗心力的往往不是算法本身,而是你拿着一个改进算法,跑完一堆测试函数,却说不清它到底好在哪、为什么好、换到工程问题上还灵不灵。这篇文章想聊一聊我最近完成的IMOCTCM算法——融合高斯扰动与竞争学习的改进型多目标部落竞争与成员合作算法。会从原始TCM机制的缺陷说起,拆解我在WFG1-WFG9九个基准函数上的实验设计,再落到盘式制动器设计这个工程应用上,最后把Matlab代码里几个关键模块的写法和踩坑过程一并讲透。适合正在做进化算法改进、准备找测试函数系统性验证算法性能、或者需要把改进算法落地到实际工程设计问题上的读者参考。
1. 原始部落竞争与成员合作算法:机制回顾与改进动机
1.1 部落间竞争与部落内协作的原始设计
部落竞争与成员合作算法(Tribe Competition and Member Cooperation,TCM)的设计灵感来自族群的生存模式,核心思路是把整个种群划分成若干部落,每个部落内部维持一定数量的成员,然后通过两套机制推动搜索。
第一套是部落间竞争。每个部落都会选出一个代表个体,代表的质量决定了部落在整个种群中的地位。质量弱的部落会被强部落挤压生存空间,最直接的表现就是部落的成员数量被削减,甚至被合并到其他部落中。这样做的效果是,搜索资源会逐步向表现更好的区域倾斜,收敛速度会比较快。
第二套是部落内合作。部落内部的成员不是独立进化的,它们会围绕本部落的代表个体形成局部结构,成员之间互相交换位置信息,也会向代表个体靠拢。这套机制相当于在每个部落周围形成了一个局部搜索区域,能够在一定程度上精细挖掘当前区域的解。
这个设计在单目标问题上表现尚可,但一放到多目标场景,问题就变得复杂了。多目标优化要的不是一个最优解,而是一组分布均匀、覆盖完整的Pareto前沿解集,这意味着算法不仅要找得到前沿,还要保得住多样性,原版TCM那套以“竞争淘汰”为核心的逻辑,在多样性维持上明显偏弱。
1.2 原始TCM在多目标场景下的两个明显短板
第一个短板是早熟收敛。部落间竞争的强度一旦设置得过高,弱部落迅速被吞并,种群多样性会急剧下降,所有个体都集中到某个局部区域。在多目标问题里,这尤其致命,因为前沿往往不是连续光滑的,局部区域很可能只是某个Pareto前沿片段,算法一旦陷进去,后面再怎么迭代都跳不出来。
我在早期测试时遇到过非常直观的例子,在WFG4这种多模态问题上,原始TCM运行到中期,所有部落几乎都聚集到了同一个局部前沿上,IGD指标在剩余迭代次数里几乎没有变化。这说明部落竞争机制带来的选择压力过强,缺少一种能够在种群陷入停滞时“踢一脚”的机制。
第二个短板是开发与探索的平衡过于依赖部落数量。部落少,成员多,局部搜索充分但全局覆盖差;部落多,成员少,全局搜索有了但每个区域都挖不深。而这个平衡在不同问题上完全不一样,靠手工调部落参数,几乎不可能有哪个参数组合能同时适配WFG1到WFG9这样特征迥异的九个测试问题。
1.3 我为什么选择高斯扰动和竞争学习这两步改进
选择高斯扰动,是因为它天然适合作为一种“保留邻域信息的小概率逃逸”机制。高斯分布的特性是,大部分扰动集中在当前个体附近,只有尾部会产生较远距离的跳变,这种性质非常适合在保持局部开发能力的同时,阶段性尝试跳出局部区域。相比柯西扰动,高斯扰动的尾部更薄,不容易出现过度的远距离飞散,更适合在进化后期使用;相比均匀分布扰动,高斯扰动对当前解的破坏更可控。
竞争学习则是为了修正部落竞争过强导致的多样性崩塌。我参考了一些经典竞争学习机制的设计思路,做法是,不直接淘汰差个体,而是从种群中随机挑出两个个体做较量,胜者保留,败者向胜者方向更新,但不是完全复制胜者的位置,而是在胜者邻域内保留一定的独立探索分量。这样做的好处是,好个体能发挥引导作用,差个体也没有被完全浪费,它变成了一个带着“上一个失败位置记忆”的探索者。
这两步改进是有分工的,高斯扰动解决的是“陷入局部走不出来”,竞争学习解决的是“好个体周围的开发不够精细、差个体信息被白白丢弃”,方向不重叠,放在同一个算法里不会互相干扰。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 基准实验从WFG1选到WFG9:每个函数都在考察算法的什么能力
2.1 WFG测试套件相比ZDT/DTLZ的价值
学术界跑多目标算法,最常用的测试问题三件套是ZDT、DTLZ和WFG。ZDT问题只能处理两个目标,约束条件也相对简单,对现代算法的区分度越来越低。DTLZ支持任意数量目标,但有几个函数的Pareto前沿形状偏规则,容易被某些算法的分布机制“意外命中”。
WFG套件最大的价值在于,它的决策变量可以拆成位置参数和距离参数两类,而且每个函数都引入了不同的退化、断开、偏移、旋转、多模态结构。这意味着你可以单独控制问题难度来源,比如WFG2是前沿断开,WFG3是前沿退化,WFG5是欺骗,WFG6是不可分离,每一个都对应一种明确的算法能力考察方向。
而且WFG1-WFG9的规模可以缩放到任意目标数和变量数,我现在测试用的两个目标、六个变量,和后期扩展成三个目标甚至五个目标,逻辑是完全一致的。这点对工程应用很重要,因为实际工程问题很少有正好两个目标的。
2.2 WFG1-WFG9各自的关键特征与难点定位
我把这九个问题的核心特征和考察点整理成一个表,方便对照,这也是我设计实验时用来预估算法表现的依据。
| 测试函数 | 前沿形状 | 关键特征 | 对算法的考验 |
|---|---|---|---|
| WFG1 | 混合凸/凹 | 参数依赖+混合形状 | 收敛速度与方向选择,种群容易整体偏向前沿一侧 |
| WFG2 | 不连续凸 | 前沿断开,多段离散 | 是否能同时覆盖多个断开的前沿片段 |
| WFG3 | 线性退化 | 前沿退化为一条线 | 在低维流形上保持均匀分布,最容易出现重叠 |
| WFG4 | 凹 | 多模态,大量局部前沿 | 跳出局部最优的能力,高斯扰动在这里最起作用 |
| WFG5 | 凹 | 欺骗性,偏差结构 | 抵抗误导信息,避免过早聚集在错误区域 |
| WFG6 | 凹 | 不可分离,变量耦合 | 算子对变量耦合的适应能力,单点变异效率低 |
| WFG7 | 凹 | 参数依赖 | 对动态依赖结构的适应,需要较强的鲁棒性 |
| WFG8 | 凹 | 不可分离+参数依赖 | 耦合与依赖同时出现,难度叠加 |
| WFG9 | 凹 | 不可分离+多模态+欺骗+参数依赖 | 综合能力,几乎所有机制都要同时发挥作用 |
从我的实测来看,WFG1和WFG9是最难啃的两块骨头,但难的原因完全不同。WFG1难在收敛方向上,混合凸凹前沿让种群很容易在前沿的某一段堆积;WFG9难在“什么都掺一点”,你很难说清是哪个机制在起作用,任何单点改进都无法稳定拉起整体性能。
这里有一个我踩过坑的细节:WFG3虽然前沿只是一条线,但很多算法在它上面的表现反而不如预期。原因是多数分布性维护机制是按目标空间均匀分布设计的,遇到一个退化流形,它们会超量保留边界点,导致前沿中段出现大面积空洞。这就好比你用一张为平面设计的网格去铺一条弯曲的细线,铺不平是正常的。
3. 盘式制动器设计问题建模:多目标算法走出仿真测试的第一站
3.1 设计变量、物理约束和优化目标
盘式制动器设计是工程材料领域常用的多目标约束优化测试案例,也是IMOCTCM从“函数测试”走向“工程应用”的第一个验证场景。问题本身的规模不大,目标函数是两个,但约束条件和变量之间的物理耦合关系很复杂,非常适合检验算法处理工程问题的能力。
在我的实现中,设计变量取四个:制动盘内半径r_i、外半径r_o、制动力F、摩擦面有效面积参数s。其中r_i的取值范围是[55, 80]mm,r_o是[90, 110]mm,F是[1000, 3000]N,s是[2, 15]。优化目标有两个,第一个是制动器总质量尽量小,第二个是制动停止时间尽量短。这两个目标本质上是有冲突的,制动盘越大,散热和制动力矩的条件越好,停止时间越短,但质量也越大。
约束条件则包括制动盘内外半径差的下限、摩擦面面积与制动力匹配合理性、以及温度约束。我在复现时使用了如下约束集合:
- r_o - r_i >= 20mm,保证制动盘的结构强度和散热空间;
- 摩擦面积不超过物理允许范围,保证制动压力在合理区间;
- 工作温度不超过材料许用温度,这个约束间接限制了质量和停止时间的同时优化。
3.2 约束处理方式的选择
这道题的约束条件有个特点,量纲差别大:有的是半径差值,毫米量级;有的是温度,摄氏度量级;有的是作用力,牛顿量级。如果把它统一加权成一个罚函数项,那个权重会非常难调,任意一个取值选择都可能让优化方向发生偏移。
我的做法是采用约束支配原则。具体来说,比较两个个体时,先看约束违反程度,违反小的个体胜出;如果两者违反程度相同,再比较目标函数。这套逻辑不会引入额外的人工权重参数,算法的行为和基准函数测试时的行为一致性也更高。在Matlab里实现时,核心代码其实很短,就是先算约束违反总量,再通过一个比较函数完成非支配排序里的个体比较。
我在课题组的测试中发现,把盘式制动器问题直接丢给不带约束处理的原始TCM,搜索过程会浪费大量时间在不可行域里游荡,原因是成员合作机制会引导个体朝代表个体靠拢,而代表个体本身可能正处在某个弧形不可行区域的边缘,这就把错误方向当成局部最优来开发了。改用约束支配之后,种群会先快速进入一个可行域,再在可行域内部逐步逼近Pareto前沿。
3.3 工程问题与基准函数的差异
虽然盘式制动器的目标函数只有两个,但工程问题和WFG基准函数之间有非常本质的差异:基准函数的前沿是已知的,算法跑完你可以直接对比误差;工程问题没有所谓“标准前沿”,你只能从物理规律出发,判断算法给出的解是否合理。
我用IMOCTCM跑盘式制动器问题时,得到的解集分布在质量约0.3到3.5公斤、停止时间约2到6秒的区间范围内。这个结果和工程文献中给出的参考解基本一致,说明算法在这个问题上没有跑偏,而且相对原始TCM,靠近质量轻、停止时间短这一角的一组解明显更加密集,说明竞争学习机制确实在引导种群向更优的Pareto前沿方向收敛。
另外,工程问题的评估成本比基准函数高一个量级,盘式制动器问题虽然可以写出解析表达式,但每一次评估都要同时计算所有约束,整体耗时是WFG函数的数倍。所以在这种场景下,最大评估次数我设置得比基准测试宽松一些,基准问题是五万次评估,工程问题放到十万次。
4. IMOCTCM在Matlab中的核心模块与实现细节
4.1 主循环和数据结构
Matlab实现上,我没有使用任何第三方优化工具箱,整个算法自包含,只需要一台装有Matlab的机器就能跑。种群数据结构使用标准的矩阵形式,每一行是一个个体,前几列放决策变量,中间放目标函数值,最后放约束违反量。部落结构用元胞数组保存,方便动态增删成员。
主循环的逻辑是:初始化并划分部落 → 部落间竞争 → 部落内合作 → 施加高斯扰动 → 执行竞争学习 → 环境选择 → 更新外部档案。整个循环在最大评估次数内反复执行,外部档案负责保存所有非支配解,最终输出的就是这组档案。
关于评估次数预算的控制,我建议不要用迭代次数来控制,而是在评估函数内部维护计数器,因为不同的变异和交叉策略会产生不同数量的子代个体,用代数控制容易导致实际评估次数不一致,后期画收敛曲线时会出现自变量的不可比问题。
4.2 高斯扰动的实现与动态参数设计
高斯扰动在代码层面不复杂,但参数设计很关键。我采用的是动态衰减的标准差策略,初始sigma为决策变量搜索范围的0.15倍,随迭代线性衰减至0.01倍。前期扰动幅度大,帮助种群快速探索不同区域;后期扰动幅度变小,只在小范围内做精细调整和局部逃逸。这个设计参考了模拟退火里的降温思想,但实现起来更简单。
下面是高斯扰动核心函数,作用在单个个体上,生成一个满足边界约束的新个体:
matlab复制function offspring = gaussianPerturb(parent, sigma, lb, ub)
delta = sigma .* randn(size(parent));
offspring = parent + delta;
% 边界处理:反射法,避免个体堆积在边界上
for j = 1:length(offspring)
if offspring(j) < lb(j) || offspring(j) > ub(j)
offspring(j) = lb(j) + rand * (ub(j) - lb(j));
end
end
end
这里的边界处理我特别提一下,最常见的做法是纯截断,也就是超过边界就贴到边界上,但这样会让大量个体堆积在变量空间的边界位置,种群多样性快速下降。我后来改成反射加随机重初始化,对于越界个体,直接在当前解空间内随机重新生成一个位置。虽然看似浪费了一些信息,但实际测试下来,这种“宁缺毋滥”的边界策略反而让多样性保持得更好。
在算法里的触发时机上,高斯扰动不是每一代都对所有个体进行,而是设定一个触发概率,默认0.25。只有当随机数小于这个概率时,才对个体执行扰动。如果每一代都全量扰动,算法的收敛会被持续干扰,父代好不容易积累的位置信息全都打散了。
4.3 竞争学习算子的实现
竞争学习算子的核心逻辑是:随机选出两个个体,在不进行比较前先看它们的支配关系,胜者获得引导权,败者在胜者邻域内重新定位,但保留一定的自主探索分量。我参考了两两竞争学习的常见实现方式,写成了如下形式:
matlab复制function [winner, loser] = competitiveLearning(p1, p2)
c1 = constraintViolation(p1);
c2 = constraintViolation(p2);
if c1 < c2
winner = p1; loser = p2;
elseif c2 < c1
winner = p2; loser = p1;
else
if dominates(p1.obj, p2.obj)
winner = p1; loser = p2;
elseif dominates(p2.obj, p1.obj)
winner = p2; loser = p1;
else
% 互不支配时用拥挤距离判断
if crowdingDistance(p1) >= crowdingDistance(p2)
winner = p1; loser = p2;
else
winner = p2; loser = p1;
end
end
end
% 败者向胜者学习,同时保留差异化探索
eta = 0.6 + 0.3 * rand;
loser.position = winner.position + eta * (winner.position - loser.position) ...
+ 0.1 * randn(size(loser.position)) .* (ub - lb);
end
这里的关键参数是eta,它的取值范围在[0.6, 0.9]之间。eta越接近1,败者向胜者靠拢得越彻底,局部开发越强但多样性损失越快;eta越接近0.6,败者保留自身信息越多,探索更强。我实测下来0.6到0.9的动态随机取值效果最好,既保证了开发强度,又不至于让种群全军覆没。
还有一个值得注意的细节是,竞争学习的触发时机放在高斯扰动之后。这样做的好处是,先由高斯扰动产生一批带有随机性的新位置,再由竞争学习在这些新位置的基础上进一步优化,两者形成串联而不是并联的关系。如果反过来,先竞争学习再高斯扰动,扰动会破坏掉竞争学习好不容易形成的引导方向。
4.4 与WFG问题和工程模型的接口封装
为了让算法能够方便地在WFG1-WFG9和盘式制动器两类问题上切换,我设计了统一的测试问题接口。每个问题只需要提供三个函数:初始化、评估、边界获取。这样主算法完全不关心测试问题内部是什么物理含义,只需要调用接口。
对WFG系列,我复现了官方C代码的标准实现并改写为Matlab版本,在目标数M和决策变量数K上做了参数化支持。有一点需要特别提醒,WFG问题中位置参数和距离参数的分配方式非常讲究,比如WFG1默认M-1个位置参数加K个距离参数,改错任何一个都不行。我一开始按直觉把所有变量都当距离参数处理,结果WFG2的前沿坏了,多段断开全部变形,浪费了好几天排查时间。
对盘式制动器工程问题,则是在评估函数里同时加入约束计算和目标函数计算。数据结构保持和WFG问题完全一致,只是最后一列多了一个约束违反量,而WFG问题的所有约束值为0。
5. 消融实验与参数整定的实测过程
5.1 四组对比设计和实验配置
消融实验是判断两个改进策略是否有效、各自起什么作用的关键手段。我设置了四组对比:原始TCM作为基线、只加高斯扰动、只加竞争学习、以及完整版IMOCTCM。每组算法在WFG1-WFG9上独立运行21次,取IGD和HV的中位数和标准差作为评价指标。
实验参数统一设置为:目标数M=2,位置参数M-1=1,距离参数6,总决策变量数K=7,最大评估次数50000,种群大小200。四个算法共用同样的外部档案策略和环境选择机制,唯一区别就是改进模块的有无,保证对比公平。
这里有一个统计学上的细节需要说明:只跑一次或者三次得出的结论是站不住脚的,进化算法具有随机性,单次结果好坏可能只是随机数种子的功劳。我用的是21次独立重复,每次都用Matlab的rng函数重置随机数种子,这样结果可以在Wilcoxon秩和检验下做显著性判断。
5.2 结果:两类改进在不同函数上的贡献差异
从我的实测结果来看,两类改进的贡献非常有意思地呈现出互补性。
纯高斯扰动在WFG4和WFG9上的IGD提升最明显,因为这两个问题都存在大量局部前沿,算法很容易陷进去,高斯扰动提供了一个持续的小概率逃逸通道。但在WFG1和WFG3上,纯高斯扰动几乎没有任何正面效果,WFG1的难点是收敛方向选择,光靠随机跳变帮不上忙,WFG3的难点是退化流形上的均匀分布,扰动多了反而让个体更容易偏离那条低维流形。
纯竞争学习在WFG1、WFG2上优势明显,这两个问题都需要更强的局部引导来精细开发前沿,而在WFG4上表现一般,甚至会因为过度向胜者聚拢而加速早熟。
完整版IMOCTCM在九个函数上的整体表现都优于两个单独改进的版本,且在WFG9上优势最大,IGD相对原始TCM下降了约40%。这个结果从直觉上说得通:难度复杂的问题,需要的是多种改进机制的协同配合,而不是某一把“万能钥匙”。
5.3 参数敏感性分析
参数敏感性我这里特意做了几组补充实验,因为只看默认参数下的结果无法回答“这个算法的改进到底是否足够鲁棒”的问题。
高斯扰动的初始sigma是关键参数。当初始sigma从默认的0.15倍搜索范围调整到0.05倍时,WFG4上的IGD比默认值恶化了约15%,原因是扰动力度太小,难以跳出局部前沿;当调整到0.3倍时,WFG3上出现了明显的性能下降,原因是大幅度扰动频繁把个体从退化流形上“震飞”,需要很长时间才能重新落回流形。这说明0.15这个取值在探索和开发之间找到了一个不错的平衡点,而且这个平衡在多样性和低维流形两类问题上都站得住脚。
竞争学习触发概率的敏感性相对更低。把触发概率从默认的0.3调整到0.15或0.5时,九个WFG函数上的整体性能变化幅度都在5%以内,说明这个算子对参数不敏感,这也是我愿意把它作为算法通用模块推荐给大家的原因,至少在实际使用中不用为不同问题反复调参数。
6. 复现这套代码时我踩过的坑
6.1 WFG函数自实现时最容易错的位置
WFG函数的实现细节是我这次过程中花时间最多的地方,也是最容易出错的地方。最大的坑在于,WFG问题的每个变量都分为位置参数和距离参数,而这两类参数确实各自作用于不同的转换逻辑。不同函数对位置参数和距离参数的处理顺序、转换函数类型都不同,尤其是WFG1的bparam处理方式,和后面几个函数差异很大,稍不留神就会写成WFG2的逻辑,导致结果跟标准前沿对不上。
我验证实现是否正确的方法是小样本可视化:先用50个随机个体跑一遍某个WFG函数,把所有非占优解画出来,和官方论文中给出的标准Pareto前沿形状对比。这个方法非常有效,能在没有代码对照的情况下发现大部分实现错误。
另外,WFG函数的目标数增加时,位置参数数量也应该同步增加,部分函数还要求位置参数数量不能小于1,否则会有多余的D=1退化问题。
6.2 工程约束问题里的物理参数处理
盘式制动器的四个设计变量中,制动力F的数量级是10的3次方,而半径变量的数量级是10的1次方,差距很大。如果不做归一化直接进化,变异算子在较大数值范围内的探索会完全占据主导,小范围变量的精细调整被稀释掉了,导致搜索效率下降。我在代码里对四个变量做了min-max归一化映射到[0,1]区间,扰动和竞争学习都在归一化空间里进行,最后只在评估函数里反归一化还原到物理坐标。
还有一个小坑是温度约束的物理表达式,不同材料、不同车型的许用温度差别很大,在文献里看到约束系数直接套用别人的经常会得到完全不可行的解。我在实现时把温度相关的约束系数作为可配置参数列在了文件头部,并且在注释里标明了参考来源,方便后续替换。
6.3 结果的公平性和可重复性问题
实验公平性是这个方向最容易被人挑刺的地方。我的做法是,同一次对比实验中,所有算法共享完全相同的初始种群生成种子,只是后续进化过程中的随机数流不同。这样做的好处是,初始种群的差异不会成为结果差异的来源,最终表现差异基本可以归因到算法机制本身。
另外,一定要记录每一代的IGD和HV值,而不只是最终结果。记录每代指标的意义在于,你能看到算法收敛的过程:IGD曲线是单调下降还是先升后降,是快速下降到平台期还是缓慢持续下降,这些信息对分析和写论文都太重要了。我实际遇到过一种情况:最终IGD值差不多,但一个算法是第1000代就收敛了,另一个算法是第9500代才收敛,前者说明算法前期搜索效率高,后者虽然结果相同但效率低下。不记录逐代数据,这种差别很难被看出来。
代码里保留一个存储所有历史种子的变量也很有用,虽然占一点存储空间,但出问题时可以逐代回放,找到种群崩塌的具体时刻和原因。
6.4 工具箱和运行环境带来的兼容问题
Matlab版本对算法运行的影响虽然没有某些仿真软件那么明显,但差距仍然存在,尤其是在随机数生成器算法上。不同版本之间,rand、randn的底层实现一致,但在并行计算环境上,多个parfor循环并行执行时,随机数串行的独立性要尤其注意,否则不同worker的随机数可能出现重复,影响结果的可信度。
我的建议是,开销明确的循环不要盲目用parfor并行,先串行跑通,再用计时函数分析性能瓶颈。一个WFG问题的单次评估时间本来就短,并行分配的开销可能大于收益。盘式制动器这种评估稍微重一点的问题适合并行,但要注意用RandStream的独立子流确保每个worker的随机数序列互不相关。
关于Matlab工具箱依赖,我的代码只使用了基础矩阵运算和randn、rand等核心函数,没有依赖任何额外工具箱,这也是为了保证代码在不同环境中能够直接跑起来。如果你是用自己的WFG实现,建议对比一下官方源码的输出,确保基准实现无误,再开始验证算法本身。
我在实际迭代中发现,反复调整改进模块和参数,用真实工程问题反复验证,这个循环才是算法研究正确的打开方式。纯粹在标准测试函数上打磨出来的改进,换到实际场景往往支撑不起来。这次把盘式制动器问题接入IMOCTCM,验证的不只是算法效果,更是整整一套从“仿真到工程”的可迁移方法论。
