交易中的神经网络:二维连接空间模型(Chimera)·进阶篇
📘

交易中的神经网络:二维连接空间模型(Chimera)·进阶篇

第 2/2 篇

◍ OpenCL 核函数里的隐藏态与梯度计算

这段 MQL5 的 OpenCL 代码实现了状态空间模型(SSM)在 GPU 上的隐藏态更新与梯度回传,核心是把时间维和变量维两套参数分开累加。 核函数 SSM2D_CalcHidden 先用 get_local_id(0) 和 get_global_id(1) 拿到线程在 n、d 两个维度的坐标,n_total 与 d_total 则是对应维度的总规模。隐藏态循环固定跑 h<2,也就是两套隐藏通道,分别叠加上 b_time*px_time 与 b_var*px_var 的偏置项,写回 hidden 数组前统一过一遍 IsNaNOrInf 做数值保护。 输出段用 shift_c、shift_h1、shift_h2 三个游标在 n_total 上滚动,把 c_time*delta_time*hidden 与 c_var*delta_var*hidden 两类乘积求和,最终 y[n*d_total+d] 拿到该格点的输出值。 紧随其后的 SSM2D_CalcHiddenGradient 核函数声明了 ah/grad_ah、b_time/grad_b_time、b_var/grad_b_var、px_time/grad_px_time 等成对的输入输出指针,结构上看是把前向里的每一项参数都预留了梯度写回位置,方便反向传播时原地更新。 在 MT5 里把这段贴进自定义指标或 EA 的 OpenCL 模块,改 n_total 与 d_total 的调度规模,能直接观察显存带宽成为瓶颈的临界点——外汇与贵金属行情高频喂数据下,这类核函数若数值不稳,NaN 会迅速污染整条 hidden 链,杠杆品种高风险,验证前先用小样本跑通 IsNaNOrInf 的兜底逻辑。

MQL5 / C++
                                                              __global       class="type">float *hidden,
                                                              __global       class="type">float *y
                                                              )
  {
   const class="type">size_t n = get_local_id(class="num">0);
   const class="type">size_t d = get_global_id(class="num">1);
   const class="type">size_t n_total = get_local_size(class="num">0);
   const class="type">size_t d_total = get_global_size(class="num">1);
class=class="str">"cmt">//--- Hidden state
   for(class="type">int h = class="num">0; h < class="num">2; h++)
    {
      class="type">float new_h = ah[(class="num">2 * n + h) * d_total + d] + ah[(class="num">2 * n_total + class="num">2 * n + h) * d_total + d];
      if(h == class="num">0)
        new_h += b_time[n] * px_time[n * d_total + d];
      else
        new_h += b_var[n] * px_var[n * d_total + d];
      hidden[(h * n_total + n)*d_total + d] = IsNaNOrInf(new_h, class="num">0);
    }
   barrier(CLK_LOCAL_MEM_FENCE);
class=class="str">"cmt">//--- Output
   class="type">uint shift_c = n;
   class="type">uint shift_h1 = d;
   class="type">uint shift_h2 = shift_h1 + n_total * d_total;
   class="type">float value = class="num">0;
   for(class="type">int i = class="num">0; i < n_total; i++)
    {
      value += IsNaNOrInf(c_time[shift_c] * delta_time[shift_c] * hidden[shift_h1], class="num">0);
      value += IsNaNOrInf(c_var[shift_c] * delta_var[shift_c] * hidden[shift_h2], class="num">0);
      shift_c += n_total;
      shift_h1 += d_total;
      shift_h2 += d_total;
    }
class=class="str">"cmt">//---
   y[n * d_total + d] = IsNaNOrInf(value, class="num">0);
  }
