交易中的神经网络:广义 3D 引用表达分段(基础篇)
📘

交易中的神经网络:广义 3D 引用表达分段(基础篇)

第 1/3 篇

◍ 把行情拆成三维引用再做特征

传统指标大多只在一维时间序列上滚动,Dmitriy Gizlyk 在 2025 年 6 月 27 日发布的这套思路,把单根 K 线周边上下文扩展成广义 3D 引用:价格、时间偏移与样本窗口三者共同定义一个特征单元。 这种表达的核心价值,是让神经网络不再只看到「当前收盘价」,而是同时拿到前 N 根、跨周期、邻域统计三类信息。MT5 里用标准 timeseries 接口就能拼出这类张量,无需额外插件。 实盘前建议先用 EURUSD 的 M15 历史数据跑一遍引用构建,确认每根 K 线的 3D 索引不越界;外汇与贵金属杠杆高、滑点大,任何特征工程都只是提高概率,不保证方向。

从单目标到任意数量的 3D 引用分段

传统 3D 引用表达分段(3D-RES)只能处理指令中指向单个目标的情况,一旦真实场景里出现“找不到”或“同时要多个”的状况,模型就直接失效。这种局限在机器人拣选、场景理解里很致命,因为自然语言指令本来就不会乖乖只说一个物体。 广义 3D 引用表达分段(3D-GRES)把任务扩到任意目标数量:用多个查询并行,每个查询只盯多对象场景里的某一个实例,再联合吐出掩码。关键点在于“解耦”——查询之间独立跑,但不丢失整句指令的语义约束。 具体实现上,多查询解耦交互网络(MDIN)负责让查询、超点、文本三者交互更顺;文本引导稀疏查询(TSQ)用引用表达覆盖点云关键目标;多对象解耦优化(MDO)把多对象掩码拆成单对象监督,保住每个查询的判别力。三类模块合起来,才让“说几个找几个”在概率上可行,而非凭空保证。

「从单目标掩码到任意数量识别的 3D-GRES」

传统 3D-RES 只能在点云里按文本描述抠出「一个」目标掩码,碰上描述无匹配、或多目标都满足的情况就直接失效,实际落地很受限。3D-GRES 把任务改成从文本 E 和场景 P 里产出掩码 M,M 可以是空、可以含一个、也可以含多个对象,还支持「nothing」类表达来显式验证目标不存在,灵活性和鲁棒性明显更贴近真实交互。 具体管线分四步:引用表达先过 RoBERTa 编码成令牌 𝒯,再投影到 D 维多模态空间并加位置编码;点云用稀疏 3D U-Net 提超点并也投到同维度 D 空间;多查询解耦互动网络(MDIN)用文本引导稀疏查询(TSQ)生成查询,而非随机初始化,靠最远点采样保证查询在点云里稀疏铺开;最后用查询-超点聚合(QSA)和查询-语言聚合(QLA)做跨模态交互,每个查询绑定特定超点,顺带把无关对象分给最近查询。 MDIN 里每个模块都跑一遍 QSA→QLA:QSA 算超点特征 S 与查询嵌入 Qf 的相似分布,查询按分数聚合超点得到 Qs;QLA 再对 Qs 做自注意力加词-查询交叉注意力,产出语言感知特征 Ql 和上下文 Qr,用 MLP 融成最终查询表示。若所有查询置信度都低,就输出 null——这意味着模型真能处理「场景里没有要说那个东西」的情况,而不是硬凑一个掩码。 想验证这套机制,可直接在 MT5 外接 Python 环境复现超点+TSQ 的初始化逻辑,重点看最远点采样后的查询覆盖率;若你做点云标的的另类数据接入,把 D 维投影维度从 256 调到 512 往往能缓解小目标漏检,但显存占用会近乎翻倍,贵金属/外汇高频场景接这类信号须警惕过拟合与滑点高风险。

◍ 在 MT5 里把 3D-GRES 跑成可训练查询

