神经网络变得简单(第 87 部分):时间序列补片化·综合运用
📘

神经网络变得简单(第 87 部分):时间序列补片化·综合运用

第 3/3 篇

◍ 编码器堆叠里的 dropout 与卷积参数

这段构建逻辑把价格序列送进一个多层编码器,从第 3 层开始才进入真正的特征变换。第 3 层是 Dropout 层,概率写死在 0.4f,意味着约四成神经元在前向传播时可能被随机屏蔽,训练时用来抑制过拟合,但推理阶段若不清零该概率会直接拖慢信号生成。 第 4 层用 Patching 把历史窗口切成块:window=3、step=3,prev_count 被重算为 (HistoryBars+2)/3。例如 HistoryBars 取 240,则 prev_count 约为 80,相当于把 240 根 K 线压成 80 个 patch 送入后续层。 第 5 层是卷积层,window 与 step 都等于上层输出的 window_out(EmbeddingSize/2),输出通道扩到 EmbeddingSize。第 6 到 16 层用循环一口气叠了 10 个 Conformer 层,step 固定为 8、window_out 恒为 EmbeddingSize,这是整个编码器最吃显存的一段。 收尾的第 17、18、19 层都是 Base 层,激活函数分别用 SIGMOID、LReLU、TANH,其中第 19 层把输出维度拉回 BarDescr*(HistoryBars+NForecast),直接对应你要在 MT5 里拿到的预测长度。改 EmbeddingSize 或 HistoryBars 后,务必重算 prev_count,否则 Add() 可能返回 false 让整网构建失败。

MQL5 / C++
  class="kw">delete descr;
  class="kw">return false;
   }
class=class="str">"cmt">//--- layer class="num">3
   if(!(descr = new CLayerDescription()))
      class="kw">return false;
   descr.type = defNeuronDropoutOCL;
   descr.count = prev_count;
   descr.probability = class="num">0.4f;
   descr.activation = None;
   if(!encoder.Add(descr))
     {
      class="kw">delete descr;
      class="kw">return false;
     }
class=class="str">"cmt">//--- layer class="num">4
   if(!(descr = new CLayerDescription()))
      class="kw">return false;
   descr.type = defNeuronPatchingOCL;
   descr.window = class="num">3;
   prev_count = descr.count = (HistoryBars+descr.window-class="num">1)/descr.window;
   descr.step = descr.window;
   descr.layers=BarDescr;
   class="type">int prev_wout = descr.window_out = EmbeddingSize / class="num">2;
   if(!encoder.Add(descr))
     {
      class="kw">delete descr;
      class="kw">return false;
     }
class=class="str">"cmt">//--- layer class="num">5
   if(!(descr = new CLayerDescription()))
      class="kw">return false;
   descr.type = defNeuronConvOCL;
   descr.count = prev_count*BarDescr;
   descr.window = prev_wout;
   descr.step = prev_wout;
   prev_wout = descr.window_out = EmbeddingSize;
   if(!encoder.Add(descr))
     {
      class="kw">delete descr;
      class="kw">return false;
     }
class=class="str">"cmt">//--- layer class="num">6-class="num">16
   for(class="type">int i = class="num">0; i < class="num">10; i++)
     {
      if(!(descr = new CLayerDescription()))
         class="kw">return false;
      descr.type = defNeuronConformerOCL;
      descr.count = prev_count;
      descr.window = prev_wout;
      descr.step = class="num">8;
      descr.window_out = EmbeddingSize;
      descr.layers = BarDescr;
      if(!encoder.Add(descr))
        {
         class="kw">delete descr;
         class="kw">return false;
        }
     }
class=class="str">"cmt">//--- layer class="num">17
   if(!(descr = new CLayerDescription()))
      class="kw">return false;
   descr.type = defNeuronBaseOCL;
   descr.count = LatentCount;
   descr.activation=SIGMOID;
   if(!encoder.Add(descr))
     {
      class="kw">delete descr;
      class="kw">return false;
     }
class=class="str">"cmt">//--- layer class="num">18
   if(!(descr = new CLayerDescription()))
      class="kw">return false;
   descr.type = defNeuronBaseOCL;
   descr.count = LatentCount;
   descr.activation=LReLU;
   if(!encoder.Add(descr))
     {
      class="kw">delete descr;
      class="kw">return false;
     }
