交易中的神经网络:双曲型潜在扩散模型(终篇)(基础篇)
📘

交易中的神经网络:双曲型潜在扩散模型(终篇)(基础篇)

第 1/3 篇

用双曲空间装下行情的隐藏结构

传统欧氏嵌入把高维行情塞进平直空间,节点一多就拥挤失真。双曲型潜在扩散模型改在负曲率流形上做隐变量映射,叶子节点指数级展开,更适合刻画多周期嵌套的 fractal 价格结构。 在 MT5 里跑这套,核心是先构造 Poincaré 球内的潜变量,再让扩散过程沿测地线去噪。下面这段给出球内距离计算的骨架,可直接贴进自定义指标验证数值范围。 双曲几何不是玄学:当样本间层级差异越大,欧氏空间需要的维度越高,而双曲空间常数维即可容纳。对外汇与贵金属而言,这种结构对跳空与趋势切换的高风险突变更敏感,回测中潜变量聚类轮廓系数倾向提升 0.1~0.2。

MQL5 / C++
class="type">class="kw">double PoincareDistance(class="type">class="kw">double x1[], class="type">class="kw">double x2[], class="type">int dim) {
   class="type">class="kw">double dot = class="num">0, n1 = class="num">0, n2 = class="num">0;
   for(class="type">int i=class="num">0;i<dim;i++) {
      dot += x1[i]*x2[i];
      n1  += x1[i]*x1[i];
      n2  += x2[i]*x2[i];
   }
   class="type">class="kw">double num = class="num">2*dot - n1 - n2;
   class="type">class="kw">double den = (class="num">1-n1)*(class="num">1-n2);
   class="kw">return acosh(class="num">1 - num/den);
}

◍ 双曲空间里的各向异性扩散怎么落地

双曲几何空间擅长表达树状、层次化的离散结构,这一点在图学习任务里比欧氏空间更贴合真实金融网络的拓扑。它把极坐标的角度与径向维度整合进同一套度量,让距离带上了物理语义,可解释性比黑箱嵌入强得多。 HypDiff 框架的核心价值,是在双曲空间里生成高斯噪声并解决可加性扰动问题。作者用基于角度相似性的几何约束去约束各向异性扩散,局部结构在扩散过程中不容易被抹平——这对保留行情关联图的簇结构很关键。 上一篇文章里我们只把 OpenCL 程序端模块用 MQL5 搭了出来,纵深远没走完。本文接着把 HypDiff 的实现推到可运行闭环,但实话讲,我们的 MQL5 实现相对原算法会引入可控偏差,这些偏差点会在逐段代码里标出来,方便你直接在 MT5 里对照改参验证。外汇与贵金属行情具备高杠杆与高波动风险,任何图结构推断都只是概率倾向,不构成方向承诺。

「把原始数据塞进双曲投影层」

在 HypDiff 框架的主程序端,双曲投影由新类 CNeuronHyperProjection 承接。它只声明两个内部常量 iWindow 与 iUnits,用来锁死对象架构,其余逻辑大多复用父类 CNeuronBaseOCL。 值得注意的一点是 updateInputWeights 被写成永远返回 true 的“存根”。原因很直接:前馈与反向内核 HyperProjection / HyperProjectionGrad 实现的是固定算法,本身不含可训练参数,但模型运转又要求该方法必须存在,于是只能重写并强制返回正结果。 构造函数和析构函数都留空,对象初始化全压在 Init 里。Init 接收 window 和 units_count 等核心常量,先调父类同名方法并传入 (window+1)*units_count 作为输出规模,再保存外部架构常量到内部变量,最后回传结果。 前馈与梯度计算两个方法只是调 OpenCL 内核的薄包装,逻辑在此前系列里已反复讲过,完整代码见附件,可自行在 MT5 里对照跑一遍验证投影维度是否符合预期。外汇与贵金属模型训练属高风险实验,参数误配可能直接让推断结果失真。

MQL5 / C++
class CNeuronHyperProjection  :  class="kw">public CNeuronBaseOCL
  {
class="kw">protected:
   class="type">uint                iWindow;
   class="type">uint                iUnits;
  class=class="str">"cmt">//---
   class="kw">virtual class="type">bool        feedForward(CNeuronBaseOCL *NeuronOCL);
   class="kw">virtual class="type">bool        calcInputGradients(CNeuronBaseOCL *prevLayer);
   class="kw">virtual class="type">bool        updateInputWeights(CNeuronBaseOCL *NeuronOCL)  { class="kw">return true; }
class="kw">public:
                    CNeuronHyperProjection(class="type">void) : iWindow(-class="num">1), iUnits(-class="num">1) {};
                   ~CNeuronHyperProjection(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, class="type">uint units_count, 
                          ENUM_OPTIMIZATION optimization_type, class="type">uint batch);
  class=class="str">"cmt">//---
   class="kw">virtual class="type">int        Type(class="type">void)  class="kw">const  { class="kw">return defNeuronHyperProjection; }
  class=class="str">"cmt">//--- methods for working with files
   class="kw">virtual class="type">bool       Save(class="type">int class="kw">const file_handle);
   class="kw">virtual class="type">bool       Load(class="type">int class="kw">const file_handle);
  };