3D-GRES 和之前验证过的 SPFormer、MAFT 最大的不同,是把可训练查询从“训练完就固定”改成按输入数据动态生成。SPFormer 和 MAFT 用的是推理阶段冻结的静态查询,模型只会照备案形态行动;3D-GRES 则基于输入点云现场造查询,覆盖更本地化。我们在 MQL5 实现里沿用这个思路,并把账户状态、持仓编码进模型输入,让嵌入引导入场或离场搜索。 为了让动态查询尽量铺满场景空间,原文引入了综合损失(diversity loss):当查询间平均距离 S_q 趋近 0 时损失为 1,距离拉大则损失趋近 0。我们不在乎损失绝对值,只看梯度方向把查询彼此推开。该计算在 OpenCL 的 DiversityLoss 内核实现,三维任务空间前两维是查询数、第三维是特征向量长度,线程按后两维分组以减少全局内存访问;每个线程只从全局内存读 2 个值,先存原生差值 delt,平方后才进距离和,保留导数所需的原始差形。 类层面新建 CNeuronGRES,核心继承 CNeuronMAFT。Init 方法里先调基类初始化,再把点云转置+卷积对齐查询集规模,逆转置投多模态空间,加位置编码;引用表达用全连接 MLP 生成同维度嵌入,拆成多个语义分量。feedForward 接收点云与引用表达两个指针,解码循环里 QSA 交叉注意力用位置偏置(MAFT 式),QLA 并行自注意力与查询-引用交叉注意力,最后 FeedForward 加残差。 反向传播分 calcInputGradients 与 updateInputWeights。关键优化:初始化时提前替换指针,让自注意力与查询-引用交叉注意力共享同一误差梯度数据,省掉冗余复制,内存占用和训练时间都降。综合损失梯度在查询生成模型各层逆向传播时注入,但特意不进位置编码流,避免污染编码。 整套实现下来,前馈是 3D-GRES 与 MAFT 的共生体,推理阶段可剥掉训练专用操作,对生产决策延迟有正向影响。外汇/贵金属市场高风险,动态查询只是提升覆盖概率,不保证信号胜率,建议直接在 MT5 用附件代码开 OpenCL 跑一遍 DiversityLoss 内核看查询散布效果。

超点反向传播的梯度回收路径

在超点生成模型里做反向传播,第一道坎是解码器层遍历时根本不往模型内部回传梯度。必须手动从键(Key)和值(Value)实体把误差梯度捞回来——每个实体至少挂一个张量,但两条数据路径并不对称:键来自超点模型最后一层输出且带位置编码,值取自倒数第二层且不加位置编码,梯度得沿各自原路退回去。 具体落地时,先算键实体第一层梯度送进内部模型末层,再按层数检查并视情况替换数据缓冲区防丢,随后循环累加到已有梯度上,循环结束返回累计缓冲指针。值实体复用同一套,但缓冲里已有后续层数据,所以直接换缓冲再从并行流收梯度,最后补一个综合误差把超点尽量压全。 查询流处理完会在输入层留一部分梯度,这时替换缓冲并把两路求和,才完成第一个数据源的双流回收。第二个源则先同步指针与 Reference 模型首层梯度缓冲,在末层从键值所有张量收梯度,过位置编码层补向量综合误差保语义多样性,再退到输入层。整套逻辑跑完只给调用方返一个执行结果。 可训练架构基本沿用前作,唯一改动是编码器里描述环境状态的单层,以及训练程序和环境交互逻辑的小修,目的只是把账户状态向量作为引用表达塞进编码器。下面这段 OpenCL 核函数就是多样性损失梯度的一块实现,注意 LOCAL_ARRAY_SIZE 需在宿主端定义,否则编译失败。

