深度神经网络 (第 IV 部)。创建, 训练和测试神经网络模型·综合运用
(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 两个包,缺一个都会报找不到对象。
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 列说明该箱样本极少,建模前建议按频率过滤。
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 导出同周期数据重算分箱边界。
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 用历史数据回测验证。
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 桥接时,可直接照抄参数组看验证误差是否复现。
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 用历史数据复跑确认样本外稳定性。
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 的中间层在样本量不大时倾向不过拟合。
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。 外汇与贵金属行情受宏观事件扰动大,这类二分类信号仅代表历史样本内的概率倾向,实盘需以小仓位验证,警惕过拟合导致的样本外衰减。
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 三元组看误差曲线是否平移。
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 代码即上述网络的构建与评估过程,逐行拆开看参数落点。
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 策略测试器导出你自己的信号矩阵,照上面几个数核对,比只看总胜率更有用。
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,比读十篇综述都实在。