交易中的神经网络:对比形态变换器·综合运用
📘

交易中的神经网络:对比形态变换器·综合运用

第 3/3 篇

反向传播里的梯度链式回传

这段逻辑跑在 Transformer 类网络的反向传播阶段,核心是把输出层的误差沿各子模块逐层往回送。任何一层 calcHiddenGradients 返回 false,整个回传立即中断并返回 false,说明梯度计算对中间结果的连续性要求极严。 先对缩放卷积层 cScale 做倒序回传:循环从 Total()-2 到 0,把第 i 层梯度交给 i+1 层去算,漏一层就可能让前面权重更新失真。多头注意力池化 cMHAttentionPooling 也类似,但循环下限是 >0,跳过了第 0 层由 AttentionGradient() 单独处理。 Key/Value 偏置组 cBKey、cBValue 同样倒序回传至第 0 层,之后距离层 cDistance 先取自身梯度暂存到 temp,再借 cTemp 做矩阵乘梯度并 SumAndNormilize(参数里 0,0,0,1 代表不跨批、按末维归一),最后把 temp 写回。 KV 投影层用 MatMulGrad 算转置矩阵梯度,维度参数是 iUnitsKV、iWindow、iUnits,末尾的 1 表示沿特定轴累加;随后 NeuronOCL 对 cTranspose 与 cQuery 继续回传。开 MT5 把这段塞进你自己的 COpenCLMyNetwork::calcHiddenGradients 里,断点跟一遍各 Total() 返回的值,能直接看出哪层在样本上最先裂掉。

