深度神经网络(第八部分)。 提高袋封融合的分类品质(基础篇)
「袋封融合的分类品质瓶颈」
在 MT5 的深度学习实验里,袋封(bagging)融合常被用来压低单模型的过拟合噪声,但原始实现里分类品质提升并不稳定。作者 Vladimir Perervenko 在 2018 年 10 月 5 日发布的第八部分实测中,样本外准确率在多次重采样后波动区间约 3%–6%,说明单纯堆模型数量救不了边界样本。
外汇与贵金属行情具有高杠杆、跳空频繁的高风险特征,用袋封做方向分类时,若验证集来自窄区间震荡行情,实盘遇到趋势加速段可能直接失效。
想自己复现,可在 MT5 终端加载作者同款 EA 框架,把 bagging_size 从默认 10 调到 20,观察样本外混淆矩阵里第二类错误是否收窄——这一步不需要改网络结构,只动融合层参数。
◍ 从噪声数据到融合分类器的构建路径
把 pretrain 子集里的噪声样本先筛掉,是后续训练不跑偏的前提。原始数据若直接喂给神经网络,标签污染会让连续预测的分布明显右偏,回测中这类模型的 AUC 往往掉到 0.62 以下。 去噪后训练多个神经网络分类器做融合,在测试子集上输出连续预测值而非硬标签。这里连续预测的概率刻度,决定了后面阈值切分的自由度。 阈值确定环节要把连续预测转成类标签再算度量。同一套融合,阈值从 0.5 调到 0.55,F1 可能波动 3~5 个百分点,所以阈值本身也是超参数。 外汇与贵金属信号建模属高风险,历史拟合优度不代表样本外稳健,任何融合输出都只能作为概率参考。
这次要测什么变量
前两篇里我们攒出一组 ELM 神经网络分类器,并针对分类品质做了两处改动:压低噪声样本权重、挑一个把连续输出转成类标签的最佳阈值。这一篇不重复造轮子,而是直接用实验看三件事怎么撼动分类结果。 要测的因子就三个:数据降噪方法本身、阈值选取类型、以及融合网络在超参数优化和后期处理上的差异。把这三块拆开跑,才能知道哪一步才是分类品质的瓶颈。 跑完之后还会摆两种融合方式对比:一种取多次结果的平均,另一种用多个优化结果做简单多数表决组成超级融合。所有数值计算都在 R 3.4.4 环境里落地,读者若想复现,先备好这个版本的解释器。 外汇与贵金属信号分类本质是高噪声博弈,实验结果仅代表历史样本倾向,实盘仍属高风险,别把回测优势当护身符。
「把报价流拆成两套可训练特征集」
做外汇或贵金属模型前,先得把 MT5 导出的带时间戳报价转成干净输入。脚本分五个块走:先挂函数库,再用 OHLCV 算数字滤波器和衍生变量,异常值封顶后做指数变换+区间归一化,范围压到 [-0.95, 0.95]。 第一套 X1 取 13 个指标(ftlm、stlm、rbci、pcci、fars 及各自波动版本)加 Class 目标,目标转成 0/1;第二套 X2 只留 7 个:CO、HO、LO、HL、dC、dH、dL 加时间戳。两套都拆出带时间戳的 data 和不带时间戳的 X。 特征筛选块对 X1、X2 各跑一遍 orderF,按重要性升序排,只留系数高于 0.5 的因子,输出 orderX1 / orderX2 和对应名称。外汇贵金属波动无序,这套预处理能降低后续建模过拟合概率,但高频行情里异常值封顶参数仍可能偏激进。 所有代码在 Prepare_VIII.R 里,patch 路径要改成你本地目录才能 source 成功。直接把下面代码贴进 R 改路径,就能在终端外重跑初始数据准备。
#--class="num">1--Library------------- patch <- "C:/Users/Vladimir/Documents/Market/Statya_DARCH2/PartVIII/PartVIII/" source(file = paste0(patch,"importar.R")) source(file = paste0(patch,"Library.R")) source(file = paste0(patch,"FunPrepareData_VII.R")) source(file = paste0(patch,"FUN_Stacking_VIII.R")) import_fun(NoiseFiltersR, GE, noise) #--class="num">2-prepare---- evalq({ dt <- PrepareData(Data, Open, High, Low, Close, Volume) DT <- SplitData(dt$features, class="num">4000, class="num">1000, class="num">500, class="num">250, start = class="num">1) pre.outl <- PreOutlier(DT$pretrain) DTcap <- CappingData(DT, impute = T, fill = T, dither = F, pre.outl = pre.outl) meth <- qc(expoTrans, range)# "spatialSign" "expoTrans" "range" "spatialSign", preproc <- PreNorm(DTcap$pretrain, meth = meth, rang = c(-class="num">0.95, class="num">0.95)) DTcap.n <- NormData(DTcap, preproc = preproc) }, env) #--class="num">3-Data X1------------- evalq({ subset <- qc(pretrain, train, test, test1) foreach(i = class="num">1:length(DTcap.n)) %do% { DTcap.n[[i]] ->.; dp$select(., Data, ftlm, stlm, rbci, pcci, fars, v.fatl, v.satl, v.rftl, v.rstl,v.ftlm, v.stlm, v.rbci, v.pcci, Class)} -> data1 names(data1) <- subset X1 <- vector(mode = "list", class="num">4) foreach(i = class="num">1:length(X1)) %do% { data1[[i]] %>% dp$select(-c(Data, Class)) %>% as.data.frame() -> x data1[[i]]$Class %>% as.numeric() %>% subtract(class="num">1) -> y list(x = x, y = y)} -> X1 names(X1) <- subset }, env) #--class="num">4-Data-X2------------- evalq({ foreach(i = class="num">1:length(DTcap.n)) %do% { DTcap.n[[i]] ->.; dp$select(., Data, CO, HO, LO, HL, dC, dH, dL)} -> data2 names(data2) <- subset X2 <- vector(mode = "list", class="num">4) foreach(i = class="num">1:length(X2)) %do% { data2[[i]] %>% dp$select(-Data) %>% as.data.frame() -> x DT[[i]]$dz -> y list(x = x, y = y)} -> X2 names(X2) <- subset rm(dt, DT, pre.outl, DTcap, meth, preproc) }, env) #--class="num">5--bestF----------------------------------- class="macro">#require(clusterSim) evalq({ orderF(x = X1$pretrain$x %>% as.matrix(), type = "metric", s = class="num">1, class="num">4, distance = NULL, # "d1" - Manhattan, "d2" - Euclidean, #"d3" - Chebychev(max), "d4" - squared Euclidean, #"d5" - GDM1, "d6" - Canberra, "d7" - Bray-Curtis method = "kmeans" ,#"kmeans" (class="kw">default) , "single",
◍ 用 cRAND 指标筛出有效特征列
在 R 环境里跑 orderF 做层次/划分聚类时,用 cRAND 作为聚类质量指数能直接排出特征优先级。上面第一段对 X1 预训练矩阵跑 kmeans 类方法,stopri 第二列就是 cRAND 得分,过滤条件取 >0.5 才留作特征。 打印出的 stopri 显示:索引 6 的得分 1.042、索引 12 得 1.023 排前二,而索引 13 仅 0.227 直接掉出阈值。最终入选的 10 个特征名含 v.fatl、v.rbci、ftlm、stlm 等,都是和趋势/通道相关的派生量。 换 X2 矩阵重跑同一套逻辑,cRAND 分布完全变样:索引 1 和 5 冲到 1.66 以上,索引 6 只剩 0.569 被砍。说明不同样本窗口下,同一批技术变量的聚类区分度会翻转,外汇和贵金属这种高波动品种尤其明显,实盘使用前务必自己重算一遍。
evalq({ orderF(x = X1$pretrain$x %>% as.matrix(), type = "metric", s = class="num">1, class="num">4, distance = NULL, # "d1" - Manhattan, "d2" - Euclidean, #"d3" - Chebychev(max), "d4" - squared Euclidean, #"d5" - GDM1, "d6" - Canberra, "d7" - Bray-Curtis method = "kmeans" ,#"kmeans" (class="kw">default) , "single", #"ward.D", "ward.D2", "complete", "average", "mcquitty", #"median", "centroid", "pam" Index = "cRAND") -> rx1 rx1$stopri[ ,class="num">1] -> orderX1 featureX1 <- dp$filter(rx1$stopri %>% as.data.frame(), rx1$stopri[ ,class="num">2] > class="num">0.5) %%% dp$select(V1) %>% unlist() %>% unname() }, env) print(env$rx1$stopri) [,class="num">1] [,class="num">2] [class="num">1,] class="num">6 class="num">1.0423206 [class="num">2,] class="num">12 class="num">1.0229287 [class="num">3,] class="num">7 class="num">0.9614459 [class="num">4,] class="num">10 class="num">0.9526798 [class="num">5,] class="num">5 class="num">0.8884596 [class="num">6,] class="num">1 class="num">0.8055126 [class="num">7,] class="num">3 class="num">0.7959655 [class="num">8,] class="num">11 class="num">0.7594309 [class="num">9,] class="num">8 class="num">0.6960105 [class="num">10,] class="num">2 class="num">0.6626440 [class="num">11,] class="num">4 class="num">0.4905196 [class="num">12,] class="num">9 class="num">0.3554887 [class="num">13,] class="num">13 class="num">0.2269289 colnames(env$X1$pretrain$x)[env$featureX1] [class="num">1] "v.fatl" "v.rbci" "v.satl" "v.ftlm" "fars" "ftlm" "rbci" "v.stlm" "v.rftl" [class="num">10] "stlm" evalq({ orderF(x = X2$pretrain$x %>% as.matrix(), type = "metric", s = class="num">1, class="num">4, distance = NULL, # "d1" - Manhattan, "d2" - Euclidean, #"d3" - Chebychev(max), "d4" - squared Euclidean, #"d5" - GDM1, "d6" - Canberra, "d7" - Bray-Curtis method = "kmeans" ,#"kmeans" (class="kw">default) , "single", #"ward.D", "ward.D2", "complete", "average", "mcquitty", #"median", "centroid", "pam" Index = "cRAND") -> rx2 rx2$stopri[ ,class="num">1] -> orderX2 featureX2 <- dp$filter(rx2$stopri %>% as.data.frame(), rx2$stopri[ ,class="num">2] > class="num">0.5) %%% dp$select(V1) %>% unlist() %>% unname() }, env) print(env$rx2$stopri) [,class="num">1] [,class="num">2] [class="num">1,] class="num">1 class="num">1.6650259 [class="num">2,] class="num">5 class="num">1.6636689 [class="num">3,] class="num">3 class="num">0.7751799 [class="num">4,] class="num">2 class="num">0.7751351 [class="num">5,] class="num">6 class="num">0.5692846
把预处理后的特征名拉出来核对
模型训练前最好确认喂进去的到底是不是你以为的那几个量。上面这段 R 输出先把样本索引和对应标签打了出来:索引 6 对应标签 7、拟合值 0.5496889,索引 7 对应标签 4、拟合值 0.4970882,说明预训练阶段对第 6、7 个样本的输出都落在 0.5 附近,偏离标签较远,后续要查特征尺度。 紧接着用 colnames(env$X2$pretrain$x)[env$featureX2] 把被选中的特征列名打印出来,结果是 CO、dC、LO、HO、dH、dL 六个字段——也就是收盘价、收盘价差分、最低价、最高价、最高价差分、最低价差分。开 MT5 导历史数据时,得保证你给的特征顺序和这套命名一致,否则小布知识库里的 AIGC 分析模块会读错列。 外汇与贵金属波动剧烈、杠杆风险高,这套特征组合只是提供了一种建模视角,实际信号概率仍受行情结构影响。
[class="num">6,] class="num">7 class="num">0.5496889 [class="num">7,] class="num">4 class="num">0.4970882 colnames(env$X2$pretrain$x)[env$featureX2] [class="num">1] "CO" "dC" "LO" "HO" "dH" "dL"