神经网络变得简单(第 87 部分):时间序列补片化·综合运用
◍ 编码器堆叠里的 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 让整网构建失败。
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 这一关。
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 页面可下,系列读到这儿,工程骨架和代码边界已经清楚,剩下就是你自己改参数、换品种跑第一轮样本了。