深度神经网络 (第七部分)。 神经网络的融合: 堆叠·综合运用
📘

深度神经网络 (第七部分)。 神经网络的融合: 堆叠·综合运用

第 3/3 篇

◍ 用R+Keras搭一个带噪声抑制的二元DNN分类器

在R环境里通过reticulate调用Keras,可以把MT5导出的特征矩阵直接喂给深度学习模型做二分类。下面这套脚本把训练集、测试集从env环境取出,标签用to_categorical做one-hot,类别数锁死为2,批次32,学习率1e-4,最多跑100个epoch。 模型主体是一条Sequential堆叠:输入先过一层高斯噪声(stddev=0.001)做正则,再连续交替BatchNorm、ELU全连接、Dropout。隐层节点数100→50→10,丢弃率前半段0.5、末段0.2,最后softmax出二类概率。优化器用RMSprop并带衰减,损失函数binary_crossentropy。 早停回调盯着val_acc,min_delta设1e-5、patience=20,避免白烧显卡。fit时validation_split=0.2,verbose=0关掉刷屏,但view_metrics=TRUE可在后台看曲线。 在测试集上跑出的混淆矩阵是:实际0类预测对184、错判46;实际1类错判78、预测对193。汇总指标Accuracy 0.752,Kappa 0.5068,No Information Rate仅0.523,P值<2.2e-16说明该模型显著优于瞎猜基线。外汇与贵金属行情具有高杠杆高风险,此类信号仅作概率参考,实盘须自担盈亏。 直接复制这段R代码到你的RStudio,把env$res替换成MT5导出的xts特征,就能复现这套75%出头的样本外准确率;若想压过NIR基线,重点调的是GN层stddev和dp1/dp2的丢弃率。

MQL5 / C++
library(reticulate)
library(keras)
py_set_seed(class="num">12345)
num_classes <- 2L
batch_size <- 32L
learning_rate <- class="num">0.0001
epochs <- 100L
#---------
x_train <- env$res$InputTrain
y_train <- env$Ytest %>% to_categorical()
x_test <- env$res$InputTest
y_test <- env$Ytest1 %>% to_categorical()
x_test1 <- env$res$InputTest1
y_test1 <- env$Ytest2 %>% to_categorical()
#----------------------------------------
early_stopping <- callback_early_stopping(monitor = "val_acc", min_delta = class="num">1e-5,
                                          patience = class="num">20, verbose = class="num">0,
                                          mode = "auto")
##----modelDNN--keras-------------------------
# define model
modDNN <- keras_model_sequential()
# add layers and compile
modDNN %>%
  layer_gaussian_noise(stddev = class="num">0.001, input_shape = dim(x_train)[class="num">2], name = "GN") %>%
  layer_batch_normalization() %>%
  layer_dense(units = class="num">100, activation = "elu", name = "dense1") %>%
  layer_dropout(rate = class="num">0.5, name = "dp1") %>%
  layer_batch_normalization() %>%
  layer_dense(units = class="num">50, activation = "elu", name = "dense2") %>%
  layer_batch_normalization() %>%
  layer_dropout(rate = class="num">0.5, name = "dp2") %>%
  layer_dense(units = class="num">10, activation = "elu", name = "dense3") %>%
  layer_batch_normalization() %>%
  layer_dropout(rate = class="num">0.2, name = "dp3") %>%
  layer_dense(units = num_classes, activation = "softmax", name = "soft") %>%
  compile(
    loss = &class="macro">#x27;binary_crossentropy&class="macro">#x27;,
    optimizer =  optimizer_rmsprop(lr = learning_rate, decay = class="num">0.0001),
    metrics = &class="macro">#x27;accuracy&class="macro">#x27;
  )
## Training & Evaluation ---------------------------
# Fit model to data
modDNN %>% fit(
  x_train, y_train,
  batch_size = batch_size,
  epochs = epochs,
  verbose = class="num">0,
  view_metrics = TRUE ,
  shuffle = TRUE,
  validation_split = class="num">0.2,
  class="macro">#validation_data = list(x_test, y_test),
  callbacks = list(early_stopping)) -> history
#--model--test-------------------------
predict(modDNN, x_test) -> Ypr.test  
Ypr.test %>% apply(class="num">1, function(x) which.max(x)) %>% subtract(class="num">1) -> y_pr_test
evalq(res_mod_test <- Eval(Ytest1, y_pr_test), env)

「分类混淆矩阵里的几个关键比率」

