自组织特征映射 (Kohonen 映射) - 再访主题(基础篇)
📘

自组织特征映射 (Kohonen 映射) - 再访主题(基础篇)

第 1/3 篇

Kohonen 映射在 MT5 里的老话题新用

自组织特征映射(SOM,也就是 Kohonen 网络)在 MT5 生态里并不是新概念,但 2016 年那篇示例帖至今仍有 2,966 次查看和 43 条讨论,说明手动跑聚类做行情状态划分的需求一直没断。 它本质是先把高维行情特征(比如波动率、动量、价差结构)压缩到二维网格,让相似状态在地图上自动邻近。交易者拿这张图就能绕开主观划波段,直接看当前价格落在哪个神经元区块。 外汇和贵金属杠杆高、跳空频繁,用 SOM 做状态归类只是概率上的辅助,不能当成方向判决。开 MT5 把历史 tick 跑一遍映射,比看任何文字描述都来得直接。

「为什么重写了 Kohonen 映射那篇」

这篇是早先《在 MetaTrader 5 中使用自组织特征映射》的续作,但不是简单搬运。原素材被大比例重构,目标是让初学者和有经验的程序员都能把 Kohonen 神经网络直接挂进自己的 EA 或指标项目里,并改得动算法主体。 原文只跑了一段范形样本,但把力气花在了多种代码调用例子上。你开 MT5 新建一个空 EA,把下文代码块贴进去编译,就能看到映射初始化的最小可用骨架,比读纯理论快得多。 外汇与贵金属品种波动非线性强,神经网络映射仅作辅助特征提取,实盘前务必在策略测试器用历史数据回测,存在模型过拟合与滑点放大风险。

◍ Kohonen 网络怎么在无监督下完成集簇

自组织特征映射(SOM)是一层神经元网络,每个节点都直连 n 维输入向量(也叫范形)的全部分量。它不需要监督信号,靠竞争机制自己学:把一个范形丢进输入层,和该范形距离最近的那个神经元胜出,常用距离就是欧氏距离。 胜出神经元周围会划出一个学习半径,半径内的节点按 Kohonen 规则更新权重,半径外的一律不动。半径在训练初期最大,随迭代次数 k 增长逐步收缩,所以越到后程,只有紧贴胜者的节点才参与调整。 学习率拆成两块:群落函数 ni(d,k) 和随迭代反比下降的速度函数 a(k)=A/(B+k)。前期半径大、速度快,范形被粗略平均;末期速度趋零,权重才精准贴合输入分布。 实际跑 MT5 下的 CSOM_Net_Base::BestMatchingNode 找胜者、CSOMNode::AdjustWeights 调权重时,会撞上“死神经元”问题——初始权重离范形太远的节点永远赢不了。本实现用两招缓解:训练范形改用 C_PRNG_UD 随机抽,保证均匀分布;神经元初值只在范形可能出现的值域内随机化。这样即便有节点始终不赢,也大概率落在胜者邻域,训练期能当集簇间的桥。

把自组织映射塞进 MT5 的类级联

Kohonen 映射在 MT5 里的落地,核心是两个参数:范形维度 m_dimension 和节点总数 m_total_nodes。节点在内存里是一维数组 CSOMNode m_som_nodes[],但显示时折叠成 m_xcells * m_ycells 的长方形网格,每个节点带着自己的权重和平面坐标。 实际工程把功能拆成五层类继承:CSOM_Net_Base 只管网络本体,往上依次挂 Data、Train、Img、Demo。如果接别的 EA 用不到绘图,直接不继承 CSOM_Net_Img 就行,链路可砍。 基类 CSOM_Net_Base 暴露的公有函数里,BestMatchingNode 有两个重载:一个带位掩码向量,一个指定截取维度,都是拿来搜最匹配节点的。GetNode(int ind) 把指定索引的节点指针塞进 public_node 共用域,外部直接读,不用碰内部数组。 附带五个用例文件,序号对应类级联顺序,从纯网络到带演示逐步叠加。想验证的话,开 MT5 建个脚本 include 基类,调 BestMatchingNode 喂一组 double 向量,看返回索引是否落在你预期的价格形态簇附近——外汇与贵金属波动无序,聚类结果仅作概率参考,实盘高风险。

