English Русский Español Deutsch 日本語 Português
preview
神经网络在交易中的应用:混合图序列模型(GSM++)

神经网络在交易中的应用:混合图序列模型(GSM++)

MetaTrader 5交易系统 |
48 0
Dmitriy Gizlyk
Dmitriy Gizlyk

近年来,源自自然语言处理和计算机视觉领域的图 Transformer 受到了特别关注。其具备建模长程依赖关系并高效处理非规则性金融结构的能力,使之成为波动性预测、市场异常检测以及最优投资策略构建等任务领域的理想工具。然而,经典 Transformer 面临着诸多根本性挑战,其中包括高昂的计算成本以及难以适配无序图结构的问题。

Best of Both Worlds: Advantages of Hybrid Graph Sequence Models(两全其美:混合图序列模型的优势)”一文的作者提出了一种统一的图序列模型 GSM++,该模型结合了各种架构的优点,为表示和处理图数据提供了一种有效的方法。该模型围绕三个关键阶段构建:图标记化、局部节点编码和全局依赖编码。这种方法使模型能够捕捉金融系统中的局部和全局关系,从而使其功能多样,适用于广泛的任务。

所提模型的核心组件是分层图标记化方法,该方法将市场数据转换为紧凑的序列表示,同时保留其拓扑和时间特征。与标准的时间序列编码方法不同,这种方法提高了特征提取的质量,并简化了大量市场数据的处理过程。将层次化标记化技术与包含 Transformer 和循环机制的混合架构相结合,可在多项任务中实现卓越的性能。这使得该方法成为处理复杂金融数据集的强大工具。

GSM++ 框架的作者所进行的实证研究和理论分析表明,所提出的模型不仅可与传统图 Transformer 相媲美,而且在多个关键指标上甚至更胜一筹。


GSM++ 算法

统一图序列模型代表了一个概念框架,该框架包含三个关键阶段:标记化局部编码全局编码。这种方法能够高效地表示和分析复杂的图结构,这在金融市场中尤为重要。复杂的市场系统包含众多参与者和相互作用,需要强大的建模工具来捕捉非线性依赖关系和隐藏的相关性。

标记化在将图结构转换为适合基于序列的模型的顺序表示方面发挥着基础性作用。主要的标记化策略包括节点或边标记化和子图标记化。标记化方法的选择对模型的有效性有显著影响,因为它决定了图的结构信息能在多大程度上得到保留,以及在分析过程中会考虑哪些组织特征。

节点或边标记化将图视为一系列单独的元素,而忽略它们之间的相互连接。保留结构信息需要额外的位置或结构编码。这种方法的主要缺点是计算复杂度高,因为序列长度与节点或边的数量相对应,这使得模型训练变得复杂。然而,当需要每个系统元素的详细信息时,例如,在基于微观特征构建个性化资产策略时它就显得非常有用。在高频交易中,这种方法能够更精确地分析短期市场波动,并检测出异常交易模式。

子图标记化通过将图表示为一系列子图来降低计算成本,从而增强了模型捕捉局部结构的能力。这种方法在金融应用中特别有用,例如分析交易模式,其中子图对应于相关资产群或投资者群体。资产之间的相互作用往往具有隐藏的网络特性,而基于子图的分析有助于揭示持续的市场模式,这对于投资组合管理、流动性评估和套利策略至关重要。

每种标记化方法都有其优点和局限性,因此选择哪种方法取决于具体任务。在某些情况下,结合这两种策略的混合方法能在数据表示保真度和计算效率之间实现更好的平衡。

基于这些想法, GSM++ 框架的作者提出了一种基于相似性对节点进行聚类的层次标记化算法(层次亲和聚类,HAC )。

该算法首先将每个图顶点视为一个单独的簇。在每一步中,将由成本最低的边(由其编码的相似性决定)连接的两个簇合并。这一过程会持续进行,直到整个图被合并成一个单一的簇。结果是一个层次树,根节点代表整个图,叶子节点对应原始节点。

这种方法有两个主要优势。首先,它对节点进行组织,使相似的元素更紧密地聚集在一起,从而改进模型中的图表示。其次,它支持多级图编码,从而实现了灵活的结构分析。开发了两种树遍历策略:深度优先搜索(DFS)和广度优先搜索(BFS)。DFS(深度优先搜索)生成的节点序列反映了它们的层级位置。BFS(广度优先搜索)产生的序列中,相似的节点是相邻的。

