神经网络变得简单(第 79 部分):在状态上下文中的特征聚合查询(FAQ)·进阶篇
🎞️

神经网络变得简单(第 79 部分):在状态上下文中的特征聚合查询(FAQ)·进阶篇

(2/3)·静态环境描述漏掉了价格变动的动态概率,这篇进阶拆解如何用相邻帧特征约束查询来补上

实战向 第 2/3 篇

不少交易者把历史价量直接堆进模型状态就当完整环境,却没意识到模型从未估测状态变化的动态。价格走势里变动的节奏有时比静态截面更能暗示后续走向,沿用图像检测的老思路容易在快速波动里掉链子。

「交叉注意力层的初始化骨架」

在 MT5 的 OpenCL 神经网络扩展里,CNeuronCrossAttention 类把多头交叉注意力拆成了 Q 与 KV 两套独立嵌入。构造函数接收 window、window_key、heads、units_count 等参数,用于描述查询序列长度、键值序列长度与头数,类型标识固定返回 defNeuronCrossAttenOCL。 Init 方法第一步先调用基类 CNeuronBaseOCL::Init,传入 window*units_count 作为展开后的输入宽度;随后用 fmax(x,1) 把六个维度参数全部下限钳到 1,避免零维导致内核崩溃。 Q_Embedding 的第三维设为 iWindowKey*iHeads,KV_Embedding 则是 2*iWindowKey*iHeads——后者多出来的一倍对应 Key 与 Value 拼接。两者激活函数都显式置为 None,说明归一化与缩放交给后续 softmax 处理。 下面这段是 Init 前半部分的可直接拷进自定义EA验证的片段:

MQL5 / C++
class="type">bool CNeuronCrossAttention::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 units_count, class="type">uint window_k, class="type">uint units_k,
                      ENUM_OPTIMIZATION optimization_type,
                      class="type">uint batch)
  {
  if(!CNeuronBaseOCL::Init(numOutputs, myIndex, open_cl, window * units_count,
                           optimization_type, batch))
     class="kw">return false;
  iWindow = fmax(window, class="num">1);
  iWindowKey = fmax(window_key, class="num">1);
  iUnits = fmax(units_count, class="num">1);
  iWindow_K = fmax(window_k, class="num">1);
  iUnits_K = fmax(units_k, class="num">1);
  iHeads = fmax(heads, class="num">1);
  activation = None;
  if(!Q_Embedding.Init(class="num">0, class="num">0, OpenCL, iWindow, iWindow, iWindowKey * iHeads, iUnits, optimization_type, batch))
     class="kw">return false;
  Q_Embedding.SetActivationFunction(None);
  if(!KV_Embedding.Init(class="num">0, class="num">0, OpenCL, iWindow_K, iWindow_K, class="num">2 * iWindowKey * iHeads, iUnits_K, optimization_type, batch))
     class="kw">return false;
  KV_Embedding.SetActivationFunction(None);

◍ 交叉注意力层的显存与内核装配

在 MT5 的 OpenCL 环境里搭交叉注意力,第一步是给 Score 矩阵开显存:大小为 sizeof(float) 乘以 iUnits * iUnits_K * iHeads,标记 CL_MEM_READ_WRITE。若 AddBuffer 返回 INVALID_HANDLE 直接 return false,这一步没过后面全白搭。 多头注意力输出、W0 映射、AttentionOut 以及两层前馈 FF[0]、FF[1] 都用 Init(0,0,OpenCL,...) 初始化,且全部 SetActivationFunction(None)——也就是说该层不做非线性激活,纯粹走线性变换与注意力加权。 梯度句柄要在 FF[1] 初始化后接管:先 BufferFree 再 delete 旧 Gradient,最后 Gradient = FF[1].getGradient()。这样反向传播只追前馈末层的梯度,显存占用倾向更可控。 attentionOut 方法里,global_work_size 设成 {iUnits, iUnits_K, iHeads},而 local_work_size 是 {1, iUnits_K, 1},相当于每个头内把 K 维压到一个本地工作组。四个 SetArgumentBuffer 分别绑 Q_Embedding、KV_Embedding、ScoreIndex、MHAttentionOut 的输出索引,任一步 GetLastError 非 0 就 printf 报错并退出,开 MT5 跑时若控制台刷出 'Error of set parameter kernel' 多半是索引绑错。

MQL5 / C++
  ScoreIndex = OpenCL.AddBuffer(class="kw">sizeof(class="type">float) * iUnits * iUnits_K * iHeads, CL_MEM_READ_WRITE);
  if(ScoreIndex == INVALID_HANDLE)
    class="kw">return false;
  if(!MHAttentionOut.Init(class="num">0, class="num">0, OpenCL, iWindowKey * iUnits * iHeads, optimization_type, batch))
    class="kw">return false;
  MHAttentionOut.SetActivationFunction(None);
  if(!W0.Init(class="num">0, class="num">0, OpenCL, iWindowKey * iHeads, iWindowKey * iHeads, iWindow, iUnits, optimization_type, batch))
    class="kw">return false;
  W0.SetActivationFunction(None);
  if(!AttentionOut.Init(class="num">0, class="num">0, OpenCL, iWindow * iUnits, optimization_type, batch))
    class="kw">return false;
  AttentionOut.SetActivationFunction(None);
  if(!FF[class="num">0].Init(class="num">0, class="num">0, OpenCL, iWindow, iWindow, class="num">4 * iWindow, iUnits, optimization_type, batch))
    class="kw">return false;
  if(!FF[class="num">1].Init(class="num">0, class="num">0, OpenCL, class="num">4 * iWindow, class="num">4 * iWindow, iWindow, iUnits, optimization_type, batch))
    class="kw">return false;
  for(class="type">int i = class="num">0; i < class="num">2; i++)
    FF[i].SetActivationFunction(None);
  Gradient.BufferFree();
  class="kw">delete Gradient;
  Gradient = FF[class="num">1].getGradient();
class=class="str">"cmt">//---
  class="kw">return true;
  }
