Kohonen 神经网络算法交易实战:从工具修正到基础铺垫(基础篇)
🧠

Kohonen 神经网络算法交易实战:从工具修正到基础铺垫(基础篇)

(1/3)· 多数 EA 优化只把自组织映像当肉眼看图,本篇先修源码错误再补工具链

案例拆解新手友好 第 1/3 篇
把 Kohonen 网络输出的拓扑图丢进眼睛里找参数,是很多人的日常。可一旦映像尺寸变了,同样数据训出来的结果也会漂,这坑藏在源码索引方式里。先搞懂工具再谈预测,否则后面全在沙上盖楼。

◍ 把 Kohonen 网络从看图工具变成自动决策器

MQL5 社区里已有文章介绍如何用自组织特征映像(Kohonen 映像)做市场可视化分析,但实盘里多数用法止步于 EA 优化结果拓扑图的肉眼判断。2019 年 2 月 18 日发布的该系列开篇提到,这种依赖个人眼光的做法只用到网络很少一部分能力,权重、学习率、学习范围等机制并未参与自动提取知识。 原文作者点出的缺口是:现有 Kohonen 实现大多只当“画图螺栓”,没有接入正规的参数寻优或行情预测闭环。想在 MT5 里验证这一点,直接打开一篇旧 Kohonen 文章里的 EA,跑一次优化,观察映像图是否真能替你排出参数优劣——大概率仍要靠你自己读图。 本篇后续会先改旧源码、修 bug、做增强,再谈用网络预测财经数据。若你连“神经元”“链接”“学习范围”都陌生,先补概念再跟代码,否则后续逐行拆解会像看天书。外汇与贵金属市场高杠杆、高波动,任何网络输出都只是概率倾向,不是下单指令。

「Kohonen 网络代码里的几个硬伤」

原 CSOM 和 CSOMNode 类里神经元用像素坐标当构造参数,逻辑别扭且让调试变复杂。两个格子相同、数据相同、随机种子相同的网络,仅因成像尺寸不同就学到不同结果——这是实打实的 bug。改成用数组下标检索:x、y 直接对应输出层的列号和行号,初始化换用 InitNode(x, y),可视化时改像素尺寸不影响神经元逻辑坐标。 输入没做常规化是另一处坑。当特征量纲差很大(比如优化结果里总利润几十万,而锋锐比率是个位数、恢复因子是个位数),网络基本只认大分量。用附带的随机矢量程序测过:三个分量分别设在 [0,1000]、[0,1]、[-1,+1],不常规化时网络只在第一个平面分出拓扑,后两个分量全是噪声,分析能力只剩三分之一;开了常规化三个平面才都有空间排列。 常规化用最普通的减均值除西格玛,均值归零、标准差归一到 1。CSOM::Normalize 里套用这个公式,InitNormalization 先扫一遍数据集算每个分量的均值和标准差。出口处 CSOM::Denormalize 做逆运算把权重映射回实际范围;因常规化后数据在零附近,神经元初始化权重也从 [0,1] 改成 [-1,+1],学习效率会好一些。 迭代计数也别按老办法写死。原代码把「喂一个矢量」算一次迭代,样本量一变就得重调。改成 EpochNumber 定义「学习时期」——全样本随机投喂一遍算一个时期,时长与数据集规模解耦。还可开 ValidationSetPercent(默认 0),比如设 10 就把 10% 当验证集,每个时期算常规化均方差,误差抬头就早停,缓解过拟合。 六边形网格原来只是画成六边形,计算仍按矩形邻域,错了。正确距离要按行奇偶修正横向偏移:获胜神经元在奇数行、目标在偶数行时 shiftx=+0.5,反过来 -0.5,相邻行真实距离变成 sqrt(1+0.25)=1.118。原代码里「距离平方>1 就跳过」的加速判断在六边形下会误杀邻神经元,附源码里已注释掉,真要加速请用 3 倍西格玛 阈值。当前实现仍非完全各向同性,留了进一步修正空间。外汇与贵金属模型训练涉及高风险,回测结论仅代表样本内概率倾向,实盘须自担风险。