MQL5 / C++
class CSOMNode
  {
class="kw">protected:
  
   class=class="str">"cmt">//--- 节点域的坐标
   class="type">int               m_x1;
   class="type">int               m_y1;
   class="type">int               m_x2;
   class="type">int               m_y2;
  
   class=class="str">"cmt">//--- 节点中心坐标
   class="type">class="kw">double            m_x;
   class="type">class="kw">double            m_y;
  
   class=class="str">"cmt">//--- 节点权重
   class="type">class="kw">double            m_weights[];
   ...
class CSOM_Net_Base
class CSOM_Net_Data   : class="kw">public CSOM_Net_Base
class CSOM_Net_Train  : class="kw">public CSOM_Net_Data
class CSOM_Net_Img    : class="kw">public CSOM_Net_Train
class CSOM_Net_Demo   : class="kw">public CSOM_Net_Img
class CSOM_Net_Base
  {
class="kw">public:
  class=class="str">"cmt">//--- Kohonen 网络的共有节点
  CSOMNode          *public_node;
  class=class="str">"cmt">//--- 函数发送指定索引集合的节点到 public_node 共有域
  class="type">void              GetNode(class="type">int ind){ public_node=m_som_nodes[ind].GetObjPointer(); };
  class=class="str">"cmt">//--- 函数返回范形维度
  class="type">int               GetDimension(){class="kw">return(m_dimension);};
  class=class="str">"cmt">//--- 函数用于从文件中加载网络               
  class="type">bool              DownloadNet(class="type">class="kw">string file_name);
  class=class="str">"cmt">//--- 函数用来基于指定携带掩码的向量搜索最佳节点
  class="type">int               BestMatchingNode(class="kw">const class="type">class="kw">double &vector[]);
  class=class="str">"cmt">//--- 函数用来基于指定截取维度的向量搜索最佳节点
  class="type">int               BestMatchingNode(class="kw">const class="type">class="kw">double &vector[],class="type">int dimension);
  class=class="str">"cmt">//--- 函数填充位掩码并用来搜索相似的范形节点 (掩码确定哪些字段进行搜索)
  class="type">bool              InitSetByteMap(class="type">int num,class="type">bool value);
  };
  
class CSOM_Net_Data : class="kw">public CSOM_Net_Base
  {
class="kw">public:
  class=class="str">"cmt">//--- 函数用于从指定文件中加载训练数据
  class="type">bool              LoadPatternDataFromFile(class="type">class="kw">string filename);
  class=class="str">"cmt">//--- 函数用来在训练集合里添加向量
  class="type">void              AddVectorToPatternsSet(class="type">class="kw">double &vector[],class="type">class="kw">string title);
  class=class="str">"cmt">//--- 函数用于从训练集合里拷贝范形   
  class="type">bool              GetPatterns(class="type">int ind_pattern,class="type">class="kw">double &vector[]);
  class=class="str">"cmt">//--- 函数返回来自训练集合的范形名称  
  class="type">class="kw">string            GetTitlesPatterns(class="type">int ind_pattern);
  class=class="str">"cmt">//--- 函数返回范形总数
  class="type">int               GetTotalsPatterns();
  };

常见问题

Kohonen 保留神经元拓扑顺序,相邻簇在价格特征上连续;K 均值无此约束。做形态归类时前者更不易把相邻波动拆散。
多在权重初始化或胜者查找时未约束索引范围。初始化用随机样本子集、胜者比对加边界判断可避坑。
小布可自动跑无监督集簇并把当前 K 线归到对应神经元格,省去你手调类数和写循环,打开品种页直接看分群。
类级联让训练、距离计算、映射输出分层调用,避免旧版全局变量纠缠,后续换品种只改参数不碰核心。
小时图波动态建议先试 5~9 个神经元网格,看 silhouette 或手工翻图;过多易过拟合少量样本。