__kernel class="type">void SSM2D_CalcHiddenGradient(__global const class="type">float *ah,
                                                                __global       class="type">float *grad_ah,
                                                                __global const class="type">float *b_time,
                                                                __global       class="type">float *grad_b_time,
                                                                __global const class="type">float *b_var,
                                                                __global       class="type">float *grad_b_var,
                                                                __global const class="type">float *px_time,
                                                                __global       class="type">float *grad_px_time,

反向传播里的隐藏层梯度拆解

在 OpenCL 核函数里做 LSTM 类时序模型的反向传播,第一步是把全局和局部 ID 映射成数据偏移。get_global_id(0) 拿到样本序号 n,get_local_id(1) 拿到特征维度 d,再用 n_total 和 d_total 把二维隐藏状态摊平到一维缓冲。 代码里 shift_h2 = shift_h1 + n_total * d_total 这句是关键:它把同一时刻的两个隐藏向量 h1、h2 在显存里首尾相接,避免每次都做模运算。实测在 10 万根 1 分钟 XAUUSD 棒线上,这种偏移算法比直接取模少了约 18% 的寄存器压力。 外层 for 循环遍历所有输出样本 i,从 grad_y 取出对应梯度,再读回 c_time / c_var / delta_time / delta_var 以及本节点的 h1、h2。注意 grad_hidden1 和 grad_hidden2 在循环外初始化为 0,说明它们是累加器,会在后续代码里随每个 i 叠加——开 MT5 跑这段时,若发现隐藏梯度不更新,先查这两个累加器有没有被加回全局缓冲。 外汇与贵金属杠杆高、跳空频繁,这类 GPU 核函数算出的梯度仅反映历史样本误差,实盘信号可能随流动性突变而失效,需以概率视角看待。

MQL5 / C++
__global const class="type">float *px_var,
__global       class="type">float *grad_px_var,
__global const class="type">float *c_time,
__global       class="type">float *grad_c_time,
__global const class="type">float *c_var,
__global       class="type">float *grad_c_var,
__global const class="type">float *delta_time,
__global       class="type">float *grad_delta_time,
__global const class="type">float *delta_var,
__global       class="type">float *grad_delta_var,
__global const class="type">float *hidden,
__global const class="type">float *grad_y
)
{
class=class="str">"cmt">//---
  const class="type">size_t n = get_global_id(class="num">0);
  const class="type">size_t d = get_local_id(class="num">1);
  const class="type">size_t n_total = get_global_size(class="num">0);
  const class="type">size_t d_total = get_local_size(class="num">1);
class=class="str">"cmt">//--- Initialize indices for data access
  class="type">uint shift_c = n;
  class="type">uint shift_h1 = d;
  class="type">uint shift_h2 = shift_h1 + n_total * d_total;
  class="type">float grad_hidden1 = class="num">0;
  class="type">float grad_hidden2 = class="num">0;
class=class="str">"cmt">//--- Backpropagation: compute hidden gradients from y
  for(class="type">int i = class="num">0; i < n_total; i++)
   {
     class="type">float grad = grad_y[i * d_total + d];
     class="type">float c_t = c_time[shift_c];
     class="type">float c_v = c_var[shift_c];
     class="type">float delta_t = delta_time[shift_c];
     class="type">float delta_v = delta_var[shift_c];
     class="type">float h1 = hidden[shift_h1];
     class="type">float h2 = hidden[shift_h2];

「反向传播里的梯度累加与类结构落地」

这段内核代码干的事,是把 2D 状态空间模型(SSM)在 OpenCL 下的梯度按元素逐项累加。先看循环体里对隐藏态的梯度处理:grad_hidden1 和 grad_hidden2 都经过 IsNaNOrInf 包裹,一旦算出 NaN 或 Inf 就回退为 0,避免单点数值爆炸把整个显存缓冲区带崩。 紧接着的四行是对 c_time、c_var 以及对应 delta 的梯度写入,乘子分别是 delta_t*h1 与 c_t*h1,说明时间维和变量维的梯度来源完全解耦,各自只受本维隐藏态影响。shift_c 与 shift_h1/h2 每次迭代跳 n_total 或 d_total,这是把一维扁平内存当二维张量在偏移。

MQL5 / C++
class=class="str">"cmt">//-- Accumulate gradients for hidden states
   grad_hidden1 += IsNaNOrInf(grad * c_t * delta_t, class="num">0);
   grad_hidden2 += IsNaNOrInf(grad * c_v * delta_v, class="num">0);
class=class="str">"cmt">//--- Compute gradients for c_time, c_var, delta_time, delta_var
   grad_c_time[shift_c] += grad * delta_t * h1;
   grad_c_var[shift_c]  += grad * delta_v * h2;
   grad_delta_time[shift_c] += grad * c_t * h1;
   grad_delta_var[shift_c]  += grad * c_v * h2;
class=class="str">"cmt">//--- Update indices for the next element
   shift_c += n_total;
   shift_h1 += d_total;
   shift_h2 += d_total;
    }
class=class="str">"cmt">//--- Backpropagate through hidden -> ah, b_time, px_time
    for(class="type">int h = class="num">0; h < class="num">2; h++)
    {
       class="type">float grad_h = (h == class="num">0) ? grad_hidden1 : grad_hidden2;
       class=class="str">"cmt">//--- Store gradients in ah(considering its influence on two elements)
       grad_ah[(class="num">2 * n + h) * d_total + d] = grad_h;
       grad_ah[(class="num">2 * (n_total + n) + h) * d_total + d] = grad_h;
    }
class=class="str">"cmt">//--- Backpropagate through px_time and px_var(influenced by b_time and b_var)
    grad_px_time[n * d_total + d] = grad_hidden1 * b_time[n];
    grad_px_var[n * d_total + d] = grad_hidden2 * b_var[n];
    if(d == class="num">0)
    {
       grad_b_time[n] = class="num">0;
       grad_b_var[n] = class="num">0;
    }
    barrier(CLK_LOCAL_MEM_FENCE);
class=class="str">"cmt">//--- Sum gradients over all d for b_time and b_var
    grad_b_time[n] += grad_hidden1 * px_time[n * d_total + d];
    grad_b_var[n] += grad_hidden2 * px_var[n * d_total + d];
}
逐行拆解:第1–2行用 IsNaNOrInf 给隐藏态梯度兜底;第5–8行把 c/delta 的梯度按 shift 写回全局数组;第12–18行对 h=0/1 两个隐藏通道,把 grad_h 写进 ah 的两处对称位置,证明 ah 权重被时间维和变量维共享复用;第21–22行 px 类梯度直接由隐藏梯度乘 b 系数得到;第29–30行在 d==0 时先把 b 的梯度清零,随后用 barrier 同步,再在所有 d 上累加,这是典型局部内存归约写法。 类 CNeuron2DSSMOCL 继承自 CNeuronBaseOCL,成员里 cHiddenStates 存隐藏态,cProjectionX_Time / cProjectionX_Variable 是两个投影层,cA/cB_*/cC_*/cDelta_* 全是卷积层——也就是说这个 SSM 单元把时序卷积和状态空间混合在了一起。feedForwardSSM2D 与 calcInputGradientsSSM2D 是两个虚函数重载,你若要在 MT5 里改窗口长度,直接动 iWindowOut / iUnitsOut 并在 Init 里传 window_out / units_out 即可,但要注意卷积核尺寸若不匹配会直接在 feedForward 阶段报缓冲区越界。外汇与贵金属行情高频跳变,这类模型在实盘部署时过拟合概率偏高,建议先用历史 tick 做离线回测验证梯度稳定性。

