在 MetaTrader 5 中使用自组织特征映射(Kohonen 映射)·进阶篇
◍ 自组织映射网络的初始化与训练入口
这段声明定义了一个 CSOM 类实例 KohonenMap,封装了自组织映射(SOM)常用的几个动作:Train 跑训练、Render 出图、ShowBMP 把位图贴到图表上,以及 AddVectorToTrainingSet / AddTrainPattern 往训练集里塞样本。ColToRGB 与 RGB256 负责颜色分量和整型色值互转,RGB256 里用了 r+256*g+65536*b 的线性编码,和 MT5 调色板底层一致。 OnInit 里先 MathSrand(200) 固定随机种子,保证每次加载 EA 权值初始化可复现。随后 KohonenMap.InitParameters(10000,15,20,400,400) 设定了 10000 次迭代、15×20 的神经元网格、400×400 的输出位图——这三个数直接决定训练耗时和图像分辨率,在 MT5 实盘加载前值得先在小周期回测里跑一遍看资源占用。 训练集加了 8 组 RGB 向量:纯红(255,0,0)、绿(0,128,0)、蓝(0,0,255)、深绿(0,100,0)、深蓝(0,0,139)、黄(255,255,0)、橙(255,165,0)、紫(128,0,128)。AddTrainPattern 只收三维输入,说明这个 SOM 把颜色当三维特征做聚类,不涉任何行情数据。 调用顺序很直白:加完样本就 Train(),再 Render() 生成位图,最后用 ShowPattern 把指定颜色和其文字标题画出来。外汇与贵金属图表上挂这类脚本属于高风险环境下的附加可视化,建议先在离线账户验证内存与刷新频率。
class=class="str">"cmt">//--- train method class="type">void Train(); class=class="str">"cmt">//--- render method class="type">void Render(); class=class="str">"cmt">//--- shows the bmp image on the chart class="type">void ShowBMP(class="type">bool back); class=class="str">"cmt">//--- adds a vector to training set class="type">void AddVectorToTrainingSet(class="type">class="kw">double &vector[]); class=class="str">"cmt">//--- shows the pattern title class="type">void ShowPattern(class="type">class="kw">double c1,class="type">class="kw">double c2,class="type">class="kw">double c3,class="type">class="kw">string name); class=class="str">"cmt">//--- adds a pattern to training set class="type">void AddTrainPattern(class="type">class="kw">double c1,class="type">class="kw">double c2,class="type">class="kw">double c3); class=class="str">"cmt">//--- returns the RGB components of the class="type">color class="type">void ColToRGB(class="type">int col,class="type">int &r,class="type">int &g,class="type">int &b); class=class="str">"cmt">//--- returns the class="type">color by RGB components class="type">int RGB256(class="type">int r,class="type">int g,class="type">int b) const {class="kw">return(r+class="num">256*g+class="num">65536*b); } class=class="str">"cmt">//--- deletes image from the chart class="type">void NetDeinit(); }; CSOM KohonenMap; class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert initialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnInit() { MathSrand(class="num">200); class=class="str">"cmt">//--- initialize net, class="num">10000 iterations will be used for training class=class="str">"cmt">//--- the net contains 15x20 nodes, bmp image size 400x400 KohonenMap.InitParameters(class="num">10000,class="num">15,class="num">20,class="num">400,class="num">400); class=class="str">"cmt">//-- add RGB-components of each class="type">color from training set KohonenMap.AddTrainPattern(class="num">255, class="num">0, class="num">0); class=class="str">"cmt">// Red KohonenMap.AddTrainPattern( class="num">0,class="num">128, class="num">0); class=class="str">"cmt">// Green KohonenMap.AddTrainPattern( class="num">0, class="num">0,class="num">255); class=class="str">"cmt">// Blue KohonenMap.AddTrainPattern( class="num">0,class="num">100, class="num">0); class=class="str">"cmt">// Dark green KohonenMap.AddTrainPattern( class="num">0, class="num">0,class="num">139); class=class="str">"cmt">// Dark blue KohonenMap.AddTrainPattern(class="num">255,class="num">255, class="num">0); class=class="str">"cmt">// Yellow KohonenMap.AddTrainPattern(class="num">255,class="num">165, class="num">0); class=class="str">"cmt">// Orange KohonenMap.AddTrainPattern(class="num">128, class="num">0,class="num">128); class=class="str">"cmt">// Purple class=class="str">"cmt">//--- train net KohonenMap.Train(); class=class="str">"cmt">//--- render map to bmp KohonenMap.Render(); class=class="str">"cmt">//--- show patterns and titles for each class="type">color KohonenMap.ShowPattern(class="num">255, class="num">0, class="num">0,"Red");
「自组织映射里的颜色训练与渲染」
在 MT5 里用自组织映射(SOM)做颜色聚类时,KohonenMap 对象先通过 ShowPattern 把若干 RGB 向量灌进显示层:例如 (0,128,0) 标为 Green、(0,0,255) 标为 Blue、(255,165,0) 标为 Orange,共 7 种基准色参与拓扑排布。随后 ShowBMP(false) 将生成的位图直接绘到图表上,不另存文件。 初始化阶段用 InitParameters(10000,15,20,400,400) 设定训练步数 10000、15×20 的节点栅格,以及 400×400 的输出图像尺寸;HexagonalCell 与 ShowBorders 这两个 input 布尔量分别控制六边形单元与边界线的绘制开关。 针对颜色场景,CSOMWeb 类继承自 CSOM 并改写两个方法:AddTrainColor 把整型颜色值经 ColToRGB 拆成 r/g/b 三通道,塞进长度为 3 的 vector 数组再送入训练集;ShowColor 则负责在对应获胜节点上标注颜色名。下面这段是 AddTrainColor 的核心实现,逐行拆开看逻辑很直白。 别把 RGB 当归一化输入 MQL5 里 ColToRGB 出来的 r/g/b 是 0–255 整数,直接进 SOM 会让数值尺度偏粗;若你后面接外汇tick颜色编码,建议先除以 255 再做距离计算,否则高通道权重会碾压低通道。
class="type">void CSOMWeb::AddTrainColor(class="type">int col) { class="type">class="kw">double vector[]; class=class="str">"cmt">// 声明 class="type">class="kw">double 数组,用来装 RGB 三分量 ArrayResize(vector,class="num">3); class=class="str">"cmt">// 把数组长度定为 class="num">3(R、G、B) class="type">int r=class="num">0; class=class="str">"cmt">// 红通道初值 class="type">int g=class="num">0; class=class="str">"cmt">// 绿通道初值 class="type">int b=class="num">0; class=class="str">"cmt">// 蓝通道初值 ColToRGB(col,r,g,b); class=class="str">"cmt">// 把整型颜色 col 拆成 r/g/b 三个整数 vector[class="num">0]=r; class=class="str">"cmt">// 第 class="num">0 维放红 vector[class="num">1]=g; class=class="str">"cmt">// 第 class="num">1 维放绿 vector[class="num">2]=b; class=class="str">"cmt">// 第 class="num">2 维放蓝 AddVectorToTrainingSet(vector); class=class="str">"cmt">// 把该向量加入 SOM 训练集 ArrayResize(vector,class="num">0); class=class="str">"cmt">// 释放数组内存 }
把 132 个网页色塞进自组织映射
上面那段循环把单个颜色转成 RGB 后交给 ShowPattern 显示,真正喂给网络的原料是下面这个 web_colors 数组。它一口气列了 132 个 MQL5 预定义颜色常量,从 clrBlack 到 clrWhite 排满整张光谱,相当于给 Kohonen 映射准备了 132 个训练样本点。 外汇和贵金属图表上套这类颜色聚类时,高风险在于历史调色分布不代表未来波动结构,样本量 132 只是静态调色板规模,不暗示任何行情概率。 CSOMWeb KohonenMap 这一行才是真正声明网络实例,前面数组只是待处理的输入池。开 MT5 把 web_colors 长度改成你自己的品种色带数量,可能更容易看出聚类边界。
ColToRGB(col,r,g,b); ShowPattern(r,g,b,name); } class=class="str">"cmt">//--- web colors array class="type">color web_colors[class="num">132]= { clrBlack, clrDarkGreen, clrDarkSlateGray, clrOlive, clrGreen, clrTeal, clrNavy, clrPurple, clrMaroon, clrIndigo, clrMidnightBlue, clrDarkBlue, clrDarkOliveGreen, clrSaddleBrown, clrForestGreen, clrOliveDrab, clrSeaGreen, clrDarkGoldenrod, clrDarkSlateBlue, clrSienna, clrMediumBlue, clrBrown, clrDarkTurquoise, clrDimGray, clrLightSeaGreen, clrDarkViolet, clrFireBrick, clrMediumVioletRed, clrMediumSeaGreen, clrChocolate, clrCrimson, clrSteelBlue, clrGoldenrod, clrMediumSpringGreen, clrLawnGreen, clrCadetBlue, clrDarkOrchid, clrYellowGreen, clrLimeGreen, clrOrangeRed, clrDarkOrange, clrOrange, clrGold, clrYellow, clrChartreuse, clrLime, clrSpringGreen, clrAqua, clrDeepSkyBlue, clrBlue, clrMagenta, clrRed, clrGray, clrSlateGray, clrPeru, clrBlueViolet, clrLightSlateGray, clrDeepPink, clrMediumTurquoise, clrDodgerBlue, clrTurquoise, clrRoyalBlue, clrSlateBlue, clrDarkKhaki, clrIndianRed, clrMediumOrchid, clrGreenYellow, clrMediumAquamarine, clrDarkSeaGreen, clrTomato, clrRosyBrown, clrOrchid, clrMediumPurple, clrPaleVioletRed, clrCoral, clrCornflowerBlue, clrDarkGray, clrSandyBrown, clrMediumSlateBlue, clrTan, clrDarkSalmon, clrBurlyWood, clrHotPink, clrSalmon, clrViolet, clrLightCoral, clrSkyBlue, clrLightSalmon, clrPlum, clrKhaki, clrLightGreen, clrAquamarine, clrSilver, clrLightSkyBlue, clrLightSteelBlue, clrLightBlue, clrPaleGreen, clrThistle, clrPowderBlue, clrPaleGoldenrod, clrPaleTurquoise, clrLightGray, clrWheat, clrNavajoWhite, clrMoccasin, clrLightPink, clrGainsboro, clrPeachPuff, clrPink, clrBisque, clrLightGoldenrod, clrBlanchedAlmond, clrLemonChiffon, clrBeige, clrAntiqueWhite, clrPapayaWhip, clrCornsilk, clrLightYellow, clrLightCyan, clrLinen, clrLavender, clrMistyRose, clrOldLace, clrWhiteSmoke, clrSeashell, clrIvory, clrHoneydew, clrAliceBlue, clrLavenderBlush, clrMintCream, clrSnow, clrWhite }; CSOMWeb KohonenMap;
◍ 自组织映射的初始化与归一化细节
这段初始化函数把 Kohonen 网络的训练前置工作一次性铺开:先用 MathSrand(200) 固定随机种子,保证每次跑 EA 时权重扰动可复现;随后 KohonenMap.InitParameters(10000,50,50,500,500) 设定 10000 次迭代、50x50 的神经元网格,输出位图尺寸 500x500。 训练集由 web_colors 数组灌入,ArraySize 拿到总数后逐个 AddTrainColor,再调 Train() 和 Render()。如果你在 MT5 里改了种子或迭代次数,颜色聚类图的边界会明显不同,这属于高随机性过程,外汇与贵金属行情建模时须警惕过拟合风险。 CSOMFood 类继承了 CSOM,并重写了 Train、Render 与 ShowPattern。关键在归一化:它用 ArrayResize 建了长度为 3 的 m_max_values / m_min_values,对训练集每个通道(j=0,1,2)扫描求极值,Print 出如 '0 m_min_value=0.0 m_max_value=255.0' 这类日志。 渲染时权重要先减最小值再除极差,乘 255 转成 RGB 整数:r=int(255*(w0-min0)/(max0-min0))。若跳过这步,神经元权重直接当颜色会溢出或发灰,图谱失去区分度。
class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert initialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">void OnInit() { MathSrand(class="num">200); class="type">int total_web_colors=ArraySize(web_colors); class=class="str">"cmt">//--- initialize net, class="num">10000 iterations will be used for training class=class="str">"cmt">//--- the net contains 15x20 nodes, bmp image size 400x400 KohonenMap.InitParameters(class="num">10000,class="num">50,class="num">50,class="num">500,class="num">500); class=class="str">"cmt">//-- add all web colors to training set for(class="type">int i=class="num">0; i<total_web_colors; i++) { KohonenMap.AddTrainColor(web_colors[i]); } class=class="str">"cmt">//--- train net KohonenMap.Train(); class=class="str">"cmt">//--- render map to bmp KohonenMap.Render(); class=class="str">"cmt">//--- show patterns and titles for each class="type">color for(class="type">int i=class="num">0; i<total_web_colors; i++) { KohonenMap.ShowColor(web_colors[i],ColorToString(web_colors[i],true)); } class=class="str">"cmt">//--- show bmp image on the chart KohonenMap.ShowBMP(false); } class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| CSOMFood class | class=class="str">"cmt">//+------------------------------------------------------------------+ class CSOMFood : class="kw">public CSOM { class="kw">protected: class="type">class="kw">double m_max_values[]; class="type">class="kw">double m_min_values[]; class="kw">public: class="type">void Train(); class="type">void Render(); class="type">void ShowPattern(class="type">class="kw">double c1,class="type">class="kw">double c2,class="type">class="kw">double c3,class="type">class="kw">string name); }; class=class="str">"cmt">//--- find minimal and maximal values of the training set ArrayResize(m_max_values,class="num">3); ArrayResize(m_min_values,class="num">3); for(class="type">int j=class="num">0; j<class="num">3; j++) { class="type">class="kw">double maxv=m_training_sets_array[class="num">3+j]; class="type">class="kw">double minv=m_training_sets_array[class="num">3+j]; for(class="type">int i=class="num">1; i<m_total_training_sets; i++) { class="type">class="kw">double v=m_training_sets_array[class="num">3*i+j]; if(v>maxv) {maxv=v;} if(v<minv) {minv=v;} } m_max_values[j]=maxv; m_min_values[j]=minv; Print(j,"m_min_value=",m_min_values[j],"m_max_value=",m_max_values[j]); } class=class="str">"cmt">// class="type">int r = class="type">int(m_som_nodes[ind].GetWeight(class="num">0)); class=class="str">"cmt">// class="type">int g = class="type">int(m_som_nodes[ind].GetWeight(class="num">1)); class=class="str">"cmt">// class="type">int b = class="type">int(m_som_nodes[ind].GetWeight(class="num">2)); class="type">int r=class="type">int ((class="num">255*(m_som_nodes[ind].GetWeight(class="num">0)-m_min_values[class="num">0])/(m_max_values[class="num">0]-m_min_values[class="num">0])));
「自组织映射里的权重着色与训练数据加载」
把 SOM 节点的第二、第三维权重映射到 0–255 的 RGB 色阶,是可视化聚类地图的常见做法。下面两行把节点 ind 的权重按各维度最小最大值归一化后乘 255 强转整型,分别作为绿(g)和蓝(b)通道,配合其他维度就能在 BMP 上看出节点分布。 int g=int ((255*(m_som_nodes[ind].GetWeight(1)-m_min_values[1])/(m_max_values[1]-m_min_values[1]))); int b=int ((255*(m_som_nodes[ind].GetWeight(2)-m_min_values[2])/(m_max_values[2]-m_min_values[2]))); 训练集不限于行情,也可以用任意多维样本。上面那张食物表有 24 行,字段是蛋白质 / 碳水 / 脂肪占比,例如牛排三项为 20.9 / 0.0 / 7.9,巴西坚果是 15.5 / 2.9 / 68.3——脂肪维度拉满的节点会和黄油(1.0 / 0.0 / 81.0)在地图上靠近。 初始化函数里先 MathSrand(200) 固定随机种子,再从 DataFileName 载入训练数据;若 LoadTrainDataFromFile 返回失败直接 Print 报错并 return(1)。成功后依次 Train()、Render()、ShowTrainPatterns()、ShowBMP(false) 把自组织地图画到图表上。 鸢尾花数据集是前 8 行样本,四个形态字段加品种标签 setosa,花萼长宽为 5.1/3.5 到 4.6/3.4 之间,花瓣长宽都极小(1.3–1.7 / 0.2–0.4),这类低维稠密样本适合拿来验证 KohonenMap 的聚类分离效果。开 MT5 把这两份 csv 格式数据塞进同款 CSOM 类,改一行种子数就能对比地图稳定性。
class="type">int g=class="type">int ((class="num">255*(m_som_nodes[ind].GetWeight(class="num">1)-m_min_values[class="num">1])/(m_max_values[class="num">1]-m_min_values[class="num">1]))); class="type">int b=class="type">int ((class="num">255*(m_som_nodes[ind].GetWeight(class="num">2)-m_min_values[class="num">2])/(m_max_values[class="num">2]-m_min_values[class="num">2]))); class=class="str">"cmt">//+------------------------------------------------------------------+ class=class="str">"cmt">//| Expert initialization function | class=class="str">"cmt">//+------------------------------------------------------------------+ class="type">int OnInit() { MathSrand(class="num">200); class=class="str">"cmt">//--- load patterns from file if(!KohonenMap.LoadTrainDataFromFile(DataFileName)) { Print("Error in loading data for training."); class="kw">return(class="num">1); } class=class="str">"cmt">//--- train net KohonenMap.Train(); class=class="str">"cmt">//--- render map KohonenMap.Render(); class=class="str">"cmt">//--- show patterns from training set KohonenMap.ShowTrainPatterns(); class=class="str">"cmt">//--- show bmp on the chart KohonenMap.ShowBMP(false); class="kw">return(class="num">0); }
用鸢尾花样本验证特征聚类边界
上面这组样本是经典鸢尾花数据集的前 50 行 setosa 与后 2 行 versicolor,每行四个浮点特征依次是花萼长、花萼宽、花瓣长、花瓣宽,末尾为类别标签。setosa 的花瓣长集中在 1.0~1.9 之间,而 versicolor 前两行直接跳到 4.5 和 4.7,边界极其清晰。 在 MT5 里如果你用 CSVMultiClassifier 类做监督训练,把这类带标签的纯文本丢进去,能直观看到线性核在小样本上的分离表现。外汇和贵金属行情虽不是这种静态特征,但用类似思路做状态切片时,高风险品种也容易出现这种“断崖式”特征跃迁。 拿这两行 versicolor 当探针:当某个维度数值从 1.x 突增至 4.x,分类器置信度往往瞬间翻转。开 MT5 自己喂几行试试,比看任何说明书都来得直接。