MQL5 / C++
      for(class="type">int i = class="num">0; i < total_nodes; i++)
      {
         class="type">class="kw">double DistToNodeSqr = (m_som_nodes[winningnode].X() - m_som_nodes[i].X()) * (m_som_nodes[winningnode].X() - m_som_nodes[i].X())
                              + (m_som_nodes[winningnode].Y() - m_som_nodes[i].Y()) * (m_som_nodes[winningnode].Y() - m_som_nodes[i].Y());
         class="type">bool odd = ((winningnode % m_ycells) % class="num">2) == class="num">1;
         for(class="type">int i = class="num">0; i < total_nodes; i++)
         {
            class="type">bool odd_i = ((i % m_ycells) % class="num">2) == class="num">1;
            class="type">class="kw">double shiftx = class="num">0;
            if(m_hexCells && odd != odd_i)
            {
               if(odd && !odd_i)
               {
                  shiftx = +class="num">0.5;
               }
               else class=class="str">"cmt">// 反之亦然 (!odd && odd_i)
               {

SOM邻域影响半径怎么算

这段逻辑出自自组织映射(SOM)训练循环里对邻居节点的权重调整。先用获胜节点与候选节点的坐标差算出平方距离 DistToNodeSqr,其中 shiftx 在边界情况下会被设成 -0.5,用来修正网格拓扑的偏移。 当 DistToNodeSqr 小于 WS(邻域半径平方)时,用 MathExp(-DistToNodeSqr / (2*WS)) 得到高斯型影响系数 influence,再调用 AdjustWeights 按学习率和该系数更新节点权重;影响随距离衰减,离获胜节点越近改得越多。 代码里还判断了 DistToNodeSqr < 9*WS 的条件,意味着实际参与计算的潜在邻居范围可能扩展到半径的三倍(3倍距离平方),但只有落进 WS 内的才真正调权。外汇与贵金属市场波动剧烈、杠杆风险高,这类算法仅作特征聚类参考,实盘信号须自行验证。

MQL5 / C++
   shiftx = -class="num">0.5;
      }
      }
      class="type">class="kw">double DistToNodeSqr = (m_node[winningnode].GetX() - (m_node[i].GetX() + shiftx)) * (m_node[winningnode].GetX() - (m_node[i].GetX() + shiftx))
                         + (m_node[winningnode].GetY() - m_node[i].GetY()) * (m_node[winningnode].GetY() - m_node[i].GetY());
      if(DistToNodeSqr < WS)
      {
         class="type">class="kw">double influence = MathExp(-DistToNodeSqr / (class="num">2 * WS));
         m_node[i].AdjustWeights(data, learning_rate, influence);
      }
      if(DistToNodeSqr < class="num">9 * WS)

◍ 把 Kohonen 网络拆成计算与绘图两层

Kohonen 网络不一定要配界面:预测、压缩、分类这些事完全可以只吐数字(类编号或事件概率)。原文把功能拆成 CSOM 和 CSOMDisplay 两个类——前者只管计算、数据和网络文件的读写,后者专门放图形。相比把图形塞进基础类的做法,这种分层在后续选 EA 参数时更顺手:CSOMDisplay 用来挑参数,CSOM 用来做预测。 网格形态(矩形或六边形)属于基础架构,因为它直接改变距离计算方式。节点行列数、输入空间维度、网格类型都必须写进网络文件;从文件加载时也只能读文件,不能读程序设置。像像素尺寸、是否画细胞边界、是否显示字幕这类只影响视觉的参数,不进文件,训练完随便改。 未派生的 CSOM 不提供带控件的 GUI,所有配置走 MQL 程序 input。CSOMDisplay 仍保留实用能力:老示例里的 MaxPictures 输入还在,作为 maxpict 传给 CSOMDisplay::Init,决定一行塞几张平面映像。配合 ImageW、ImageH 能凑出全屏适配;但映像一多(比如扫大量 EA 设置),尺寸就被压到很难看清。 把 MaxPictures 设 0 可切到时间轴模式:映像从 OBJ_BITMAP_LABEL(像素对齐)换成 OBJ_BITMAP(时间刻度对齐),能放大到占满图表高度,用横向滚动条、滚轮或键盘拖拽。数量不再受屏幕限制,但要保证柱线数够。放大的好处是细胞里能看突触权重、命中次数、均值和离散度——默认不画,鼠标悬停弹 tooltip,里面带平面名和神经元坐标。 双击任意神经元,会在当前和所有平面里用互补色同步高亮,方便横向比对所有特征的活动。顺带一提,绘图已迁到标准类 CCanvas,Y 轴改成自上而下计数,图例因此跑到映像上方而非下方,功能无影响。

「给 Kohonen 网络补五张服务平面」

要把裸的 SOM 映像用起来,先得在 CSOMNode 里扩出 5 个附加维度。除了原本 2D 突触权重图,U 型矩阵、命中次数、量化误差、网络响应、聚类这五张服务平面才是后面能拿来做决策的底图。 U 型矩阵算的是每个神经元和四邻或六邻(正方形网格 nbhsize=4,六边形=6)的平均权重距离。权重差大的地方在调色板里是“峰”,差小的是“洼地”——洼地才是后续聚类的种子。遍历邻域用模板类 Neighborhood 的 loop 方法,它按模板方法范式留了 reset/iterate/getResult 三个抽象钩子,类型 T 参数化,避免每处都重写边界判断。 命中与量化误差在 RegisterPatternHit 里顺手算:m_hitCount++ 记密度,再取获胜神经元 code vector 算输入向量与权重的分量差平方和。CSOM::CalculateStats 在每个 epoch 收敛阶段被调,把所有量化误差求和去比 m_dataMSE,就是前面验证章说的 NMSE;返回值一旦开始抬头,Train 就停。 网络响应最简单,给每个神经元投喂信号(缺测试向量就喂零向量)记下激活偏移,用来判断获胜神经元是不是压着一群接近的对手。最后聚类平面,先用 ALGLIB 的 KMeansGenerate(头文件 <alglib/dataanalysis.mqh>,KMEANS_RETRY_NUMBER=10 次重选初值),但它必须预置聚类数、球状假设、易陷局部极小,且 ALGLIB 自带随机数状态不随 MathSrand 重置,OnInit 里不清 CMath::m_state 就难复现。 替代方案直接吃自己人:把神经元按 GetDistance()*MathSqrt(GetMSE()) 排序(原伪代码用 GetDistance()/sqrt(GetHitsCount()),乘以量化误差更稳),从最小 U 距离起建聚类,邻域检查交给 Neighborhood 的后代类,只“流”向峰值高度更低的邻居。这套不预置数量、形状随意、按重要性排序的玩法我称作 Korotky 聚类。多轮实测里 K-均值给出的中心在分析优化结果时反而更差,所以后面只接这套。外汇/贵金属样本做 SOM 聚类波动大,结论仅代表历史样本结构,实盘高风险。

交给小布盯盘看盘口
这些诊断与可视化思路小布盯盘的 AIGC 已内置,打开对应品种页即可看到网络特征分布的简化视图,你专注决策就好。

常见问题

基础用法是把 EA 优化结果做成拓扑映像做直观分析,正规做法可自动提取参数集合或预测财经范围,本篇先补齐工具。
旧源码用像素坐标索引神经元,映像大小会干扰计算;改为行列数字索引后,坐标与图像尺寸解耦,结果才稳定。
不同特征量纲差异会让距离计算偏向大数值分量,网络收敛偏向失真,修正源码时需补上常规化步骤。
目前小布内置的是简化特征分布视图而非完整训练,复杂网络构建仍要在 MT5 端写代码,小布负责替你省去重复看盘劳动。
建议先弄懂网络、层、神经元、权重等概念再往下看,本篇定位基础篇,重源码修正与工具改进而非从头讲课。