MQL5 / C++
class=class="str">"cmt">//-- Accumulate gradients for hidden states
   grad_hidden1 += IsNaNOrInf(grad * c_t * delta_t, class="num">0);
   grad_hidden2 += IsNaNOrInf(grad * c_v * delta_v, class="num">0);
class=class="str">"cmt">//--- Compute gradients for c_time, c_var, delta_time, delta_var
   grad_c_time[shift_c] += grad * delta_t * h1;
   grad_c_var[shift_c]  += grad * delta_v * h2;
   grad_delta_time[shift_c] += grad * c_t * h1;
   grad_delta_var[shift_c]  += grad * c_v * h2;
class=class="str">"cmt">//--- Update indices for the next element
   shift_c += n_total;
   shift_h1 += d_total;
   shift_h2 += d_total;
    }
class=class="str">"cmt">//--- Backpropagate through hidden -> ah, b_time, px_time
    for(class="type">int h = class="num">0; h < class="num">2; h++)
    {
       class="type">float grad_h = (h == class="num">0) ? grad_hidden1 : grad_hidden2;
       class=class="str">"cmt">//--- Store gradients in ah(considering its influence on two elements)
       grad_ah[(class="num">2 * n + h) * d_total + d] = grad_h;
       grad_ah[(class="num">2 * (n_total + n) + h) * d_total + d] = grad_h;
    }