MQL5 / C++
__kernel class="type">void DiversityLoss(__global class="kw">const class="type">float *data,
 __global class="type">float *grad,
 class="kw">const class="type">int activation,
 class="kw">const class="type">int add
 )
 {
 class="kw">const class="type">size_t main = get_global_id(class="num">0);
 class="kw">const class="type">size_t slave = get_local_id(class="num">1);
 class="kw">const class="type">size_t dim = get_local_id(class="num">2);
 class="kw">const class="type">size_t total = get_local_size(class="num">1);
 class="kw">const class="type">size_t dimension = get_local_size(class="num">2);
 __local class="type">float Temp[LOCAL_ARRAY_SIZE];
 class="kw">const class="type">int shift_main = main * dimension + dim;
 class="kw">const class="type">int shift_slave = slave * dimension + dim;
 class="kw">const class="type">int value_main = data[shift_main];
 class="kw">const class="type">int value_slave = data[shift_slave];
 class="type">float delt = value_main - value_slave;
 for(class="type">int d = class="num">0; d < dimension; d++)
 {
 for(class="type">int i = class="num">0; i < total; i += LOCAL_ARRAY_SIZE)
 {
 if(d == dim)
 {
 if(i <= slave && (i + LOCAL_ARRAY_SIZE) > slave)
 {
 class="type">int k = i % LOCAL_ARRAY_SIZE;
 class="type">float val = pow(delt, class="num">2.0f) / total;
 if(isinf(val) || isnan(val))
 val = class="num">0;
 Temp[k] = ((d == class="num">0 && i == class="num">0) ? class="num">0 : Temp[k]) + val;
 }
 }
 barrier(CLK_LOCAL_MEM_FENCE);
 }
 }
 class="kw">const class="type">int ls = min((class="type">int)total, (class="type">int)LOCAL_ARRAY_SIZE);
 class="type">int count = ls;
 do
 {
 count = (count + class="num">1) / class="num">2;
 if(slave < count)
 {

「梯度归约与 GRES 类骨架」

这段 OpenCL 内核收尾在做两件事:先把单线程算出的梯度 gr 用树形归约累加到 Temp[0],再按维度 d 是否等于当前 dim 把结果写回 grad[shift_main]。gr 的计算式为 2 * pow(loss, 2.0f) * delt / total,其中 loss = exp(-Temp[0]),若 gr 出现 NaN 或 Inf 直接置 0,避免训练发散。 归约循环里 count 从 ls 起每次 (count+1)/2 折半,slave 线程只处理自己负责的那一半;当 slave==0 且 d==dim 时,Temp[0] 若非法也清零,随后通过 Deactivation 函数把激活导数叠进主梯度。注意 add>0 走累加分支,否则直接覆盖,这决定了多层反向传播时梯度是否保留历史。 内核之后定义的 CNeuronGRES 类继承自 CNeuronMAFT,保护成员一口气挂了 cReference、cRefKey、cRefValue、cMHRefAttentionOut、cRefAttentionOut 五个 CLayer 对象,明显是为多头参考注意力预留的缓冲结构。CreateBuffers 被声明为虚函数,说明子类可能重写以分配这些层的本地内存。 在 MT5 里打开对应的 .mqh 把这段内核和类声明贴进自定义神经元工程,改一下 LOCAL_ARRAY_SIZE 观察归约步数变化,能直接验证显存栅栏 CLK_LOCAL_MEM_FENCE 是否成为瓶颈。外汇与贵金属模型训练波动剧烈,此类 GPU 核的数值稳定性只代表回测环境表现,实盘仍属高风险。

MQL5 / C++
   Temp[slave] += ((slave + count) < ls ? Temp[slave + count] : class="num">0);
   if(slave + count < ls)
      Temp[slave + count] = class="num">0;
   }
   barrier(CLK_LOCAL_MEM_FENCE);
  }
 class="kw">while(count > class="num">1);
 class="type">float loss = exp(-Temp[class="num">0]);
 class="type">float gr = class="num">2 * pow(loss, class="num">2.0f) * delt / total;
 if(isnan(gr) || isinf(gr))
   gr = class="num">0;
 for(class="type">int d = class="num">0; d < dimension; d++)
   {
    for(class="type">int i = class="num">0; i < total; i += LOCAL_ARRAY_SIZE)
      {
       if(d == dim)
         {
          if(i <= slave && (i + LOCAL_ARRAY_SIZE) > slave)
            {
             class="type">int k = i % LOCAL_ARRAY_SIZE;
             Temp[k] = ((d == class="num">0 && i == class="num">0) ? class="num">0 : Temp[k]) + gr;
            }
         }
       barrier(CLK_LOCAL_MEM_FENCE);
      }
    class=class="str">"cmt">//---
    class="type">int count = ls;
    do
      {
       count = (count + class="num">1) / class="num">2;
       if(slave < count && d == dim)
         {
          Temp[slave] += ((slave + count) < ls ? Temp[slave + count] : class="num">0);
          if(slave + count < ls)
             Temp[slave + count] = class="num">0;
         }
       barrier(CLK_LOCAL_MEM_FENCE);
      }
    class="kw">while(count > class="num">1);
    if(slave == class="num">0 && d == dim)
      {
       if(isnan(Temp[class="num">0]) || isinf(Temp[class="num">0]))
          Temp[class="num">0] = class="num">0;
       if(add > class="num">0)
          grad[shift_main] += Deactivation(Temp[class="num">0],value_main,activation);
       else
          grad[shift_main] = Deactivation(Temp[class="num">0],value_main,activation);
      }
    barrier(CLK_LOCAL_MEM_FENCE);
   }
}
class CNeuronGRES :  class="kw">public CNeuronMAFT
 {
class="kw">protected:
  CLayer          cReference;
  CLayer          cRefKey;
  CLayer          cRefValue;
  CLayer          cMHRefAttentionOut;
  CLayer          cRefAttentionOut;
  class=class="str">"cmt">//---
  class="kw">virtual class="type">bool      CreateBuffers(class="type">void);

常见问题

按文中方法将行情映射为三维引用张量,分别编码价格区间、量能档位与时间片段,再喂给后续分段模型做特征提取。
改用任意数量识别的3D-GRES结构,把每个品种视作独立引用分段,用统一骨架并行输出多目标掩码,避免逐个跑模型。
可以,小布内置了3D-GRES类识别流程,打开对应品种页即可自动生成三维引用分段与异常标注,你只需复核决策。
梯度回收断链会导致上层权重不更新,可在训练日志中检查超点连接掩码,确认归约节点是否漏接前向引用。
不是,归约层只负责聚合多引用梯度,损失函数可在骨架外挂接,只要保证反向路径映射到同一超点即可。