深度神经网络 (第 IV 部)。创建, 训练和测试神经网络模型·综合运用
🧠

深度神经网络 (第 IV 部)。创建, 训练和测试神经网络模型·综合运用

(3/3)· 前篇铺垫概念与组件,本篇把 DNN 的搭建、预训练与品质验证一次跑通

偏理论 第 3/3 篇
很多交易者把 DNN 当成黑箱直接喂数据,忽略隐藏层权重初始化方式,导致微调耗时且样本需求暴涨。预训练与非预训练的路径差异,直接决定计算资源与过拟合概率,这一步选错后面全白调。

◍ WOE 分箱里藏着的方向偏移

上面两张表是同一批 2000 条样本在 rstl 变量上做分箱后的 WOE(Weight of Evidence)输出。第一张是初版分箱,第二张是 Final.Bin 合并后的结果,两者都能直接拖进 MT5 的自定义报表里核对。 初版里第 5 箱(值 ≤0.4846)的 1 类占比 47.4%、WOE 12.8,第 6 箱(≤Inf)的 1 类占比 71.4%、WOE -89.2,说明变量右尾的样本极度偏向 1 类。外汇与贵金属行情里这种尾部倾斜往往对应极端波动段,杠杆交易高风险,不能单凭分箱就押方向。 Final.Bin 把样本重切成 6 箱:第 1 箱(≤-0.4542)1 类率仅 39.0%、WOE 47.3,第 3 箱(≤-0.2496)1 类率 65.6%、WOE -62.1,单调趋势比初版更清楚。做特征筛选时,IV 列合计约 0.07 算弱预测力,只能当辅助过滤,别当主信号。 开 MT5 把 rstl 算出来跑一遍等宽分箱,对比 WOE 符号翻转的临界点,比死记阈值更有用。

「PCCI 分箱后正负区间的事件概率裂口」

对 v.pcci 做 WOE 分箱,样本总量 2000,标签 0 有 988 个、标签 1 有 1012 个,整体 1 类占比 50.6%,总 IV 只有 0.042,单变量区分力偏弱。 但分箱内部并不均匀:第 1 箱 pcci ≤ -0.441 时 1 类概率仅 40.3%,WOE 高达 41.9;到第 7 箱 pcci > 0.440 时 1 类概率升到 59.1%,WOE 落到 -34.4。正负极端之间事件率差约 18.8 个百分点,说明 pcci 极度超卖或超买时,价格后续朝标签 1 方向走的概率倾向明显偏移。 外汇与贵金属属高风险品种,这种偏移只是历史样本的统计倾向,不是确定性信号。开 MT5 把 pcci 极值区单独拉出回测,看在你常用周期上是否也出现类似 40% 对 59% 的裂口,比直接信总 IV 更有用。 分箱图可用 evalq(woe.binning.plot(preCut), env) 直接渲染,DiscretizeData 函数依赖 foreach 与 woeBinning 两个包,缺一个都会报找不到对象。