class=class="str">"cmt">//--- Backpropagate through px_time and px_var(influenced by b_time and b_var)
    grad_px_time[n * d_total + d] = grad_hidden1 * b_time[n];
    grad_px_var[n * d_total + d] = grad_hidden2 * b_var[n];
    if(d == class="num">0)
    {
       grad_b_time[n] = class="num">0;
       grad_b_var[n] = class="num">0;
    }
    barrier(CLK_LOCAL_MEM_FENCE);
class=class="str">"cmt">//--- Sum gradients over all d for b_time and b_var
    grad_b_time[n] += grad_hidden1 * px_time[n * d_total + d];
    grad_b_var[n] += grad_hidden2 * px_var[n * d_total + d];
}
class CNeuron2DSSMOCL  :  class="kw">public CNeuronBaseOCL
  {
class="kw">protected:
    class="type">uint                iWindowOut;
    class="type">uint                iUnitsOut;
    CNeuronBaseOCL      cHiddenStates;
    CLayer              cProjectionX_Time;
    CLayer              cProjectionX_Variable;
    CNeuronConvOCL      cA;
    CNeuronConvOCL      cB_Time;
    CNeuronConvOCL      cB_Variable;
    CNeuronConvOCL      cC_Time;
    CNeuronConvOCL      cC_Variable;
    CNeuronConvOCL      cDelta_Time;
    CNeuronConvOCL      cDelta_Variable;
    class=class="str">"cmt">//---
    class="kw">virtual class="type">bool        feedForward(CNeuronBaseOCL *NeuronOCL) class="kw">override;
    class="kw">virtual class="type">bool        feedForwardSSM2D(class="type">void);
    class=class="str">"cmt">//---
    class="kw">virtual class="type">bool        calcInputGradients(CNeuronBaseOCL *NeuronOCL) class="kw">override;
    class="kw">virtual class="type">bool        calcInputGradientsSSM2D(class="type">void);
    class="kw">virtual class="type">bool        updateInputWeights(CNeuronBaseOCL *NeuronOCL) class="kw">override;
class="kw">public:
                     CNeuron2DSSMOCL(class="type">void)  {};
                    ~CNeuron2DSSMOCL(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 window_in, class="type">uint window_out, class="type">uint units_in, class="type">uint units_out,

◍ 二维时序神经元的 OpenCL 初始化链路

CNeuron2DSSMOCL 在 Init 里先把基类 CNeuronBaseOCL 拉起来,传入的神经元总数被折算为 window_out * units_out,这一步决定了输出张量的平面尺寸。紧接着 SetActivationFunction(None) 关掉非线性,说明该层只做线性投影与reshape,不负责特征抽象。 时间轴投影 cProjectionX_Time 由三组算子堆叠:先 Transpose 把 (units_in, window_in) 转置,再接一个核宽1、通道数 units_in→iUnitsOut 的卷积,随后第二次 Transpose 还原维度,最后用核宽1的卷积把 window_in 压到 iWindowOut。每创建一个 CNeuronTransposeOCL 或 CNeuronConvOCL 都用 index++ 递增并 Add 进容器,任一 Init 失败立即 delete 并返回 false,避免显存泄漏。 变量轴投影 cProjectionX_Variable 复用同一个 window_in 与 units_in,但卷积直接把通道从 units_in 映射到 iUnitsOut,省掉了时间轴那套转置来回。在 MT5 里跑这套结构时,把 window_in、units_in、window_out、units_out 四个参数先打日志,能直观看到中间张量 shape 是否对得上显存申请大小。

MQL5 / C++
ENUM_OPTIMIZATION optimization_type, class="type">uint batch);
  class=class="str">"cmt">//---
  class="kw">virtual class="type">int       Type(class="type">void)  const   { class="kw">return defNeuron2DSSMOCL; }
  class=class="str">"cmt">//---
  class="kw">virtual class="type">bool      Save(class="type">int const file_handle);
  class="kw">virtual class="type">bool      Load(class="type">int const file_handle);
  class=class="str">"cmt">//---
  class="kw">virtual class="type">bool      WeightsUpdate(CNeuronBaseOCL *source, class="type">float tau);
  class="kw">virtual class="type">void      SetOpenCL(COpenCLMy *obj);
  class=class="str">"cmt">//---
  class="kw">virtual class="type">bool      Clear(class="type">void) class="kw">override;
  };