上面这组指标来自一个二分类模型的评估输出,直接反映了样本在正负类上的分布与判别质量。Prevalence 为 0.5230,说明标注为「Positive」类(此处代码里 Positive Class 定义为 0)的样本占比略过一半,类别本身不算极端失衡。 Neg Pred Value 读到 0.7122,意味着模型判为负类的结果里,约有 71% 确实为负;Detection Rate 0.3673 则表示真正命中正类的样本只占全体的 36.73%,而 Detection Prevalence 0.4591 是模型总共打出正类标签的比例。 Balanced Accuracy 0.7549 是两类召回率的均值,比单纯看准确率更抗类别倾斜。外汇与贵金属行情里用这类模型做状态识别,仍属高风险,信号只是概率倾向,不能直接当入场依据。 末尾的 plot(history) 是把训练过程历史画出来,开 MT5 接好数据后跑一遍,就能在终端看图核对收敛节奏。

MQL5 / C++
plot(history)

五个模型误差分解后谁更抗噪

把均值融合、多数表决、逻辑回归变体、DNN(7,2) 和 DNN500 这五个模型的测试品质摊开看,varb(500个融合输出合并)拿下了最高 Accuracy 0.7804,DNN(7,2) 紧随其后 0.7565。两者在测试样本上的最小误差 PredErr 分别为 0.2199 与 0.2460,且平方偏差 sqBias 比其余模型低一个数量级(varb 为 0.000398,DNN(7,2) 为 0.000195)。 所有模型的误差方差 PredVar 几乎都贴在 0.25 附近,融合没有如预期那样压低方差,反而换来更低的乖离,这点值得在 MT5 里复算时留意。predictionTargetCov 最佳的是 varb(0.13964),说明它与真实响应的联动最紧。 DNN500 各项分数全部垫底,证明在简单任务上堆复杂度反而拖累表现。实战里更划算的组合是:调好基础融合参数,再叠 varb + DNN(7,2) 做合并器。外汇与贵金属信号建模高风险,以上结论仅来自回测分解,实盘概率仍会漂移。 下面这段 R 侧脚本负责把 EnsAver 与 EnsVot 的分类误差拆成噪声、平方偏差、 estimator 方差和协方差,逻辑和其它模型一致,可直接照抄换 predictions 字段跑。

MQL5 / C++
<span class="preprocessor">#---bias--test-------------------------------
</span>import_fun(randomUniformForest, biasVarCov, BiasVar)
evalq({
&nbsp;&nbsp;target = Ytest1
&nbsp;&nbsp;biasAver &lt;- BiasVar(predictions = ScoreAver$clAver, 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; target = target, 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; regression = <span class="macro">FALSE</span>, idx = <span class="number">class="num">1</span>:length(target))
&nbsp;&nbsp;biasVot &lt;- BiasVar(predictions = ScoreVot$ClVot, 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; target = target, 
&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; regression = <span class="macro">FALSE</span>, idx = <span class="number">class="num">1</span>:length(target))
}, env)
-----------------------------
Noise: <span class="number">class="num">0.2488224</span>
Squared bias: <span class="number">class="num">0.002107561</span>
Variance of estimator: <span class="number">class="num">0.250475</span>
Covariance of estimator and target: <span class="number">class="num">0.1257046</span>
Assuming binary classification with classes {<span class="number">class="num">0</span>,<span class="number">class="num">1</span>}, where <span class="class="type">class="kw">string">&class="macro">#x27;class="num">0&class="macro">#x27;</span> is the majority <span class="keyword">class</span>.
Misclassification rate = P(Y = <span class="number">class="num">1</span>)P(Y = <span class="number">class="num">0</span>) + {P(Y = <span class="number">class="num">1</span>) - P(Y_hat = <span class="number">class="num">1</span>)}^<span class="number">class="num">2</span> + P(Y_hat = <span class="number">class="num">0</span>)P(Y_hat = <span class="number">class="num">1</span>) - <span class="number">class="num">2</span>*Cov(Y, Y_hat)
Misclassification rate = P(Y = <span class="number">class="num">1</span>) + P(Y_hat = <span class="number">class="num">1</span>) - <span class="number">class="num">2</span>*E(Y*Y_hat) = <span class="number">class="num">0.2499958</span>
---------------------
Noise: <span class="number">class="num">0.2488224</span>
Squared bias: <span class="number">class="num">0.004079665</span>
Variance of estimator: <span class="number">class="num">0.2499721</span>
Covariance of estimator and target: <span class="number">class="num">0.1274411</span>
Assuming binary classification with classes {<span class="number">class="num">0</span>,<span class="number">class="num">1</span>}, where <span class="class="type">class="kw">string">&class="macro">#x27;class="num">0&class="macro">#x27;</span> is the majority <span class="keyword">class</span>.
Misclassification rate = P(Y = <span class="number">class="num">1</span>)P(Y = <span class="number">class="num">0</span>) + {P(Y = <span class="number">class="num">1</span>) - P(Y_hat = <span class="number">class="num">1</span>)}^<span class="number">class="num">2</span> + P(Y_hat = <span class="number">class="num">0</span>)P(Y_hat = <span class="number">class="num">1</span>) - <span class="number">class="num">2</span>*Cov(Y, Y_hat)
Misclassification rate = P(Y = <span class="number">class="num">1</span>) + P(Y_hat = <span class="number">class="num">1</span>) - <span class="number">class="num">2</span>*E(Y*Y_hat) = <span class="number">class="num">0.2479918</span>
&gt; env$biasAver
$predError
[<span class="number">class="num">1</span>] <span class="number">class="num">0.2499958</span>
$squaredBias
[<span class="number">class="num">1</span>] <span class="number">class="num">0.002107561</span>
$predictionsVar
[<span class="number">class="num">1</span>] <span class="number">class="num">0.250475</span>
$predictionsTargetCov
[<span class="number">class="num">1</span>] <span class="number">class="num">0.1257046</span>

