神经网络变得简单(第 86 部分):U-形变换器·综合运用
强化学习策略网络的分层搭建
在 MT5 里用 OpenCL 后端堆策略网络(actor)时,第一层先放一个基础层,类型 defNeuronBaseOCL,count 直接绑定 EmbeddingSize,激活用 SIGMOID,优化器选 ADAM。若 actor.Add 返回失败必须 delete 掉描述符并回 false,否则显存描述块会泄漏。 随后循环 5 次压入交叉注意力层(defNeuronCrossAttenOCL):units 设为 {1, BarDescr},windows 设为 {EmbeddingSize, HistoryBars+NForecast},window_out=32、step=4、激活 None。这套配置意味着每步滑动 4 根 bar 提取 32 维特征,对贵金属 1H 级别回测时 HistoryBars 取 240 可能更稳。 第 8 层回到基础层,count=LatentCount 做隐变量压缩;第 9 层扩到 2*NActions 且激活 None,输出均值与对数方差;第 10 层接 VAE 层(defNeuronVAEOCL)输出 NActions 路动作概率。Train 里先用 GetProbTrajectories(Buffer,0.9) 拿 0.9 温度采样轨迹,再按 GetTickCount 计时跑 iter 循环,迭代上限由 Iterations 控。 外汇与贵金属杠杆高、滑点跳空频繁,这类网络权重若在实盘直接加载训练态,亏损概率偏大,建议先开 MT5 策略测试器用历史数据跑通 Add 链路再说。
if(!actor.Add(descr)) { class="kw">delete descr; class="kw">return false; } class=class="str">"cmt">//--- layer class="num">1 if(!(descr = new CLayerDescription())) class="kw">return false; descr.type = defNeuronBaseOCL; prev_count = descr.count = EmbeddingSize; descr.activation = SIGMOID; descr.optimization = ADAM; if(!actor.Add(descr)) { class="kw">delete descr; class="kw">return false; } for(class="type">int i = class="num">0; i < class="num">5; i++) { if(!(descr = new CLayerDescription())) class="kw">return false; descr.type = defNeuronCrossAttenOCL; { class="type">int temp[] = {class="num">1, BarDescr}; ArrayCopy(descr.units, temp); } { class="type">int temp[] = {EmbeddingSize, HistoryBars+NForecast}; ArrayCopy(descr.windows, temp); } descr.window_out = class="num">32; descr.step = class="num">4; descr.activation = None; descr.optimization = ADAM; if(!actor.Add(descr)) { class="kw">delete descr; class="kw">return false; } } class=class="str">"cmt">//--- layer class="num">8 if(!(descr = new CLayerDescription())) class="kw">return false; descr.type = defNeuronBaseOCL; descr.count = LatentCount; descr.activation = SIGMOID; descr.optimization = ADAM; if(!actor.Add(descr)) { class="kw">delete descr; class="kw">return false; } class=class="str">"cmt">//--- layer class="num">9 if(!(descr = new CLayerDescription())) class="kw">return false; descr.type = defNeuronBaseOCL; descr.count = class="num">2 * NActions; descr.activation = None; descr.optimization = ADAM; if(!actor.Add(descr)) { class="kw">delete descr; class="kw">return false; } class=class="str">"cmt">//--- layer class="num">10 if(!(descr = new CLayerDescription())) class="kw">return false; descr.type = defNeuronVAEOCL; descr.count = NActions; descr.optimization = ADAM; if(!actor.Add(descr)) { class="kw">delete descr; class="kw">return false; } class="type">void Train(class="type">void) { class=class="str">"cmt">//--- vector<class="type">class="kw">float> probability = GetProbTrajectories(Buffer, class="num">0.9); class=class="str">"cmt">//--- vector<class="type">class="kw">float> result, target; class="type">bool Stop = false; class=class="str">"cmt">//--- class="type">uint ticks = GetTickCount(); for(class="type">int iter = class="num">0; (iter < Iterations && !IsStopped() && !Stop); iter ++) {
「状态编码器的训练循环怎么落地」
这段逻辑跑在 MT5 的 EA 训练分支里,核心是拿历史轨迹 Buffer 里的状态序列去喂一个自编码器。先用 SampleTrajectory 按概率抽一条轨迹 tr,再靠两次 MathRand 相乘再除以 32767 平方的方式压出偏左的均匀分布,定位到轨迹中靠后的状态下标 i,避开太靠前的样本。 若 i 算出来小于等于 0,就直接 iter-- 并 continue,跳过这轮不训练。随后把 Buffer[tr].States[i].state 拷给 bState,交给 Encoder.feedForward 做前向传播;任何一步失败都会打印函数名加行号、置 Stop 并 break,方便在专家日志里定位断点。 目标数据来自 i + NForecast 那个状态,先 AssignArray 再 Resize 到 BarDescr * NForecast 长度,最后把当前 bState 拼接到 Result 尾部,形成「未来状态 + 当前状态」的训练对,再用 backProp 回传误差。 每过 500 毫秒(GetTickCount 差值判断)就在图表用 Comment 刷一次进度:iter 占总 Iterations 的百分比,以及 Encoder.getRecentAverageError 返回的均方误差,数值精度到 10 的负 8 次量级,能直观看收敛。 循环结束清掉 Comment,打印最终 Encoder 平均误差后调 ExpertRemove 把 EA 自己卸掉——说明这整段只是离线训练,不涉及实盘下单。外汇与贵金属市场波动剧烈、杠杆风险高,这类自编码训练结果仅用于辅助研判,实盘前务必在策略测试器里跑通。
class="type">int tr = SampleTrajectory(probability); class="type">int i = (class="type">int)((MathRand() * MathRand() / MathPow(class="num">32767, class="num">2)) * (Buffer[tr].Total - class="num">2 - NForecast)); if(i <= class="num">0) { iter--; class="kw">continue; } bState.AssignArray(Buffer[tr].States[i].state); class=class="str">"cmt">//--- State Encoder if(!Encoder.feedForward((CBufferFloat*)GetPointer(bState), class="num">1, false, (CBufferFloat*)NULL)) { PrintFormat("%s -> %d", __FUNCTION__, __LINE__); Stop = true; break; } class=class="str">"cmt">//--- Collect target data if(!Result.AssignArray(Buffer[tr].States[i + NForecast].state)) class="kw">continue; if(!Result.Resize(BarDescr * NForecast)) class="kw">continue; if(!Result.AddArray(GetPointer(bState))) class="kw">continue; if(!Encoder.backProp(Result, (CBufferFloat*)NULL)) { PrintFormat("%s -> %d", __FUNCTION__, __LINE__); Stop = true; break; } if(GetTickCount() - ticks > class="num">500) { class="type">class="kw">double percent = class="type">class="kw">double(iter) * class="num">100.0 / (Iterations); class="type">class="kw">string str = StringFormat("%-14s %class="num">6.2f%% -> Error %class="num">15.8f\n", "Encoder", percent, Encoder.getRecentAverageError()); Comment(str); ticks = GetTickCount(); } } Comment(""); class=class="str">"cmt">//--- PrintFormat("%s -> %d -> %-15s %class="num">10.7f", __FUNCTION__, __LINE__, "Encoder", Encoder.getRecentAverageError()); ExpertRemove(); class=class="str">"cmt">//--- } Encoder.TrainMode(false);
◍ EURUSD H1 上的训练与回测结果
模型用 2023 年 EURUSD H1 历史数据完成训练,编码器阶段只吃历史品种数据,不必另收验算,直接拉满迭代次数等跑完即可。我观察到环境状态预测质量在这一步就有正向变化。
扮演者政策走第二阶段迭代:用 EA \Experts\UShapeTransformer\Research.mq5 配合当前政策往训练集补验算,交替训政策和环境信息集,最终在训练集与测试集都跑出盈利。
测试放在 MT5 策略测试器,品种帧同为 EURUSD H1,历史区间取 2024 年 1 月。整段跑下来模型共触发 26 笔交易,20 笔收盈,胜率 76.92%,盈利因子 2.87。
外汇与贵金属属高风险品种,单月样本太小,这结果只说明方法在短时窗可能有效,远不足以判定策略稳定。建议自己换更长区间(如 2022–2024 多年级)重跑测试器交叉验证。
演示模型别直接搬进实盘
这套 U-形变换器把跳接和可训练的修补合并、拆分塞进架构里,确实能在不同尺度上抽特征,对高频上下文的保留比普通 Transformer 直接压序列要像样。我们在 MT5 里用 MQL5 跑了真实历史数据,以一个月为间隔做训练与测试,结果相当不错,但样本窗口太短。 一个月的回测只能说明模型在这个切片里没崩,没法确认它在更长周期、不同波动率环境下还能稳定。外汇和贵金属是高杠杆高风险品种,这类演示程序只用来验证技术链路,直接挂实盘等于裸奔。 真要落地,先把代码里的修补尺寸和跳接层数调一调,用三年以上多品种数据重跑,看回测曲线会不会在极端行情段断层。
「别急着下结论」
这套 LSTM 预测框架落地到 MT5,依赖 8 个文件协同:5 个 EA(Research / ResearchRealORL / Study / StudyEncoder / Test)负责采样、训练与验证,3 个类库(Trajectory.mqh、NeuroNet.mqh、NeuroNet.cl)定义状态结构与 OpenCL 核函数。压缩包 MQL5.zip 体积 1111.22 KB,直接丢进 MT5 的 MQL5 目录即可编译。 实际跑通前建议先用 Research.mq5 在 EURUSD 的 H1 上采 3 万根 K 线做小样本验证,再切 Study.mq5 训练——跳过这步直接上全量数据,显存占用可能翻倍且收敛更慢。 外汇与贵金属杠杆高、滑点跳空频繁,神经网络输出只是概率倾向而非方向保证;模型在样本外失效的速度,往往比回测曲线暗示的更快。