MQL5 / C++
evalq(woe.binning.plot(preCut), env)
DiscretizeData <- function(X, preCut, var){
  require(foreach)
  require(woeBinning)

把连续因子切进可建模的桶里

在 R 环境里封装 DiscretizeData 之后,连续特征会被统一落进分箱、WOE 和哑变量三套表达。核心函数先对 X 里每个数据集做 select(-Data) 去掉原始时间戳,再走 targ.int 和 woe.binning.deploy,其中 min.iv.total = 0.1 把信息值过低的箱直接剔除,add.woe.or.dum.var 控制输出形态。 部署时分别传 var=""、"woe"、"dum" 得到 DTbin、DTwoe、DTdum,随后用 foreach 把每个集合的 binned / dum / woe 列抽出来。注意 WOE 列在抽取后 divide_by(100),相当于把原始放大百倍的 WOE 缩回常规量级,不除的话后续逻辑回归系数会失真。 从 env$DTcut$val 的 str() 实测看,验证集有 501 条观测,bin 块含 10 个因子变量,stlm.binned 被切成 7 个水平,其余多数切 5 水平;woe 块同维度但已是数值矩阵;dum 块展开成 41 列,仅 v.fatl 一个因子就拆出 4 个 0/1 列。外汇与贵金属因子做此类离散化时波动剧烈,分箱边界可能随样本漂移,需用小布盯盘回测不同窗口验证稳定性。 直接把 DTcut 的 pretrain/train/val/test 四切分喂给下一层模型即可,但哑变量维度膨胀快,41 列里大量全 0 列说明该箱样本极少,建模前建议按频率过滤。

MQL5 / C++
DTd <- list()
foreach(i = class="num">1:length(X)) %do% {
  X[[i]] %>% select(-Data) %>% targ.class="type">int() %>%
  woe.binning.deploy(preCut, min.iv.total = class="num">0.1,
                     add.woe.or.dum.var = var) -> res
  class="kw">return(res)
} -> DTd
list(pretrain = DTd[[class="num">1]] ,
     train = DTd[[class="num">2]] ,
     val =   DTd[[class="num">3]] ,
     test =  DTd[[class="num">4]] ) -> DTd
class="kw">return(DTd)
evalq({
  require(dplyr)
  require(foreach)
    DTbin = DiscretizeData(DTcap.n, preCut = preCut, var = "")
    DTwoe = DiscretizeData(DTcap.n, preCut = preCut, var = "woe")
    DTdum = DiscretizeData(DTcap.n, preCut = preCut, var = "dum")
    X.woe <- list()
    X.bin <- list()
    X.dum <- list()
    foreach(i = class="num">1:length(DTcap.n)) %do% {
      DTbin[[i]] %>% select(contains("binned")) -> X.bin[[i]]
      DTdum[[i]] %>% select(starts_with("dum")) -> X.dum[[i]]
      DTwoe[[i]] %>% select(starts_with("woe")) %>%
        divide_by(class="num">100) -> X.woe[[i]]
      class="kw">return(list(bin =  X.bin[[i]], woe = X.woe[[i]],
                  dum = X.dum[[i]], raw = DTcap.n[[i]]))
    } -> DTcut
    list(pretrain = DTcut[[class="num">1]],
         train = DTcut[[class="num">2]],
         val =   DTcut[[class="num">3]],
         test =  DTcut[[class="num">4]] ) -> DTcut
    rm(DTwoe, DTdum, X.woe, X.bin, X.dum)
}, env)

◍ 单因子里 v.satl 分箱最扛造

把 501 根 15 分钟 K 线(2017-02-23 15:30 起)的 ftlm、stlm、rbci、pcci 以及它们各自的 velocity 版本做分箱后,用 OneR 跑了一遍训练集单因子分类。输出显示 v.satl.binned 以 63.14% 的准确率排第一,v.fatl.binned 62.64%、ftlm.binned 62.54% 紧随,最弱的 v.ftlm.binned 只有 56.14%。 模型最终选了 v.satl 分箱作规则:当 v.satl.binned 落在 (-Inf, -0.0184] 区间时判 Class=-1,落在 (-0.0184, 0.3247] 与 (0.3247, 0.4004] 时判 Class=1。这个慢速自适应趋势线的速度分量,在样本内对涨跌二分类的贡献略高于其他因子。 跑的时候 R 抛了『data contains unused factor levels』警告,说明训练集某些分箱区间计数为全 0(原文 dump 里 dum.ftlm.* 等前几行全是 0 已印证)。外汇与贵金属行情高波动,这种单因子 63% 准确率只是样本内倾向,实盘复制前建议在 MT5 导出同周期数据重算分箱边界。

MQL5 / C++
evalq({
  require(OneR)
  require(dplyr)
  require(magrittr)
  train <- cbind(DTcut$train$bin, Class = DTcut$train$raw$Class) %>% as.data.frame()
  val <- cbind(DTcut$val$bin, Class = DTcut$val$raw$Class) %>% as.data.frame()
  test <- cbind(DTcut$test$bin, Class = DTcut$test$raw$Class) %>% as.data.frame()
  model <- OneR(data = train, formula = NULL, ties.method = "chisq", verbose = TRUE)
}, env)

「单变量分箱把方向判对六成出头」

把 v.satl 这个变量做分箱后,仅用「大于 0.4003869443」这一档就能把类别标为 1,训练集 1001 条里判对 632 条,准确率 63.14%。卡方检验 X-squared=74.429、df=3、p-value=4.803e-16,说明分箱后的类别分布和真实标签并非独立,存在统计意义上的关联。 验证集 501 条上绝对混淆矩阵显示:预测 -1 实际 -1 有 106 条,预测 1 实际 1 有 208 条,整体准确率 0.6267(314/501),相对基线的误差削减 0.0922(p=0.04597),刚过显著线。测试集 501 条准确率略升至 0.6447(323/501),预测 1 的命中占比从验证集 0.42 掉到 0.39,假阳性(实际 -1 却预测 1)从 100 条降到 76 条。 别把单变量六成当圣杯 外汇与贵金属属高风险品种,这套分箱在样本外仅把随机猜的误差砍了不到一成,实盘直接跟单可能亏在滑点和点差上。真要落地,建议把它当过滤器而非信号源,在 MT5 里用 iCustom 把 v.satl 算出来分桶,再叠一层波动率门槛才值得盯。

深度网络预训练的参数骨架

在 R 环境里搭深度信念网络,第一轮无监督预训练先把结构参数锁死。上面这段把输入到输出的隐层设为 c(0,16,8,0),实际输入输出维度会在喂数据后自动识别,等于先留了 16 和 8 两个隐层神经元数。 随机种子固定为 54321,保证每次初始化权重可复现;RBM 阶段学率 0.3、批大小 50、跑 25 个 epoch,且 rbm.lastLayer = -1 表示最上面的 SRBM 不参与预训练。 微调阶段只训最上层(darch.trainLayers = c(F,F,T)),用 backpropagation,学习率 0.5,dropout 设成 c(0.1,0.2,0.1),weightDecay 为 0.0002。这套配置下,某次拟合得到的系数 0.1359,p-value = 0.005976,在 1% 水平显著,说明该特征对分类有统计意义。 外汇与贵金属市场高风险,此类模型输出仅作概率参考,实盘前请在 MT5 用历史数据回测验证。

MQL5 / C++
evalq({
  require(darch)
  require(dplyr)
  require(magrittr)
  Ln <- c(class="num">0, class="num">16, class="num">8, class="num">0)      class=class="str">"cmt">// 输入和输出神经元的数量将从数据集合中自动识别
  nEp_0 <- class="num">25
  #------------------
  par_0 <- list(
    layers = Ln,            class=class="str">"cmt">// 我们把这个参数放在列表中 (为简单起见)
    seed = class="num">54321,            class=class="str">"cmt">// 如果我们想在初始化期间获得相同的数据
    logLevel = class="num">5,            class=class="str">"cmt">// 我们需要什么等级的信息输出
    # params RBM========================
    rbm.consecutive = F,     # 每个 RBM 一次训练一个世代
    rbm.numEpochs = nEp_0,
    rbm.batchSize = class="num">50,
    rbm.allData = TRUE,
    rbm.lastLayer = -class="num">1,      class=class="str">"cmt">// 不要训练 SRBM 的上层
    rbm.learnRate = class="num">0.3,
    rbm.unitFunction = "tanhUnitRbm",
    # params NN ========================
    darch.batchSize = class="num">50,
    darch.numEpochs = nEp_0, class=class="str">"cmt">// 为了简单起见, 将此参数从列表中删除
    darch.trainLayers = c(F,F,T), class=class="str">"cmt">//仅上层
    darch.unitFunction = c("tanhUnit","maxoutUnit", "softmaxUnit"),
    bp.learnRate = class="num">0.5,
    bp.learnRateScale = class="num">1,
    darch.weightDecay = class="num">0.0002,
    darch.dither = F,
    darch.dropout = c(class="num">0.1,class="num">0.2,class="num">0.1),
    darch.fineTuneFunction = backpropagation, class="macro">#rpropagation
    normalizeWeights = T,
    normalizeWeightsBound = class="num">1,
    darch.weightUpdateFunction = c("weightDecayWeightUpdate",
                                   "maxoutWeightUpdate",
                                   "weightDecayWeightUpdate"),
    darch.dropout.oneMaskPerEpoch = T,
    darch.maxout.poolSize = class="num">2,
    darch.maxout.unitFunction = "linearUnit")
  #---------------------------
  DNN_default <- darch(darch = NULL,
                       paramsList = par_0,
                       x = DTcut$pretrain$woe %>% as.data.frame(),
                       y = DTcut$pretrain$raw$Class %>% as.data.frame(),
                       xValid = DTcut$train$woe %>% as.data.frame(),
                       yValid = DTcut$train$raw$Class %>% as.data.frame()
                       )
}, env)

◍ 微调后验证集误差落在 34% 附近

一次深度网络微调跑完,训练集最优模型分类误差 31.95%(639/2000),交叉熵 1.233;验证集分类误差 35.86%(359/1001),交叉熵 1.306。第 3 个 epoch 就拿到 best model,说明早期停止阈值若设太松会浪费后续算力。 用 0.632 加权修正后,验证集交叉熵降到 1.279、分类误差 34.42%,整个过程耗时 5.975 秒。外汇与贵金属行情里这类模型只代表历史样本下的概率倾向,实盘仍属高风险,别把 34% 误判率当可忽略。 下面这段 R 代码是第二阶段微调的实际配置:SRBM 不再训练(rbm.numEpochs=0),三层全开反向传播,dropout 按 0.1/0.2/0.1 铺,maxout 池大小 2、激活用 ELU(alpha=2)。你在 MT5 外接 R 桥接时,可直接照抄参数组看验证误差是否复现。

MQL5 / C++
evalq({
  require(darch)
  require(dplyr)
  require(magrittr)
  nEp_1 <- class="num">100
  bp.learnRate <- class="num">1
  par_1 <- list(
    layers = Ln,
    seed = class="num">54321,
    logLevel = class="num">5,
    rbm.numEpochs = class="num">0,# SRBM is not to be trained!
    darch.batchSize = class="num">50,
    darch.numEpochs = nEp_1,
    darch.trainLayers = c(T,T,T), class="macro">#TRUE,
    darch.unitFunction = c("tanhUnit","maxoutUnit", "softmaxUnit"),
    bp.learnRate = bp.learnRate,
    bp.learnRateScale = class="num">1,
    darch.weightDecay = class="num">0.0002,
    darch.dither = F,
    darch.dropout = c(class="num">0.1,class="num">0.2,class="num">0.1),
    darch.fineTuneFunction = backpropagation, class="macro">#rpropagation backpropagation
    normalizeWeights = T,
    normalizeWeightsBound = class="num">1,
    darch.weightUpdateFunction = c("weightDecayWeightUpdate",
                                   "maxoutWeightUpdate",
                                   "weightDecayWeightUpdate"),
    darch.dropout.oneMaskPerEpoch = T,
    darch.maxout.poolSize = class="num">2,
    darch.maxout.unitFunction = exponentialLinearUnit,
    darch.elu.alpha = class="num">2)
    #------------------------------
    DNN_1 <- darch( darch = DNN_default, paramsList = par_1,
                    x = DTcut$train$woe %>% as.data.frame(),
                    y = DTcut$train$raw$Class %>% as.data.frame(),
                    xValid = DTcut$val$woe %>% as.data.frame(),

「深度网络跑完后的验证集误差与混淆矩阵」

上面这段是某次 DNN 微调收尾时的日志。100 轮训练在 37.22 秒内完成,最佳模型出现在第 96 轮;训练集分类误差 31.97%(320/1001),验证集分类误差 31.14%(156/501),交叉熵验证误差最终落在 1.203。 用独立测试集再算一遍:xValid 取 DTcut$test$woe,yValid 取真实 Class 向量,predict 走 type="class",结果误分类 166 个,占比 33.13%。混淆矩阵显示 -1 类错判成 1 类有 77 个,1 类错判成 -1 类有 89 个,整体准确率 0.6687。 准确率 95% 置信区间(0.6255, 0.7098),无信息率(NIR)0.5649,Acc>NIR 的 p 值约 1.307e-06。说明模型显著优于瞎猜基准,但外汇/贵金属行情里仅六成多命中仍属高风险,实盘前建议在 MT5 用历史数据复跑确认样本外稳定性。

MQL5 / C++
evalq({
  xValid = DTcut$test$woe %>% as.data.frame()
  yValid = DTcut$test$raw$Class %>% as.vector()
  Ypredict <- predict(DNN_1, newdata = xValid, type = "class")
  numIncorrect <- sum(Ypredict != yValid)
  cat(paste0("Incorrect classifications on all examples: ", numIncorrect, " (",
             round(numIncorrect/nrow(xValid)*class="num">100, class="num">2), "%)\n"))
  caret::confusionMatrix(yValid, Ypredict)
}, env)

深度网络在 WOE 特征上的分类表现

把 WOE 编码后的样本喂给两层 DNN,训练完在测试集上跑出的混淆矩阵给出了一组硬指标:敏感度 0.5917、特异度 0.7279,正类(-1)的阳性预测值 0.6262、阴性预测值 0.6983。平衡准确率落在 0.6598,说明模型对正负样本的兼顾程度中等偏上,并非单边偏科。 Kappa 系数只有 0.3217,Mcnemar 检验 P 值 0.3932,意味着模型预测与随机基准的一致性偏弱,且两模型差异在统计上不显著。外汇与贵金属行情受突发流动性冲击,此类信号仅作概率参考,实盘须控仓。 下面这段 R 函数封装了用 darch 包做两阶段训练的逻辑,先以训练折拟合,再用验证折微调,可直接在装好 darch、magrittr 的环境里复跑。 DNN.train.woe <- function(param, X){ require(darch) require(magrittr) darch( darch = NULL, paramsList = param[[1]], x = X[[1]]$woe %>% as.data.frame(), y = X[[1]]$raw$Class %>% as.data.frame(), xValid = X[[2]]$woe %>% as.data.frame(), yValid = X[[2]]$raw$Class %>% as.data.frame() ) %>% darch( ., paramsList = param[[2]], x = X[[2]]$woe %>% as.data.frame(), y = X[[2]]$raw$Class %>% as.data.frame(), xValid = X[[3]]$woe %>% as.data.frame(), yValid = X[[3]]$raw$Class %>% as.data.frame() ) -> Darch return(Darch) } evalq({ require(darch) require(magrittr) Ln <- c(0, 16, 8, 0) nEp_0 <- 25 nEp_1 <- 25 rbm.learnRate = c(0.5,0.3,0.1) bp.learnRate <- c(0.5,0.3,0.1) list(par_0, par_1) %>% DNN.train.woe(DTcut) -> Dnn.woe xValid = DTcut$test$woe %>% as.data.frame() yValid = DTcut$test$raw$Class %>% as.vector() Ypredict <- predict(Dnn.woe, newdata = xValid, type = "class") numIncorrect <- sum(Ypredict != yValid) cat(paste0("Incorrect classifications on all examples: ", numIncorrect, " (", round(numIncorrect/nrow(xValid)*100, 2), "%)\n")) caret::confusionMatrix(yValid, Ypredict) -> cM.woe }, env) 调参时把 nEp_0、nEp_1 从 25 往上加,可能提升拟合但也会拉长 MT5 外接 R 的回测耗时;Ln 向量控制每层神经元数,当前 16 与 8 的中间层在样本量不大时倾向不过拟合。

MQL5 / C++
DNN.train.woe <- function(param, X){
  require(darch)
  require(magrittr)
  darch( darch = NULL, paramsList = param[[class="num">1]],
         x = X[[class="num">1]]$woe %>% as.data.frame(),
         y = X[[class="num">1]]$raw$Class %>% as.data.frame(),
         xValid = X[[class="num">2]]$woe %>% as.data.frame(),
         yValid = X[[class="num">2]]$raw$Class %>% as.data.frame()
  ) %>%
    darch( ., paramsList = param[[class="num">2]],
           x = X[[class="num">2]]$woe %>% as.data.frame(),
           y = X[[class="num">2]]$raw$Class %>% as.data.frame(),
           xValid = X[[class="num">3]]$woe %>% as.data.frame(),
           yValid = X[[class="num">3]]$raw$Class %>% as.data.frame()
    ) -> Darch
  class="kw">return(Darch)
}
evalq({
  require(darch)
  require(magrittr)
  Ln <- c(class="num">0, class="num">16, class="num">8, class="num">0)
  nEp_0 <- class="num">25
  nEp_1 <- class="num">25
  rbm.learnRate = c(class="num">0.5,class="num">0.3,class="num">0.1)
  bp.learnRate <- c(class="num">0.5,class="num">0.3,class="num">0.1)
  list(par_0, par_1) %>% DNN.train.woe(DTcut) -> Dnn.woe
  xValid = DTcut$test$woe %>% as.data.frame()
  yValid = DTcut$test$raw$Class %>% as.vector()
  Ypredict <- predict(Dnn.woe, newdata = xValid, type = "class")
  numIncorrect <- sum(Ypredict != yValid)
  cat(paste0("Incorrect classifications on all examples: ", numIncorrect, " (",
             round(numIncorrect/nrow(xValid)*class="num">100, class="num">2), "%)\n"))
  caret::confusionMatrix(yValid, Ypredict) -> cM.woe
}, env)

◍ 深度网络在原始特征上的验证表现

把训练集、验证集、测试集依次灌进 darch 堆叠网络后,用测试集原始特征做分类预测,得到混淆矩阵。整体准确率 0.6826,95% 置信区间落在 (0.6399, 0.7232),No Information Rate 仅 0.5629,P-Value [Acc > NIR] 为 2.667e-08,说明该网络显著优于盲猜基线。 具体拆开看:预测 -1 实际 -1 有 133 个,预测 -1 实际 1 有 73 个;预测 1 实际 -1 有 86 个,预测 1 实际 1 有 209 个。Sensitivity 0.6073、Specificity 0.7411,Kappa 0.3508,Balanced Accuracy 0.6742。 外汇与贵金属行情受宏观事件扰动大,这类二分类信号仅代表历史样本内的概率倾向,实盘需以小仓位验证,警惕过拟合导致的样本外衰减。

MQL5 / C++
y = X[[class="num">1]]$raw$Class %>% as.data.frame(),
 xValid = X[[class="num">2]]$raw %>% tbl_df %>% select(-c(Data, Class)),
 yValid = X[[class="num">2]]$raw$Class %>% as.data.frame()
) %>
	darch( ., paramsList = param[[class="num">2]],
			 x = X[[class="num">2]]$raw %>% tbl_df %>% select(-c(Data, Class)),
			 y = X[[class="num">2]]$raw$Class %>% as.data.frame(),
			 xValid = X[[class="num">3]]$raw %>% tbl_df %>% select(-c(Data, Class)),
			 yValid = X[[class="num">3]]$raw$Class %>% as.data.frame()
	 ) -> Darch
	class="kw">return(Darch)
}
#-------------------------------
evalq({
	require(darch)
	require(magrittr)
	Ln <- c(class="num">0, class="num">16, class="num">8, class="num">0)
	nEp_0 <- class="num">25
	nEp_1 <- class="num">25
	rbm.learnRate = c(class="num">0.5,class="num">0.3,class="num">0.1)
	bp.learnRate <- c(class="num">0.5,class="num">0.3,class="num">0.1)
	list(par_0, par_1) %>% DNN.train.raw(DTcut) -> Dnn.raw
	xValid = DTcut$test$raw %>% tbl_df %>% select(-c(Data, Class))
	yValid = DTcut$test$raw$Class %>% as.vector()
	Ypredict <- predict(Dnn.raw, newdata = xValid, type = "class")
	numIncorrect <- sum(Ypredict != yValid)
	cat(paste0("Incorrect classifications on all examples: ", numIncorrect, " (",
				 round(numIncorrect/nrow(xValid)*class="num">100, class="num">2), "%)\n"))
	caret::confusionMatrix(yValid, Ypredict) -> cM.raw
}, env)

「用 WOE 特征喂深度网络的实际误差」

把原始变量先做证据权重(WOE)编码,再塞进三层 DNN(结构 0-16-8-0,激活依次为 tanh、maxout、softmax),是这类价格行为模型里常见的一步。下面这段 R 代码在训练集 2000 条、验证集 1001 条上跑了 100 个 epoch,fine-tune 用了 37.13 秒。 训练集最佳模型分类误差 28.7%(574/2000),交叉熵 1.140;验证集最佳模型误差 35.86%(359/1001),交叉熵 1.299。0.632 引导的验证误差收敛在 33.23%,全样本误分类率 29.94%(150/501)。外汇与贵金属行情受杠杆与跳空影响,模型误差如此量级只说明样本内拟合有限,实盘信号概率倾向偏弱。 maxout 层 poolSize=2、elu.alpha=2,dropout 设成 0.0/0.2/0.1,weightDecay=0.0002——这些超参直接决定过拟合程度。若你在 MT5 外接 R 做特征工程,建议先固定 seed=54321 复现,再调 batchSize=50 与 learnRate 三元组看误差曲线是否平移。

MQL5 / C++
evalq({
  require(darch)
  require(magrittr)
  Ln <- c(class="num">0, class="num">16, class="num">8, class="num">0)
  nEp_1 <- class="num">100
  bp.learnRate <- c(class="num">0.5,class="num">0.7,class="num">0.1)
  #--param----------------
  par_1 <- list(
    layers = Ln,
    seed = class="num">54321,
    logLevel = class="num">5,
    rbm.numEpochs = class="num">0,# SRBM is not to be trained!
    darch.batchSize = class="num">50,
    darch.numEpochs = nEp_1,
    darch.trainLayers = c(T,T,T), class="macro">#TRUE,
    darch.unitFunction = c("tanhUnit","maxoutUnit", "softmaxUnit"),
    bp.learnRate = bp.learnRate,
    bp.learnRateScale = class="num">1,
    darch.weightDecay = class="num">0.0002,
    darch.dither = F,
    darch.dropout = c(class="num">0.0,class="num">0.2,class="num">0.1),
    darch.fineTuneFunction = backpropagation, class="macro">#rpropagation backpropagation
    normalizeWeights = T,
    normalizeWeightsBound = class="num">1,
    darch.weightUpdateFunction = c("weightDecayWeightUpdate",
                                   "maxoutWeightUpdate",
                                   "weightDecayWeightUpdate"),
    darch.dropout.oneMaskPerEpoch = T,
    darch.maxout.poolSize = class="num">2,
    darch.maxout.unitFunction = exponentialLinearUnit,
    darch.elu.alpha = class="num">2)
  #--train---------------------------
  darch( darch = NULL, paramsList = par_1,
         x = DTcut[[class="num">1]]$woe %>% as.data.frame(),
         y = DTcut[[class="num">1]]$raw$Class %>% as.data.frame(),
         xValid = DTcut[[class="num">2]]$woe %>% as.data.frame(),
         yValid = DTcut[[class="num">2]]$raw$Class %>% as.data.frame()
  ) -> Dnn.woe.I
  #---test--------------------------
  xValid = DTcut$val$woe %>% as.data.frame()
  yValid = DTcut$val$raw$Class %>% as.vector()
  Ypredict <- predict(Dnn.woe.I, newdata = xValid, type = "class")
  numIncorrect <- sum(Ypredict != yValid)
  cat(paste0("Incorrect classifications on all examples: ", numIncorrect, " (",
             round(numIncorrect/nrow(xValid)*class="num">100, class="num">2), "%)\n"))
  caret::confusionMatrix(yValid, Ypredict) -> cM.woe.I
}, env)

深度网络在 EURUSD 原始特征上的混淆矩阵与训练误差

用三层 DArch 网络(结构 0-16-8-0,含 tanh、maxout、softmax)对 EURUSD 原始行情切片做二分类,Positive 类定义为 -1(下跌)。测试集 500 个样本里错分 161 个,误判率 32.14%,整体准确率约 0.7006。 混淆矩阵显示:真实 -1 共 206 个,模型判对 144 个;真实 1 共 295 个,判对 207 个。灵敏度 0.6207、特异度 0.7695,说明模型更擅长抓反向(1 类)样本,对下跌类的召回偏弱。 95% 置信区间 (0.6584, 0.7404),No Information Rate 0.5369,P-Value[Acc > NIR] 5.393e-14——模型显著优于瞎猜基线。但 Kappa 仅 0.3932,提示分类一致性与随机重合仍占不小比重。 训练日志里最优模型出现在第 45 个 epoch:训练集分类误差 30.75%(615/2000),验证集 33.67%(337/1001),交叉熵分别为 1.189 与 1.236。外汇与贵金属属高杠杆高风险品种,此类历史回测准确率不预示未来实盘表现,开 MT5 接 R 后端跑同一套参数前应先做样本外压力测试。 下面这段 R 代码即上述网络的构建与评估过程,逐行拆开看参数落点。

MQL5 / C++
evalq({
  require(darch)
  require(magrittr)
  Ln <- c(class="num">0, class="num">16, class="num">8, class="num">0)            # 网络层节点:输入0占位、隐层16与8、输出0占位(由unitFunction定)
  nEp_1 <- class="num">100                    # 微调阶段最大epoch数
  bp.learnRate <- c(class="num">0.5,class="num">0.7,class="num">0.1)  # 三层各自反向传播学习率
  #--param-----------------------------
  par_1 <- list(
    layers = Ln,
    seed = class="num">54321,                 # 随机种子,保证可复现
    logLevel = class="num">5,                 # 日志详细等级
    rbm.numEpochs = class="num">0,            # SRBM不预训练
    darch.batchSize = class="num">50,         # 每批50样本
    darch.numEpochs = nEp_1,
    darch.trainLayers = c(T,T,T), # 三层均参与训练
    darch.unitFunction = c("tanhUnit","maxoutUnit", "softmaxUnit"), # 各层激活
    bp.learnRate = bp.learnRate,
    bp.learnRateScale = class="num">1,
    darch.weightDecay = class="num">0.0002,   # L2权重衰减
    darch.dither = F,             # 不加噪声抖动
    darch.dropout = c(class="num">0.1,class="num">0.2,class="num">0.1), # 各层dropout比率
    darch.fineTuneFunction = backpropagation, # 微调用BP
    normalizeWeights = T,
    normalizeWeightsBound = class="num">1,    # 权重归一化上界
    darch.weightUpdateFunction = c("weightDecayWeightUpdate",
                                   "maxoutWeightUpdate",
                                   "weightDecayWeightUpdate"),
    darch.dropout.oneMaskPerEpoch = T, # 每epoch换dropout掩码
    darch.maxout.poolSize = class="num">2,    # maxout池化大小
    darch.maxout.unitFunction = exponentialLinearUnit,
    darch.elu.alpha = class="num">2)          # ELU斜率参数
  #---train------------------------------
  darch( darch = NULL, paramsList = par_1,
         x = DTcut[[class="num">1]]$raw %>% tbl_df %>% select(-c(Data, Class)) ,   # 训练集特征
         y = DTcut[[class="num">1]]$raw$Class %>% as.vector(),                     # 训练集标签
         xValid = DTcut[[class="num">2]]$raw %>% tbl_df %>% select(-c(Data, Class)) , # 验证集特征
         yValid = DTcut[[class="num">2]]$raw$Class %>% as.vector()                 # 验证集标签
  ) -> Dnn.raw.I
  #---test--------------------------------
  xValid = DTcut[[class="num">3]]$raw %>% tbl_df %>% select(-c(Data, Class))  # 测试集特征
  yValid = DTcut[[class="num">3]]$raw$Class %>% as.vector()                   # 测试集标签
  Ypredict <- predict(Dnn.raw.I, newdata = xValid, type = "class") # 预测类别
  numIncorrect <- sum(Ypredict != yValid)                         # 错分计数
  cat(paste0("Incorrect classifications on all examples: ", numIncorrect, " (",
             round(numIncorrect/nrow(xValid)*class="num">100, class="num">2), "%)\n"))     # 打印误判率
  caret::confusionMatrix(yValid, Ypredict) -> cM.raw.I             # 生成混淆矩阵
}, env)
#---------Ris17----------------------------------
env$cM.raw.I
plot(env$Dnn.raw.I, type = "class")

◍ 二分类信号的混淆矩阵怎么读

下面是一份 EURUSD 小时线二分类模型(-1 为空头信号,1 为多头信号)在 501 个样本上的混淆矩阵与统计量输出。 预测为 -1 且实际为 -1 的有 140 个,预测为 1 且实际为 1 的有 200 个;预测错的有 66+95=161 个。整体 Accuracy 0.6786,95% 置信区间 (0.6358, 0.7194),明显高于 No Information Rate 0.5309,P-Value [Acc > NIR] 仅 1.283e-11,说明模型显著优于瞎猜多数类。 Kappa 0.3501 代表中等偏低一致度;Sensitivity(召回空头)0.5957,Specificity(排多头)0.7519。Pos Pred Value 0.6796、Neg Pred Value 0.6780 接近,Balanced Accuracy 0.6738。Mcnemar 检验 P 值 0.02733,提示两类误判数差异在 5% 水平显著——模型更擅长不误报多头,却容易漏掉真实空头。 外汇与贵金属杠杆高、滑点大,这类 67% 左右准确率信号只能当过滤层,实盘仍需配合止损与仓位上限。打开 MT5 策略测试器导出你自己的信号矩阵,照上面几个数核对,比只看总胜率更有用。

MQL5 / C++
Confusion Matrix and Statistics

    Reference
Prediction   -class="num">1   class="num">1
         -class="num">1 class="num">140  class="num">66
         class="num">1   class="num">95 class="num">200

               Accuracy : class="num">0.6786
                 class="num">95% CI : (class="num">0.6358, class="num">0.7194)
    No Information Rate : class="num">0.5309
    P-Value [Acc > NIR] : class="num">1.283e-11

                  Kappa : class="num">0.3501
Mcnemar&class="macro">#x27;s Test P-Value : class="num">0.02733

            Sensitivity : class="num">0.5957
            Specificity : class="num">0.7519
         Pos Pred Value : class="num">0.6796
         Neg Pred Value : class="num">0.6780
             Prevalence : class="num">0.4691
         Detection Rate : class="num">0.2794
   Detection Prevalence : class="num">0.4112
   Balanced Accuracy : class="num">0.6738

    &class="macro">#x27;Positive&class="macro">#x27; Class : -class="num">1

「darch 的边界与下一篇的着力点」

darch 包虽然只提供两种基础训练方法,但足够搭出结构和参数各异的神经网络,作为手动深挖网络的入口是够用的。它暴露出的弱项,大多源于直接用了默认参数或贴近默认的取值,并没有把调参空间吃透。 在 $raw 集合上,DNN.woe 相对基准没有显出优势;这个现象说明问题不在模型类别,而在超参数与集成方式。外汇与贵金属行情序列短、噪声大,这类小样本下网络默认配置容易欠拟合,属高风险验证环境。 下一篇会挑 DNN.woe 里的一部分超参数做优化,再用 TensorFlow 手写一个 DNN 跑同样任务做对照。还会试袋式与堆叠式集成,看多网络融合能否把预测质量往上拉一点。

画得少,看得清

GitHub 上的 PartIV 仓库把复现成本压到了最低:FunPrepareData.R 和 RunPrepareData.R 负责把原始行情整理成可用矩阵,Experiment.R 直接跑通训练,Part_IV.RData 已经存好了预处理后的所有对象,打开 R 载入即可接着验。 Darch_default.txt 列明了 DArch 结构的省缺参数,SessionInfo.txt 记下了依赖版本——这意味着你不必从零配环境,只要本地 R 版本相近就能复现文中结果。 有读者在讨论里提到,用默认参数的 DNN 甚至没比 OneR 模型强多少;作者回应这只是默认配置下的现象,第五部分做超参数贝叶斯优化后差距才拉开。外汇与贵金属市场波动剧烈、杠杆风险高,任何模型输出都只是概率倾向,实盘前务必在 MT5 策略测试器里用历史数据自测一遍。 把仓库 clone 下来、载入 RData、改一行特征列名跑一次 Experiment.R,比读十篇综述都实在。

把重复劳动交给小布
这些诊断小布盯盘的 AIGC 已内置,打开对应品种页即可看到模型训练品质与输入准备的可视化提示,你只管判断信号置信度。

常见问题

RBM 无监督预训练把隐藏层权重放到接近最优解的空间,可减少后续微调所需的标记样本数与训练世代数,对外汇贵金属这类小样本场景概率上更稳。
deepnet 参数少、只支持 SAE/SRBM 预训练,复杂包开放激活函数与稳定方法选择,便于按数据分布做方法 2 的初始化,但计算开销更高。
观察验证集误差随代数波动且训练集持续下降,结合不同初始化种子的多次实验对比,倾向认为陷入了局部最优,需回退预训练或调稳定项。
目前小布内置的是基于价格行为学的 AIGC 诊断,不支持外部 DNN 权重热插拔,但可对照它的可视化输入准备建议来校验你的数据管线。
DCNN/RNN 的初始化与激活函数假设和时序金融数据有偏差,直接套用可能放大过拟合,贵金属与外汇属高风险市场,需先做小样本概率验证。