这种标记化方法保留了图的局部结构,并且能够有效地与循环模型配合使用,尤其适用于需要全局连通性分析的任务。

此外,还采用了一种分层位置编码方法,该方法考虑了节点之间的最短路径以及它们在聚类层次结构中的位置。实验表明,这种编码方式显著提高了图表示的质量。

由于不同的节点可能根据图结构和任务的不同而需要不同的标记化方法,因此提出了混合标记化MoT)方法。它允许每个节点通过选择最优的标记器并组合它们的表示,来使用最合适的编码方法。

标记化后,将数据转换为向量表示,以研究局部图特征。在这一阶段,通常使用图神经网络(Graph Neural Networks,GNNs),因为它们能有效地捕捉节点之间的局部依赖关系。在金融市场中,这有助于分析资产相关性、检测局部异常,并根据微观结构数据生成预测。由于其适应性强且能够提取复杂模式,图神经网络被应用于算法交易和市场波动预测。

全局编码对于在图中建模长期依赖关系至关重要。采用顺序编码来识别结构元素之间的复杂关系。在金融应用中,这使得能够基于深度数据互联,对宏观经济趋势进行建模,分析全球因素对市场的影响,并制定相关策略。长期金融趋势,如货币政策或全球经济危机的影响,需要能够捕捉跨多个时间范围的复杂依赖关系的算法。

在为图训练选择序列模型时,一个关键问题出现了:哪种模型最有效?标准方法允许将各种序列编码器与不同的标记化方法相结合,从而生成多种可能的架构。然而,对于哪种方法最适合特定的图任务,目前尚无明确共识。

计数任务需要确定特定类型节点的数量。没有因果依赖关系的基于注意力的模型无法正确解决此类任务。这就引出了一个问题:能够考虑顺序的循环模型能处理这个问题吗?

事实证明,如果循环模型的宽度与不同节点类的数量相匹配,那么它就能准确地进行计数。这证明了在顺序结构比图拓扑更关键的任务中,循环模型的有效性。

某些图任务,例如算法推理,需要严格遵守节点顺序。现代序列模型主要依赖于因果依赖关系,在将其与图模型集成时必须考虑这一点。研究表明,过度压缩信息会降低表征的保真度。在循环模型中,对初始数据的敏感度会随着元素间距离的增加而降低,而 Transformer 则保持恒定的敏感度。然而,随着深度的增加,这两种模型都容易出现表征崩溃的问题。

序列开始部分的信息更容易被记住。这导致了一种 U 型效应,即序列开头和结尾的标记比在中间的标记更能保持其重要性。这种行为在 Transformer 和循环模型中都有观察到。因此,在按顺序排列节点时,应将重要元素放置得更紧密,以增强它们之间的相互影响。

与连通性相关的任务需要进行全局图结构分析。连通性问题可以被视为一个二元分类问题。研究表明,具有足够深度和嵌入规模的 Transformer 可以有效解决此类任务。然而,循环模型和有限注意力 Transformer 需要显著更多的参数或深度才能达到类似的效果。

当数据具有自然顺序且标记化遵循图结构时,循环模型最为有效。节点局部性是定义相邻顶点之间最大距离的一个重要参数。对于局部性有限的图,一个紧凑的循环模型可以高效地确定其连通性。相比之下,固定参数的 Transformer 在处理此类任务时则显得力不从心。

选择模型需要理解图分析任务中的权衡。对不同架构的考察凸显出几个关键观察点:

  1. Transformer 擅长使用最少的参数完成连通任务。它们对于需要并行计算的复杂图特别有用。它们能够形成依赖于上下文的表征,这使得它们在分析复杂网络和图时具有强大的能力。
  2. 循环神经网络RNNs)在顶点连接具有明显局部结构的图上表现良好。它们需要的参数和计算量更少,因此具有节能性,适合用于流式数据处理。
  3. 混合模型结合了 RNN 和 Transformer,充分利用了两种架构的优势。它们在计算复杂性和预测准确性之间取得了平衡,尤其是在全局背景和局部细节都至关重要的情况下。
  4. 在严格的元素顺序很重要的情况下,状态空间模型非常有效。它们具有长期记忆特性,因此适用于时间序列分析以及基于代理的系统中序列动作的建模。
  5. 稀疏注意力机制降低了 Transformer 的计算成本,特别是在处理大型图时。其有效使用需要额外的机制来识别最重要的节点连接,这可能会使实现变得复杂。