class="type">bool CNeuronCrossAttention::attentionOut(class="type">void)
  {
  if(!OpenCL)
    class="kw">return false;
class=class="str">"cmt">//---
  class="type">uint global_work_offset[class="num">3] = {class="num">0};
  class="type">uint global_work_size[class="num">3] = {iUnitsclass=class="str">"cmt">/*Q units*/, iUnits_Kclass=class="str">"cmt">/*K units*/, iHeads};
  class="type">uint local_work_size[class="num">3] = {class="num">1, iUnits_K, class="num">1};
  ResetLastError();
  if(!OpenCL.SetArgumentBuffer(def_k_MH2AttentionOut, def_k_mh2ao_q, Q_Embedding.getOutputIndex()))
   {
     printf("Error of set parameter kernel %s: %d; line %d", __FUNCTION__, GetLastError(), __LINE__);
     class="kw">return false;
   }
  if(!OpenCL.SetArgumentBuffer(def_k_MH2AttentionOut, def_k_mh2ao_kv, KV_Embedding.getOutputIndex()))
   {
     printf("Error of set parameter kernel %s: %d; line %d", __FUNCTION__, GetLastError(), __LINE__);
     class="kw">return false;
   }
  if(!OpenCL.SetArgumentBuffer(def_k_MH2AttentionOut, def_k_mh2ao_score, ScoreIndex))
   {
     printf("Error of set parameter kernel %s: %d; line %d", __FUNCTION__, GetLastError(), __LINE__);
     class="kw">return false;
   }
  if(!OpenCL.SetArgumentBuffer(def_k_MH2AttentionOut, def_k_mh2ao_out, MHAttentionOut.getOutputIndex()))
   {
     printf("Error of set parameter kernel %s: %d; line %d", __FUNCTION__, GetLastError(), __LINE__);
     class="kw">return false;
   }

交叉注意力前传与内部梯度的 OpenCL 落地

在 MT5 的 OCL 神经网络实现里,CNeuronCrossAttention::feedForward 把一次交叉注意力拆成可验证的串行步骤:Q_Embedding 和 KV_Embedding 各自前传,再走 attentionOut(),随后 W0 映射、SumAndNormilize 做残差归一,最后两层 FF[] 全连接加第二次残差归一。任何一步返回 false 都会中断,实盘加载自定义模型时若某层维度不匹配,这里就是第一现场。 attentionOut() 内核执行前先用 SetArgument 把窗口键长 iWindowKey 以 int 强转写入,再 Execute 三维任务网格(3 维参数、global_work_offset 全 0、global_work_size 与 local_work_size 由调用方给定)。若 SetArgument 或 Execute 失败,printf 会带 __FUNCTION__ 与 __LINE__ 报出具体错误码,方便在 MetaEditor 里直接定位到行。 反向阶段的 AttentionInsideGradients 同样依赖 OpenCL 句柄非空,否则直接返回 false。它把 Q/KV 的输出与梯度缓冲区的索引通过 SetArgumentBuffer 逐个绑给 def_k_MH2AttentionInsideGradients 内核,global_work_size 固定为 {iUnits, iWindowKey, iHeads} 三维。想验证多头划分是否合理,把 iHeads 改小后重编译,若梯度收敛变慢即说明头数本身在起作用。 外汇与贵金属行情高波动,GPU 加速的推理只是降低延迟,不替代风控;在 MT5 策略测试器里用历史 tick 跑一遍该类的 feedForward,核对各缓冲区索引是否越界,比盲目上实盘更稳妥。

MQL5 / C++
  if(!OpenCL.SetArgument(def_k_MH2AttentionOut, def_k_mh2ao_dimension, (class="type">int)iWindowKey))
    {
      printf("Error of set parameter kernel %s: %d; line %d", __FUNCTION__, GetLastError(), __LINE__);
      class="kw">return false;
    }
  if(!OpenCL.Execute(def_k_MH2AttentionOut, class="num">3, global_work_offset, global_work_size, local_work_size))
    {
      printf("Error of execution kernel %s: %d", __FUNCTION__, GetLastError());
      class="kw">return false;
    }
class=class="str">"cmt">//---
  class="kw">return true;
  }
