深度神经网络(第八部分)。 提高袋封融合的分类品质(基础篇)
📘

深度神经网络(第八部分)。 提高袋封融合的分类品质(基础篇)

第 1/3 篇

「袋封融合的分类品质瓶颈」

在 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 改路径,就能在终端外重跑初始数据准备。

MQL5 / C++
#--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 被砍。说明不同样本窗口下,同一批技术变量的聚类区分度会翻转,外汇和贵金属这种高波动品种尤其明显,实盘使用前务必自己重算一遍。

MQL5 / C++
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 分析模块会读错列。 外汇与贵金属波动剧烈、杠杆风险高,这套特征组合只是提供了一种建模视角,实际信号概率仍受行情结构影响。

MQL5 / C++
[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"

常见问题

很可能是。原始报价里混有大量随机波动,先筛掉低信息量的噪声列,再进融合分类器,品质会明显提升。
按时间尺度和波动结构拆成趋势类与扰动类两组,坑在于两组特征高度同质化,等于没拆。用cRAND指标核对列间差异可避坑。
可以。小布能直接读取你预处理后的特征名清单,标出cRAND偏低、可能无效的列,省去手动核对。
文中以相对随机度排序,通常cRAND显著偏高(接近1)的列信息量少,建议删掉排名后20%的特征列再训练。
把预处理输出的特征名列表拉出来逐行核对品种与周期后缀,错配会导致训练集标签偏移,验证前必查。