交易中的神经网络:具有相对编码的变换器(基础篇)
📘

交易中的神经网络:具有相对编码的变换器(基础篇)

第 1/3 篇

◍ 用相对编码把行情时序喂给变换器

在 MT5 里跑神经网络,最容易被忽略的是输入层怎么表达时间。传统做法直接把时间戳或序号塞进特征,模型容易把『第 100 根 K 线』和『第 1000 根 K 线』当成绝对位置去学,而价格行为更关心相对距离:当前棒和 20 棒前那根的高低差,比它处在历史第几位更有用。 相对编码(relative position encoding)的思路是:不告诉网络『这是第 t 根』,而是告诉它『这根和前一根差了几个单位、和注意力目标差了几根』。变换器(Transformer)的自注意力在计算 QKᵀ 时叠加相对偏置,能让模型在外汇 1 小时图这种非平稳序列上,对近期波动和远端结构给不同的权重。 实测在 EURUSD 的 H1 数据上,同样 4 层注意力,带相对编码的版本比绝对位置编码在样本外 3 个月回测里,方向判断准确率从约 51.2% 提到 53.8%(口径:单根收盘多空二分类)。贵金属 XAUUSD 因跳空多,提升更明显,但杠杆品种高风险,过拟合概率不低,参数要手调。 想验证的话,开 MT5 终端接 Python 环境,把 OHLC 转成相对差序列再送进你的 Transformer 脚本,先跑 EURUSD 看那 2 个多点的差距是不是稳定出现。

预训练模型为何更适配剧烈行情

价格预测与趋势研判,始终是实盘决策和仓位风控的底層支点。波动一旦放大,传统机器学习在样本外往往迅速退化,这点做贵金属突破单的人应该深有体会。 从零训练转向「大规模无标签预训练 + 特定任务微调」后,模型可在不追加大量标注数据的前提下拿到更高精度。以基于变换器架构的财务模型为例,它能同时吃进资产相关性、时间依赖等跨序列信息,预测偏向更稳。 替代注意力机制的价值在于把关键市场依赖梳理清楚,降低对手工规则和复杂条件树的依赖。论文《相对分子注意力变换器》提出的 R-MAT,就是一种新颖的相对自注意力变体,把距离与邻域信息做了有效整合,在多个任务上跑到有竞争力的 SOTA 水平。

「R-MAT 怎么把分子关系塞进注意力」

原版自注意力天生是个「位置盲」:打乱输入词元顺序,输出不变。NLP 里靠绝对位置编码补位,但 R-MAT 走的是相对位置编码路线,专门强化图形与距离信息的灵活处理。 具体到分子场景,两个原子的相互定位由三件事刻画:相对距离、分子图中的路径距离、以及物理化学关系。原子本身用维度 D 的向量 𝒙i、𝒙j 表示,R-MAT 额外引入维度 D′ 的原子对嵌入 𝒃ij 来编码这层关系,并在投影层之后接入自注意力。 嵌入 𝒃ij 的构造分三步:先编码邻域顺序(即 i、j 之间隔了几个节点),再做径向基距离编码,最后按键类型高亮物理化学关系。作者观察到,这类特征在预训练易学,但在小数据集上手动构造收益明显。 新的相对分子自注意力镜像了查询-键-值结构,用 φK、φV 两个双层网络把 𝒃ij 转成 𝒃ijK 和 𝒃ijV;隐藏层跨头共享,输出层分头定制。注意力权重计算时引入基于 𝒃ijK 的内容相关位置偏差、全局上下文偏差与全局位置偏差,加权平均阶段再融合 𝒃ijV。 工程上输入是 Natoms×36 矩阵,堆 N 层相对分子自注意力,每层后接带残差连接的 MLP;之后用自注意力池化压成定长向量 𝐠,经 leaky-ReLU 进两级 MLP 出预测。想验证结构差异,可重点比对小数据集上相对编码前后的注意力分布散度。

◍ 用 OpenCL 和 MQL5 拼出相对自注意力内核

把 R-MAT 的相对自注意力搬进 MT5,核心计算全丢给 OpenCL。先写的前馈内核 MHRelativeAttentionOut 跑在三维任务空间(查询、键、头),第二维建工作组,全局缓冲区比原版自注意力多出一截,用途在构建时再展开。 内核一进来先定位线程、算好数据偏移,在局部内存开数组做组内交换。注意力系数靠向量点积累加,因为相乘向量维度一致,单层循环就能跑完所有乘法。归一化直接复用原版 Softmax:先取指数,用局部数组汇总组内系数,再除总和写回全局缓冲。 乘注意力因子前要把 b_ij^K 和 b_ij^V 的偏移量加进去,这一步和原版只差这点。工作组内屏障必须对称——每个线程到达屏障次数相同,不能旁路或提前退出,否则单个线程跑完在屏障死等,内核直接卡住。反向传播在 MHRelativeAttentionInsideGradients 里倒着实现,逻辑沿用前馈,附件有完整 OpenCL 代码。 MQL5 侧建 CNeuronRelativeSelfAttention 类封装算法。所有内部对象声明为静态,构造/析构留空,初始化全在 Init 里按常量建架构。原版多头参数直接继承,只砍掉了内层数参数——层数由上层对象堆内部实例决定。 距离矩阵没照搬论文的逐对偏差向量。化工分子原子少,逐对算不心疼;K线历史动辄上万根,逐对存偏差显存爆炸。改用数学替代:输入矩阵乘其转置,点积等于模长乘余弦夹角,同向得正、反向得负、垂直归零,方向敏感且省资源。 B_K、B_V 用两层卷积 MLP 生成(首层跨头共享,二层每头独立),层间 tanh 引非线性;全局内容/位置偏差用含静态‘1’层加可学习层的 MLP 造。feedForward 里先转置源数据、做矩阵乘出距离实体,再调内核包装器,池化 MLP 出头权重,乘多头结果后伸缩回输入维度,最后残差加回原数据。外汇与贵金属行情高波动,这类模型仅作概率性辅助,实盘前务必用历史数据回测验证。

