交易中的神经网络:受控分段·综合运用
📘

交易中的神经网络:受控分段·综合运用

第 3/3 篇

◍ LPC 神经元的重写接口与初始化链路

CNeuronLPC 在派生时把基类的虚函数几乎全量重写:Type() 直接返回 defNeuronLPC 常量,Save / Load 负责序列化,WeightsUpdate 接收源神经元与 tau 学习率做权重更新,SetOpenCL 则把计算上下文塞给 OpenCL 对象。 Init() 的入口参数暴露了这套结构的真实规模:window、heads、units_count、layers 均为 uint,且 cOne 用 window * units_count 作为初始化维度,cPrimitives 同样吃下 window * units_count。这意味着显存占用量级随窗口长度和单元数线性放大,贵金属 1 分钟序列若把 window 设到 512、units_count 设到 64,单这一层前向缓冲就超过 3 万个 float。 feedForward() 里有个训练开关判断:bTrain 为真时才先让 cPrimitives 吃 cOne 的输出,再走交叉注意力前传。calcInputGradients() 则在反向时额外调用 DiversityLoss(cPrimitives.AsObject(), iUnits, iWindow, true),说明梯度阶段强制做了多样性约束——对外汇高噪声样本,这倾向降低神经元同质化,但是否提升泛化需你在 MT5 用自有 tick 回测验证。 开 MT5 把上面 Init 的参数打印出来,比对你的显存上限,再决定 window 和 units_count 要不要砍半。

MQL5 / C++
class="kw">virtual class="type">int Type(class="type">void) const class="kw">override { class="kw">return defNeuronLPC; }
class=class="str">"cmt">//---
class="kw">virtual class="type">bool Save(class="type">int const file_handle) class="kw">override;
class="kw">virtual class="type">bool Load(class="type">int const file_handle) class="kw">override;
class=class="str">"cmt">//---
class="kw">virtual class="type">bool WeightsUpdate(CNeuronBaseOCL *source, class="type">class="kw">float tau) class="kw">override;
class="kw">virtual class="type">void SetOpenCL(COpenCLMy *obj) class="kw">override;
};
class="type">bool CNeuronLPC::Init(class="type">uint numOutputs, class="type">uint myIndex, COpenCLMy *open_cl,
                      class="type">uint window, class="type">uint window_key, class="type">uint heads, class="type">uint heads_kv,
                      class="type">uint units_count, class="type">uint units_count_kv, class="type">uint layers,
                      class="type">uint layers_to_one_kv, ENUM_OPTIMIZATION optimization_type, class="type">uint batch)
  {
   if(!CNeuronMLCrossAttentionMLKV::Init(numOutputs, myIndex, open_cl, window, window_key,
heads, window, heads_kv, units_count, units_count_kv,
layers, layers_to_one_kv, optimization_type, batch))
      class="kw">return class="kw">false;
   if(!cOne.Init(window * units_count, class="num">0, OpenCL, class="num">1, optimization, iBatch))
      class="kw">return class="kw">false;
   CBufferFloat *out = cOne.getOutput();
   if(!out.BufferInit(class="num">1, class="num">1) || !out.BufferWrite())
      class="kw">return class="kw">false;
   if(!cPrimitives.Init(class="num">0, class="num">1, OpenCL, window * units_count, optimization, iBatch))
      class="kw">return class="kw">false;
class=class="str">"cmt">//---
   class="kw">return true;
  }
class="type">bool CNeuronLPC::feedForward(CNeuronBaseOCL *NeuronOCL)
  {
   if(!NeuronOCL)
      class="kw">return class="kw">false;
   if(bTrain && !cPrimitives.FeedForward(cOne.AsObject()))
      class="kw">return class="kw">false;
   if(!CNeuronMLCrossAttentionMLKV::feedForward(cPrimitives.AsObject(), NeuronOCL.getOutput()))
      class="kw">return class="kw">false;
class=class="str">"cmt">//---
   class="kw">return true;
  }
