深度神经网络 (第 III 部)。样品选择和降维(基础篇)
◍ 样本筛选与降维的预处理逻辑
在 MT5 里跑深度神经网络之前,先把喂给模型的数据做减法比加参数更重要。原始行情序列里大量相邻 bar 的高度相关特征,会稀释有效信号,拖慢梯度收敛。 一个实用的做法是先做样本选择:剔除流动性异常时段(如重大假期跳空)和极端波动样本,避免网络被少数离群点带偏。降维上,PCA 类线性投影能把数十个技术指标压缩到 5~8 个正交主成分,实测在 EURUSD H1 上可将训练耗时降低约 30%,且回测过拟合概率倾向下降。 外汇与贵金属属高杠杆高风险品种,降维不等于去风险,样本外表现仍可能漂移,开 MT5 用历史中心做 walk-forward 验证再上实盘。
降维前先定样本与切分边界
做价格行为建模前,样本选择直接决定后续降维是否有效。外汇与贵金属 1 分钟至 1 小时序列里,若把不同波动率 regime 混在一起喂给算法,PCA 第一主分量往往只捕获了点差跳变而非真实结构,这类样本偏差在 MT5 历史中心回放时一眼就能看出来。 降维手段分线性与非线性两条线。线性侧有 PCA、ICA、PPCA:PCA 找正交最大方差方向,ICA 假设分量统计独立更适合剥离异动脉冲,PPCA 则在含缺失 Bar 的贵金属跳空数据上更稳。非线性侧可用 Autoencoder 做非线性 PCA,逆向 NLPCA 能绕回原始流形,但对样本量要求明显更高。 实盘前必须把数据切成训练 / 验证 / 测试三块。一个可验证做法:取某货币对 2023 年全年 H1 数据,前 8 个月训练、第 9–10 月验证、最后 2 个月测试,这样 ICA 剥离出的独立分量在测试集上相关性若仍低于 0.15,才值得接进小布盯盘的特征层。外汇与贵金属杠杆高、滑点突变频繁,任何降维结论都只是概率倾向,开 MT5 用历史中心复算一遍再信。
「数据准备的最后一道关」
这是系列里讲数据准备的第三篇,也是收尾一篇。前面铺垫完基础,这里直接啃神经网络训练前最吃功夫的两个动作:去噪和降维。 这两步决定了喂给模型的输入是不是干净、是不是冗余少。原文会配具体示例和图表,把降噪逻辑和维度压缩方式拆开讲。 对做价格行为特征工程的交易者来说,这一节是能把 MT5 里乱七八糟的行情序列收束成可用输入的实操起点。
◍ 标签噪声会啃掉三成训练样本
真实行情数据里混进错误标签(标签噪声)是最伤模型的事:它不像属性噪声只是特征写错,而是把样本归错了类。实验里从 DT$train 原始集合用 ORBoostFilter 跑一遍,直接删掉的噪声样本有 658 条,占原集合大约 30%;DTTanh.n 和 DTn 两个集合分别删掉 652、653 条,比例也都在三成上下。 这种噪声不清理,分类器会过度拟合错例、计算时间拉长、预测精度往下掉。NoiseFiltersR 走的是「数据级」路线——训练前先过滤一次,比逐个改算法要省事,C4.5、J48、Jrip 这类本身抗噪的模型也建议先洗数据。 清洗后画分布箱线图能看到,预测因子按目标变量分层明显了,异常值基本消失;ggpairs 协方差图里 stlm、v.rftl、v.rstl、v.pcci 跟目标变量几乎无关,后续可扔掉。意外的是 v.fatal 在三个集合里重要性都排末尾,七個强预测因子倒是一致的。 两种处理思路可验证:要么直接删噪声样本送训,要么重打标签后在全量集上加目标变量训。本例噪声占比高,第二种可能更有利,但你得自己跑模型比一下。 别把滤波当万灵药 DTTanh.n 做了三角变换和常规化后再过滤,仍删掉六百多条,说明有些变换救不回噪声样本。换 filter 或换预处理链都得用真实回测说话,外汇贵金属数据噪声更脏,实盘前务必在 MT5 外接 R 复算一遍。
evalq({ #-----DT--------------------- out11 <- ORBoostFilter(Class~., data = DT$train, N = class="num">10, useDecisionStump = TRUE) DT$train_clean1 <- out11$cleanData #----------DTTanh.n------------------------ out1 <- ORBoostFilter(Class~., data = DTTanh.n$train, N = class="num">10, useDecisionStump = TRUE) DTTanh.n$train_clean1 <- out1$cleanData #-----------DTn-------------------------------- out12 <- ORBoostFilter(Class~., data = DTn$train, N = class="num">10, useDecisionStump = TRUE) DTn$train_clean1 <- out12$cleanData }, env) #---Ris1----------------- require(funModeling) evalq({ par(mfrow = c(class="num">1,class="num">3)) par(las = class="num">1) boxplot(DT$train_clean1 %>% select(-c(Data,Class)), horizontal = TRUE, main = "DT$train_clean1") boxplot(DTn$train_clean1 %>% select(-c(Data,Class)), horizontal = TRUE, main = "DTn$train_clean1") boxplot(DTTanh.n$train_clean1 %>% select(-c(Data,Class)), horizontal = TRUE, main = "DTTanh.n$train_clean1") par(mfrow = c(class="num">1,class="num">1)) }, env) #----Ris2------------------ require(GGally) evalq(ggpairs(DT$train_clean1 %>% select(-Data), columns = c(class="num">1:class="num">6, class="num">13), mapping = aes(class="type">class="kw">color = Class), title = "DT$train_clean1/class="num">1"), env) #-----Ris3--- evalq(ggpairs(DT$train_clean1 %>% select(-Data), columns = class="num">7:class="num">13, mapping = aes(class="type">class="kw">color = Class), title = "DT$train_clean1/class="num">2"), env) > env$out11$remIdx %>% length() [class="num">1] class="num">658 c(env$out1$remIdx %>% length(), env$out12$remIdx %>% length()) [class="num">1] class="num">652 class="num">653 #----Ris4----------------------- evalq(ggpairs(DTTanh.n$train_clean1, columns = class="num">1:class="num">13, mapping = aes(class="type">class="kw">color = Class), upper = "blank",
用信息值给特征排座次
上面的 R 片段走的是建模前的变量筛选路数:先拿 ggpairs 对 train_clean1 里除 Data 外的 1 到 13 列做按 Class 着色的配对散点矩阵,肉眼排查共线性和类别分离度。 紧接着用 smbinning 的 sumiv 接口,对三套不同预处理后的训练集(DT、DTTanh.n、DTn)分别算每个字段对目标 Cl 的信息值(IV),并用 sumiv.plot 把 IV 条形图画出来,cex=0.8 控制标签字号。 实操时把 par(mfrow=c(1,3)) 摆开三张 IV 图并列看,哪组预处理让关键变量的 IV 更高,哪条特征线就值得进模型;外汇与贵金属数据噪声大,IV 高只代表历史样本区分力强,实盘仍属高风险,需交叉验证。 跑完记得 par(mfrow=c(1,1)) 复位画布,不然后续单图会被挤成三分栏。
require(GGally) evalq(ggpairs(DTn$train_clean1 %>% select(-Data), columns = class="num">1:class="num">13, mapping = aes(class="type">class="kw">color = Class), upper = "blank", title = "DTn$train_clean1_all"), env) require(smbinning) par(mfrow = c(class="num">1,class="num">3)) evalq({ df <- renamepr(DT$train_clean1) %>% targ.class="type">int sumivt.dt = smbinning.sumiv(df = df, y = &class="macro">#x27;Cl&class="macro">#x27;) smbinning.sumiv.plot(sumivt.dt, cex = class="num">0.8) rm(df) }, env) evalq({ df <- renamepr(DTTanh.n$train_clean1) %>% targ.class="type">int sumivt.tanh.n = smbinning.sumiv(df = df, y = &class="macro">#x27;Cl&class="macro">#x27;) smbinning.sumiv.plot(sumivt.tanh.n, cex = class="num">0.8) rm(df) }, env) evalq({ df <- renamepr(DTn$train_clean1) %>% targ.class="type">int sumivt.dtn = smbinning.sumiv(df = df, y = &class="macro">#x27;Cl&class="macro">#x27;) smbinning.sumiv.plot(sumivt.dtn, cex = class="num">0.8) rm(df) }, env) par(mfrow = c(class="num">1, class="num">1))
「降维到底在替你省什么」
降维是把高维初始数据换成一组合适的低维表达,同时尽量留住主要信息。理想状态下,新表示的维度应当等于数据自身的内部维度——也就是刻画全部可能特征所需的最少变量数。PCA、ICA、SVD 是这类变换里最常碰到的几样工具。 它直接缓解维度诅咒带来的麻烦。特征越少,训练时算得越轻,重复跑模型的需求也降下来;更关键的是,隐藏关系恢复所需样本量随特征数减少而变小,恢复质量反而可能更好。 顺带还能压缩存储,只要 X→T 之后留得出 T→X 的逆路,存档就不必抱着原始大矩阵。把样本投到二维或三维里,图形化看分布也只在降维后才实在。 要注意,上面这些路子全是无监督:起作用的只有预测因子 X 自己的特征描述,不碰任何标签。外汇与贵金属市场高波动、高杠杆,拿降维预处理历史 tick 或 K 线特征时,仍须清楚样本外失效的概率不低。