因此,模型选择取决于输入数据的结构和可用的计算资源。Transformer 适用于具有明显全局依赖性的复杂图, RNN 最适合局部序列,状态空间模型擅长需要严格操作顺序的任务。混合方法能够在计算效率与预测准确性之间取得平衡,因此成为许多应用场景中的灵活选择。

基于此分析,作者提出了 GSM++ 框架,该框架结合了基于相似性的分层节点标记化、卷积 GNN 作为局部编码器以及包含 MambaTransformer 模块的混合全局编码器。

GSM++


MQL5 中的实现

在回顾了 GSM++ 框架作者提出的方法的理论方面之后,我们现在转向我们工作的实际部分。在本节中,我们将重点介绍如何使用 MQL5 工具来实现我们对所讨论方法的独特解读。

值得注意的是,在保留原始方法所蕴含的一般概念的同时,我们的实现细节上存在显著差异。

首先,在我们的实现中,我们决定放弃基于层次相似性的聚类(HAC)。有理由认为,金融工具图表上形成的蜡烛图是动态且不断演变的对象,难以轻易标准化。它们的分析和聚类是一个复杂且多层面的过程,需要采用更为深入和全面的方法。

因此,与之前一样,我们使用可训练模块对分析中的柱表示进行标记化处理。这种方法使我们能够在真实世界的数据条件下保持模型的灵活性和适应性,这在处理金融市场时尤为重要。

不过,在我们的实现中,我们采用了所提出的混合标记化( MoT )算法,尽管是以一种经过一定程度修改和调整的形式,以适应我们任务的具体情况。GSM++ 框架的作者建议使用可训练的聚类模型来选择两个最相关的标记化算法,然后将这些算法的输出相加以生成最终表示。相比之下,我们为每个柱准备四种不同类型的标记,并使用从 R-MAT 框架中借鉴的注意力池化算法将它们的输出组合起来。

这种方法显著提高了分析质量,因为它允许考虑数据的更多方面,并能更精确地提取相关信息。在我们的工作中,采用了以下几种标记化方法:

  • 节点标记化 — 每个柱代表模型处理以提取数据的单独分析元素。
  • 边缘标记化 — 关注两个连续柱之间的交互,突出数据不同部分之间的重要联系。
  • 子图标记化 — 通过考虑单个组内多个柱之间的连接,可以形成更复杂的结构。
  • 对单个单元序列进行子图标记化 — 提供结构序列的详细分析,从而实现更深层次的数据处理。

使用这些标记化方法可以同时考虑各个元素及其相互关系,从而显著提高每个柱的信息表示质量,并增强整体模型性能。通过注意力池化将所有标记组合起来,使模型能够灵活地适应并专注于最重要的特征,从而提高决策能力。

为了实现此解决方案,我们创建了一个新对象 CNeuronMoT 。其结构如下所示。

class CNeuronMoT  :  public CNeuronMHAttentionPooling
  {
protected:
   CNeuronConvOCL             cNodesTokenizer;
   CNeuronConvOCL             cEdgesTokenizer;
   CNeuronConvOCL             cSubGraphsTokenizer;
   CLayer                     cUnitarSubGraphsTokenizer;
   CNeuronBaseOCL             cConcatenate;
   //---
   virtual bool      feedForward(CNeuronBaseOCL *NeuronOCL) override;
   virtual bool      calcInputGradients(CNeuronBaseOCL *NeuronOCL) override;
   virtual bool      updateInputWeights(CNeuronBaseOCL *NeuronOCL) override;

public:
                     CNeuronMoT(void){};
                    ~CNeuronMoT(void){};
   //---
   virtual bool      Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                          uint window, uint units_count,
                          ENUM_OPTIMIZATION optimization_type, uint batch);
   //---
   virtual int       Type(void) override   const   {  return defNeuronMoT; }
   //---
   virtual bool      Save(int const file_handle) override;
   virtual bool      Load(int const file_handle) override;
   //---
   virtual bool      WeightsUpdate(CNeuronBaseOCL *source, float tau) override;
   virtual void      SetOpenCL(COpenCLMy *obj) override;
  };

