神经网络变得简单(第 89 部分):频率增强分解变换器(FEDformer)(基础篇)
📘

神经网络变得简单(第 89 部分):频率增强分解变换器(FEDformer)(基础篇)

第 1/3 篇

◍ 用 FEDformer 把行情频率拆开看

频率增强分解变换器(FEDformer)的思路,是把价格序列先做季节-趋势分解,再在频域里做稀疏注意力,而不是像标准 Transformer 那样在时域全连接。对 MT5 上的外汇与贵金属来说,这种结构可能更扛得住非平稳行情里的突变噪声。 原作者在 2025 年 1 月 31 日发布的这一节,帖文浏览量为 1154,说明这类把深度学习结构塞进 MQL5 生态的尝试,在实战交易者里并非冷门。开 MT5 用自定义指标加载这类模型前,先确认你的历史数据长度够做多尺度分解,否则频域稀疏会退化成普通均线。 外汇与贵金属杠杆高、跳空频繁,任何基于频域分解的信号都只是概率倾向,实盘前务必在策略测试器里跑过至少一年 Tick 级回测。

「变换器为何在长序列预测上翻车」

长序列时间序列预测里,原生变换器最大的坑是高计算复杂度和内存占用,导致很难直接给长序列建模。更隐蔽的问题是:它在捕捉序列分布的共性特征时会失效。 论文《FEDformer:频率增强分解变压器进行长期序列预测》里放了实况与预测的对比截屏——预测序列的分布和真实数据差得很远。根因在逐点注意力:每个时间步的预测是独立且无依赖的,模型没法把整条序列当整体,全局统计属性就丢了。 作者给了两条路。一是用季节性趋势分解,把序列拆开再逼近真实分布;二是把傅里叶分析塞进变换器,用频率特征替代时间维度的直接建模,让全局属性更好捕获。两者合起来就是 FEDformer。 傅里叶分析里挑哪些频率分量是个关键。常规做法留低频、扔高频,但时间序列里某些突变恰恰绑着重要事件,全砍高频会丢信息。作者基于‘序列在傅里叶基下通常是未知稀疏表示’这个事实,理论上证明随机选子集(高低频都含)表现更好,实证也站得住。 顺带一个硬指标:变换器加频率分析后,计算成本从二次方降到线性复杂度。六个基线数据集上,多变量和单变量预测相对 SOTA 分别提了 14.8% 和 22.6%——外汇或贵金属用这类模型做长周期预判时,仍属高风险,线性加速不代表预测必然可靠。

FEDformer 的频率分解与混合专家架构

FEDformer 给出两个变体:傅里叶基版走纯频域路线,小波浪版把时域和频域揉在一起看。长期序列预测被当成序列到序列问题——输入长度 I、输出长度 O、状态向量维度 D,编码器吃下 I*D 的张量,解码器拿到 (I/2+O)*D 的矩阵,这是跑通模型前必须先对齐的维度约定。 原版 Transformer 被塞进了深度分解架构,核心三件套是频率响应分析单元(FEB)、频率增强关注度(FEA)和混合专家分解(MOEDecomp)。编码器多级堆叠,单级先经 MOEDecomp 抽季节性分量 S_en,再进 FEB;解码器更宽,吐出 S_de 和趋势 T_de,用 W_l 投影提趋势,FEA 替掉交叉关注度。 FEB-f 与 FEA-f 靠离散傅里叶变换(DFT)落地,工程上用快速傅里叶变换(FFT)提速。实现里先线性投影,时域转频域,随机采 M 个谐波,乘可训参数核,补零到全频响维度后做逆变换回时域。FEA-f 的 Query 来自解码器、Key/Value 来自编码器,但三者在频域里算关注度,结果补零逆变换——复杂度靠事先选定模式索引控住。 小波浪版(FEB-w / FEA-w)用 Legendre 小波浪基的固定矩阵做递归三分:高频、低频、残差各过一组 FEB-f,按 1/2 间隔自顶向下拆,重造阶段递归拼回输出张量。FEA-w 仅把分解阶段的 FEB-f 换成 FEA-f,多挂一个模块处理最琐碎残差。 MOEDecomp 专治复杂周期里趋势难剥的问题:一组不同窗口大小的均值滤波器抽多重趋势,再按数据相关权重合并。外汇与贵金属序列常带这种混周期,直接套固定窗口均值容易把真趋势滤掉,用混合专家权重更稳,但模型过拟合风险也更高,建议在 MT5 用真实 tick 数据先小样本验证分解层数。

◍ 在 MT5 里手搓 Legendre 小波分解层

