交易中的神经网络:用于精确分类的高效特征提取(构建对象)
引言
在上一篇文章中,我们探讨了 Mantis 框架的理论内容。该框架是一款时序分类基础模型,它在不损失精度的前提下规避了回归算法高昂的计算复杂度。它可将待分析序列的原始数据转换为置信度明确的清晰信号,带来更高质量的分析结果。
设想一个传统预测模型:它以最小化预测误差为训练目标,但面对波动率突然飙升时往往表现欠佳。这类算法会随市场变化发生模型漂移,自适应能力较差,容易出现过拟合,无法提供透明的可靠性评估。我们所需要的不只是未来价格的预测值。还需要实时识别市场状态,判断当前价格走势属于常态行情还是异常行情。而 Mantis 正是为此而生:该模型训练的核心目标并非回归预测,而是解读市场形态。
Mantis 的设计思路源自视觉 Transformer 中的时序分词技术。模型无需扫描数万条报价数据或秒级数据,而是将时间序列切分为固定数量的时序块(Patch)。由此,不同长度的时间序列均可通过相同步数完成运算,无需针对不同数据规模重新调整网络结构。随后引入混合注意力机制:在第一视角下,模型借助卷积与池化操作捕捉细粒度的局部变化;在第二视角下,则通过全局注意力捕获长期趋势。两种视角相互结合,使模型既能捕捉到微小波动,又能感知行情的整体走向。
该模型的核心 制胜秘诀 为对比预训练。假设取同一段价格历史,生成两份轻微扰动后的样本。Mantis 会学习将二者的嵌入向量在向量空间中拉近,如同将多束光线汇聚成一束;同时将完全不同的时序片段在嵌入空间中拉远。经过对比学习,模型能够稳定识别行情形态,当行情振幅发生变化、时序小幅偏移或是出现非标准噪声时,该特性尤为关键。
训练流程的最后一步为温度缩放,也就是模型输出结果的最终校准。交易者关心的不仅仅是 “反转 / 不反转” 的判定结果,更需要对应的置信水平。Mantis 输出的并非抽象评分值,而是经过良好校准、具备实际参考价值的概率。实践表明:若模型预测 “反转概率为 80%”,那么 100 条此类信号中,大约有 80 条信号最终判定正确。
当多通道数据输入模型时,Mantis 的真正价值便得以显现。同步变化的 RSI 指标、成交量、移动平均线以及货币对相关性共同构成了复杂的行情图景。直接拼接上述信号会造成参数量爆炸;而分开处理各通道又会丢失通道间的相互依赖关系。Mantis 的解决方案是采用轻量适配器对通道交互信息进行压缩,仅保留最核心的数据:各指标之间关联强度。该设计减少显存占用,同时减少交易者配置模型的耗时。
下面分步解析 Mantis 的网络架构,以对其进行深入了解。第一阶段:初始卷积。拥有 d 个通道的时间序列首先送入一个输出通道数为 256 的卷积层,生成高维稠密特征表示。随后张量被均匀切分,经过按通道均值池化处理后生成 32 个时序token,每个token均携带局部行情信息。
并行构建差分数据流:对输入数据求取一阶差分,以此提升模型对短期行情变化的敏感度。差分后的数据执行同样的时序分块流程。
第三、第四路数据流为统计数据流:在上述 32 个时间窗口内,分别计算输入序列的均值与标准差,以此传递波动率与价格基准的整体背景信息。
四路数据流分别经过独立的线性投影层完成维度对齐,随后拼接在一起并映射为指定尺寸的全局token。
向编码完成的token序列中加入分类token与正弦位置编码,保证模型不会丢失时序顺序信息。处理后的数据送入 Transformer 模块:该模块包含 6 层网络、8 头多头注意力、归一化层、双层前馈神经网络(FFN)搭配 GELU 激活函数,训练阶段丢弃率(Dropout)设置为 10%。
这些模型分两个阶段进行训练。第一阶段为对比学习阶段,训练数据集由 10 个公开数据集合并而成:包含超 700 万条时间序列样本,迭代 100 次,批次大小 2048,训练硬件为 4 张 NVIDIA Tesla V100 显卡。该阶段就好比模型的高强度集训营。第二阶段接入分类头,并对模型输出执行温度缩放。这种两阶段训练方式,实现了神经网络中较为少见的效果:高精度输出与可靠可解释性兼得。
最后我们介绍模型的灵活性设计。为适配不同规模的任务、针对不同算力预算与数据结构定制模型,Mantis 框架的开发者提供了多种适配器方案:
- PCA(主成分分析)与截断奇异值分解(Truncated SVD):久经实践检验的经典线性降维方法;
- 随机投影(Random Projection):一种快速简洁且高效的特征向量降维方案;
- 方差选择器(Variance‑Based Selector):基于方差筛选出信息含量最高的数据通道;
- 可微线性组合器(LComb):可随主模型同步训练的自适应适配器,适配各类专项任务。
借助上述适配器,开发者能够在运算速度与预测精度之间取得平衡、节约算力资源,同时避免丢失通道之间关键的关联信息。
归根结底,Mantis 并不只是一个单一模型,而是一整套面向深度时序分析的工程工具套件。它的优势不在于宏大的理论口号,而在于严谨细致的工程实现:精心处理每一个时序块、有机融合局部特征与全局上下文、严格校准输出概率、审慎完成通道信息压缩。数据分析与人工智能的结合,让交易者能够依托统计规律和成熟的算法原理理性决策,而非依靠主观猜测。在这个速度与精度决定成败的时代,Mantis 就是一款帮助交易者在起伏不定的市场之中站稳脚跟的利器。
下文附有作者绘制的 Mantis 框架可视化示意图。