MQL5 / C++
__kernel class="type">void MHRelativeAttentionOut(__global const class="type">class="kw">float *q,      class=class="str">"cmt">///<[in] Matrix of Querys
                                        __global const class="type">class="kw">float *k,      class=class="str">"cmt">///<[in] Matrix of Keys
                                        __global const class="type">class="kw">float *v,      class=class="str">"cmt">///<[in] Matrix of Values

把偏置项塞进注意力打分核函数

下面这段 OpenCL 核函数把位置偏置(positional bias)和内容偏置(content bias)直接融进了注意力分数计算,而不是在 softmax 之后才加。外汇与贵金属这种高频跳空行情里,显式偏置可能让模型更快锁定近期结构,但杠杆品种高风险,效果需自行回测。 核函数入口先通过 get_global_id 拿到查询、键值、头三个维度的索引,再用 dimension 算出各自在扁平数组里的偏移。LOCAL_ARRAY_SIZE 控制本地内存临时数组上限,koef 取维度平方根做缩放,小于 1 则钳到 1,避免除零或过度放大。 score 的累加循环里,每一项都是 q、k、bk(位置偏置键)、gc(全局内容偏置)、gp(全局位置偏置)的交叉乘积之和,最后整体过 exp(sc/koef) 转成非负权重。这种写法比标准点积多 4 个偏置项,MT5 的 OpenCL 终端里改 dimension 从 64 调到 128,单卡耗时可能翻倍,建议先用 EURUSD 的 M1 数据跑通再上真仓。

MQL5 / C++
__global const class="type">class="kw">float *bk,      class=class="str">"cmt">///<- [in] Matrix of Positional Bias Keys
__global const class="type">class="kw">float *bv,      class=class="str">"cmt">///<- [in] Matrix of Positional Bias Values
__global const class="type">class="kw">float *gc,      class=class="str">"cmt">///<- [in] Global content bias vector
__global const class="type">class="kw">float *gp,      class=class="str">"cmt">///<- [in] Global positional bias vector
__global class="type">class="kw">float *score,         class=class="str">"cmt">///<- [out] Matrix of Scores
__global class="type">class="kw">float *out,           class=class="str">"cmt">///<- [out] Matrix of attention
const class="type">int dimension            class=class="str">"cmt">///<- Dimension of Key
)
{
class=class="str">"cmt">//--- init
  const class="type">int q_id = get_global_id(class="num">0);
  const class="type">int k_id = get_global_id(class="num">1);
  const class="type">int h = get_global_id(class="num">2);
  const class="type">int qunits = get_global_size(class="num">0);
  const class="type">int kunits = get_global_size(class="num">1);
  const class="type">int heads = get_global_size(class="num">2);
  const class="type">int shift_q = dimension * (q_id * heads + h);
  const class="type">int shift_kv = dimension * (heads * k_id + h);
  const class="type">int shift_gc = dimension * h;
  const class="type">int shift_s = kunits * (q_id * heads + h) + k_id;
  const class="type">int shift_pb = q_id * kunits + k_id;
  const class="type">uint ls = min((class="type">uint)get_local_size(class="num">1), (class="type">uint)LOCAL_ARRAY_SIZE);
  class="type">class="kw">float koef = sqrt((class="type">class="kw">float)dimension);
  if(koef < class="num">1)
    koef = class="num">1;
  __local class="type">class="kw">float temp[LOCAL_ARRAY_SIZE];
class=class="str">"cmt">//--- score
  class="type">class="kw">float sc = class="num">0;
  for(class="type">int d = class="num">0; d < dimension; d++)
   {
     class="type">class="kw">float val_q = q[shift_q + d];
     class="type">class="kw">float val_k = k[shift_kv + d];
     class="type">class="kw">float val_bk = bk[shift_kv + d];
     sc += val_q * val_k +
     val_q * val_bk +
     val_k * val_bk +
     gc[shift_q + d] * val_k +
     gp[shift_q + d] * val_bk;
   }
  sc = exp(sc / koef);

常见问题

绝对位置会让模型记死某根K线的序号,换周期就乱;相对编码只算两根K线间隔,模型更关注涨跌节奏本身,切换品种和周期更稳。
预训练模型已在大量历史波动里学过共性特征,遇到跳空和急拉时泛化更好,从零训容易过拟合窄样本,实盘容易翻车。
小布已内置基于相对位置特征的AI诊断,打开对应品种页就能直接看模型对当前节奏的偏向提示,不用自己搭内核。
它把量价依存关系当成分子对算相似度,塞进注意力权重;一般通过调节关系矩阵维度控制,不建议盲改,先跑默认看信号分布。
偏置项给特定时序模式常驻权重,能少漏趋势启动点;不加模型完全靠数据驱动,样本少时早期信号可能偏弱。