在这种情况下,父对象是 CNeuronMHAttentionPooling ,它已经实现了注意力池化算法,该算法旨在用于模块的输出阶段,以组合不同的标记化方法。这种方法具有几个显著的优势。

首先,使用父类可以避免代码冗余,从而无需在其他对象或组件中重新实现注意力池模块。相反,我们集成了该算法的一个现成的、经过优化的实现,保持了高度的抽象性,简化了代码维护。

其次,所有与标记组合和处理相关的操作都是通过父类的功能来执行的。由于父类已经包含了注意力处理所需的所有方法和算法,这极大地简化了系统架构并提高了资源效率。这样可以最大限度地减少功能重复,增强模块化和可扩展性。

新对象的结构包含一组熟悉的虚拟可重写方法,这对我们模型的实现至关重要。这些方法提供了灵活性,允许根据任务需求定制对象行为。

此外,该类包含多个内部对象,这些对象在我们的算法中发挥着关键作用。在类方法的实现过程中,将详细解释它们的具体用途,并全面描述它们的操作和交互方式。

所有内部对象都声明为静态的,这样我们就可以将类的构造函数和析构函数留空。所有已声明和继承的对象的初始化,都像往常一样在 Init方 法中处理。

bool CNeuronMoT::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl,
                      uint window, uint units_count,
                      ENUM_OPTIMIZATION optimization_type, uint batch)
  {
   if(!CNeuronMHAttentionPooling::Init(numOutputs, myIndex, open_cl, window, units_count, 4, optimization_type, batch))
      return false;

该方法参数接收描述输入数据维度的常量。请注意,此实现要求输出维度与原始数据维度相同。因此,这些参数会立即传递给同名父类方法,该方法已经为所有继承的对象和接口实现了初始化。

在成功执行父类方法后,我们继续初始化新声明的对象。首先,我们初始化节点标记化对象。它被实现为一个卷积层,其卷积窗口大小、步长和滤波器数量均等于描述单个序列元素的向量。

这种方法能够高效处理序列数据,其中每个元素(节点)都表示为与特定特征相对应的向量。卷积能够提取重要的局部特征,为后续的数据处理和标记化奠定基础。将卷积参数与元素描述向量相匹配,可确保卷积层与整体模型结构无缝集成,从而在所有处理阶段保持效率和一致性。

int index = 0;
if(!cNodesTokenizer.Init(0, index, OpenCL, iWindow, iWindow, iWindow, iUnits, 1, optimization, iBatch))
   return false;
cNodesTokenizer.SetActivationFunction(SoftPlus);

接下来,我们初始化边缘标记化卷积层。与之前的对象不同,这里我们使用的是两个连续元素的卷积窗口。这允许对相邻元素之间的相互作用和连接进行建模,这对于更深入的结构分析至关重要。

index++;
if(!cEdgesTokenizer.Init(0, index, OpenCL, 2 * iWindow, iWindow, iWindow, iUnits, 1, optimization, iBatch))
   return false;
cEdgesTokenizer.SetActivationFunction(SoftPlus);

需要注意的是,使用单步双窗口通常会使序列长度减少一个元素。然而,后续的标记组合要求所有阶段的张量维度必须兼容。因此,我们通过在末尾用零填充缺失的元素来保持原始序列长度。

同样,我们对子图标记化卷积层进行初始化,将窗口扩展到三个序列元素,同时保持所有其他参数不变。

index++;
if(!cSubGraphsTokenizer.Init(0, index, OpenCL, 3 * iWindow, iWindow, iWindow, iUnits, 1, optimization, iBatch))
   return false;
cSubGraphsTokenizer.SetActivationFunction(SoftPlus);

在所有标记化级别,我们都应用 SoftPlus 激活函数。这种选择有几个优点。SoftPlus 平滑且单调,避免了剧烈的跳跃,提高了训练稳定性。与 ReLU 不同, SoftPlus 没有从零到正值的突变,从而避免了“死神经元”问题。

此外, SoftPlus 导数始终为正,有利于在反向传播过程中实现良好的可微性和平滑的权重更新。对于需要精确稳定参数调整的复杂神经网络而言,这一点尤为重要。

在所有标记化级别应用 SoftPlus 可以创建一个更灵活、更稳定的模型,确保平滑性和稳健性,这对于处理和分析复杂的序列数据至关重要。

为多维时间序列的单位序列生成标记需要执行多个顺序操作。为了实现此功能,我们需要执行几个顺序操作,我们将这些操作组合成一个内部模型,并将指向存储在动态数组 cUnitarSubGraphsTokenizer 中的对象的指针存储起来。

我们首先初始化一个动态数组,并声明局部变量用于临时存储对象指针。

cUnitarSubGraphsTokenizer.Clear();
cUnitarSubGraphsTokenizer.SetOpenCL(OpenCL);
CNeuronConvOCL *conv = NULL;
CNeuronTransposeOCL *transp = NULL;

为了方便处理单位序列,我们对输入数据进行了转置。

index++;
transp = new CNeuronTransposeOCL();
if(!transp ||
   !transp.Init(0, index, OpenCL, iUnits, iWindow, optimization, iBatch) ||
   !cUnitarSubGraphsTokenizer.Add(transp))
  {
   delete transp;
   return false;
  }

然后,我们使用卷积层生成子图标记。在这里,我们再次分析 3 元子图。每个元素由一个单一的值表示,且分析的变量数量等于单位序列的数量。

index++;
conv = new CNeuronConvOCL();
if(!conv ||
   !conv.Init(0, index, OpenCL, 3, 1, 1, iUnits, iWindow, optimization, iBatch) ||
   !cUnitarSubGraphsTokenizer.Add(conv))
  {
   delete conv;
   return false;
  }
conv.SetActivationFunction(SoftPlus);

这种方法能够对单个单位序列中的转换和模式进行详细分析。

生成的值随后被转换回其原始状态。

index++;
transp = new CNeuronTransposeOCL();
if(!transp ||
   !transp.Init(0, index, OpenCL, iWindow, iUnits, optimization, iBatch) ||
   !cUnitarSubGraphsTokenizer.Add(transp))
  {
   delete transp;
   return false;
  }
transp.SetActivationFunction((ENUM_ACTIVATION)conv.Activation());

最后,我们初始化一个对象,该对象负责将不同标记化方法的结果进行拼接。

   index++;
   if(!cConcatenate.Init(0, index, OpenCL, 4 * iWindow * iUnits, optimization, iBatch))
      return false;
   cConcatenate.SetActivationFunction(None);
//---
   return true;
  }