class="type">bool CNeuronCrossAttention::feedForward(CNeuronBaseOCL *NeuronOCL, CNeuronBaseOCL *Context)
  {
class=class="str">"cmt">//---
  if(!Q_Embedding.FeedForward(NeuronOCL))
      class="kw">return false;
class=class="str">"cmt">//---
  if(!KV_Embedding.FeedForward(Context))
      class="kw">return false;
  if(!attentionOut())
      class="kw">return false;
  if(!W0.FeedForward(GetPointer(MHAttentionOut)))
      class="kw">return false;
  if(!SumAndNormilize(W0.getOutput(), NeuronOCL.getOutput(), AttentionOut.getOutput(), iWindow))
      class="kw">return false;
  if(!FF[class="num">0].FeedForward(GetPointer(AttentionOut)))
      class="kw">return false;
  if(!FF[class="num">1].FeedForward(GetPointer(FF[class="num">0])))
      class="kw">return false;
  if(!SumAndNormilize(FF[class="num">1].getOutput(), AttentionOut.getOutput(), Output, iWindow))
      class="kw">return false;
class=class="str">"cmt">//---
  class="kw">return true;
  }
class="type">bool CNeuronCrossAttention::AttentionInsideGradients(class="type">void)
  {
  if(!OpenCL)
      class="kw">return false;
class=class="str">"cmt">//---
  class="type">uint global_work_offset[class="num">3] = {class="num">0};
  class="type">uint global_work_size[class="num">3] = {iUnits, iWindowKey, iHeads};
  ResetLastError();
  if(!OpenCL.SetArgumentBuffer(def_k_MH2AttentionInsideGradients, def_k_mh2aig_q, Q_Embedding.getOutputIndex()))
    {
      printf("Error of set parameter kernel %s: %d; line %d", __FUNCTION__, GetLastError(), __LINE__);
      class="kw">return false;
    }
  if(!OpenCL.SetArgumentBuffer(def_k_MH2AttentionInsideGradients, def_k_mh2aig_qg, Q_Embedding.getGradientIndex()))
    {
      printf("Error of set parameter kernel %s: %d; line %d", __FUNCTION__, GetLastError(), __LINE__);
      class="kw">return false;
    }
  if(!OpenCL.SetArgumentBuffer(def_k_MH2AttentionInsideGradients, def_k_mh2aig_kv, KV_Embedding.getOutputIndex()))
    {
      printf("Error of set parameter kernel %s: %d; line %d", __FUNCTION__, GetLastError(), __LINE__);
      class="kw">return false;
    }
  if(!OpenCL.SetArgumentBuffer(def_k_MH2AttentionInsideGradients, def_k_mh2aig_kvg, KV_Embedding.getGradientIndex()))

「交叉注意力层的梯度回传与描述符构建」

在 MT5 的 OpenCL 神经网络实现里,交叉注意力层的内层梯度核 def_k_MH2AttentionInsideGradients 需要依次绑定四个参数缓冲:键索引、得分索引、输出梯度索引以及 K 侧单元数 iUnits_K。任何一次 SetArgumentBufferSetArgument 失败都会通过 printf 打出函数名、错误码与行号,并直接 return false,这意味着 GPU 侧参数未就位时训练不会往下走。 内核实际执行时指定了 3 维工作空间:OpenCL.Execute(def_k_MH2AttentionInsideGradients, 3, global_work_offset, global_work_size)。若执行返回失败,同样打印错误并退出,训练循环可能在这一步静默中断,排查时优先看 Experts 日志里的 Error of execution kernelcalcInputGradients 方法揭示了反向传播的调用顺序:先算前馈子层 FF[1]、FF[0] 的梯度,再做 SumAndNormilize 归一,随后回溯 W0、注意力内部梯度、KV 与 Q 嵌入。注意 SumAndNormilize 被调用两次,第二次把 prevLayer.getGradient() 同时作为源和目标传入,属于原地累加写法。 CreateDescriptions 负责懒初始化对象数组:当传入的 dotdecoder 指针为空时,函数内部 new CArrayObj() 补建,若 new 失败则 return false。在加载自定义 AI 模型前,可单步跟这个函数确认描述符容器是否被正确创建,避免后续网络装配报空指针。

MQL5 / C++
  {
      printf("Error of set parameter kernel %s: %d; line %d", __FUNCTION__, GetLastError(), __LINE__);
      class="kw">return false;
  }
  if(!OpenCL.SetArgumentBuffer(def_k_MH2AttentionInsideGradients, def_k_mh2aig_score, ScoreIndex))
  {
      printf("Error of set parameter kernel %s: %d; line %d", __FUNCTION__, GetLastError(), __LINE__);
      class="kw">return false;
  }
  if(!OpenCL.SetArgumentBuffer(def_k_MH2AttentionInsideGradients, def_k_mh2aig_outg, MHAttentionOut.getGradientIndex()))
  {
      printf("Error of set parameter kernel %s: %d; line %d", __FUNCTION__, GetLastError(), __LINE__);
      class="kw">return false;
  }
  if(!OpenCL.SetArgument(def_k_MH2AttentionInsideGradients, def_k_mh2aig_kunits, (class="type">int)iUnits_K))
  {
      printf("Error of set parameter kernel %s: %d; line %d", __FUNCTION__, GetLastError(), __LINE__);
      class="kw">return false;
  }
  if(!OpenCL.Execute(def_k_MH2AttentionInsideGradients, class="num">3, global_work_offset, global_work_size))
  {
      printf("Error of execution kernel %s: %d", __FUNCTION__, GetLastError());
      class="kw">return false;
  }
class=class="str">"cmt">//---
  class="kw">return true;
  }