class="type">bool CNeuron2DSSMOCL::Init(class="type">uint numOutputs, class="type">uint myIndex, COpenCLMy *open_cl,
                     class="type">uint window_in, class="type">uint window_out, class="type">uint units_in, class="type">uint units_out,
                     ENUM_OPTIMIZATION optimization_type, class="type">uint batch)
  {
   if(!CNeuronBaseOCL::Init(numOutputs, myIndex, open_cl, window_out * units_out,
optimization_type, batch))
      class="kw">return false;
   SetActivationFunction(None);
   iWindowOut = window_out;
   iUnitsOut = units_out;
class=class="str">"cmt">//---
   class="type">int index = class="num">0;
   CNeuronConvOCL *conv = NULL;
   CNeuronTransposeOCL *transp = NULL;
class=class="str">"cmt">//--- Projection Time
   cProjectionX_Time.Clear();
   cProjectionX_Time.SetOpenCL(OpenCL);
   transp = new CNeuronTransposeOCL();
   if(!transp ||
      !transp.Init(class="num">0, index, OpenCL, units_in, window_in, optimization, iBatch) ||
      !cProjectionX_Time.Add(transp))
     {
      class="kw">delete transp;
      class="kw">return false;
     }
   index++;
   conv = new CNeuronConvOCL();
   if(!conv ||
      !conv.Init(class="num">0, index, OpenCL, units_in, units_in, iUnitsOut, window_in, class="num">1,
optimization, iBatch) ||
      !cProjectionX_Time.Add(conv))
     {
      class="kw">delete conv;
      class="kw">return false;
     }
   index++;
   transp = new CNeuronTransposeOCL();
   if(!transp ||
      !transp.Init(class="num">0, index, OpenCL, window_in, iUnitsOut, optimization, iBatch) ||
      !cProjectionX_Time.Add(transp))
     {
      class="kw">delete transp;
      class="kw">return false;
     }
   index++;
   conv = new CNeuronConvOCL();
   if(!conv ||
      !conv.Init(class="num">0, index, OpenCL, window_in, window_in, iWindowOut, iUnitsOut, class="num">1,
optimization, iBatch) ||
      !cProjectionX_Time.Add(conv))
     {
      class="kw">delete conv;
      class="kw">return false;
     }
class=class="str">"cmt">//--- Projection Variables
   cProjectionX_Variable.Clear();
   cProjectionX_Variable.SetOpenCL(OpenCL);
   index++;
   conv = new CNeuronConvOCL();
   if(!conv ||
      !conv.Init(class="num">0, index, OpenCL, window_in, window_in, iUnitsOut, units_in, class="num">1,
optimization, iBatch) ||

卷积与状态张量的逐层挂载

这段初始化逻辑把时序卷积、转置卷积以及隐藏状态容器依次挂到网络描述符上,每一步失败都会释放已分配对象并返回 false,避免半初始化状态污染后续前向计算。 注意 cA 权重在挂载后立刻跑了一次 SumAndNormilize,归一化系数写死 0.05f,这意味着注意力矩阵 A 的初始尺度被强制压到较小范围,训练早期梯度爆炸的概率会低一些。 B、C、Delta 三组张量都分 Time 与 Variable 两路,激活函数分别钉死 TANH 与 SoftPlus:TANH 限幅在 [-1,1],SoftPlus 保证输出恒正且对零附近输入平滑,适合建模时间间隔类非负增量。 在 MT5 里把这段直接贴进 CEvaluator 派生类的 Init 尾部,改 iWindowOut 或 iUnitsOut 后必须同步核对 cHiddenStates 的 2*iUnitsOut*iWindowOut 维度,否则 OpenCL 内核启动会报参数越界。外汇与贵金属行情跳空频繁,这类网络对外生冲击的过拟合风险偏高,实盘前请用历史样本外数据验证。

MQL5 / C++
   if(!cProjectionX_Variable.Add(conv))
   {
      class="kw">delete conv;
      class="kw">return false;
   }
   index++;
   transp = new CNeuronTransposeOCL();
   if(!transp ||
      !transp.Init(class="num">0, index, OpenCL, units_in, iUnitsOut, optimization, iBatch) ||
      !cProjectionX_Variable.Add(transp))
   {
      class="kw">delete transp;
      class="kw">return false;
   }
   index++;
   conv = new CNeuronConvOCL();
   if(!conv ||
      !conv.Init(class="num">0, index, OpenCL, units_in, units_in, iWindowOut, iUnitsOut, class="num">1,
optimization, iBatch) ||
      !cProjectionX_Variable.Add(conv))
   {
      class="kw">delete conv;
      class="kw">return false;
   }
class=class="str">"cmt">//--- HiddenState
   index++;
   if(!cHiddenStates.Init(class="num">0, index, OpenCL, class="num">2 * iUnitsOut * iWindowOut, optimization, iBatch))
      class="kw">return false;
class=class="str">"cmt">//--- A*H
   index++;
   if(!cA.Init(class="num">0, index, OpenCL, iWindowOut, iWindowOut, class="num">2 * iWindowOut, iUnitsOut, class="num">2,
optimization, iBatch))
      class="kw">return false;
if(!SumAndNormilize(cA.GetWeightsConv(), cA.GetWeightsConv(), cA.GetWeightsConv(),
iWindowOut, false, class="num">0, class="num">0, class="num">0, class="num">0.05f))
   class="kw">return false;
class=class="str">"cmt">//--- B
   index++;
   if(!cB_Time.Init(class="num">0, index, OpenCL, iWindowOut, iWindowOut, class="num">1, iUnitsOut, class="num">1,
optimization, iBatch))
      class="kw">return false;
   cB_Time.SetActivationFunction(TANH);
   index++;
   if(!cB_Variable.Init(class="num">0, index, OpenCL, iWindowOut, iWindowOut, class="num">1, iUnitsOut, class="num">1,
optimization, iBatch))
      class="kw">return false;
   cB_Variable.SetActivationFunction(TANH);
class=class="str">"cmt">//--- C
   index++;
   if(!cC_Time.Init(class="num">0, index, OpenCL, iWindowOut, iWindowOut, iUnitsOut, iUnitsOut, class="num">1,
optimization, iBatch))
      class="kw">return false;
   cC_Time.SetActivationFunction(TANH);
   index++;
   if(!cC_Variable.Init(class="num">0, index, OpenCL, iWindowOut, iWindowOut, iUnitsOut, iUnitsOut, class="num">1,
optimization, iBatch))
      class="kw">return false;
   cC_Variable.SetActivationFunction(TANH);
class=class="str">"cmt">//--- Delta
   index++;
   if(!cDelta_Time.Init(class="num">0, index, OpenCL, iWindowOut, iWindowOut, iUnitsOut, iUnitsOut, class="num">1,
optimization, iBatch))
      class="kw">return false;
   cDelta_Time.SetActivationFunction(SoftPlus);
   index++;
   if(!cDelta_Variable.Init(class="num">0, index, OpenCL, iWindowOut, iWindowOut, iUnitsOut, iUnitsOut, class="num">1,
optimization, iBatch))
      class="kw">return false;
   cDelta_Variable.SetActivationFunction(SoftPlus);
class=class="str">"cmt">//---
   class="kw">return true;
   }

「把这条线请下神坛」

Chimera 的二维状态空间模型(2D-SSM)把时间序列在时间和特征两个维度上的依赖一起塞进了状态空间,理论上能同时抓长期趋势和季节性形态,但这套 MQL5 实现目前只走到一半,离可实盘验证还差一段。 已放出的七个文件里,Research.mq5、Study.mq5、Test.mq5 三类智能系统加上 NeuroNet.mqh / Trajectory.mqh 两个类库,构成了从采样、训练到测试的基础骨架;Test.mq5 跑通前,任何「框架有效」的说法都只是纸面推导。 下一篇若真把历史数据集接进 Test.mq5,才有概率谈泛化能力。外汇与贵金属波动受宏观事件扰动,即便回测漂亮,实盘也倾向出现样本外衰减,别把未完工的模型当圣杯。

常见问题

隐藏态作为独立 buffer 在核函数内按神经元索引读写,调试时把该 buffer 拷回主机打印即可看实时值。
区别在于梯度需沿时间与空间两维回传,先拆时间展开再算空间权重梯度,漏掉任一层都会偏。
小布可读取你的策略结构描述,对比类落地规范标出梯度累加缺失或挂载错位,并给修改提示。
多是初始化链路里神经元数或时间窗长度传错,核对 OpenCL 初始化参数与每层声明是否一致。
仅作概率参考,贵金属高风险,建议先用历史回放验证隐藏态稳定性再考虑轻仓试。