请注意,我们特意禁用了拼接对象的激活功能。当然,我们对所有生成标记的对象使用相同的激活函数。我们可以将其移至连接对象中,这样可以在反向传播过程中略微简化梯度分布算法。然而,一般来说,我们允许为单个标记生成对象使用不同的激活函数。在这种情况下,为拼接对象指定激活函数只会使数据失真。 

在该方法结束时,我们将执行的操作的逻辑结果返回给调用程序。

接下来,我们需要在 feedForward 方法中实现前馈传递算法。它的算法非常简单。

bool CNeuronMoT::feedForward(CNeuronBaseOCL *NeuronOCL)
  {
   if(!cNodesTokenizer.FeedForward(NeuronOCL))
      return false;
   if(!cEdgesTokenizer.FeedForward(NeuronOCL))
      return false;
   if(!cSubGraphsTokenizer.FeedForward(NeuronOCL))
      return false;

该方法接收一个指向包含输入数据的对象的指针,该指针被传递给不同标记化级别内部对象的相应方法。

对于单位序列中的标记生成,一个循环会遍历内部模型中的对象。

   CNeuronBaseOCL *prev = NeuronOCL, *current = NULL;
   for(int i = 0; i < cUnitarSubGraphsTokenizer.Total(); i++)
     {
      current = cUnitarSubGraphsTokenizer[i];
      if(!current ||
         !current.FeedForward(prev))
         return false;
      prev = current;
     }

所有生成的标记都被收集到一个表示序列元素的单个张量中。

   if(!Concat(cNodesTokenizer.getOutputIndex(), cEdgesTokenizer.getOutputIndex(),
              cSubGraphsTokenizer.getOutputIndex(), current.getOutputIndex(),
              cConcatenate.getOutputIndex(), iWindow, iWindow, iWindow, iWindow, iUnits))
      return false;

将得到的张量传递给同名父类方法,以获得最终的图表示。

   return CNeuronMHAttentionPooling::feedForward(cConcatenate.AsObject());
  }