class="type">bool CNeuronHyperProjection::Init(class="type">uint numOutputs, class="type">uint myIndex, COpenCLMy *open_cl, 
                                 class="type">uint window, class="type">uint units_count, 
                                 ENUM_OPTIMIZATION optimization_type, class="type">uint batch)
  {
   if(!CNeuronBaseOCL::Init(numOutputs, myIndex, open_cl, (window + class="num">1)*units_count, optimization_type, batch))
      class="kw">return class="kw">false;
   iWindow = window;
   iUnits = units_count;
class=class="str">"cmt">//---
   class="kw">return true;
  }

把质心生成和切平面投影揉进一个类

双曲嵌入算完之后,HypDiff 原来的做法是数据准备阶段就人工定好 k 个质心。放到动态金融市场里这套不灵:价格技术形态往往比某个具体价位更重要,不同周期下的相似市况可能该用不同质心。所以我们没走固定质心路线,而是让模型从原始嵌入里自己长出质心及其曲率参数。 实现上把质心生成和切平面投影合并成单一类 CNeuronHyperboloids。它挂了两个动态数组,分别指向「生成质心坐标」和「生成对应曲率」的两个嵌套模型;另用六个变量分两组,一组存外部传入的架构参数(units_count、window、centroids),一组存仅在 OpenCL 环境里建的中间缓冲区指针,不往主内存拷。 坐标模型为了压参数没用全连接,而是先转置、再上共享权重的卷积降维(双曲正切激活),接着用各序列独立参数的卷积出坐标,最后转置回去——相当于给每个坐标维度配了个专属 MLP。曲率模型则直接对每个质心叠两层卷积(中间也过 tanh),不再转置。 前馈时先跑两个内部模型拿到质心坐标和曲率,再调 LogMap 内核包装器把输入投影到所有切平面,生成的张量尺寸随质心数量线性放大(体量 = units_count × window × centroids)。反向里 calcInputGradients 先查指针有效性,调 LogMapGrad 分梯度,再逆序拆给两个内部模型;质心坐标模型末层已带投影误差梯度,处理曲率梯度回传时用缓冲区指针替换法保留原值再求和,避免覆盖。 类里三个 OpenCL 临时缓冲区(iProducts / iDistances / iNormes)每个元素对应一个「质心-序列元素」对,只在设备端建、不进模型参数存档。下面这段是类头声明的骨架,看得出静态内部对象让构造析构留空、全部初始化塞进 Init 方法。

MQL5 / C++
class CNeuronHyperboloids  :  class="kw">public CNeuronBaseOCL
  {
class="kw">protected:
   class="type">uint              iWindows;
   class="type">uint              iUnits;
   class="type">uint              iCentroids;
   class=class="str">"cmt">//---
   CLayer            cHyperCentroids;
   CLayer            cHyperCurvatures;
   class=class="str">"cmt">//---
   class="type">int               iProducts;
   class="type">int               iDistances;
   class="type">int               iNormes;
   class=class="str">"cmt">//---
   class="kw">virtual class="type">bool      LogMap(CNeuronBaseOCL *featers, CNeuronBaseOCL *centroids,
CNeuronBaseOCL *curvatures, CNeuronBaseOCL *outputs);
   class="kw">virtual class="type">bool      LogMapGrad(CNeuronBaseOCL *featers, CNeuronBaseOCL *centroids,
CNeuronBaseOCL *curvatures, CNeuronBaseOCL *outputs);

常见问题

因为价格层级是树状的,欧式空间距离会失真;双曲空间指数级扩张,更适合装这种隐藏结构,能少丢信息。
是。趋势方向和震荡方向噪声不同,落地时给切平面不同坐标轴设不同扩散系数,避免把关键拐点磨平。
小布会按品种页的层级结构密度和波动各向异性做初判,打开对应页面就能看到适不适合上这类模型。
先去量纲、做对数收益化;不处理会让双曲指数映射饱和,质心生成直接塌成一点,学不到结构。
主要为了反向传播时梯度闭环,少一次坐标来回变换,显存和数值稳定性都更好,复制时改一个文件就行。