机器学习模型的变量评估和选择(基础篇)
「先给特征排个序再喂模型」
做 ML 模型最容易被忽略的一步,是在训练前先评估每个输入变量的信息量。MT5 里跑回归或分类,若直接把几十个指标一股脑塞进模型,过拟合和噪声放大基本是早晚的事。 一个实用的做法是先做单变量评估:用互信息、卡方或基于树模型的重要性打分,把特征按贡献度降序排列。Vladimir Perervenko 在 2016 年那套测试里,对 14 395 次指标调用做排序,前 12 个特征贡献了约 68% 的总重要性,剩余上百个指标边际贡献极低。 外汇与贵金属杠杆高、跳空频繁,这种环境下低贡献特征往往只是拟合了样本内的随机波动。先砍掉尾部变量,再让小布这类 AIGC 工具做交叉验证,模型在样本外才可能更稳。
◍ 预测因子工程的两条技术路线
这一节先把后续要折腾的输入变量(预测因子)选择、预处理与评估框架摆清楚:核心不是堆指标,而是先判断哪些因子真正携带信息量、彼此怎么相互作用,再决定怎么喂给模型。外汇与贵金属行情具有高杠杆与跳空风险,任何因子筛选结论都只是概率倾向,不能直接当方向指令。 一条路线用 RandomUniformForests 包,计算变量不同取值与组合的重要性,把预测因子与目标的关系、因子间交互效应都摊开看,再反推最优因子子集。另一条用 RoughSets 包,从粗糙集视角切入,证明不光因子集能压缩优化,连用于训练的样本集也能做约简。 所有实验在 R 语言下跑,具体环境是 Revolution R Open 3.2.1。想在 MT5 外接 R 做因子实验的交易者,可以先对照这个版本配好本地环境,避免后续随机森林结果因底层数学库差异而漂移。
预测因子的四种数据类型
做 MT5 上的行情建模,先得把喂给模型的变量分清楚类。输入(预测因子)和输出(目标)都逃不出四种类型:二元值、有限类别因子、连续量化值,以及由指标加工出的数值矩阵。 二元值最简单,只有两个取值,比如 {0,1}、{-1,1},或者 {"yes","no"}、{"male","female"}。它适合做分类标签或开关型特征。 有限类别因子要留意有序和无序的区别。"星期几"有 7 个类别且有序,"一天中的小时"有 24 个类别也有序;但"城市分区"哪怕有 32 类也是无序的,各类别权重平等。建有序因子时必须显式声明顺序,否则模型会误当无序处理。 连续量化值范围从负无穷到正无穷。原始 OHLC 报价不能直接当数值输入,得取差分对数或报价比对数;实战里更常用各类指标组合成输入集。数据通常按矩阵排:每列一个变量,每行是某时刻的快照,目标变量放最后一列(或首列)。
「训练前的数据清理四步」
把原始行情或因子矩阵直接喂给模型,十有八九会跑崩。清理这一步就是先把脏数据、废变量剔掉,再谈训练。 缺失值(NA)多数模型根本不吃。要么整行删掉,要么用插值补缺口;动手前自己显式跑一遍 na.omit(dt) 比依赖默认行为更稳,能避免训练集悄悄混进不确定样本。 异常值也得清。变量做变换时偶尔会冒出极低频的近零方差预测因子,让不少算法数值不稳定甚至直接挂掉。caret 里的 nearZeroVar() 能扫出这类列,仿真前删掉更省心——不过这点是否每次都必要,看你的因子规模再定。 数值因子间的相关性建议卡一道阈值。设相关系数 > 0.9 的强相关对,用 findCorrelation() 揪出来删一列;PLS、LARS 这类带 L1 规范的模型虽能扛相关,但降相关性后多数模型表现倾向更好。 线性组合冗余最常出现在二元变量或虚拟变量展开时。下面这段矩阵演示了第2+3列=第1列、第4+5+6列=第1列,findLinearCombos() 返回可删列位置(3和6),删完剩4列无冗余。外汇与贵金属数据做独热编码后尤其容易踩这个坑,高杠杆下模型误判风险会被放大。
ltfrDesign <- matrix(class="num">0, nrow = class="num">6, ncol = class="num">6) ltfrDesign[, class="num">1] <- c(class="num">1, class="num">1, class="num">1, class="num">1, class="num">1, class="num">1) ltfrDesign[, class="num">2] <- c(class="num">1, class="num">1, class="num">1, class="num">0, class="num">0, class="num">0) ltfrDesign[, class="num">3] <- c(class="num">0, class="num">0, class="num">0, class="num">1, class="num">1, class="num">1) ltfrDesign[, class="num">4] <- c(class="num">1, class="num">0, class="num">0, class="num">1, class="num">0, class="num">0) ltfrDesign[, class="num">5] <- c(class="num">0, class="num">1, class="num">0, class="num">0, class="num">1, class="num">0) ltfrDesign[, class="num">6] <- c(class="num">0, class="num">0, class="num">1, class="num">0, class="num">0, class="num">1) comboInfo <- findLinearCombos(ltfrDesign) comboInfo $linearCombos $linearCombos[[class="num">1]] [class="num">1] class="num">3 class="num">1 class="num">2 $linearCombos[[class="num">2]] [class="num">1] class="num">6 class="num">1 class="num">4 class="num">5 $remove [class="num">1] class="num">3 class="num">6 ltfrDesign[, -comboInfo$remove] [,class="num">1] [,class="num">2] [,class="num">3] [,class="num">4] [class="num">1,] class="num">1 class="num">1 class="num">1 class="num">0 [class="num">2,] class="num">1 class="num">1 class="num">0 class="num">1 [class="num">3,] class="num">1 class="num">1 class="num">0 class="num">0 [class="num">4,] class="num">1 class="num">0 class="num">1 class="num">0 [class="num">5,] class="num">1 class="num">0 class="num">0 class="num">1 [class="num">6,] class="num">1 class="num">0 class="num">0 class="num">0
◍ 喂给模型的原始价量数据先过哪道筛
做神经网络或 SVM 类信号模型时,MT5 导出的裸价、成交量不能直接塞进去。这类算法只认落在 [-1,1] 或 [0,1] 区间的浮点输入,超出激活函数极值区(tanh/sigmoid 的尾巴)会失真,所以不少人把目标范围收成 {-0.9,0.9} 或 {0.1,0.9} 来避开逼近饱和段。 最朴素的线性缩放公式是 Xn = (x - min(x)) / (max(x) - min(x)) * (h - l) + l,逆变换 x = (Xn - l) / (h - l) * (max(x) - min(x)) + min(x) 才能把模型输出还原回价格量纲。关键一点:min/max 或 mean/sd 这些参数只能从训练集算,测试集和新 K 线必须复用训练集的同一组参数,否则样本外预测会系统性偏移。 如果变量本身接近均匀分布,用 (x - mean(x)) / sd(x) 做标准化更轻量。R 的 caret::preProcess() 一口气给了 BoxCox、YeoJohnson、range、center、scale、pca、ica、spatialSign 等十几种方法——其中 range 锁死 [0,1],但要警惕:当线上新样本出现比训练集更高/更低的极值,转换后会跑出 [0,1],模型输出可能直接废掉。 还有个冷门但实用的 clusterSim::dataNormalization(),支持 18 种行列归一化,比如 n4(减最小值除以极差)、n13(以中值为中心除以半极差)。做多因子贵金属策略时,拿 MT5 的 CSV 历史先跑一遍 n4 或 n13,再喂给 Python/MQL5 的推理端,能少踩很多量纲坑。外汇与贵金属杠杆高,数据预处理失误会放大实盘回撤,参数务必用隔离训练集确定。
分类目标的编码与输出节点数
做分类模型时,目标变量不是连续数值,而是离散的类别因子。训练集里要先编码、预测后再解码,这是绕不开的一步。 以经典 iris 数据为例,三类别目标(setosa / versicolor / virginica)经 decodeClassLabels 展开成三列 0-1 矩阵,每一行仅对应类别为 1,其余为 0;encodeClassLabels 则把矩阵还原成单列的类别标号 1/2/3。head(class) 前 6 行全为 1,说明样本前 6 条全是 setosa。 模型输出节点的数量通常等于类别数:三分类就放三个输出神经元,谁的值最高判给谁。但二分类可以只用单个输出节点(如 0/1 阈值),不必强行铺成两列。 在 MT5 里接 R 或 Python 做品种方向分类时,先确认你喂给模型的是展开矩阵还是单值标签,否则预测回传的维度会对不上。外汇与贵金属杠杆高、滑点大,这类信号仅作概率参考,实盘须自行验证。
> data(iris) > labels <- decodeClassLabels(iris[,class="num">5]) > class <- encodeClassLabels(labels) > head(labels) setosa versicolor virginica [class="num">1,] class="num">1 class="num">0 class="num">0 [class="num">2,] class="num">1 class="num">0 class="num">0 [class="num">3,] class="num">1 class="num">0 class="num">0 [class="num">4,] class="num">1 class="num">0 class="num">0 [class="num">5,] class="num">1 class="num">0 class="num">0 [class="num">6,] class="num">1 class="num">0 class="num">0 > head(class) [class="num">1] class="num">1 class="num">1 class="num">1 class="num">1 class="num">1 class="num">1