我们将执行操作的逻辑结果返回给调用程序,并完成方法的执行。

尽管概念上很简单,但 feedForward 方法执行四个并行信息流,这使得梯度分布变得复杂。其算法在 calcInputGradients 方法中实现。

bool CNeuronMoT::calcInputGradients(CNeuronBaseOCL *NeuronOCL)
  {
   if(!NeuronOCL)
      return false;

该方法接收指向同一源数据对象的指针。这次我们还需要将输入数据对模型输出的影响量作为误差梯度的一部分纳入其中。数据只能传递给有效对象。因此,首先要验证指针的有效性。

后续对象的误差梯度通过父类向下传播到标记连接对象。

if(!CNeuronMHAttentionPooling::calcInputGradients(cConcatenate.AsObject()))
   return false;

然后,将梯度分配给所有信息流。

CNeuronBaseOCL *current = cUnitarSubGraphsTokenizer[-1];
if(!current ||
   !DeConcat(cNodesTokenizer.getGradient(), cEdgesTokenizer.getGradient(),
             cSubGraphsTokenizer.getGradient(), current.getGradient(),
             cConcatenate.getGradient(), iWindow, iWindow, iWindow, iWindow, iUnits))
   return false;

接下来,我们需要将误差梯度分配到所有信息流中。

从拼接对象中,我们得到了未针对激活函数导数进行调整的误差梯度。因此,在开始每个信息流的运算之前,我们需要将值调整为对应激活函数的导数值。

首先,我们沿着单变量序列分布误差梯度。首先,我们检查激活函数是否存在,如有必要,对获取的值进行调整。

if(current.Activation() != None &&
   !DeActivation(current.getOutput(), current.getGradient(),
                 current.getGradient(), current.Activation()))
   return false;

接下来,我们对内部模型的对象运行反向传播循环,依次调用相关方法。

for(int i = cUnitarSubGraphsTokenizer.Total() - 2; i >= 0; i--)
  {
   current = cUnitarSubGraphsTokenizer[i];
   if(!current ||
      !current.calcHiddenGradients(cUnitarSubGraphsTokenizer[i + 1]))
      return false;
  }

之后,我们将误差梯度传播到源数据层面。

if(!NeuronOCL.calcHiddenGradients(current.AsObject()))
   return false;

在这个阶段,我们仅通过一个分支将误差梯度传播到了源数据层。我们还需要对另外三个分支进行同样的操作。

为了保留之前获取的值,我们对指向相应数据缓冲区的指针进行了交换。

CBufferFloat *temp = NeuronOCL.getGradient();
if(!NeuronOCL.SetGradient(current.getPrevOutput(), false))
   return false;

一旦我们确定之前获取的数据已保存,我们就会沿着剩余的分支传播梯度。在此,我们依次检查激活函数是否存在,如有必要,则根据激活函数的相应导数调整其值。

if(cNodesTokenizer.Activation() != None &&
   !DeActivation(cNodesTokenizer.getOutput(), cNodesTokenizer.getGradient(),
                 cNodesTokenizer.getGradient(), cNodesTokenizer.Activation()))
   return false;
if(!NeuronOCL.calcHiddenGradients(cNodesTokenizer.AsObject()) ||
   !SumAndNormilize(temp, NeuronOCL.getGradient(), temp, iWindow, false, 0, 0, 0, 1))
   return false;

然后,我们将误差梯度传播到源数据的层级,并将其与之前累积的值相加。对下一个分支重复上述操作。

if(cEdgesTokenizer.Activation() != None &&
   !DeActivation(cEdgesTokenizer.getOutput(), cEdgesTokenizer.getGradient(),
                 cEdgesTokenizer.getGradient(), cEdgesTokenizer.Activation()))
   return false;
if(!NeuronOCL.calcHiddenGradients(cEdgesTokenizer.AsObject()) ||
   !SumAndNormilize(temp, NeuronOCL.getGradient(), temp, iWindow, false, 0, 0, 0, 1))
   return false;
if(cSubGraphsTokenizer.Activation() != None &&
   !DeActivation(cSubGraphsTokenizer.getOutput(), cSubGraphsTokenizer.getGradient(),
                 cSubGraphsTokenizer.getGradient(), cSubGraphsTokenizer.Activation()))
   return false;
if(!NeuronOCL.calcHiddenGradients(cSubGraphsTokenizer.AsObject()) ||
   !SumAndNormilize(temp, NeuronOCL.getGradient(), temp, iWindow, false, 0, 0, 0, 1))
   return false;

在成功地将误差梯度传播到所有信息流之后,我们将指针返回到它们的原始状态,将操作的布尔结果返回给调用者,并完成该方法。

   if(!NeuronOCL.SetGradient(temp, false))
      return false;
//---
   return true;
  }