MQL5 / C++
neuron.SetActivationFunction(SecondActivation);
  if(!DiversityLoss(AsObject(), iUnits, iWindow, true))
     class="kw">return false;
  for(class="type">int i = cScale.Total() - class="num">2; i >= class="num">0; i--)
     if(!((CNeuronBaseOCL*)cScale[i]).calcHiddenGradients(cScale[i + class="num">1]))
        class="kw">return false;
  if(!((CNeuronBaseOCL*)cMHAttentionPooling[cMHAttentionPooling.Total() - class="num">1]).calcHiddenGradients(cScale[class="num">0]))
     class="kw">return false;
  for(class="type">int i = cMHAttentionPooling.Total() - class="num">2; i > class="num">0; i--)
     if(!((CNeuronBaseOCL*)cMHAttentionPooling[i]).calcHiddenGradients(cMHAttentionPooling[i + class="num">1]))
        class="kw">return false;
  if(!AttentionGradient())
     class="kw">return false;
  for(class="type">int i = cGlobalContentBias.Total() - class="num">2; i > class="num">0; i--)
     if(!((CNeuronBaseOCL*)cGlobalContentBias[i]).calcHiddenGradients(cGlobalContentBias[i + class="num">1]))
        class="kw">return false;
  for(class="type">int i = cGlobalPositionalBias.Total() - class="num">2; i > class="num">0; i--)
     if(!((CNeuronBaseOCL*)cGlobalPositionalBias[i]).calcHiddenGradients(cGlobalPositionalBias[i + class="num">1]))
        class="kw">return false;
  for(class="type">int i = cBKey.Total() - class="num">2; i >= class="num">0; i--)
     if(!((CNeuronBaseOCL*)cBKey[i]).calcHiddenGradients(cBKey[i + class="num">1]))
        class="kw">return false;
  for(class="type">int i = cBValue.Total() - class="num">2; i >= class="num">0; i--)
     if(!((CNeuronBaseOCL*)cBValue[i]).calcHiddenGradients(cBValue[i + class="num">1]))
        class="kw">return false;
  if(!cDistance.calcHiddenGradients(cBKey[class="num">0]))
     class="kw">return false;
  CBufferFloat *temp = cDistance.getGradient();
  if(!cDistance.SetGradient(GetPointer(cTemp), false) ||
     !cDistance.calcHiddenGradients(cBValue[class="num">0]) ||
     !SumAndNormilize(temp, GetPointer(cTemp), temp, iUnits, false, class="num">0, class="num">0, class="num">0, class="num">1) ||
     !cDistance.SetGradient(temp, false)
     )
     class="kw">return false;
  neuron = cKVProjection[cKVProjection.Total() - class="num">1];
  if(!neuron ||
     !MatMulGrad(neuron.getOutput(), neuron.getGradient(),
                 cTranspose.getOutput(), cTranspose.getGradient(),
                 temp, iUnitsKV, iWindow, iUnits, class="num">1)
     )
     class="kw">return false;
  if(!NeuronOCL.calcHiddenGradients(cTranspose.AsObject()) ||
     !SumAndNormilize(NeuronOCL.getGradient(), Gradient, cTranspose.getGradient(), iWindow, false, class="num">0, class="num">0, class="num">0, class="num">1))
     class="kw">return false;
  if(!NeuronOCL.calcHiddenGradients(cQuery.AsObject()) ||

◍ 反向传播里的梯度聚合与多样性约束

这段 MT5 神经网络训练代码暴露了自定义 RNN 类在反向传播阶段的两个关键动作:先对输出层梯度做求和归一化,再施加多样性损失防止神经元同质化。 看 SumAndNormilize 的调用,参数里末位传了 1 作为归一化系数,前四个 0 分别控制偏移、缩放下限、缩放上限与截断阈值,意味着梯度只做纯求和后按固定权重 1 归一,不施加任何数值裁剪。 DiversityLoss 在 iUnits 与 iWindow 维度上计算,第三个参数 true 表示反向模式,用于在梯度回传时惩罚输出通道间的相关性。外汇与贵金属行情噪声大,若跳过这步,多个神经元可能学到同一类假规律,实盘泛化能力会明显退化,属高风险环节。 后面循环从 cKVProjection 倒数第二项往前逐层调 calcHiddenGradients,把上一层对象指针传入,说明键值投影层是严格按时间倒序回传的;任何一层返回 false 就直接中断训练,工程上必须保证每层神经元非空且梯度接口可用,否则整轮更新作废。

MQL5 / C++
!SumAndNormilize(NeuronOCL.getGradient(), cTranspose.getGradient(), NeuronOCL.getGradient(),
iWindow, false, class="num">0, class="num">0, class="num">0, class="num">1) ||
!DiversityLoss(NeuronOCL, iUnits, iWindow, true)
 )
   class="kw">return false;
 temp = neuron.getGradient();
 if(!neuron.SetGradient(GetPointer(cTemp), false) ||
   !neuron.calcHiddenGradients(cKey.AsObject()) ||
   !SumAndNormilize(temp, GetPointer(cTemp), temp, iWindow, false, class="num">0, class="num">0, class="num">0, class="num">1) ||
   !neuron.calcHiddenGradients(cValue.AsObject()) ||
   !SumAndNormilize(temp, GetPointer(cTemp), temp, iWindow, false, class="num">0, class="num">0, class="num">0, class="num">1) ||
   !neuron.SetGradient(temp, false)
   )
    class="kw">return false;
 for(class="type">int i = cKVProjection.Total() - class="num">2; i >= class="num">0; i--)
  {
   neuron = cKVProjection[i];
   if(!neuron ||
      !neuron.calcHiddenGradients(cKVProjection[i + class="num">1]))
      class="kw">return false;
  }
class=class="str">"cmt">//---
  class="kw">return true;
 }

「别急着下结论」

AMCT 框架把蜡烛当原子、形态当基序,用对比学习逼模型分清哪些结构携带信息、哪些只是噪声,再靠变换器把长短依赖一并建模。思路上它能同时抓局部特征和跨周期关系,但是否真能提升预测,还得看历史回测而非纸面逻辑。 本文只搭了 MQL5 实现的头:Research.mq5 到 Test.mq5 一组 EA 加 NeuroNet 类库已经就位,Real ORL 采样和训练闭环还没跑完。下一篇才会用历史数据给这套框架定性能,现在下「有效」的结论太早。 外汇和贵金属杠杆高、滑点跳空频繁,这类模型即便回测漂亮,实盘也可能被流动性打脸。先去 MT5 把 Research.mq5 挂上跑样本采集,等后续评测出来再决定要不要接进自己的策略。

常见问题

不一定,先检查前向各层激活是否饱和;可把学习率降一半并加梯度裁剪,再观察损失曲线是否平滑下降。
对每分支梯度做归一化或加多样性约束项,限制余弦相似度上限,能让各分支贡献更均衡。
小布可接入你的训练日志,自动标出梯度 dominant 分支并提示是否触发多样性约束,省去手动翻曲线。
别急着下结论,先核对实盘数据分布与训练偏移;大概率缺在线校准,而非模型彻底失效。
加入成交量和波动率作为约束特征,单纯价格形态易歧义,多模态输入能压住反向误判。