使用 Python 的深度学习 GRU 模型到使用 EA 的 ONNX,以及 GRU 与 LSTM 模型的比较·进阶篇
📘

使用 Python 的深度学习 GRU 模型到使用 EA 的 ONNX,以及 GRU 与 LSTM 模型的比较·进阶篇

第 2/3 篇

「用 sklearn 搭一个能跑的模型」

做模型这件事,最顺手的工具是 scikit-learn(社区里习惯写 sklearn)。它天生吃 DataFrame 结构的数据,和 pandas 接起来几乎零摩擦,外汇量价序列塞进去就能训。 完整链路就四步:先定义模型类型,比如选决策树还是别的,顺手把该类型的超参定下来;再拿历史数据训练,让模型从里面抓模式,这是核心;然后用训好的模型对没见过的新数据出预测;最后拿预测值和真实结果比对,看偏差有多大。 这套流程给的是结构化框架,你开 MT5 把 EURUSD 的 H1 收盘和波动率导成 CSV,用 pandas 读进 DataFrame,sklearn 里四步跑完,就能得到一套对未知样本有概率性判断的模型。外汇与贵金属杠杆高,模型输出只作参考,实盘仍属高风险。

GRU 回归网络的搭建与训练落点

GRU 作为 LSTM 的轻量变体,用重置门和更新门取代遗忘/输入/输出三门,参数更少、计算更快,在序列建模里常作为存在算力约束时的替代方案。本文选它做价格序列的回归预测,但网络主体用全连接 Dense 堆叠而非递归层,靠历史窗口平铺成特征喂入。 特征上只取单列(如 Close)作目标与输入,按 20% 切测试集且不打乱顺序,避免未来信息泄漏。训练集再拆 20% 作验证集,监控未参与更新的样本表现。 下面这段是模型定义与训练的核心代码,注意第一层 input_shape 对应特征数,L2 正则化惩罚大权重,Dropout 仅在早期版本里以 0.05 比例存在: model.add(Dense(128, activation='relu', input_shape=(inp_history_size,1), kernel_regularizer=l2(k_reg))) model.add(Dropout(0.05)) model.add(Dense(256, activation='relu', kernel_regularizer=l2(k_reg))) model.add(Dropout(0.05)) model.add(Dense(128, activation='relu', kernel_regularizer=l2(k_reg))) model.add(Dropout(0.05)) model.add(Dense(64, activation='relu', kernel_regularizer=l2(k_reg))) model.add(Dropout(0.05)) model.add(Dense(1, activation='linear')) if 'Close' in data.columns: data['target'] = data['Close'] else: data['target'] = data.iloc[:, 0] x_features = data[[0]] y_target = data['target'] x_train, x_test, y_train, y_test = train_test_split(x_features, y_target, test_size=0.2, shuffle=False) model = Sequential() model.add(Dense(128, activation='relu', input_shape=(X_train.shape[1],), kernel_regularizer=l2(k_reg))) model.add(Dense(256, activation='relu', kernel_regularizer=l2(k_reg))) model.add(Dense(128, activation='relu', kernel_regularizer=l2(k_reg))) model.add(Dense(64, activation='relu', kernel_regularizer=l2(k_reg))) model.add(Dense(1, activation='linear')) model.compile(optimizer='adam', loss='mean_squared_error') model.fit(X_train_scaled, y_train, epochs=int(epoch), batch_size=256, validation_split=0.2, verbose=1) 编译用 adam 优化器配均方误差损失,训练时 batch_size=256、epoch 数由变量控制。外汇与贵金属波动具有高风险,该结构仅提供建模思路,实盘预测倾向失效,请先在 MT5 历史数据上跑通再谈参数。

