层次化向量变换器基础:从模块到单一复杂结构的合并逻辑(基础篇)
(1/3)· 把智驾轨迹预测的 HiVT 拆成可复用的局部与全局建模阶段,交易端该怎么拼回去
「在 MT5 里跑通一套分层向量变换器」
把神经网络塞进 EA 并不玄学,核心是把行情特征做层次化向量映射,再交给变换器做非线性组合。MetaTrader 5 的 MQL5 环境原生支持矩阵与向量运算,足以承载轻量级的在线推断。 下面这段初始化代码在 OnInit 里建了两个权重矩阵:第一层把 10 维行情特征压到 16 维,第二层再升到 8 维。外汇与贵金属杠杆高、滑点随机,实盘前务必用策略测试器跑至少 3 个月 tick 数据验证推断延迟。 权重用 MathRandom 做初始化只是占位,真实训练应在 Python 侧完成再导出常量;直接让 EA 在盘中去梯度下降,大概率因报价事件间隔不稳而发散。
matrix w1 = matrix(class="num">10, class="num">16); matrix w2 = matrix(class="num">16, class="num">8); for(class="type">int i=class="num">0;i<w1.Rows();i++) for(class="type">int j=class="num">0;j<w1.Cols();j++) w1[i][j] = MathRandom(); for(class="type">int i=class="num">0;i<w2.Rows();i++) for(class="type">int j=class="num">0;j<w2.Cols();j++) w2[i][j] = MathRandom();
◍ HiVT 怎么把多车轨迹预测拆开做
上篇讲过层次化矢量变换器(HiVT)的原理论述,它本是为汽车智驾里多个体运动预测而设计的。核心思路是把预测问题拆成局部上下文提取、全局互动建模等几个阶段,而不是一把梭哈全部场景。 作者把流程分成 3 个阶段。第一阶段提取对象局部上下文:整个场景被切成一组以某个个体为中心的局部区域,每个区域单独抽特征。第二阶段在这些以个体为中心的局部区域之间传信息,记下场景级的全局大范围依赖。 局部加全局的表示合并后,解码器在模型单次前向里就能把场上所有个体的未来轨迹都测出来。上篇我们已经把各个模块分别实现了一遍,本篇要做的就是把它们并成一个完整结构,方便直接在 MT5 外接验证。
在 CNeuronHiVTOCL 里搭 HiVT 主干
HiVT 的整套前向与反向逻辑,被收进 CNeuronHiVTOCL 类,它直接继承全连接基类 CNeuronBaseOCL。所有嵌套对象都声明为静态,构造函数和析构函数留空,真正的初始化全压在 Init 方法里:调用父类初始化之后,先卡一道硬性检查——序列元素至少要有 2 个,因为状态矢量化依赖当前步与前一步两个参考点算动态变化。 内部对象按前馈使用顺序创建,先建环境状态的三维张量(T 时间步 / A 个体 / D 描述向量),单变量序列每个元素的描述向量长度等于序列数乘 2。卷积层生成嵌入后,先转置成 ATD 布局,再用注意力层抓单时间步内的个体-个体局部依赖;这里用了两个注意力子层,键值张量的头数是查询张量的一半。 局部依赖之后回原始布局、加可训练定位编码,接 CNeuronMVMHAttentionMLKV 抓时间依赖。路线图信息在外汇/贵金属场景没有硬约束(RSI 仅在 0–100 是孤立例外),所以用实际轨迹补片嵌入代替:当前状态动态覆盖 1 步,轨迹补片用 3 元素、步长 1 的模块,再经交叉注意力融合。全局互动模块用 4 内层的注意力对整个场景建模,预测阶段转置后跑 3 个连续卷积层组成的 MLP,对外输出若干可能轨迹变体——变体数由外部参数指定,再用 SoftMax 投影到概率域、加权平均得最终轨迹。该多轨迹预测是 HiVT 特点,但外汇/贵金属高杠杆下仅代表概率倾向,非确定性结果。 反向通验 calcInputGradients 镜像前向、逆序分发误差梯度。加权平均层要把梯度同时分给轨迹张量和概率向量,但为避免概率分布误差污染独立轨迹预测,作者刻意不再往投影层深传,只把梯度送进 MLP 预测层,再经全局、局部、时态、个体-个体模块逐层转置回传。updateInputWeights 仅对含可训练参数的内部对象调对应方法,无参数层(如转置)直接跳过。
class CNeuronHiVTOCL : class="kw">public CNeuronBaseOCL { class="kw">protected: class="type">uint iHistory;
「把层级时序网络拼成可调用类」
上面这段声明把 HiVT 风格的网络骨架落到了一个 CNeuronHiVTOCL 类里:时间维、轨迹维、预测维三层注意力分别由 cTemporalEncoder、cALEncoder、cGlobalEncoder 承载,cDecoder[3] 注释直接写明是 Agent * Traj * Forecast 三路并行卷积。想验证结构,开 MT5 的 MetaEditor 搜 defNeuronHiVTOCL 就能看到这个类挂进了神经元类型枚举。 Init 里有条硬约束容易踩坑:units_count 小于 2 直接返回 false,且基类初始化要求 window * forecast 作为可见宽度。也就是说时间窗口乘预测步数才是输入张量总维度,调参时若把 forecast 从 1 改成 12,输入规模会等比放大。 iNumTraj 用 MathMax(num_traj, 1) 兜了底,意味着即便外部传 0 轨迹数,内部也至少留 1 条,避免后续转置层 cTransposeTA 出现空维崩溃。外汇与贵金属行情用这类模型推理属于高风险,过拟合历史可能导致样本外表现显著衰减。
class="type">uint iVariables; class="type">uint iForecast; class="type">uint iNumTraj; class=class="str">"cmt">//--- CNeuronBaseOCL cDataTAD; CNeuronConvOCL cEmbeddingTAD; CNeuronTransposeRCDOCL cTransposeATD; CNeuronHiVTAAEncoder cAAEncoder; CNeuronTransposeRCDOCL cTransposeTAD; CNeuronLearnabledPE cPosEmbeddingTAD; CNeuronMVMHAttentionMLKV cTemporalEncoder; CNeuronLearnabledPE cPosLineEmbeddingTAD; CNeuronPatching cLineEmbeddibg; CNeuronMVCrossAttentionMLKV cALEncoder; CNeuronMLMHAttentionMLKV cGlobalEncoder; CNeuronTransposeOCL cTransposeADT; CNeuronConvOCL cDecoder[class="num">3]; class=class="str">"cmt">// Agent * Traj * Forecast CNeuronConvOCL cProbProj; CNeuronSoftMaxOCL cProbability; class=class="str">"cmt">// Agent * Traj CNeuronBaseOCL cForecast; CNeuronTransposeOCL cTransposeTA; class=class="str">"cmt">//--- class="kw">virtual class="type">bool Prepare(const CNeuronBaseOCL *history); class=class="str">"cmt">//--- class="kw">virtual class="type">bool feedForward(CNeuronBaseOCL *NeuronOCL) class="kw">override ; class=class="str">"cmt">//--- class="kw">virtual class="type">bool calcInputGradients(CNeuronBaseOCL *NeuronOCL) class="kw">override; class="kw">virtual class="type">bool updateInputWeights(CNeuronBaseOCL *NeuronOCL) class="kw">override; class="kw">public: CNeuronHiVTOCL(class="type">void) {}; ~CNeuronHiVTOCL(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 window_key, class="type">uint heads, class="type">uint units_count, class="type">uint forecast, class="type">uint num_traj, ENUM_OPTIMIZATION optimization_type, class="type">uint batch); class=class="str">"cmt">//--- class="kw">virtual class="type">int Type(class="type">void) const { class="kw">return defNeuronHiVTOCL; } class=class="str">"cmt">//--- class="kw">virtual class="type">bool Save(class="type">int const file_handle); class="kw">virtual class="type">bool Load(class="type">int const file_handle); class=class="str">"cmt">//--- class="kw">virtual class="type">bool WeightsUpdate(CNeuronBaseOCL *source, class="type">float tau); class="kw">virtual class="type">void SetOpenCL(COpenCLMy *obj); }; class="type">bool CNeuronHiVTOCL::Init(class="type">uint numOutputs, class="type">uint myIndex, COpenCLMy *open_cl, class="type">uint window, class="type">uint window_key, class="type">uint heads, class="type">uint units_count, class="type">uint forecast, class="type">uint num_traj, ENUM_OPTIMIZATION optimization_type, class="type">uint batch) { if(units_count < class="num">2 || !CNeuronBaseOCL::Init(numOutputs, myIndex, open_cl, window * forecast, optimization_type, batch)) class="kw">return false; iVariables = window; iHistory = units_count - class="num">1; iForecast = forecast; iNumTraj = MathMax(num_traj, class="num">1);