class="type">bool CNeuronLPC::calcInputGradients(CNeuronBaseOCL *NeuronOCL)
  {
   if(!NeuronOCL)
      class="kw">return class="kw">false;
   if(!CNeuronMLCrossAttentionMLKV::calcInputGradients(cPrimitives.AsObject(), NeuronOCL.getOutput(),
                                                      NeuronOCL.getGradient(),
                                                      (ENUM_ACTIVATION)NeuronOCL.Activation()))
      class="kw">return class="kw">false;
   if(!DiversityLoss(cPrimitives.AsObject(), iUnits, iWindow, true))
      class="kw">return class="kw">false;
class=class="str">"cmt">//---
   class="kw">return true;
  }

多头注意力神经元的内部骨架

在 MT5 的 OpenCL 神经网络扩展里,CNeuronOCM 这个类直接继承自 CNeuronBaseOCL,专门用来搭多头交叉注意力结构。它把原始序列和上下文序列的窗口、单元数、头数分开记:iPrim* 管查询侧,iCont* 管键值侧,iWindowKey 单独锁键窗口,这样前向时能避免两套维度混算。 类里挂了 cQuery / cKey / cValue 三个 CLayer,以及 cMHAttentionOut、cAttentionOut、cResidual、cFeedForward,外加一个 CArrayInt 类型的 cScores 存注意力权重下标。也就是说,一次完整推理要在显存里同时维护至少 8 个层对象,显存占用随 heads 线性走,4 头 128 单元时单神经元缓冲轻松过 2MB。 两个虚函数 AttentionOut 和 AttentionInsideGradients 是核心,参数里 units / heads / units_kv / heads_kv / dimension 全部显式传入,说明头数在查询和键值两侧可以不一致——这是交叉注意力的典型写法。 feedForward 和 calcInputGradients 被 override 成直接返回 false,意味着这个类不走基类那套逐神经元前馈,梯度也不在这层回传,调用方必须靠外部的编码器闭环来驱动。开 MT5 把这段声明丢进自定义 EA 的神经网络头文件,编译过即说明你的 OpenCL 环境认这个继承链。

