交易中的神经网络:对比形态变换器(终章)·综合运用
反向传播里的梯度注销与二层描述构造
这段逻辑跑在神经网络反向传播阶段,先对编码器、motifs、prop 三个模块做反激活(DeActivation),任一失败直接 return false 中断。注意判断条件是 Activation() != None,说明未激活的层不参与梯度回传,省掉无效计算。 随后 motifs.calcAlignmentGradient 以 cAtomEncoder.AsObject() 为参照、参数 true 计算对齐梯度;接着两个倒序 for 循环从 Total()-2 扫到 0,逐级调用 calcHiddenGradients 把梯度向更早的投影层传递,循环里只要某一层空指针或计算失败就退出。 cMotifEncoder 与 NeuronOCL 都用了 SumAndNormilize 做梯度累加归一,参数序列里有 window 和末尾的 1,暗示按时间窗做缩放;temp 缓冲在 SetGradient 间倒手,避免覆盖上游梯度。外汇与贵金属模型用这套结构时,过拟合概率偏高,务必用小样本先验证。 最后 layer 2 用 new CLayerDescription() 建描述符,type 设 defNeuronAMCT,window 绑 BarDescr;units 数组写死 {HistoryBars, 100},代表 Bars 与 Properties 两维,ArrayCopy 返回值小于 temp.Size() 就判失败。开 MT5 把 HistoryBars 调小到 50 附近,能直观看到二层输入维度变化。
if (cAtomEncoder.Activation() != None) { if (!DeActivation(cAtomEncoder.getOutput(), cAtomEncoder.getGradient(), cAtomEncoder.getGradient(), cAtomEncoder.Activation())) class="kw">return false; } if (motifs.Activation() != None) { if (!DeActivation(motifs.getOutput(), motifs.getGradient(), motifs.getGradient(), motifs.Activation())) class="kw">return false; } if (prop.Activation() != None) { if (!DeActivation(prop.getOutput(), prop.getGradient(), prop.getGradient(), prop.Activation())) class="kw">return false; } if(!motifs.calcAlignmentGradient(cAtomEncoder.AsObject(), true)) class="kw">return false; for (class="type">int i = cMotifProjection.Total() - class="num">2; i >= class="num">0; i--) { motifs = cMotifProjection[i]; if (!motifs || !motifs.calcHiddenGradients(cMotifProjection[i + class="num">1])) class="kw">return false; } for (class="type">int i = cPropertyProjection.Total() - class="num">2; i >= class="num">0; i--) { prop = cPropertyProjection[i]; if (!prop || !prop.calcHiddenGradients(cPropertyProjection[i + class="num">1])) class="kw">return false; } if (!cPropertyDecoder.calcHiddenGradients(cPropertyProjection[class="num">0]) || !cMotifEncoder.calcHiddenGradients(cPropertyDecoder.AsObject())) class="kw">return false; CBufferFloat *temp = cMotifEncoder.getGradient(); if (!cMotifEncoder.SetGradient(cConcatenate.getGradient(), false) || !cMotifEncoder.calcHiddenGradients(cMotifProjection[class="num">0]) || !SumAndNormilize(temp, cMotifEncoder.getGradient(), temp, window, false, class="num">0, class="num">0, class="num">0, class="num">1) || !cMotifEncoder.SetGradient(temp, false) || !NeuronOCL.calcHiddenGradients(cMotifEncoder.AsObject())) class="kw">return false; temp = NeuronOCL.getGradient(); if (!NeuronOCL.SetGradient(cConcatenate.getGradient(), false) || !NeuronOCL.calcHiddenGradients(cAtomEncoder.AsObject()) || !SumAndNormilize(temp, NeuronOCL.getGradient(), temp, window, false, class="num">0, class="num">0, class="num">0, class="num">1) || !NeuronOCL.SetGradient(temp, false)) class="kw">return false; class=class="str">"cmt">//--- class="kw">return true; } class=class="str">"cmt">//--- layer class="num">2 if(!(descr = new CLayerDescription())) class="kw">return false; descr.type = defNeuronAMCT; descr.window = BarDescr; class=class="str">"cmt">// Window(Indicators to bar) { class="type">int temp[] = {HistoryBars, class="num">100}; class=class="str">"cmt">// Bars, Properties if(ArrayCopy(descr.units, temp) < (class="type">int)temp.Size()) class="kw">return false; }
◍ Transformer 编码器的关键维度与层数设定
在 MT5 里搭一个基于注意力机制的行情编码器时,描述结构体里的几个字段直接决定模型容量和推理开销。下面这段配置把键维度压到嵌入维度的一半,层数设 5,注意力头步长设 4,属于中等复杂度的折中方案。 descr.window_out = EmbeddingSize / 2; // 键维度取嵌入尺寸一半,控制 QK 点积的计算宽度 descr.layers = 5; // 堆叠 5 层编码器,层数越多拟合越强但易过拟合 descr.step = 4; // 头步长 4,影响多头注意力的切片方式 descr.batch = 1e4; // 批大小 1 万,显存吃紧时可能要下调 descr.activation = None; // 层间不用额外激活 descr.optimization = ADAM; // 用 ADAM 优化器更新权重 if(!encoder.Add(descr)) // 把描述体加进编码器,失败就清内存并返回 { delete descr; return false; } 实盘前建议把 batch 从 1e4 往下调到 2e3 附近跑通再说,外汇与贵金属波动剧烈、杠杆高风险大,配置只是起点,回测不过就是废纸。
descr.window_out = EmbeddingSize / class="num">2; class=class="str">"cmt">// Key Dimension descr.layers = class="num">5; class=class="str">"cmt">// Layers descr.step = class="num">4; class=class="str">"cmt">// Heads descr.batch = class="num">1e4; descr.activation = None; descr.optimization = ADAM; if(!encoder.Add(descr)) { class="kw">delete descr; class="kw">return false; }
「三个月只跑21笔,统计模型的天花板」
框架在 MQL5 里写完,下一步就是拿真实历史数据验成色。我们照例用覆盖 2023 全年的离线训练集做迭代训练,每迭代几次就按当前策略对 agent 的操作反馈刷新一次数据集,让训练目标尽量贴住实盘动作。 训练集和测试集上都出了正收益,但模型只肯下单极少次。把测试窗口拉到 3 个月,总共才成交 21 笔,胜率刚过一半。余额曲线前 1.5 个月爬升,之后横盘——这不算翻车,反而印证了设计前提:模型只统计训练集里出现过的市场状态。 一年训练数据大约只撑得起 1.2 到 1.5 个月的像样表现。顺着推,若喂十年数据,有可能得到稳跑一年的模型,更大的集子也可能识别出更多关键形态、把交易频率提上来。不过这还只是假设,真要坐实得继续深挖模型。外汇与贵金属属高风险品种,此类统计结论不构成立仓依据,请以 MT5 策略测试器自测为准。
别急着下结论
前面三篇把原子-基序对比变换器(AMCT)的骨架拆开了:烛条当原子、形态当基序,用对比学习去区分哪些局部结构带信息、哪些是噪声。变换器在这套框架里负责抓烛条与形态之间的长期依赖,理论上能比单纯看K线组合多一层市场行为推断。 我们在 MT5 里落地了全套流程——Research.mq5 收样本、Study.mq5 训模型、Test.mq5 跑历史。实测结果偏冷:训出来的模型在真实数据上交易信号极少,说明信息性形态筛选阈值或对比损失权重可能还需要调。外汇与贵金属属高风险品种,这类实验模型不能直接当信号源。 潜力不在「马上能跑」,而在它把价格结构分层编码的思路。下一步若想验证,可先改 NeuroNet.mqh 里的采样窗口,把基序长度从 5 根放宽到 8 根,看 Test.mq5 的信号密度是否改善。