MQL5 / C++
model.add(Dense(class="num">128, activation=&class="macro">#x27;relu&class="macro">#x27;, input_shape=(inp_history_size,class="num">1), kernel_regularizer=l2(k_reg)))
model.add(Dropout(class="num">0.05))
model.add(Dense(class="num">256, activation=&class="macro">#x27;relu&class="macro">#x27;, kernel_regularizer=l2(k_reg)))
model.add(Dropout(class="num">0.05))
model.add(Dense(class="num">128, activation=&class="macro">#x27;relu&class="macro">#x27;, kernel_regularizer=l2(k_reg)))
model.add(Dropout(class="num">0.05))
model.add(Dense(class="num">64, activation=&class="macro">#x27;relu&class="macro">#x27;, kernel_regularizer=l2(k_reg)))
model.add(Dropout(class="num">0.05))
model.add(Dense(class="num">1, activation=&class="macro">#x27;linear&class="macro">#x27;))
if &class="macro">#x27;Close&class="macro">#x27; in data.columns:
    data[&class="macro">#x27;target&class="macro">#x27;] = data[&class="macro">#x27;Close&class="macro">#x27;]
else:
    data[&class="macro">#x27;target&class="macro">#x27;] = data.iloc[:, class="num">0]
x_features = data[[class="num">0]]
y_target = data[&class="macro">#x27;target&class="macro">#x27;]
x_train, x_test, y_train, y_test = train_test_split(x_features, y_target, test_size=class="num">0.2, shuffle=False)
model = Sequential()
model.add(Dense(class="num">128, activation=&class="macro">#x27;relu&class="macro">#x27;, input_shape=(X_train.shape[class="num">1],), kernel_regularizer=l2(k_reg)))
model.add(Dense(class="num">256, activation=&class="macro">#x27;relu&class="macro">#x27;, kernel_regularizer=l2(k_reg)))
model.add(Dense(class="num">128, activation=&class="macro">#x27;relu&class="macro">#x27;, kernel_regularizer=l2(k_reg)))
model.add(Dense(class="num">64, activation=&class="macro">#x27;relu&class="macro">#x27;, kernel_regularizer=l2(k_reg)))
model.add(Dense(class="num">1, activation=&class="macro">#x27;linear&class="macro">#x27;))
model.compile(optimizer=&class="macro">#x27;adam&class="macro">#x27;, loss=&class="macro">#x27;mean_squared_error&class="macro">#x27;)
model.fit(X_train_scaled, y_train, epochs=class="type">int(epoch), batch_size=class="num">256, validation_split=class="num">0.2, verbose=class="num">1)

◍ 从单输入神经元看线性单元怎么算

拆开一个只接单个输入 x 的神经元,最先看到的是连接线上的权重 w。信号从 x 进来到神经元内部,实际参与计算的是乘积 w * x;权重不是固定常数,而是网络在训练过程中持续修正的量,修正方向决定了神经元对这类输入的响应强度。 光有 w * x 还不够,神经元还需要一个不依赖输入也能平移输出的量,这就是偏差 b。它本质上也是一种权重,但输入端恒为 1,所以流进神经元的值就是 1 * b = b。加上 b 之后,哪怕 x 全为 0,单元依然能输出非零值,避免了所有神经元被输入集体拖死。 把上面两步合起来,单个线性单元对输入 X 的输出就是下面这行。注意这里 X 是输入,W 是权重,b 是偏差,写成表达式后你直接在 MT5 里建个脚本算一条线就能验证它的线性本质。 别把偏差当装饰 很多初学者调网络只动 w,忘了 b 才是让激活整体抬升或压低的关键。贵金属与外汇品种波动区间常跳变,固定 w 不调 b,单元输出容易整体偏离价格尺度,回测里就可能漏掉一段趋势。

MQL5 / C++
Y = X*W + b*class="num">1

「给神经元加输入其实就一行加权求和」

想把更多行情特征塞进一个神经元,做法并不玄学:直接扩输入连接数,每个连接挂一个独立权重,对应一个特征。输出就是各输入乘权重后相加,再叠一个偏置项。 从数学上看,三输入线性单元写成 y = w0·x0 + w1·x1 + w2·x2 + b。两个输入时它能在三维空间里模拟一个平面;输入超过两个,拟合对象变成超平面——一种捕捉多特征交叉关系的多维曲面,这也是神经网络能吃进复杂价格结构的基础。 在 Keras 里搭这种结构靠 Sequential() 堆叠层,其中 Dense 层就是干这活的。下面这行代码已经把容器建出来了,后面接 Dense 就能填输入维度与神经元数。 外汇与贵金属波动受宏观与流动性扰动,多维模型只提高拟合概率,不预示方向,实盘前务必在 MT5 历史数据做 Walk-forward 验证。

MQL5 / C++
model = Sequential()

深度网络靠隐藏层与激活函数破局

想把单层的线性单元堆出预测力,先得加隐藏层。隐藏层把神经元按密集层(dense layer)排布,同一组输入喂给一层里的所有线性单元,让它们协同提取交叉特征;层数上去后,网络才有能力逼近数据里那些非平面的复杂关系。 但有个坑必须点明:连续叠两个密集层,如果中间没有任何非线性元件,等效结果不会比一个密集层更强,数学上仍被锁死在线性变换里。要打破这条天花板,只能靠激活函数把非线性灌进去。 最常用的是 ReLU,把线性单元的输出改成 max(0, w*x+b)。这一刀下去,网络才真正脱离「线和平面的边界」,贵金属与外汇的小时级量价序列里那些拐点形态,才可能被子网络捕捉到。MT5 接 Keras 做特征工程时,记得每层 dense 后必挂激活,否则多层等于白搭。 外汇与贵金属杠杆高、滑点跳空频繁,即便网络测出某种模式倾向重复,也仅是概率优势,实盘须严控仓位。

◍ 递减单元数搭出回归网络的骨架

把多层 Dense 串成顺序模型时,中间层叫隐藏层,输出不被直接观测;最后一层用线性单元不带激活,是回归预测连续值的典型做法,分类任务才需要在输出端补激活函数。 单元数不是拍脑袋定的。简单关系从 4 个单元起步就够,复杂且多维的关系要加量;数据集大才能喂饱更多单元,小样本硬堆容易过拟合学噪声。L2 正则化(kernel_regularizer)跨层一致挂着,能压住大权重。 我们落地的那版是 128→256→128→64→1:首层 128 个 ReLU 单元配 L2 抓初始复杂特征,二层提到 256 扩容量,之后递减到 64 提炼抽象表示,末层单线性单元直接吐回归值。这种逐层收窄的结构在表达力和防过拟合之间倾向更稳。 具体数字靠验证集跑出来的,不是通解。交叉验证看泛化,units 和 k_reg 都值得来回试错调。 [CODE] model = keras.Sequential([     # the hidden ReLU layers     layers.Dense(units=4, activation='relu', input_shape=[2]),     layers.Dense(units=3, activation='relu'),     # the linear output layer     layers.Dense(units=1), ]) model = Sequential() model.add(Dense(128, activation='relu', input_shape=(X_train.shape[1],), kernel_regularizer=l2(k_reg))) model.add(Dense(256, activation='relu', kernel_regularizer=l2(k_reg))) model.add(Dense(128, activation='relu', kernel_regularizer=l2(k_reg))) model.add(Dense(64, activation='relu', kernel_regularizer=l2(k_reg))) model.add(Dense(1, activation='linear')) [/CODE] 代码上半段是最小 demo:输入二维、两层 ReLU 隐藏(4 和 3 单元)、末层线性单单元,适合跑通流程。下半段是实战架构——Sequential 空壳后逐层 add,128 接收输入并 L2 正则,256 扩容量,128 与 64 递减提炼,最后线性输出。复制进 MT5 的 Python 环境改 X_train.shape 就能验。

MQL5 / C++
model = keras.Sequential([
    # the hidden ReLU layers
    layers.Dense(units=class="num">4, activation=&class="macro">#x27;relu&class="macro">#x27;, input_shape=[class="num">2]),
    layers.Dense(units=class="num">3, activation=&class="macro">#x27;relu&class="macro">#x27;),
    # the linear output layer 
    layers.Dense(units=class="num">1),
])
model = Sequential()
model.add(Dense(class="num">128, activation=&class="macro">#x27;relu&class="macro">#x27;, input_shape=(X_train.shape[class="num">1],), kernel_regularizer=l2(k_reg)))
model.add(Dense(class="num">256, activation=&class="macro">#x27;relu&class="macro">#x27;, kernel_regularizer=l2(k_reg)))
model.add(Dense(class="num">128, activation=&class="macro">#x27;relu&class="macro">#x27;, kernel_regularizer=l2(k_reg)))
model.add(Dense(class="num">64, activation=&class="macro">#x27;relu&class="macro">#x27;, kernel_regularizer=l2(k_reg)))
model.add(Dense(class="num">1, activation=&class="macro">#x27;linear&class="macro">#x27;))

常见问题

直接用 sklearn 的 Pipeline 串起 StandardScaler 和线性回归或简单 MLPRegressor,先跑通数据进出再换复杂结构,避免一上来就调网络。
把 PyTorch 或 Keras 训好的 GRU 导出为 ONNX 文件,EA 侧用支持 ONNX 的推理接口加载,输入特征顺序须和训练时完全一致。
小布盯盘的 AIGC 可加载你导出的两种 ONNX 模型做样本外推演对比,直接标出回撤和信号抖动差异,省去你手动跑回测。
单层线性只能拟合直线,隐藏层加激活函数(如 ReLU)后才具备逼近任意非线性函数的能力,这是破局关键点。
常见落点是按 64→32→16 递减,输入维度高可前端加 128;用验证集监控 MSE,若尾层降太狠就回升一层单元数。