MQL5 / C++
class CNeuronOCM  :  class="kw">public CNeuronBaseOCL
  {
class="kw">protected:
   class="type">uint                iPrimWindow;
   class="type">uint                iPrimUnits;
   class="type">uint                iPrimHeads;
   class="type">uint                iContWindow;
   class="type">uint                iContUnits;
   class="type">uint                iContHeads;
   class="type">uint                iWindowKey;
   class=class="str">"cmt">//---
   CLayer              cQuery;
   CLayer              cKey;
   CLayer              cValue;
   CLayer              cMHAttentionOut;
   CLayer              cAttentionOut;
   CArrayInt           cScores;
   CLayer              cResidual;
   CLayer              cFeedForward;
   class=class="str">"cmt">//---
   class="kw">virtual class="type">bool        CreateBuffers(class="type">void);
   class="kw">virtual class="type">bool        AttentionOut(CNeuronBaseOCL *q, CNeuronBaseOCL *k, CNeuronBaseOCL *v,
                                     const class="type">int scores, CNeuronBaseOCL *out,
                                     const class="type">int units,
                                     const class="type">int heads,
                                     const class="type">int units_kv,
                                     const class="type">int heads_kv,
                                     const class="type">int dimension);
   class="kw">virtual class="type">bool        AttentionInsideGradients(CNeuronBaseOCL *q, CNeuronBaseOCL *k, CNeuronBaseOCL *v,
                                     const class="type">int scores, CNeuronBaseOCL *out,
                                     const class="type">int units, const class="type">int heads,
                                     const class="type">int units_kv, const class="type">int heads_kv,
                                     const class="type">int dimension);
   class=class="str">"cmt">//---
   class="kw">virtual class="type">bool        feedForward(CNeuronBaseOCL *NeuronOCL) class="kw">override { class="kw">return class="kw">false; }
   class="kw">virtual class="type">bool        calcInputGradients(CNeuronBaseOCL *NeuronOCL) class="kw">override { class="kw">return class="kw">false; }

「OCM 神经元的接口与权重冻结」

CNeuronOCM 类在 MT5 的 OpenCL 神经网络框架里承担原语(Primitives)与上下文(Context)两套序列的融合角色。它的声明里最值得注意的一点是 updateInputWeights(CNeuronBaseOCL *NeuronOCL) 被直接 override 返回 false,意味着这一类神经元不会通过基类默认的单输入通道去回传权重更新。 从 public 接口看,Init 的参数表暴露了它的结构约束:prim_window / prim_units / prim_heads 与 cont_window / cont_units / cont_heads 分别描述两套多头注意力的窗口与头数,batch 控制小批量大小。Type() 固定返回 defNeuronOCM,用于运行时类型判别。 真正参与训练的是三个带双指针的重载:feedForward 同时吃 Primitives 和 Context,calcInputGradients 与 updateInputWeights 也按这套双路结构计算。WeightsUpdate 保留基类风格的 tau 学习率系数,但底层只更新内部融合层,不触碰上游单路权重。 如果你在 EA 里继承这个类,记住基类版 updateInputWeights 已被锁死返回 false,想改上游梯度流必须重写双参数版本,否则回测里可能看到原语分支权重完全不动。外汇与贵金属模型训练波动大,此类冻结行为在高杠杆品种上容易放大过拟合风险。

MQL5 / C++
 class="kw">virtual class="type">bool        updateInputWeights(CNeuronBaseOCL *NeuronOCL) class="kw">override { class="kw">return class="kw">false; }
class="kw">public:
                    CNeuronOCM(class="type">void) {};
                   ~CNeuronOCM(class="type">void) {};
 class=class="str">"cmt">//---
 class="kw">virtual class="type">bool       Init(class="type">uint numOutputs, class="type">uint myIndex, COpenCLMy *open_cl,
                      class="type">uint prim_window, class="type">uint window_key, class="type">uint prim_units, class="type">uint prim_heads,
                      class="type">uint cont_window, class="type">uint cont_units, class="type">uint cont_heads,
                      ENUM_OPTIMIZATION optimization_type, class="type">uint batch);
 class=class="str">"cmt">//---
 class="kw">virtual class="type">int        Type(class="type">void) class="kw">override  const   {  class="kw">return defNeuronOCM; }
 class=class="str">"cmt">//---
 class="kw">virtual class="type">bool       Save(class="type">int const file_handle) class="kw">override;
 class="kw">virtual class="type">bool       Load(class="type">int const file_handle) class="kw">override;
 class=class="str">"cmt">//---
 class="kw">virtual class="type">bool       WeightsUpdate(CNeuronBaseOCL *source, class="type">class="kw">float tau) class="kw">override;
 class="kw">virtual class="type">void       SetOpenCL(COpenCLMy *obj) class="kw">override;
 class=class="str">"cmt">//---
 class="kw">virtual class="type">bool       feedForward(CNeuronBaseOCL *Primitives, CNeuronBaseOCL *Context);
 class="kw">virtual class="type">bool       calcInputGradients(CNeuronBaseOCL *Primitives, CNeuronBaseOCL *Context);
 class="kw">virtual class="type">bool       updateInputWeights(CNeuronBaseOCL *Primitives, CNeuronBaseOCL *Context);
};

◍ 一点提醒

RefMask3D 的思路是用 3D 参考分割把多模态市场形态里的关键特征拎出来,压住噪声和不相关输入,对贵金属与外汇这类高波动品种的多维状态识别有可验证的扩展空间。 前文已落地的 MQL5 对象只覆盖了两个建议模块,系列里配套的 Research.mq5、Study.mq5、Test.mq5 等七个文件(ZIP 约 1950 KB)才是能直接开 MT5 跑通样本收集与训练闭环的实体,单篇文章的篇幅远没把纵深讲完。 这类基于神经网络的判定只是概率倾向,外汇和贵金属杠杆交易风险极高,实盘前请用 Test.mq5 在策略测试器里先验证回测稳定性,别把实验室结论直接当信号源。

常见问题

检查重写接口的指针是否在创建后第一时间绑定权重数组,并在首根 K 线强制跑一次初始化函数,避免空指针导致后续计算失效。
实盘建议先设 4~8 个 head,过多会拉长推理耗时且过拟合,可用历史分段回测看哪个区间的均方误差最低。
可以,小布能直接读取品种页里的神经元状态,标出冻结位并提示是否该解冻,省去你手动翻接口代码的麻烦。
会,冻结后历史权重不变,复盘只跑前向不更新,若想重训需显式解除冻结标志再载入新样本。
把不同神经元的学习率混用,导致某段权重震荡,应按分段单独设学习率并做离线验证再上实盘。