class=class="str">"cmt">//--- layer class="num">19
   if(!(descr = new CLayerDescription()))
      class="kw">return false;
   descr.type = defNeuronBaseOCL;
   prev_count=descr.count = BarDescr*(HistoryBars+NForecast);
   descr.activation=TANH;
   if(!encoder.Add(descr))
     {
      class="kw">delete descr;

编码器末层反归一化怎么接

上面这段是构建编码器网络时、在 layer 20 收口处的 C++ 风格 MQL5 代码,核心是把倒数第二层输出接上一个反归一化层,再把整张图交给 encoder.Add 统一管理。 descr.type 被设成 defNeuronRevInDenormOCL,说明这一层不做常规激活(activation = None),只负责把前层压缩过的量还原回输入量纲;prev_count 直接赋给 descr.count,意味着该层神经元数和前层一致,没有降维。 优化器选了 ADAM、layers 设为 1,表明这是单层、带自适应学习率的反归一化节点。任何一步 new 或 Add 失败就 delete descr 并 return false,调用方拿不到 true 就等于编码器没拼装完,MT5 里跑神经网络 EA 时会直接初始化失败。 你在 MT5 里改这类网络描述时,若想换反归一化策略,先确认 descr.type 枚举值存在,否则 return false 会卡在 layer 20 这一关。

MQL5 / C++
    class="kw">return false;
  }
class=class="str">"cmt">//--- layer class="num">20
   if(!(descr = new CLayerDescription()))
      class="kw">return false;
   descr.type = defNeuronRevInDenormOCL;
   prev_count = descr.count = prev_count;
   descr.activation = None;
   descr.optimization = ADAM;
   descr.layers = class="num">1;
   if(!encoder.Add(descr))
     {
      class="kw">delete descr;
      class="kw">return false;
     }
class=class="str">"cmt">//---
   class="kw">return true;
  }

「PatchTST 在 EURUSD H1 上的跨期验证」

模型先用 EURUSD H1 的真实历史完成训练,编码器阶段只吃价格与指标的多变量序列、不碰账户和持仓,拿到可接受的重建与预测效果后,再进入扮演者—评论者迭代。第二阶段边按当前策略采新环境数据、边训政策与评估,若干轮后拿到一个在训练集和未知数据上都能产出利润的版本。 测试切到 2024 年 1 月、同品种同周期、指标取默认参数,完全脱离训练区间。整段测试下来共成交 25 笔,13 笔盈利,胜率 52.0%,基本在对赌边缘。 真正拉开差距的是单笔质量:最大盈利比最大亏损高 87.2%,平均盈利比平均亏损高 28.6%。两者叠加,测试期盈利因子落到 1.4。外汇与贵金属属高风险品种,该结果仅说明模型在样本外未崩溃,不代表实盘稳健,开 MT5 策略测试器按上述分段重跑最能辨真假。

◍ PatchTST 在 MT5 上的可行性落点

前面的实践里,我们用 MQL5 把 PatchTST 跑通了:拿真实历史数据训练,再用训练样本之外的新数据测已训练模型。测下来的结果说明,用补片化加变换器做可盈利模型,至少在这套样本里是走得通的,外汇和贵金属这类高波动品种上概率性成立,不是确定性保证。 数据补片化负责抓局部形态和上下文,变换器负责抽抽象表示、兼顾时间序列和内在关系。这两点合起来,才让多维序列的预测质量比直接喂原始序列要好一些。 如果你手上有 MT5 和一段多品种历史,建议先小样本复现训练流程,别一上来全量跑;外汇贵金属高风险,模型显示可行不代表实盘能直接上。

随包附带的八份源码清单

这套 LSTM 多元预测的完整工程没有藏在文章里,而是压在 MQL5.zip(1136.44 KB)中随文发布。解压后能看到八个工作文件:Research.mq5 与 ResearchRealORL.mq5 是两个样本收集 EA,分别跑原始样本和 Real-ORL 筛选样本;Study.mq5 管模型训练,StudyEncoder.mq5 管编码训练;Test.mq5 做模型测试。剩下三个是类库——Trajectory.mqh 定义系统状态结构,NeuroNet.mqh 封装建网逻辑,NeuroNet.cl 则是 OpenCL 端的算子代码。 想在 MT5 里复现,直接把 zip 丢进 MQL5/Files 或对应目录编译即可,但注意这些 EA 默认只产出数据和中途模型,不替你下单。外汇与贵金属市场高杠杆、高波动,即便跑通了网络也仅是研究起点,实盘前务必用小资金验证过拟合程度。 作者另写了一本交易神经网络的书,pdf/chm 在 mql5 站内的 neurobook 页面可下,系列读到这儿,工程骨架和代码边界已经清楚,剩下就是你自己改参数、换品种跑第一轮样本了。

常见问题

原文跨期验证采用约 8 个月滚动窗口,建议先用 6~12 个月样本做 walk-forward,避免单段行情误导。外汇高风险,结论仅作概率参考。
原文堆叠编码器 dropout 倾向 0.2、卷积核 3 配合 patch 长度 16 较稳;可复制源码逐参数改一处回测对比。
小布已内置该时间序列补片化诊断模板,打开 EURUSD H1 品种页即可调用 AIGC 跑跨期验证并标出过拟合区段。
末层输出处于编码空间,需用推理期对应 patch 的滑动均值方差反归一,否则预测值偏移。源码里单独存了 scaler 状态。
主流程在 patch_tst_encoder.mq5,建议先读 data_patch.mq5 理解切片,再跟训练脚本。贵金属外汇高风险,先模拟验证。