class="type">bool CNeuronCrossAttention::calcInputGradients(CNeuronBaseOCL *prevLayer, CNeuronBaseOCL *Context)
  {
  if(!FF[class="num">1].calcInputGradients(GetPointer(FF[class="num">0])))
      class="kw">return false;
  if(!FF[class="num">0].calcInputGradients(GetPointer(AttentionOut)))
      class="kw">return false;
  if(!SumAndNormilize(FF[class="num">1].getGradient(), AttentionOut.getGradient(), W0.getGradient(), iWindow, false))
      class="kw">return false;
  if(!W0.calcInputGradients(GetPointer(MHAttentionOut)))
      class="kw">return false;
  if(!AttentionInsideGradients())
      class="kw">return false;
  if(!KV_Embedding.calcInputGradients(Context))
      class="kw">return false;
  if(!Q_Embedding.calcInputGradients(prevLayer))
      class="kw">return false;
  if(!SumAndNormilize(prevLayer.getGradient(), W0.getGradient(), prevLayer.getGradient(), iWindow, false))
      class="kw">return false;
class=class="str">"cmt">//---
  class="kw">return true;
  }
class="type">bool CreateDescriptions(CArrayObj *dot, CArrayObj *decoder, CArrayObj *actor, CArrayObj *critic)
  {
class=class="str">"cmt">//---
  CLayerDescription *descr;
class=class="str">"cmt">//---
  if(!dot)
  {
      dot = new CArrayObj();
      if(!dot)
          class="kw">return false;
  }
  if(!decoder)
  {
      decoder = new CArrayObj();

◍ 解码器与 actor 网络的层级装配

在 MT5 的强化学习模型里,decoder、actor、critic 三个容器对象必须先做空指针兜底,任一 new 失败就直接 return false,避免后续层描述写入野指针。 解码器第一层从潜变量层 LatentLayer 取描述,把 po.count * po.window 作为基础神经元数,激活函数设 None、优化器 ADAM;这一层输出维度直接由潜层规模决定。 第二层用 defNeuronFAQOCL,units 拷入 {QueryCount, po.count},window_out 固定 16、step=4。第三层接编码器末层做交叉注意力(defNeuronCrossAttenOCL),windows 设 {16, encoder.window},同样 window_out=16、step=4。 actor 网络输入层复用 decoder 末层,prev_count = units[0] * windows[0];其第一层用 defNeuronConcatenate,count=LatentCount、激活 SIGMOID,把账户描述 AccountDescr 作为 step 参入网。外汇与贵金属杠杆高,这套结构只是网络装配,实盘前务必在策略测试器跑回测验证梯度更新是否收敛。

MQL5 / C++
  if(!decoder)
    {
     decoder = new CArrayObj();
     if(!decoder)
       class="kw">return false;
    }
  if(!actor)
    {
     actor = new CArrayObj();
     if(!actor)
       class="kw">return false;
    }
  if(!critic)
    {
     critic = new CArrayObj();
     if(!critic)
       class="kw">return false;
    }
class=class="str">"cmt">//--- Decoder
decoder.Clear();
class=class="str">"cmt">//--- Input layer
  CLayerDescription *po = dot.At(LatentLayer);
  if(!po || !(descr = new CLayerDescription()))
     class="kw">return false;
  descr.type = defNeuronBaseOCL;
  descr.count = po.count * po.window;
  descr.activation = None;
  descr.optimization = ADAM;
  if(!decoder.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 = defNeuronFAQOCL;
    {
     class="type">int temp[] = {QueryCount, po.count};
     ArrayCopy(descr.units, temp);
    }
  descr.window = po.window;
  descr.window_out = class="num">16;
  descr.optimization = ADAM;
  descr.step = class="num">4;
  descr.activation = None;
  if(!decoder.Add(descr))
    {
     class="kw">delete descr;
     class="kw">return false;
    }
class=class="str">"cmt">//--- layer class="num">2
  CLayerDescription *encoder = dot.At(dot.Total() - class="num">1);
  if(!encoder || !(descr = new CLayerDescription()))
     class="kw">return false;
  descr.type = defNeuronCrossAttenOCL;
    {
     class="type">int temp[] = {QueryCount, encoder.count};
     ArrayCopy(descr.units, temp);
    }
    {
     class="type">int temp[] = {class="num">16, encoder.window};
     ArrayCopy(descr.windows, temp);
    }
  descr.window_out = class="num">16;
  descr.step = class="num">4;
  descr.activation = None;
  descr.optimization = ADAM;
  if(!decoder.Add(descr))
    {
     class="kw">delete descr;
     class="kw">return false;
    }
class=class="str">"cmt">//--- Actor
  actor.Clear();
class=class="str">"cmt">//--- Input layer
  encoder = decoder.At(decoder.Total() - class="num">1);
  if(!encoder || !(descr = new CLayerDescription()))
     class="kw">return false;
  descr.type = defNeuronBaseOCL;
  prev_count = descr.count = encoder.units[class="num">0] * encoder.windows[class="num">0];
  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">1
  if(!(descr = new CLayerDescription()))
     class="kw">return false;
  descr.type = defNeuronConcatenate;
  descr.count = LatentCount;
  descr.window = prev_count;
  descr.step = AccountDescr;
  descr.optimization = ADAM;
  descr.activation = SIGMOID;
  if(!actor.Add(descr))
    {

Actor 与 Critic 的逐层堆叠写法

这段构建逻辑把策略网络(Actor)拆成四层,Critic 则复用 Actor 前两层再做三层扩展。每一层都先 new 一个 CLayerDescription,赋值失败立刻 return false 并 delete,避免 MT5 里悬空指针拖垮整个 EA。 Actor 第二层用 LatentCount 个 sigmoid 神经元做隐空间压缩,第三层直接铺 2 * NActions 个无激活节点,第四层切到 defNeuronVAEOCL 类型、输出维度等于 NActions。注意第三层 count 写成 2 * NActions 意味着动作均值与对数方差各占一半,是 VAE 式策略头的典型排布。 Critic 的 Input 和 layer 1 直接 Copy(actor.At(0)) 和 Copy(actor.At(1)),layer 1 额外设 step = NActions 并套 sigmoid,后续两层分别映射到 LatentCount 与 NRewards。Rewards 层用 None 激活,说明价值估计走线性输出。 在 MT5 里把 NActions 设为 4、NRewards 设为 1 时,Actor 第三层会生成 8 个节点,Critic 末层生成 1 个节点。改这几个宏定义就能直接看网络规模变化,外汇与贵金属行情下用这类结构做强化学习策略,回测过拟合概率偏高,属高风险玩法。

MQL5 / C++
   class="kw">delete descr;
   class="kw">return false;
   }
class=class="str">"cmt">//--- layer class="num">2
   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">3
   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">4
   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=class="str">"cmt">//--- Critic
   critic.Clear();
class=class="str">"cmt">//--- Input layer
   if(!(descr = new CLayerDescription()))
      class="kw">return false;
   descr.Copy(actor.At(class="num">0));
   if(!critic.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.Copy(actor.At(class="num">1));
   descr.step = NActions;
   descr.optimization = ADAM;
   descr.activation = SIGMOID;
   if(!critic.Add(descr))
     {
      class="kw">delete descr;
      class="kw">return false;
     }
class=class="str">"cmt">//--- layer class="num">2
   if(!(descr = new CLayerDescription()))
      class="kw">return false;
   descr.type = defNeuronBaseOCL;
   descr.count = LatentCount;
   descr.activation = SIGMOID;
   descr.optimization = ADAM;
   if(!critic.Add(descr))
     {
      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 = defNeuronBaseOCL;
   descr.count = NRewards;
   descr.activation = None;
   descr.optimization = ADAM;
   if(!critic.Add(descr))
     {
      class="kw">delete descr;
      class="kw">return false;
     }
class=class="str">"cmt">//---
   class="kw">return true;
   }
CNet                DOT;
CNet                Decoder;
CNet                Actor;
让小布替你跑这套帧间聚合
这些状态上下文的诊断小布盯盘已内置,打开对应品种页即可看到相邻时段特征约束后的查询权重变化,你只需判断概率倾斜是否值得跟单。

常见问题

原版朴素做法把随机初始化的相邻查询直接均化,缺乏与对应帧的语义关联;FAQ 改用基于输入帧特征的余弦相似性生成可学习权重,并约束查询使其随相邻帧调整,缓解快速运动带来的性能下降。
论文作者实测这种方式难以训练且结果更糟,随机初始化查询虽无时态约束但提供了优化空间,直接生成会破坏变换器查询的平衡,因此采用动态更新而非完全替换。
小布盯盘在品种页内置了状态上下文特征聚合的可视化,可对比当前帧与相邻时段查询权重,省去自己写聚合模块的重复劳动。
外汇贵金属杠杆高、跳空频繁,状态动态更剧烈,聚合窗口过宽可能引入噪声,建议先用小周期验证权重约束的稳定性,相关风险可能放大。
通常用小型全连接网络或线性层把帧特征映射到权重空间,再经常规化得到 w,具体结构按特征维度调参,属于实战向的模型细节。