◍ 融合之外还能怎么榨分类精度

ELM 神经网络分类器用平均或简单多数表决做融合,在计算速度极高时分类品质尚可;但把连续输出先调阈值转成标称变量、求平均前先做校正,品质还能再抬一点,误差方差没看到明显缩减。 把融合输出的平均值换成简单神经网络的 softmax,乖离能压低一个数量级,且方差没有明显恶化;反过来用更复杂的网络去替掉修剪和平均,结果并不好。 用贝叶斯变量选择(varbvs 包)跑出来的逻辑回归模型表现非常好,它挑出的最佳输出可以直接拿去喂神经网络。自预处理后就一直消不掉的 24% 噪声,暗示这些样本可能该在某个环节重标成独立一类。 接 keras 在时间序列上直接训,是后续提高分类品质的可行路径,外汇与贵金属行情高风险,模型信号仅作概率参考。

「随附的 R 与神经网络脚本清单」

这一节的实质是一份随附工程包的目录说明,作者把第八部分用到的 R 语言脚本和记录文件打进了 PartVII.zip(14.75 KB),在 MT5 社群文章页可直接下载。包内不是 MQL5 代码,而是配合 keras/TensorFlow 做深度神经网络融合的 R 侧工具链。 具体文件分工很明确:Importar.R 负责软件包导入,Library.R 是通用函数库,FunPrepareData_VII.R 做初始数据准备,FunStacking.R 创建并测试堆叠集合,Prepare.R 为可训练合并器备数据,Varb.R 跑 varb 基础模型,model_DNN7_2.R 与 model_DNN_500.R 分别是 DNN(7-2) 和 DNN500 网络脚本,SessionInfo_VII.txt 列了全部依赖包版本。 如果你要在本地复现文中的分类融合实验,先下这个 zip,用 R 3.x 环境装好对应包再顺序跑 Prepare.R → FunStacking.R 即可;外汇与贵金属相关的模型验证属高风险尝试,回测结论仅代表历史样本倾向,不等于实盘概率。

一点提醒

R 语言接 Keras 做交易建模,社区里更推荐用 RStudio 团队维护的接口,而不是早期第三方 kerasR——后者在 tensorflow 后端冲到 1.8 版时跟不上节奏,跑起来容易踩坑。 有开发者在 2019 年初提过把强化学习(DQN、DDPG、A3C、TRPO)接进交易训练,这类思路在 MT5+Python 的桥接项目里已有开源实现,比如 PyTrader 连接器,可直连实时行情。 外汇和贵金属杠杆高、滑点跳空频繁,神经网络信号只是概率倾向,实盘前务必用历史数据回测并小仓验证,别把实验室准确率当真金白银。

常见问题

看验证集上混淆矩阵里的假阳性率变化,若加噪声层后假阳性率下降且召回率不塌,就说明抑制有效;可直接对比有无噪声层的两次训练日志。
贵金属短线信号偏重召回率防漏单,外汇震荡市看精确率控假信号;F1只在两类成本接近时作综合参考,别单押一个比率。
可以,小布内置了多模型误差分解与抗噪评分,打开对应品种页选DNN融合诊断即可直接看五个模型的噪点鲁棒排名。
先做输入特征去相关和样本重加权,再用贝叶斯阈值调决策面;实盘前用滚动窗口回测确认增益,外汇贵金属高风险勿盲信离线分数。
必须做时间序列切分而非随机切分,并用样本外窗口验证;噪声抑制层参数别随品种乱搬,贵金属和外汇波动结构不同需重调。