在上一篇文章的实操章节中,我们实现了一款基础时序处理组件:CNeuronConcatDiff 模块。该对象集成了一阶差分计算算法以及多路数据流张量拼接机制。这标志着 Mantis 框架核心代码实操开发工作的开端。
今天我们将继续搭建该架构,实现神经网络模型下一个关键基础模块。在上一篇文章的方案规划阶段,我们确定了需要在数据流中实现时序位置编码。做出该决策,目的在于提升模型可解释性,保留原始数据的位置结构信息,同时不降低运算性能。当然我们无需从零开发:好在我们已有经过实战检验的成熟对象CMamba4CastEmbedding。该对象是我们开发 Mamba4Cast 框架时所编写,如今完全适配当前开发任务。
下一步需要完成时序分块功能。正是在该模块,模型摆脱了输入序列长度的约束;无论原始数据规模多大,模型都能够基于固定数量的时序块开展运算。毫不夸张地说,该模块属于分类器架构中最重要的组件之一,后续所有运算流程能否稳定运行都取决于该模块的实现质量。那么我们就从此处着手开发:一步步构建一套机制,将原始信息流切分为紧凑且富含信息的时序块。
分段处理对象
原版 Mantis 框架的设计者采用了一种新颖巧妙的思路解决时序分段问题。模型并非简单将序列切分为等长片段,而是先通过卷积运算完成数据变换。并且卷积运算并非多余操作:它将一维时间序列转换为多通道张量。乍看之下该方案或许多此一举,但在实际应用中优势显著:借助小窗口卷积,模型能够检测出信号的局部波动,捕捉到那些容易被整体趋势掩盖的市场细节特征。
经过变换后得到一个多通道张量。每一条通道都是原始时序数据的独立投影,也就是从各自视角生成的一份信号 “简化表示”。随后张量被均匀切分为固定数量的时序块(Patch)。这些时序块就是模型后续运算的基础单元。接下来进入下一阶段:对每个分段内部的信息进行聚合。
Mantis 的设计者选用经典的按通道均值池化(mean‑pooling)完成信息聚合。该算法简单直观,可以输出平滑后的分段特征。而最大池化(max‑pooling)只会保留分段内的最大值,均值池化则能够保留上下文信息,时序序列中每一个数据点都会对最终生成的token产生贡献。当单个时序块覆盖较大范围的数据时,该特性尤为重要,开发者需要综合全部可用信息,而不能只关注波峰波谷。通过该方式生成的token,紧凑完整地保留了原始数据的时序结构信息,可供 Transformer 模型开展后续分析。
但实践发现,均值池化有时过于 “温和”:它在平滑信息的同时,也将信号中的异常值与突变走势一并平均抵消。对于基础大模型来说,这属于可接受的取舍,算法足够简洁、运算迅速、泛化能力强。而我们选择更进一步优化实现方案。在本次开发中,我们舍弃标准均值池化,改用按通道卷积 + 最大池化的组合方案。
这项优化带来哪些实际收益?首先,卷积运算会在各个通道内部提取特征,完成初步过滤并放大局部行情变化。随后最大池化筛选出特征中最显著的部分。该方案让模型对时序动态变化更加敏感。模型不再对所有数据简单取平均值,而是提取核心特征,因此能够更快、更精准地响应行情突变。该特性对于高频交易或是波动率较高的品种至关重要。
正是得益于这套改进版时序分块算法,我们开发的分类器可以适配各类时间序列的形态与结构。模块不再只是单纯切割数据片段,而是同步完成数据分析、特征提取、过滤与特征评估。在该系统中,单个时序块不再只是一段信号切片,而是一个信息完备、同时承载上下文以及信号形态特征的高质量token。
本文提出的算法封装在 CNeuronMantisPatching 类中实现。该专用模块负责将时间序列切分为结构化时序块,输出可供后续运算使用的高层特征表示。数据真正意义上的特征解读便由此开启:从一维原始数据流中提取局部形态,转换为稠密向量特征。
CNeuronMantisPatching 类采用多阶段计算流水线结构。其内部包含多个子对象,每一个子对象负责一项高度专一的运算任务。该类的结构将在下文中展示。
class CNeuronMantisPatching : public CNeuronTransposeOCL { protected: CNeuronTransposeOCL cToVarSeq; CNeuronConvOCL cProjecting; CNeuronTransposeVRCOCL cToVarProjSeq; CNeuronConvOCL cPatchingProj; CNeuronProofOCL cProof; //--- virtual bool feedForward(CNeuronBaseOCL *NeuronOCL) override; virtual bool updateInputWeights(CNeuronBaseOCL *NeuronOCL) override; virtual bool calcInputGradients(CNeuronBaseOCL *NeuronOCL) override; public: CNeuronMantisPatching(void) {}; ~CNeuronMantisPatching(void) {}; //--- virtual bool Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl, uint count, uint patchs, uint variables, uint embedding_size, uint patch_filters, ENUM_OPTIMIZATION optimization_type, uint batch); //--- virtual bool Save(int const file_handle) override; virtual bool Load(int const file_handle) override; //--- virtual int Type(void) override const { return defNeuronMantisPatching; } //--- virtual void SetOpenCL(COpenCLMy *obj) override; virtual bool WeightsUpdate(CNeuronBaseOCL *source, float tau) override; };
所有内部对象均采用静态声明,由此类的构造函数与析构函数便可置空。这些内部对象在 Init 方法中完成配置,该方法传入一组常量参数,能够唯一确定待创建对象的网络架构。
bool CNeuronMantisPatching::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl, uint count, uint patchs, uint variables, uint embedding_size, uint patch_filters, ENUM_OPTIMIZATION optimization_type, uint batch) { if(!CNeuronTransposeOCL::Init(numOutputs, myIndex, open_cl, variables * embedding_size, patchs, optimization_type, batch)) return false;
值得注意的是,送入 CNeuronMantisPatching 对象的输入数据应当为多通道时序数值矩阵,矩阵当中每一列代表一条独立通道。并且,每一条通道都由一组随时间变化的数值观测序列构成。换句话来讲,我们处理的矩阵维度为 [T * C],其中 T 代表时间步总数,C 代表通道(变量)数量。
模块内部借助数据转置对象,实现各个通道的独立运算处理。该操作是为了保证通道轴与时间轴能够分开运算;如果不显式调换张量轴顺序,则很难实现这种运算隔离。除此之外,CNeuronMantisPatching 类本身继承自 CNeuronTransposeOCL。因此在完成全部变换运算后,便可调用父类方法执行逆转置,输出格式符合预期的张量。
初始化方法的第一步调用父类 CNeuronTransposeOCL 的同名初始化函数。该方法会搭建基础运算结构,对各项参数执行初始化预处理。只有完成该步骤,所有继承而来的运算机制才可以正常运行。
父类初始化成功之后,便可开始配置各个内部组件。首个待配置对象为 cToVarSeq,它是一层矩阵转置层,能够便捷访问每一条通道对应的独立时序序列。其作用是转换输入数据排布形式,让每一条通道变为一条独立时序序列,以便开展后续局部卷积运算。
int index = 0; if(!cToVarSeq.Init(0, index, OpenCL, count, variables, optimization, iBatch)) return false;
接下来初始化局部编码对象 cProjecting。该对象本质是卷积层,相当于一个 “敏锐观测器”,使用宽度为 3、步长为 1 的固定滑动窗口扫描时序数据的每一条通道。它负责提取短期局部行情形态,并将特征映射至维度为 embedding_size 的固定特征空间。
index++; if(!cProjecting.Init(0, index, OpenCL, 3, 1, embedding_size, count - 2, variables, optimization, iBatch)) return false; cProjecting.SetActivationFunction(SoftPlus);
这里需要着重说明:运算过程严格按照单通道独立执行,每条通道单独分析,从而使模型能够适配各个信号独有的特征。并且每一条通道都配有一组互不相同的卷积滤波器,极大提升运算过程的灵活性与结果精度。
经过卷积运算之后得到维度为 [C * T' * D] 的张量,其中:
- C — 通道(特征)数量,
- T' — 使用窗口尺寸 3 完成卷积运算之后,输出结果在时间轴上的长度,
- D — 编码完成后新特征向量的维度。
该张量属于原始数据经过局部压缩、且已完成初步结构化处理后的结果,可作为后续分段与特征聚合运算的输入。这就相当于生成了一份每个特征短期动态走势的精简快照;传统时序分析方法往往需要复杂且高资源开销的变换,才能够捕获这类信息。
接下来进入时序分段阶段。此处有一处关键细节需要理解。在本文的实现方案当中,分段操作本质上就是沿着时间轴切割数据,也就是将长序列切分为若干固定长度的片段。但是上一步卷积输出得到的张量,其时间轴位于第二维度。为了便于后续运算、同时匹配后续网络层的架构,我们需要重新排布张量各个轴的顺序。
于是我们调用三维张量转置对象,调整各维度的排列顺序,得到适配时序分段运算的数据排布形式。转置完成后张量的新结构为 [C * D * T']。
index++; if(!cToVarProjSeq.Init(0, index, OpenCL, variables, count-2, embedding_size, optimization, iBatch)) return false;
经过排布转换后的数据形式直观易用:此时便可直接沿着时间轴处理序列,将序列切分为多个等长片段,并且每一个片段都视作一条独立语义单元。这正契合我们最初的设计目标:从分析单个采样点升级为分析行情形态。
张量以该新结构送入下一阶段,执行核心的分段运算与信息聚合。该阶段的核心任务:将时序序列切分为固定数量的片段,并且从每一段当中提取富含信息的特征表示。但程序初始仅传入目标片段数量(patchs),因此首先需要计算出单个片段的尺寸,也就是每个时序块包含多少个时间步。
接下来初始化卷积层 cPatchingProj,由该层完成时序分段。卷积窗口大小与前面计算得出的片段尺寸相等,并且沿时间轴滑动的步长与窗口尺寸保持一致,以此生成互不重叠的时序片段。
index++; int patch_size = (int(count + patchs) - 3) / int(patchs); if(!cPatchingProj.Init(0, index, OpenCL, patch_size, patch_size, patch_filters, patchs, variables * embedding_size, optimization, iBatch)) return false; cPatchingProj.SetActivationFunction(SoftPlus);
这里需要注意,该实现方案不同于简单的均值池化,此处使用带有多组滤波器(patch_filters)的完整卷积运算。模型不再只是对片段内部的数据求取平均值,而是通过识别片段内典型的局部形态,提取出每个片段中最有价值的特征。
张量经过 cPatchingProj层运算之后,输出一个四维张量,张量形状为 [C × D × P × F],其中:
- P — 分段(时序块)的数量,
- F — 每个时序块所使用的滤波器数量。
该张量存储了所有滤波器作用下,每一条通道内各个分段经过特征增强后的结果。但是后续运算还需要对这份数据执行聚合处理。于是沿着最后一个维度,也就是滤波器轴 F执行 max‑pooling最大池化运算。该运算选取每个维度当中响应最显著的值,筛除冗余或是激活程度较低的滤波器结果。池化完成后,张量维度变为 [C × D × P]。
index++; if(!cProof.Init(0, index, OpenCL, patch_filters, patch_filters, patchs*variables*embedding_size, optimization, iBatch)) return false; //--- return true; }
最后一步的数据变换交由前面已经初始化完成的父类执行。因此本方法末尾向调用方返回一个布尔值,反馈整套初始化流程的执行结果。
值得一提,CNeuronMantisPatching 架构采用高度模块化的设计思路。每一个内层网络层都能够独立处理单通道数据,这使得整套架构拥有极强的可扩展性。无论待分析通道数量多少,运算逻辑均可保持一致,每一条通道都被当作一条独立的数据流进行运算。
除此之外,这种独立通道运算结构能够让计算任务实现高效并行。由于全部神经网络层都基于 OpenCL开发实现,网络层会自动调用可用的 GPU 或是其他兼容运算设备。系统可同时启动数百条并行线程,每条线程单独处理一条通道,几乎不会带来额外的耗时开销。
我们在讲解初始化方法内部结构的时候,已经详细介绍了 CNeuronMantisPatching 类的主体运算逻辑以及各个内部组件之间的协作关系。多通道时序数据处理的全部关键步骤,都是在该方法当中按顺序搭建完成:从最开始的数据转置,到时序分段,最后完成特征聚合。
为避免文章内容过于冗长,本文省略了前向传播(feedForward)以及反向传播(calcInputGradients、updateInputWeights)的相关说明。上述函数内部按照流水线架构,依次调用各个内部对象的同名成员方法即可完成运算。
该类的完整源代码(包括所有辅助方法的实现)已附于文后,读者可自行查阅并进一步了解技术细节。
注意力模块
遵照 Mantis框架的整体设计思路,经过预处理、已经由各通道时序块转换为token形式的时序序列,送入 Transformer模块。时序信息与跨通道信息的最终处理就在该模块内执行:程序会向token序列添加一个特殊的分类token,并且嵌入位置编码信息。
class token分类token充当一种全局信息聚合器。当token流经六层多头自注意力(Self‑Attention)网络时,它会聚合整条序列的结构化特征信息。最终输出向量即可视作待分析时序序列的类别标识;无论任务是识别市场行情阶段、指标状态分类,还是预判未来行情走势类型,都依靠该向量完成判断。
而在我们本次的实现版本中,对该阶段流程作出了一定改动。首先,我舍弃位置编码。原因在于借助引自 Mamba4Cast架构的内置时序编码,token本身就已经显式存储了序列的时序结构信息。该方案无需像传统 Transformer 那样使用正弦位置向量或是可学习位置向量,就可以完成通道内每个元素绝对位置、相对位置的编码。
这项改动同时带来两点好处。第一,降低模型对于输入序列偏移与形变的敏感度。第二,消除固定位置编码与数据本身自带时序上下文之间产生冲突的隐患。将该方案运用于真实金融时序行情时,可以提升模型鲁棒性,使 Transformer 能够更加专注于输入信号本身的内容特征。
除此之外,原版 Mantis的实现方案在 Transformer 输出端,只会取用分类token,该专用向量用于累积整条序列的全局概括信息。分类token被放置在输入序列首部,经过 Transformer 全部网络层运算之后,再从输出结果中将该token提取出来。
但是在我们的实现方案当中,选用了另一种解决思路。放弃传统 “序列首部插入分类token、运算结束后再提取token” 的实现方式,转而使用交叉注意力模块(Cross‑Attention)。该方案的独到之处:分类token被设置为主查询向量(query),原始序列生成的token则充当上下文(键向量与值向量)。这种配置可以让分类token聚焦于序列中最关键的元素,过滤噪声干扰,强化有效特征之间的关联。
更进一步,我们采用了通道独立版交叉注意力机制,每一条通道都单独完成注意力计算。由此便可单独评估每一条通道对注意力结果的贡献占比。该方案在多模态、异质信号分析场景下效果突出,能够避免单条通道权重过高、压制其余通道,得到一份客观均衡、覆盖时序完整结构的聚合特征。
最终由交叉注意力模块输出的全局向量,已经包含整条时序序列均衡且高度概括的特征表示,完全可以送入后续分类或预测流程。
从代码实现层面来讲,整套逻辑封装在 CNeuronMantisAttentionUnit 类当中,该类继承自 CNeuronSoftMaxOCL。也就意味着该类可以直接输出待分析序列属于各个类别的预测概率。新版对象的架构示意图展示于下文。
class CNeuronMantisAttentionUnit : public CNeuronSoftMaxOCL { protected: CNeuronBaseOCL cClassToken[2]; CNeuronMVCrossAttentionMLKV cAttention; //--- virtual bool feedForward(CNeuronBaseOCL *NeuronOCL) override; virtual bool updateInputWeights(CNeuronBaseOCL *NeuronOCL) override; virtual bool calcInputGradients(CNeuronBaseOCL *NeuronOCL) override; public: CNeuronMantisAttentionUnit(void) {}; ~CNeuronMantisAttentionUnit(void) {}; //--- virtual bool Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl, uint token_size, uint window, uint window_key, uint heads, uint units_count, uint layers, uint variables, ENUM_OPTIMIZATION optimization_type, uint batch); //--- virtual bool Save(int const file_handle) override; virtual bool Load(int const file_handle) override; //--- virtual int Type(void) override const { return defNeuronMantisAttentionUnit; } //--- virtual void SetOpenCL(COpenCLMy *obj) override; virtual bool WeightsUpdate(CNeuronBaseOCL *source, float tau) override; };
在这个新建的类当中,包含两大核心组件:
- cClassToken[2] — 一个双层MLP多层感知机,用于生成可训练的分类token;
- cAttention 即为多层交叉注意力对象本身。
所有内部对象均采用静态声明,由此类的构造函数与析构函数便可置空。与往常一样,内部对象在 Init 方法中完成初始化。
bool CNeuronMantisAttentionUnit::Init(uint numOutputs, uint myIndex, COpenCLMy *open_cl, uint token_size, uint window, uint window_key, uint heads, uint units_count, uint layers, uint variables, ENUM_OPTIMIZATION optimization_type, uint batch) { if(!CNeuronSoftMaxOCL::Init(numOutputs, myIndex, open_cl, token_size, optimization_type, batch)) return false;
此处我们首先调用父类的同名初始化方法。如前文所述,继承而来的对象与接口已经在父类方法内完成初始化处理。当父类方法成功执行完毕后,便可开始初始化各类内部对象。第一步初始化用于生成可训练分类token的MLP多层感知机。其第一层输入固定为一个常数标量,第二层再通过可训练参数生成指定尺寸的分类token。
int index = 0; if(!cClassToken[0].Init(token_size, index, OpenCL, 1, optimization, iBatch)) return false; if(!cClassToken[0].getOutput().Fill(1)) return false; index++; if(!cClassToken[1].Init(0, index, OpenCL, token_size, optimization, iBatch)) return false; cClassToken[1].SetActivationFunction(SIGMOID);
为了让token内各元素的数值落在指定区间,我们在最后一层使用 Sigmoid 激活函数。
下一步对交叉注意力模块执行初始化。初始化所需的全部配置参数均由调用方通过本方法的入参传入。
index++; if(!cAttention.Init(0, index, OpenCL, token_size, window_key, heads * variables, window, heads, 1, units_count, layers, 1, 1, variables, optimization, iBatch)) return false; //--- return true; }
方法执行的最后,向调用程序返回布尔值以反馈整套运算的执行结果。
feedForward前向传播方法同样十分简洁。方法入参接收待分析序列上下文对象的指针,并第一时间校验该指针是否有效。
bool CNeuronMantisAttentionUnit::feedForward(CNeuronBaseOCL *NeuronOCL) { if(!NeuronOCL) return false;
接下来需要生成分类token。但是该操作仅会在模型训练阶段执行。模型推理运行期间参数不会发生变化,因此分类token将保持固定不变。我们无需在每一次迭代时都重新生成。
//--- if(bTrain) { if(!cClassToken[1].FeedForward(cClassToken[0].AsObject())) return false; }
生成完成的分类token,连同待分析序列的上下文数据,一并送入注意力模块。
if(!cAttention.FeedForward(cClassToken[1].AsObject(), NeuronOCL.getOutput())) return false;
注意力模块的输出结果,也就是经过特征增强后的分类token,将送入父类CNeuronSoftMaxOCL的输入端。该步骤会把预测结果转换为概率形式,输出各个类别上的概率分布。
if(!CNeuronSoftMaxOCL::feedForward(cAttention.AsObject())) return false; //--- return true; }
最后补充说明:反向传播相关方法(calcInputGradients以及updateInputWeights)内部依次调用各个嵌套子组件对应的成员函数。读者可自行查阅源码实现,完整理解CNeuronMantisAttentionUnit模块的训练流程与权重更新逻辑。该类所有方法的完整源代码已附在文章附件中。
至此,本文计划完成的开发工作已经全部结束。在下一篇文章当中,我们将剖析整套模型架构,并采用真实的历史行情数据对当前实现方案的实际效果开展评测。
结论
我们完成了从初始数据预处理(一阶差分计算、特征拼接、基于CMamba4CastEmbedding的时序编码),再到时序块复杂处理、借助分类token实现交叉注意力智能聚合的全部开发流程。流水线当中的每一步运算,从张量转置、局部卷积运算,再到max‑pooling最大池化与多头注意力,都经过精心设计。在依托OpenCL并行计算优化算力开销的同时,尽可能完整地保留时序动态特征信息。
最终得到一套模块化架构。在不改动底层运算逻辑的前提下,就可以灵活调整通道数量、时序块尺寸、嵌入向量深度以及注意力头数。该特性能够让模型快速适配各类金融场景,既可以用于高频交易,也能够胜任长期趋势分析任务。
在下一部分内容里,我们将给出基于上述组件搭建完成的可训练模型完整结构图,并展示模型在历史数据集上的运行结果。
参考
本文使用的程序
| # | 名称 | 类型 | 说明 |
|---|---|---|---|
| 1 | Research.mq5 | EA | 用于采集训练样本的EA |
| 2 | ResearchRealORL.mq5 | EA | 使用Real-ORL方法采集训练样本的EA |
| 3 | StudyContrast.mq5 | EA | 用于对比编码器训练的EA |
| 4 | Study.mq5 | EA | 用于离线模型训练的智能交易系统 |
| 5 | StudyOnline.mq5 | EA | 用于在线模型训练的智能交易系统 |
| 6 | Test.mq5 | EA | 模型测试EA |
| 7 | Trajectory.mqh | 类库 | 用于描述系统状态与模型架构的结构体 |
| 8 | NeuroNet.mqh | 类库 | 用于构建神经网络的类库 |
| 9 | NeuroNet.cl | 库文件 | OpenCL程序代码库 |
本文由MetaQuotes Ltd译自俄文
原文地址: https://www.mql5.com/ru/articles/18307
注意: MetaQuotes Ltd.将保留所有关于这些材料的权利。全部或部分复制或者转载这些材料将被禁止。
本文由网站的一位用户撰写,反映了他们的个人观点。MetaQuotes Ltd 不对所提供信息的准确性负责,也不对因使用所述解决方案、策略或建议而产生的任何后果负责。
交易中的神经网络:面向准确分类的有效特征提取(Mantis)
新手在交易中的10个基本错误
市场模拟(第 23 部分):仓位视图(一)