高相关性筛选法:特征工程中处理冗余与共线性的实操指南 刚接触特征工程那段时间我最常干的一件事就是把几十个特征一股脑丢进模型指望算法自己“消化”掉冗余信息。结果就是训练时间越拉越长模型效果却不见涨偶尔换一批数据跑指标还能给你表演一波过山车。后来我逐步把特征筛选的流程前置其中高相关性筛选法成了我每次建模几乎必做的第一步——它的原理不复杂计算成本也低但能把“无效维度”和“冗余维度”从源头截住省下后面一大半调参的力气。这篇文章我就把自己实操高相关性筛选法的完整思路、代码和踩坑记录整理出来包括相关性指标怎么选、阈值怎么定、跑完相关矩阵之后怎么处理特征之间的“抱团”问题希望能给同样在做特征工程的读者一些可以直接用的经验。1. 高相关性筛选法到底在筛选什么特征选择的第一道闸很多人听到“高相关性筛选法”第一反应是“找跟标签最相关的几个特征”。这个理解不算错但视角窄了。我在实际项目中把这一道工序拆成两个层面一是筛选出与目标变量相关性高的特征作为有效信号二是剔除特征之间相关性过高的冗余项避免信息重复和共线性问题。1.1 为什么模型不能自动处理所有特征理论上树模型对特征选择有一定鲁棒性不会用的特征它就不分裂逻辑回归也有正则化项可以压低无关特征的权重。但现实中问题没那么简单特征数量大时模型训练和调参的成本成倍上升一次网格搜索跑几十个小时很常见。噪声特征过多尤其是样本量不足时模型容易“记住”噪声出现过拟合。特征之间存在强线性关系时线性模型的参数估计会变得极不稳定系数符号甚至可能反转。业务侧需要可解释性时塞进去几十个相关性不明的特征很难向非技术角色交代。所以我的习惯是不管后面用树模型还是线性模型先把相关性这道闸过了保证进入模型的数据是相对“干净”的。1.2 高相关性筛选法在特征选择方法谱系中的位置特征选择方法通常分成三类过滤法、包装法和嵌入法。高相关性筛选法是典型的过滤法因为它不依赖任何特定模型仅仅通过统计量来衡量特征与目标之间的关系。过滤法Filter计算快、模型无关适合超高维数据的初筛。包装法Wrapper用模型效果反馈来挑选特征子集比如递归特征消除效果好但计算开销大。嵌入法Embedded在模型训练过程中自动完成选择比如 L1 正则、树模型的特征重要性效率高但依赖模型类型。我的工作流通常是先用高相关性筛选法做初筛把维度降下来再用包装法或嵌入法做细选。这个组合兼顾了效率和效果。初筛不做直接上递归特征消除在几百个特征的场景下是很奢侈的事情。2. 三种相关性度量方式的原理与选择边界高相关性筛选法的核心是“相关性”的衡量方式。不同的度量方式对“相关”的定义完全不同选错了指标筛选结果可能南辕北辙。我平时主要权衡三种指标皮尔逊相关系数、斯皮尔曼秩相关系数和互信息。2.1 皮尔逊相关系数衡量线性相关的主力皮尔逊相关系数衡量的是两个变量之间的线性相关程度计算方式是协方差除以两个变量的标准差乘积取值在 -1 到 1 之间。绝对值越接近 1线性关系越强接近 0 表示没有线性关系。这个指标有个前提假设需要注意它假设变量大致服从正态分布或至少分布比较对称并且对异常值极其敏感。我碰到过一个案例某特征和目标变量原本毫无关系但因为有两个极端点皮尔逊相关系数被拉到 0.8 以上差点误导我把它选进模型。2.2 斯皮尔曼秩相关系数对非线性单调关系的补充斯皮尔曼秩相关系数不直接看原始数值而是先把数据转换为秩次再计算秩次之间的皮尔逊相关系数。它衡量的是单调关系哪怕 x 和 y 的关系不是直线而是指数曲线只要 y 随 x 单调变化斯皮尔曼就能捕捉到。这里有一个常见的反直觉例子y x²当 x 在负区间和正区间都有取值时皮尔逊相关系数接近 0但斯皮尔曼秩相关系数也可能是 0因为它不是单调关系。所以遇到明显非线性、非单调的情况皮尔逊和斯皮尔曼都会失效这时候要考虑互信息。2.3 互信息捕捉任意非线性关系的利器互信息度量的是一个变量携带另一个变量的信息量取值非负不假设任何函数关系。它的优势是能发现任意形状的相关性但劣势也很明显——计算需要估计概率分布对分箱参数、核函数参数比较敏感结果在不同参数下可能波动。下表是我在实操中对三种指标的选择倾向指标适用场景主要限制皮尔逊线性关系、连续特征、正态性较好对异常值敏感无法捕捉非线性斯皮尔曼单调非线性关系、分布偏态严重无法捕捉非单调关系互信息非线性强、无明显函数形状参数敏感计算成本较高在真实项目里我会先跑皮尔逊做快速筛选把明显有线性关系的特征挑出来再用互信息做交叉验证防止漏掉一些强非线性但皮尔逊值很低的特征。两者结合使用比单看任何一个指标都稳妥。2.4 分类问题里的补充工具如果目标变量是分类标签而不是连续值特征相关性衡量就要换一套工具。常用的是卡方检验适用于类别特征与类别目标之间的独立性检验。方差分析 F 值适用于连续特征与类别目标之间本质是比较不同类别组内的均值差异。信息增益与互信息同源常用于决策树分裂。我自己的习惯是分类任务中会把数值型特征先做分箱再算信息增益能同时兼顾非线性关系效果比单用 F 值稳定。3. 实操用 Python 跑一次完整的高相关性筛选讲了这么多原理下面进入正题。我用一个模拟数据集来演示高相关性筛选法的完整操作流程包括数据检查、指标计算、特征筛选和冗余剔除。以下代码均基于 Python 3.8 环境核心依赖为 pandas、numpy、scipy。3.1 准备数据与初始化检查我习惯在算相关性之前先做三件事检查缺失值、确认数据类型、剔除常量和近常量特征。这些前置检查虽然简单但能避免后面相关矩阵里出现 NaN 或者无意义的计算结果。import pandas as pd import numpy as np from scipy.stats import pearsonr, spearmanr # 生成模拟数据集含15个特征其中5个与目标相关其余为噪声 np.random.seed(42) n_samples 2000 X pd.DataFrame({ feat_1: np.random.normal(0, 1, n_samples), feat_2: np.random.normal(0, 1, n_samples), feat_3: np.random.normal(0, 1, n_samples), feat_4: np.random.normal(0, 1, n_samples), feat_5: np.random.normal(0, 1, n_samples), }) # 构造部分冗余特征feat_6 是 feat_1 的线性组合 X[feat_6] X[feat_1] * 2 np.random.normal(0, 0.1, n_samples) # 构造非线性相关特征 X[feat_7] X[feat_2] ** 2 np.random.normal(0, 0.05, n_samples) # 构造与目标无关的纯噪声特征 for i in range(8, 16): X[ffeat_{i}] np.random.normal(0, 1, n_samples) # 构造目标变量 y依赖 feat_1 和 feat_3 y 2 * X[feat_1] 1.5 * X[feat_3] np.random.normal(0, 0.3, n_samples) # 前置检查 print(缺失值数量, X.isnull().sum().sum()) print(特征数量, X.shape[1])提示如果数据里有常量特征相关矩阵会出现 NaN因为分母标准差为 0。建议先剔除方差极低的列再继续。3.2 计算特征与目标的相关性接下来分别计算每个特征与目标 y 的皮尔逊相关系数和对应的 p 值同时计算斯皮尔曼秩相关系数作为补充。results [] for col in X.columns: # 计算皮尔逊 pearson_corr, pearson_p pearsonr(X[col], y) # 计算斯皮尔曼 spearman_corr, spearman_p spearmanr(X[col], y) results.append({ feature: col, pearson: pearson_corr, pearson_p: pearson_p, spearman: spearman_corr, spearman_p: spearman_p, }) df_corr pd.DataFrame(results).sort_values(pearson, ascendingFalse) print(df_corr.to_string(indexFalse))运行结果会清晰显示feat_1 和 feat_3 的皮尔逊相关系数很高feat_7 的皮尔逊可能接近 0但斯皮尔曼会很高feat_6 与 y 的相关性也会很高因为它本质上是 feat_1 的线性组合这部分信息是冗余的。3.3 阈值设定与初筛逻辑相关性筛选的关键参数是阈值。阈值没有绝对标准我的经验是特征数量在百级以下、样本量几千以上时皮尔逊绝对值大于 0.1 就可保留“潜在有用特征”。特征数量大且噪声多时阈值可以提高到 0.2 或 0.3优先保证精炼度。如果后续还要做嵌入法细选初筛阈值可以放宽避免过早丢弃弱信号。由于 p 值受样本量影响极大样本量大时很小的相关系数也会 p 0.05我通常不拿 p 值做唯一标准。threshold 0.1 selected_features df_corr[(df_corr[pearson].abs() threshold) | (df_corr[spearman].abs() threshold)][feature].tolist() print(初筛后保留特征, selected_features)3.4 计算特征间相关矩阵并剔除冗余到了这一步我们要处理特征之间的高相关性。特征间相关性过高意味着它们携带的信息高度重叠会带来多重共线性问题。# 对初筛后的特征计算相关矩阵 sub_X X[selected_features] corr_matrix sub_X.corr().abs() # 找出相关系数高于0.8的特征对 upper_tri corr_matrix.where(np.triu(np.ones(corr_matrix.shape), k1).astype(bool)) high_corr_pairs [(col1, col2) for col1, col2 in zip(*np.where(upper_tri 0.8))] print(高相关特征对, high_corr_pairs) # 剔除策略保留与目标相关性更高的特征删除另一个 to_drop set() for f1, f2 in high_corr_pairs: corr_f1 df_corr.loc[df_corr[feature] f1, pearson].values[0] corr_f2 df_corr.loc[df_corr[feature] f2, pearson].values[0] # 保留与目标相关性更高的那个特征 drop_feature f2 if abs(corr_f1) abs(corr_f2) else f1 to_drop.add(drop_feature) final_features [f for f in selected_features if f not in to_drop] print(剔除冗余特征, list(to_drop)) print(最终保留特征, final_features)这段代码的实际效果是把构建出来的 feat_6feat_1 的线性组合从特征列表里剔除同时可能保留 feat_1 和 feat_3。整个流程跑下来不过几秒却能大幅降低后续建模的复杂度。4. 特征之间的高相关性多重共线性才是隐形的坑很多教程讲高相关性筛选法只讲“特征与目标相关”却忽略了“特征与特征相关”这个更隐蔽的问题。我在实际项目中对这一环尤其重视因为它直接关系到模型稳定性和可解释性。4.1 多重共线性的危害是怎么体现的当两个特征高度线性相关时线性回归试图同时估计它们的系数实际上是在“分工”一段共同的信息。样本噪声稍微波动系数就会剧烈震荡甚至出现符号反转——一个特征本来应该正向影响目标结果系数却是负数。这种模型拿去解释业务场景很容易给出误导性结论。即便在树模型里冗余特征也会分摊重要性分数干扰我们对关键驱动因素的判断。4.2 相关性阈值与方差膨胀因子的配合使用相关系数矩阵能看出一阶线性关系但三个以上特征的多重共线性无法直接从两两相关矩阵里发现。这种情况我会用方差膨胀因子VIF做进一步诊断。from statsmodels.stats.outliers_influence import variance_inflation_factor # 对初筛后的特征计算VIF vif_data pd.DataFrame() vif_data[feature] final_features vif_data[VIF] [variance_inflation_factor( sub_X[final_features].values, i) for i in range(len(final_features))] print(vif_data.sort_values(VIF, ascendingFalse))VIF 的计算逻辑是对每个特征做一次回归用其他特征解释它再根据回归的 R² 计算 VIF 1 / (1 - R²)。R² 越接近 1说明这个特征能被其他特征解释得越充分VIF 越高。经验阈值是VIF 大于 5 需要警惕大于 10 基本可以判定存在严重的多重共线性。提示VIF 的阈值要结合业务判断。比如同一实体衍生出的多个相似指标VIF 高是正常的不代表该删但如果这个特征不是业务核心删除后模型更稳定那就值得删。技术指标永远为业务解释服务。4.3 剔除特征时的保留优先级遇到高相关特征对时剔除谁保留谁我的优先级排序是先保留与目标变量相关性更高的一方。如果相关性接近再比较业务可解释性优先保留业务语义明确、口径稳定的特征。最后比较缺失率和波动性缺失率低、波动稳定的特征更可靠。这个优先级顺序我建议在代码里落地成函数而不是每次手动判断尤其在特征数量几十个以上的时候。5. 高相关性筛选法的适用边界和常见误区在写这篇内容之前我专门回想了一下自己在项目中踩过的几个坑有些是错误的筛选姿势导致的有些是筛选后对结果过度自信导致的简单整理如下。5.1 误区一相关系数高就代表因果这是最危险的一个误区。我见过有同学在做特征筛选时把相关系数最高的特征直接当成“业务因子的主因”跑去业务方汇报结果被现场打脸。相关性只说明统计关联不说明因果方向。比如某电商场景下“页面停留时长”和“购买金额”相关性强但到底是停留时长推动了购买还是购买意愿强的人本来就待得久单靠相关性无法回答。特征筛选是高相关性筛选的终点但应该是因果分析的起点这一点务必分清楚。5.2 误区二阈值定得越高越好阈值高确实能留下精炼的特征集但也容易把一些弱信号特征过早筛掉。尤其在目标变量本身受多维因素影响时单个特征和目标的相关系数可能都不高但组合起来解释力很强。我现在的做法是先以 0.1 的低阈值粗筛一轮保证信号不遗漏等特征数量可控后再用模型重要性、递归特征消除等方法做第二轮细筛。初筛负责“保底”细筛负责“精选”两者分工明确。5.3 误区三只用皮尔逊系数打天下在多数轻度非线性业务数据中皮尔逊确实够用。但一旦数据里存在明显的平方项、交互项或者分段关系皮尔逊的盲区就暴露了。至少你也应该像上面代码里那样同时跑一列斯皮尔曼做对照。如果要更严谨互信息也可以安排上尤其是对特征数量不大但关系复杂的场景。5.4 什么时候不推荐用高相关性筛选法有几个场景我对这套方法会格外谨慎特征数量极大比如上百万维的文本或用户行为序列此时更推荐先用基于频率或方差的粗筛再考虑相关性。特征本身已经过强先验选择或者业务方明确要求保留某些特征不需要再做数据驱动筛选。非线性模型配合强正则化时少量冗余特征的负面影响有限筛选收益不高。如果你碰到这些情况高相关性筛选法可以作为辅助诊断而不是主导流程。5.5 与业务语义校验配合的最终检查最后一步也是我强烈建议加入流程的动作把筛选结果拿给业务侧或领域专家做一次语义校验。数据统计上保留的特征业务上可能口径混乱或含义重复被筛掉的特征也可能只是因为当前样本下相关性弱业务逻辑上仍然重要。我在一个客户流失预测项目里就遇到过——一个业务上核心的“最近登录距今天数”特征因为和另一个使用时长类特征强相关被候选删除但业务方坚持保留后续模型在分群分析里这个特征确实更稳定。所以筛选代码跑完后一定要再过一道人工确认的关卡。5.6 我现在的实操习惯经过多次迭代我现在做高相关性筛选已经形成一套固定流程前置检查缺失值、常量特征、数据类型统一。快速初筛皮尔逊 斯皮尔曼双指标并行各保留绝对值大于阈值的特征再取并集。冗余剔除计算特征间相关矩阵对高相关特征对按优先级函数做剔除。共线性诊断对剩余特征计算 VIF定位多重共线性热点。业务校验将最终保留清单交业务侧确认语义和口径。细选接力如果特征数量仍偏多把结果交给递归特征消除或 L1 正则模型做二次精选。这套流程基本把高相关性筛选法的价值发挥到了最大化。我也建议你从自己的项目里抽出一个小型数据集先按本文代码跑一遍再逐步调整阈值和指标组合慢慢找到适合自己业务的筛选参数。数据特征千差万别但“先过滤冗余再精细建模”的思路是通用的。