神经Boid优化算法2(NOA2)·综合运用
(3/3)· 当Boid群智能遇上个体神经网络,前篇铺垫的模型如何在真实函数上跑出探索与开发的平衡
◍ 停滞重启与群体聚散的代码落点
优化器陷入停滞时,代码会把探索率按 1.5 倍放大但封顶 0.5,同时每累计 50 次无进展就触发部分重启:淘汰尾部 70% 个体、保留最优代理,并在边界内随机重撒坐标与微小速度。 若进展正常,探索率回退到参数位 [11] 的值,未配置时掉回 0.1 默认值——这意味着你调参时第 12 个输入直接决定「顺风顺水时的保守程度」。 聚合力 Cohesion 按邻居质量加权求质心:只纳入距离小于 distanceMax*cohesionDist 的代理,用各自质量 agent[i].m 做权重,再乘 cohesionWeight 拽向中心。分离力 Separation 则反向推开避免重叠,两者权重比例会明显改变种群在参数空间的铺开形态。 外汇与贵金属行情高波动、滑点随机,这类群体算法在 MT5 中回测大概率对止损与采样区间敏感,建议先固定 popSize=50、restart 比例 0.7 跑一轮 EURUSD 的 H1 观察收敛节奏。
explorationRate = MathMin(class="num">0.5, explorationRate * class="num">1.5); class=class="str">"cmt">// Perform a partial restart every class="num">50 stagnation iterations if (m_stagnationCounter % class="num">50 == class="num">0) { class=class="str">"cmt">// Restart class="num">70% of the population leaving the best agents class="type">int restart_count = (class="type">int)(popSize * class="num">0.7); for (class="type">int i = popSize - restart_count; i < popSize; i++) { for (class="type">int c = class="num">0; c < coords; c++) { agent [i].x [c] = u.RNDfromCI(rangeMin [c], rangeMax [c]); agent [i].dx [c] = (rangeMax [c] - rangeMin [c]) * u.RNDfromCI(-class="num">1.0, class="num">1.0) * class="num">0.001; } } class=class="str">"cmt">// Reset the stagnation counter m_stagnationCounter = class="num">0; } else { class=class="str">"cmt">// If progress is good enough, use the normal research level if (class="num">11 < ArraySize(params)) { explorationRate = params [class="num">11].val; } else { explorationRate = class="num">0.1; class=class="str">"cmt">// class="kw">default value } } } class=class="str">"cmt">// Find the center of mass of other boids and adjust the speed towards the center of mass class="type">void C_AO_NOA2::Cohesion(S_NeuroBoids_Agent &boid, class="type">int pos) { class="type">class="kw">double centerX []; ArrayResize(centerX, coords); ArrayInitialize(centerX, class="num">0.0); class="type">int numNeighbors = class="num">0; class="type">class="kw">double sumMass = class="num">0; for (class="type">int i = class="num">0; i < popSize; i++) { if (pos != i) sumMass += agent [i].m; } for (class="type">int i = class="num">0; i < popSize; i++) { if (pos != i) { if (Distance(boid, agent [i]) < distanceMax * cohesionDist) { for (class="type">int c = class="num">0; c < coords; c++) { centerX [c] += agent [i].x [c] * agent [i].m; } numNeighbors++; } } } if (numNeighbors > class="num">0 && sumMass > class="num">0.0) { for (class="type">int c = class="num">0; c < coords; c++) { centerX [c] /= sumMass; boid.dx [c] += (centerX [c] - boid.x [c]) * cohesionWeight; } } } class=class="str">"cmt">// Pushing away from other boids to avoid collisions class="type">void C_AO_NOA2::Separation(S_NeuroBoids_Agent &boid, class="type">int pos) { class="type">class="kw">double moveX []; ArrayResize(moveX, coords); ArrayInitialize(moveX, class="num">0.0); for (class="type">int i = class="num">0; i < popSize; i++) { if (pos != i) {
「分离、对齐与限速的三段式推进」
这段 NeuroBoids 的群体逻辑里,分离阶段先判断邻居距离是否小于 distanceMax 乘 separationDist,命中就把对方相对坐标差累加进 moveX,最后统一乘 separationWeight 回写速度。它只处理「太近就推开」,不关心方向优劣,所以权重调大容易让个体乱窜。 对齐函数 Alignment 则反过来收集邻域内 agent 的各轴速度,按 numNeighbors 取平均,再让当前 boid 的速度向均值靠拢,靠拢幅度由 alignmentWeight 控制。若邻域为空(numNeighbors=0),这步直接跳过,个体保持原速。 LimitSpeed 用平方和开根号算总速,低于 speedMax[0]*minSpeed 会被拉到最小速,高于 speedMax[0]*maxSpeed 则压回最大速;速近零时给各轴塞一个 (-1.0,1.0) 的随机微速避免死锁。外汇与贵金属行情用这类模型做聚类试探时,参数敏感度高,实盘前务必在 MT5 策略测试器里跑多品种回测。
if (Distance(boid, agent [i]) < distanceMax * separationDist) { for (class="type">int c = class="num">0; c < coords; c++) { moveX [c] += boid.x [c] - agent [i].x [c]; } } } } for (class="type">int c = class="num">0; c < coords; c++) { boid.dx [c] += moveX [c] * separationWeight; } } class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">// Align speed with other boids class="type">void C_AO_NOA2::Alignment(S_NeuroBoids_Agent &boid, class="type">int pos) { class="type">class="kw">double avgDX []; ArrayResize(avgDX, coords); ArrayInitialize(avgDX, class="num">0.0); class="type">int numNeighbors = class="num">0; for (class="type">int i = class="num">0; i < popSize; i++) { if (pos != i) { if (Distance(boid, agent [i]) < distanceMax * alignmentDist) { for (class="type">int c = class="num">0; c < coords; c++) { avgDX [c] += agent [i].dx [c]; } numNeighbors++; } } } if (numNeighbors > class="num">0) { for (class="type">int c = class="num">0; c < coords; c++) { avgDX [c] /= numNeighbors; boid.dx [c] += (avgDX [c] - boid.dx [c]) * alignmentWeight; } } } class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">// Speed limit class="type">void C_AO_NOA2::LimitSpeed(S_NeuroBoids_Agent &boid) { class="type">class="kw">double speed = class="num">0; for (class="type">int c = class="num">0; c < coords; c++) { speed += boid.dx [c] * boid.dx [c]; } speed = MathSqrt(speed); class=class="str">"cmt">// If the speed is not zero(prevent division by zero) if (speed > class="num">1e-10) { class=class="str">"cmt">// If the speed is too low, increase it if (speed < speedMax [class="num">0] * minSpeed) { for (class="type">int c = class="num">0; c < coords; c++) { boid.dx [c] = boid.dx [c] / speed * speedMax [c] * minSpeed; } } class=class="str">"cmt">// If the speed is too high, reduce it else if (speed > speedMax [class="num">0] * maxSpeed) { for (class="type">int c = class="num">0; c < coords; c++) { boid.dx [c] = boid.dx [c] / speed * speedMax [c] * maxSpeed; } } } else { class=class="str">"cmt">// If the speed is almost zero, set a small random speed for (class="type">int c = class="num">0; c < coords; c++) { boid.dx [c] = u.RNDfromCI(-class="num">1.0, class="num">1.0) * speedMax [c] * minSpeed; } } } class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
边界反弹与群体距离度量的代码内核
神经群算法里每个 agent 不能无限制漂出搜索域,KeepWithinBounds 做的就是边界硬约束:某一维坐标低于 rangeMin 就速度反向并加 0.1% 区间宽度的推力,高于 rangeMax 则反向减同样推力,避免贴边死锁。 Distance 用欧氏距离的平方根衡量两个 boid 的差距,逐维平方累加再开方,coords 维空间通用。回测中若把 coords 设为 2(比如两参数寻优),这个距离直接决定分离与聚合力的权重。 Revision 负责刷新全局最优:当某个 agent 的适应度 f 大于记录值 fB 就覆盖 cB 并清零停滞计数器;若前后最优差异小于 0.000001 视为无进展,explorationRate 按 params[11].val*0.5 与自身 0.9 倍取大值收缩,逼算法从探索转向利用。外汇与贵金属参数寻优高风险,过拟合可能让实盘概率优势消失。 让小布替你跑这套:把上面三段直接贴进 MT5 的 include 类文件,改 coords 和 popSize 就能在 EURUSD 15M 上验证收敛速度。
class=class="str">"cmt">// Keep the boid within the boundaries. If it gets too close to the edge, class=class="str">"cmt">// push it back and change direction. class="type">void C_AO_NOA2::KeepWithinBounds(S_NeuroBoids_Agent &boid) { for (class="type">int c = class="num">0; c < coords; c++) { if (boid.x [c] < rangeMin [c]) { boid.dx [c] *= -class="num">1.0; class=class="str">"cmt">// Add a small push from the border boid.dx [c] += (rangeMax [c] - rangeMin [c]) * class="num">0.001; } if (boid.x [c] > rangeMax [c]) { boid.dx [c] *= -class="num">1.0; class=class="str">"cmt">// Add a small push from the border boid.dx [c] -= (rangeMax [c] - rangeMin [c]) * class="num">0.001; } } } class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">// Calculate the distance between two boids class="type">class="kw">double C_AO_NOA2::Distance(S_NeuroBoids_Agent &boid1, S_NeuroBoids_Agent &boid2) { class="type">class="kw">double dist = class="num">0; for (class="type">int c = class="num">0; c < coords; c++) { dist += MathPow(boid1.x [c] - boid2.x [c], class="num">2); } class="kw">return MathSqrt(dist); } class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">//—————————————————————————————————————————————————————————————————————————————— class=class="str">"cmt">// Update the best solution found class="type">void C_AO_NOA2::Revision() { class=class="str">"cmt">// Update the best coordinates and fitness function value for (class="type">int i = class="num">0; i < popSize; i++) { class=class="str">"cmt">// Update the global best solution if (a [i].f > fB) { fB = a [i].f; for (class="type">int c = class="num">0; c < coords; c++) { cB [c] = a [i].c [c]; } class=class="str">"cmt">// Reset the stagnation counter when a better solution is found m_stagnationCounter = class="num">0; } } class=class="str">"cmt">// Check for progress to adapt the algorithm parameters class="type">bool hasProgress = MathAbs(fB - m_prevBestFitness) > class="num">0.000001; if (hasProgress) { m_prevBestFitness = fB; explorationRate = MathMax(params [class="num">11].val * class="num">0.5, explorationRate * class="num">0.9); } } class=class="str">"cmt">//——————————————————————————————————————————————————————————————————————————————
◍ NOA2 基准跑分与种群排名位置
把 NOA2 直接丢进三类标准测试函数:Hilly、Forest、Megacity,每个规模跑 10000 次函数调用。Hilly 在 5/25/500 规模下得分 0.4768 / 0.3076 / 0.2545;Forest 同规模 0.3238 / 0.2098 / 0.1574;Megacity 离散场景下 0.2708 / 0.1468 / 0.0973。三项加总 2.24472,折算百分比 24.94%,在 39 个对比算法里排最末。
| 可视化上能看出 Boid 集群在复杂地形里容易过早收拢,搜索覆盖偏窄。但算法对外暴露了全局参数,改 `50.0 | 0.6 | 0.001 | 0.005 | 0.03 | 0.1` 这一串就能切换邻域半径、分离/对齐/聚合权重,MT5 里接全局变量实时调参能看到行为模式跳变。 |
|---|
外汇与贵金属行情属高风险环境,这类弱基准的群体算法只适合做参数探索辅助,别拿去直接出信号。排进种群优化表仅作信息参考,实盘前务必自己跑回测。
「低维基准里跑分垫底的一批优化器」
上面这组对照把 CGO、ATAm、CFO、ASHA、ASBO、MEC 以及 NOA2 在 13 维测试函数上的结果摊开看,最后两列是综合评分 3.4~3.9 与相对百分比 38%~43%,而 NOA2 直接掉到 2.245 / 24.94。 这些算法在贵金属或外汇参数寻优里如果只跑低维基准,容易给人“够用”的错觉;但 NOA2 相对前几名掉了约 14 个百分点,说明同样迭代预算下收敛稳定性可能明显偏弱。 实盘前建议把候选优化器接进 MT5 的 frame 测试,用你自己的 EURUSD 或 XAUUSD 历史段重跑,别直接信这张表里的中段分数。外汇与贵金属杠杆高,参数过拟合会导致实盘回撤失控。
把 NOA2 当试验田而非实盘武器
神经 Boid 优化算法(NOA2)把集群智能的 Boid 模型与单层神经网络拧在一起:输入层吃坐标、速度、离最优解距离和适应度,输出层吐速度修正值与三条集群规则的自适应参数。当前实现没有隐藏层,输入维度固定为 coords*2+2,输出则是每坐标修正量加 3 个规则参数,调参空间大得离谱。 我在测试函数上把各种参数组合都跑了一遍,仍没揪出能打的理想配置;直方图评分区间 0–100,NOA2 实测得分极低,离 100 的理论上限差着量级。就现在这版,它只是混合优化思路的概念验证,别指望直接拿来寻优。 外汇和贵金属市场高波动、高杠杆,任何优化器上岗前都得先过历史回测与样本外验证。NOA2 的源码包(NOA2.zip,186.45 KB)和测试台 Test_AO_NOA2.mq5 都开放,值得你拉进 MT5 改两层网络或重排输入特征,看评分能不能往上抬。 真要上手,先跑通附带的统一测试平台 Testing AOs.mq5,对照 CalculationTestResults.mqh 出的排名表,确认自己改的版本是否真的优于原版——否则它永远只是个教学标本。