自组织特征映射 (Kohonen 映射) - 再访主题(基础篇)
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 向量,看返回索引是否落在你预期的价格形态簇附近——外汇与贵金属波动无序,聚类结果仅作概率参考,实盘高风险。
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(); };