原文作者没有严格照搬 FEDformer 论文,而是挑了 DWT(离散小波变换)路线。他对比论文里的 Exchange 列测试结果,发现 DWT 模型在多项数据上明显优于 DFT——推测原因是 DFT 丢掉了输入的时间分量,而 DWT 同时分析频率和时间两个维度,对没有明确周期的外汇、贵金属 tick 序列更友好。这类品种波动受新闻与流动性扰动,周期模糊,实盘里用 DWT 倾向更稳。 落地时他新建了 CNeuronLegendreWavelets 类,直接继承卷积层 CNeuronConvOCL。核心思路很暴力:小波基是固定矩阵,信号向量乘这个矩阵就完成分解。因为基固定,类里没有可训练参数,updateInputWeights 被重写成了空存根。下面这段就是类里唯一被覆盖的三个方法之一的骨架。 [CODE] class CNeuronLegendreWavelets : public CNeuronConvOCL { protected: virtual bool updateInputWeights(CNeuronBaseOCL *NeuronOCL) { return true; } 作者选了 9 个 Legendre 多项式当基,多项式定义域锁在 [0,1],所以初始化时把窗口长度归一为 1,再按序列元素个数切步长——这意味着不管你挂的是 M1 还是 H1,喂进去的只是窗口内的相对频率特征。基矩阵不在编译期写死,而是用宏替换公式在模型 Init 阶段按实际窗口大小现场填,方便换不同小波做对照实验。 对交易者来说,这套实现的副作用很实在:剔除了 DFT/FFT 后,训练与推理的算力开销大幅下降,但预测精度可能略恶化。外汇与贵金属属高风险品种,任何简化模型都要先在 MT5 策略测试器里跑回测再上实盘。

MQL5 / C++
class CNeuronLegendreWavelets :  class="kw">public CNeuronConvOCL
  {
class="kw">protected:
   class="kw">virtual class="type">bool      updateInputWeights(CNeuronBaseOCL *NeuronOCL)  { class="kw">return true; }

「勒让德小波神经元的权重初始化套路」

在 MT5 的 OpenCL 卷积神经元体系里,CNeuronLegendreWavelets 用勒让德多项式把卷积核预置成 wavelet 形态,而不是随机初始化。类里先留了空构造 / 析构,Type() 直接返回 defNeuronLegendreWavelets 常量,方便上层网络做类型分发。 宏里写死了三条多项式:Legendre4 最高次系数是 70,Legendre6 是 924,Legendre8 是 12870,对应四次、六次、八次展开。注意 Init 实际还调了 Legendre10,但宏定义被截断了,复制代码时要自己补完十次项,否则编译会报未定义符号。 Init 里先以窗口大小 window、固定卷积核宽度 9 调父类 CNeuronConvOCL::Init;随后把 WeightsConv 全填 0,再按 iWindow 循环:每个位置塞入 k=i/iWindow 下的 Legendre4、6、8、10 四个值,shift 每次跳 iWindow+1。这意味着一层卷积核实际由 4 组不同阶小波拼接,阶数越高对局部拐点的响应越尖。 外汇与贵金属行情高频噪声大,直接拿这套预置核跑 EURUSD 5 分钟可能过拟合到无意义波动;建议先在小样本回测里把 window 从 9 调到 20 观察置信度变化,再决定是否接实盘信号。

MQL5 / C++
class="kw">public:
                CNeuronLegendreWavelets(class="type">void) {};
                ~CNeuronLegendreWavelets(class="type">void) {};
  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 step, 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) const   { class="kw">return defNeuronLegendreWavelets;  }
};
class="macro">#define Legendre4(x)    (class="num">70*pow(x,class="num">4) - class="num">140*pow(x,class="num">3) + class="num">90*pow(x,class="num">2) - class="num">20*x + class="num">1)
class="macro">#define Legendre6(x)    (class="num">924*pow(x,class="num">6) - class="num">2772*pow(x,class="num">5) + class="num">3150*pow(x,class="num">4) - class="num">1680*pow(x,class="num">3) + \
                        class="num">420*pow(x,class="num">2) - class="num">42*x + class="num">1)
class="macro">#define Legendre8(x)    (class="num">12870*pow(x,class="num">8) - class="num">51480*pow(x,class="num">7) + class="num">84084*pow(x,class="num">6) - class="num">72072*pow(x,class="num">5) + \
                        class="num">34650*pow(x,class="num">4) - class="num">9240*pow(x,class="num">3) + class="num">1260*pow(x,class="num">2) - class="num">72*x + class="num">1)
class="type">bool CNeuronLegendreWavelets::Init(class="type">uint numOutputs, class="type">uint myIndex, COpenCLMy *open_cl,
                                   class="type">uint window, class="type">uint step, class="type">uint units_count,
ENUM_OPTIMIZATION optimization_type, class="type">uint batch)
  {
  if(!CNeuronConvOCL::Init(numOutputs, myIndex, open_cl, window, step, class="num">9, units_count,
optimization_type, batch))
      class="kw">return false;
  WeightsConv.BufferInit(WeightsConv.Total(), class="num">0);
  for(class="type">uint i = class="num">0; i < iWindow; i++)
    {
     class="type">uint shift = i;
     class="type">float k = class="type">float(i) / iWindow;
     if(!WeightsConv.Update(shift, Legendre4(k)))
       class="kw">return false;
     shift += iWindow + class="num">1;
     if(!WeightsConv.Update(shift, Legendre6(k)))
       class="kw">return false;
     shift += iWindow + class="num">1;
     if(!WeightsConv.Update(shift, Legendre8(k)))
       class="kw">return false;
     shift += iWindow + class="num">1;
     if(!WeightsConv.Update(shift, Legendre10(k)))
       class="kw">return false;
     shift += iWindow + class="num">1;

常见问题

变换器自注意力复杂度随序列长度平方增长,内存和计算容易爆;长序列里噪声被放大,趋势信号反而被淹没,预测倾向漂移。可改用频率域压缩后再建模。
它用傅里叶或小波把价格序列投到不同频率桶,低频抓趋势、高频看扰动,再分别用专家网络处理。你只需知道:拆开后能分清楚哪段是噪音、哪段是方向。
小布盯盘的 AIGC 已内置品种诊断,打开对应页会给出序列平稳性与周期特征提示,帮你判断长序列模型是否合适,你专注决策即可。
按勒让德多项式正交区间缩放初始化,偏置靠近零,学习率调小一档。实盘前用历史片段跑几轮看梯度是否爆炸再上量。
不是,专家数过多易过拟合且推理慢;2~4 个专家配门控已够用。端侧跑不动就把分解放本地、推理交云助手,控制单批长度。