至此,我们对自适应混合标记化对象 CNeuronMoT 的方法的算法构建进行了回顾。该对象及其所有方法的完整实现可在附件中找到。
 
我们已行至本文末尾,然而工作仍未圆满完成。让我们稍作休息,下一部分再继续这项工作。


结论

在本文中,我们探索了一种使用混合图序列模型(GSM++)的创新方法,将图结构的功能与序列数据分析相结合。这些模型在预测和分析方面具有高精度,能够高效处理复杂的金融数据。此外,它们优化了计算资源的使用,这使得它们在处理大量数据时特别有价值。GSM++ 的一个主要优势是它能够适应快速变化的市场环境。

在实践部分,我们实现了我们对所提出方法的理解,并构建了一个混合标记化模块。在下一篇文章中,我们将继续完成这项工作,包括测试我们的实现方法在真实历史数据上的有效性。


参考


本文中用到的程序

# 名称 类型 描述
1 Research.mq5 EA 交易 样本采集 EA
2 ResearchRealORL.mq5
EA 交易
使用 Real-ORL 方法采集样本的 EA
3 Study.mq5 EA 交易 模型训练 EA
4 Test.mq5 EA 交易 模型测试 EA
5 Trajectory.mqh 类库 系统状态和模型架构描述结构
6 NeuroNet.mqh 类库 用于创建神经网络的类库
7 NeuroNet.cl 代码库 OpenCL 程序代码

本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/17279

附加的文件 |
MQL5.zip (2482.85 KB)
基于分形的算法(FBA) 基于分形的算法(FBA)
本文提出了一种新型元启发式算法,该算法基于分形思想对搜索空间进行划分,以求解优化问题。该算法通过逐步识别并分离有前景的区域,构建出自相似的分形结构,从而将计算资源集中到最有前景的搜索区域。其独特的、面向更优解的变异机制,有助于在搜索空间的全局探索与局部开发之间取得良好的平衡,显著提升了算法效率。
大逃杀优化器(BRO) 大逃杀优化器(BRO)
本文探讨了大逃杀优化器算法 —— 这是一种元启发式算法,其中各解与其最近邻进行竞争,累积"伤害",当超过阈值时被替换,并周期性地在当前最优解周围缩小搜索空间。文章提供了CAOBRO类的伪代码及MQL5中的实现,包括邻近搜索、向最优解移动以及自适应δ间隔。在Hilly、Forest和Megacity测试函数上的实验结果突出了该方法的优势与局限性。读者可以获得一套开箱即用的基础框架,用于实验和调优关键参数,如种群大小(popSize) 和最大伤害值(maxDamage)。
骆驼算法(CA) 骆驼算法(CA)
骆驼算法(CA)于 2016 年被提出,该算法模拟沙漠中骆驼的行为特征来求解优化问题,同时考量温度、供给储备和耐力三大因素。本文还提出了该算法的改进版本(CAm),核心改进包括:在解的生成过程中引入高斯分布,并对绿洲效应参数进行优化。
图论:Dijkstra(迪杰斯特拉)算法在交易中的应用 图论:Dijkstra(迪杰斯特拉)算法在交易中的应用
Dijkstra(迪杰斯特拉)算法是图论中一种经典的最短路径解决方案,它可以通过对市场网络进行建模来优化交易策略。交易者可以利用它从 K 线图表数据中找到最高效的路线。