ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

机器学习特征工程:相关性分析消除冗余特征实战指南

机器学习特征工程:相关性分析消除冗余特征实战指南 1. 项目缘起为什么我们总在“杀”特征做机器学习项目尤其是数据挖掘和建模你肯定遇到过这种情况辛辛苦苦从业务里扒拉出几百个特征满怀信心地扔进模型结果训练时间长得离谱模型效果却提升有限甚至还不如只用几十个特征的时候。更让人头疼的是模型变得难以解释你搞不清楚到底是哪个特征在真正起作用。这背后十有八九是“冗余特征”在作祟。冗余特征顾名思义就是那些提供的信息跟其他特征高度重叠、甚至一模一样的特征。比如在一个房价预测模型里你同时引入了“房屋建筑面积平方米”和“房屋使用面积平方米”。这两个特征虽然名字不同但高度相关它们所携带的关于房屋大小的信息是重复的。模型在训练时会为这两个高度相关的特征都分配权重这不仅浪费了计算资源更严重的是它可能导致模型权重变得不稳定统计学上称为“多重共线性”让模型在遇到新数据时表现得很差也就是我们常说的“过拟合”。所以特征选择特别是消除冗余特征是建模前至关重要的一步。它不是为了炫技而是为了构建一个更健壮、更高效、更可解释的模型。今天我们不谈那些复杂的嵌入式或包装式特征选择方法就从最基础、最直观也最容易被忽视的“相关性分析”说起。这个方法简单到用Excel都能做但用好了能帮你避开建模路上80%的坑。2. 相关性分析不只是看个数字那么简单提到相关性分析很多人第一反应就是计算皮尔逊相关系数Pearson Correlation Coefficient得到一个介于-1到1之间的数字然后根据经验阈值比如0.8或0.9来判断是否相关。这个思路没错但太粗糙了很容易掉坑里。2.1 理解三种核心的相关性系数首先你得知道面对不同类型的数据该用哪种“尺子”去量。皮尔逊相关系数这是最常用的但它衡量的是两个连续变量之间的线性相关程度。它的前提假设是数据服从正态分布且关系是线性的。如果你的两个特征之间的关系是曲线比如先增后减皮尔逊系数可能会很低误导你认为它们不相关。计算时它用的是协方差除以各自标准差的乘积公式是ρ cov(X, Y) / (σ_X * σ_Y)。在Python里用pandas的.corr()方法默认计算的就是它。斯皮尔曼等级相关系数这就是热搜词里的“spearman相关性分析”。它衡量的是两个变量之间的单调关系即一个变量增大另一个变量也倾向于增大或减小不一定是直线。它不关心具体数值只关心排名顺序。因此它对异常值不敏感也适用于不服从正态分布的数据或等级数据。当你怀疑特征间可能存在非线性但趋势一致的关系时或者数据中有很多异常值时斯皮尔曼是更好的选择。在pandas中调用.corr(methodspearman)即可。肯德尔等级相关系数和斯皮尔曼类似也是基于等级排序的非参数相关度量。它在数据量小、或者有很多相同等级Tie的时候更稳健。但在机器学习特征筛选中斯皮尔曼更常见。怎么选我的经验是对于大多数数值型特征先画个散点图看看。如果点大致沿着一条直线分布用皮尔逊如果呈单调的曲线或者点比较散乱但趋势明显用斯皮尔曼。拿不准的时候可以两个都算一下如果结果差异很大那就要深入探究数据分布了。2.2 相关系数矩阵你的第一张特征“关系网”实操的第一步永远是先计算所有特征两两之间的相关系数生成一个矩阵并可视化。在Python中这非常简单import pandas as pd import numpy as np import seaborn as sns import matplotlib.pyplot as plt # 假设 df 是你的DataFrame列是特征 corr_matrix df.corr(methodpearson) # 或 spearman # 可视化 plt.figure(figsize(12, 10)) sns.heatmap(corr_matrix, annotTrue, fmt.2f, cmapcoolwarm, squareTrue, cbar_kws{shrink: .8}) plt.title(Feature Correlation Matrix) plt.show()这张热图是你的“藏宝图”。颜色越红接近1表示正相关性越强越蓝接近-1表示负相关性越强。对角线永远是1自己和自己相关。你的目标就是寻找那些非对角线上的深红色或深蓝色方块。注意热图的annotTrue会把数值显示在格子里特征多的时候会显得很乱可以先关掉annotFalse看整体色块分布再对高相关区域进行局部放大查看具体数值。3. 设定阈值与筛选艺术与科学的结合看到相关系数矩阵后下一个问题就是多高的相关才算“冗余”0.70.80.9网上有很多经验值但我告诉你没有放之四海而皆准的黄金阈值。这需要结合你的业务知识、模型特性和后续步骤来综合判断。3.1 阈值的动态决策逻辑业务层面有些特征即使数学上高度相关业务上也可能代表不同含义需要保留。比如“用户最近一次登录时间”和“用户本周登录天数”在预测用户流失时都可能有价值虽然它们相关。这时你需要基于业务逻辑决定保留哪一个或者都保留但意识到它们共线性的风险。模型层面线性模型如线性回归、逻辑回归对多重共线性非常敏感会导致系数估计不准、标准误增大。建议设置较严格的阈值如|corr| 0.8或0.85时就必须处理。树模型如随机森林、XGBoost对特征间的相关性不敏感因为这些模型是单变量分裂的。阈值可以放宽一些比如|corr| 0.9。消除高度冗余的特征主要为了提升训练效率和模型简洁度。深度学习模型通常能自动学习特征间的交互但过多的冗余特征同样会增加不必要的计算复杂度和过拟合风险。阈值可参考树模型或结合特征重要性如果可用来判断。经验法则一个常用的起步策略是设定一个阈值例如0.9然后观察特征簇。在热图中你会看到几组特征彼此之间都高度相关形成一个“簇”。我们的目标是在每个簇里保留一个最具代表性业务意义明确、或与其他簇特征相关性较低的特征剔除簇内其他特征。3.2 自动化筛选的代码实现手动看热图选特征在特征多的时候不现实。下面是一个基于相关性的自动化特征筛选函数它采用“保留高相关特征组中其中一个”的策略def remove_redundant_features_by_correlation(df, threshold0.9): 根据相关系数阈值移除冗余特征。 策略对于一组高度相关的特征只保留其中第一个按列顺序移除组内其他特征。 参数: df: pandas DataFrame仅包含特征列。 threshold: 相关系数绝对值阈值高于此值视为高度相关。 返回: selected_features: 筛选后的特征列表。 # 计算相关系数矩阵 corr_matrix df.corr().abs() # 取绝对值关心相关性强弱而非方向 # 取上三角矩阵不包括对角线避免重复比较 upper_tri corr_matrix.where(np.triu(np.ones(corr_matrix.shape), k1).astype(bool)) # 找出相关系数大于阈值的特征对 to_drop [column for column in upper_tri.columns if any(upper_tri[column] threshold)] print(f基于阈值 {threshold}建议移除的特征数量: {len(to_drop)}) print(f建议移除的特征: {to_drop}) # 保留的特征 selected_features [col for col in df.columns if col not in to_drop] print(f保留的特征数量: {len(selected_features)}) return selected_features # 使用示例 # 假设 X 是你的特征DataFrame features_to_keep remove_redundant_features_by_correlation(X, threshold0.85) X_filtered X[features_to_keep]这个函数提供了一个基线方法。但它有个明显的缺点它只是按列的顺序DataFrame的列顺序保留第一个特征这个选择可能不是最优的。更高级的做法是在每个高相关组内计算每个特征与组外所有特征的平均相关性保留那个与组外特征平均相关性最低的即最具独特信息的或者结合业务知识来选择。4. 超越简单相关陷阱与进阶策略仅仅依靠两两相关系数来消除冗余可能会遇到一些陷阱需要更精细的工具和策略。4.1 陷阱一多重共线性与VIF两两相关只能发现“一对一”的冗余。但现实中更常见的是“多对一”的冗余即一个特征可以由其他多个特征线性组合而成。这就是经典的多重共线性问题。检测它需要用方差膨胀因子。VIF衡量的是一个特征由于与其他特征相关其回归系数的方差被放大了多少倍。经验上VIF 1表示该特征与其他特征无关。1 VIF 5中度相关通常可以接受。VIF 5 或 10存在严重多重共线性需要考虑处理。计算VIF通常需要借助statsmodels库from statsmodels.stats.outliers_influence import variance_inflation_factor import pandas as pd def calculate_vif(X): 计算特征DataFrame的VIF值。 注意需要给数据添加常数项截距。 X_with_const pd.DataFrame(X).copy() # 确保没有无穷大或缺失值 X_with_const X_with_const.replace([np.inf, -np.inf], np.nan).dropna() vif_data pd.DataFrame() vif_data[feature] X_with_const.columns vif_data[VIF] [variance_inflation_factor(X_with_const.values, i) for i in range(X_with_const.shape[1])] return vif_data # 使用 vif_df calculate_vif(X_filtered) # 用之前相关性筛选后的数据 print(vif_df.sort_values(byVIF, ascendingFalse))处理高VIF特征是一个迭代过程移除VIF最高的特征重新计算剩余特征的VIF直到所有特征的VIF都低于阈值如5。这个过程可以和前面的相关性筛选结合。4.2 陷阱二非线性关系与互信息皮尔逊和斯皮尔曼捕捉的是单调关系。但如果两个特征之间存在复杂的非线性关系比如圆形、正弦波这些系数都会接近0让你误以为它们独立。然而在机器学习中这种非线性关系可能被模型如神经网络、带核函数的SVM所利用盲目剔除可能会损失信息。这时可以引入互信息。互信息衡量的是两个变量共享的信息量对关系形式没有假设能捕捉任何类型线性或非线性的统计依赖。scikit-learn提供了计算函数from sklearn.feature_selection import mutual_info_regression, mutual_info_classif # 回归问题用 mutual_info_regression分类问题用 mutual_info_classif # 假设 X 是特征y 是连续型目标变量 mi_scores mutual_info_regression(X, y) mi_series pd.Series(mi_scores, indexX.columns).sort_values(ascendingFalse) # 你可以画出来看看 mi_series.plot.bar(figsize(10, 6)) plt.ylabel(Mutual Information Score) plt.title(Feature Importance based on Mutual Information)互信息可以帮助你从“预测目标”的角度理解特征的重要性。一个与目标变量互信息高但与其他特征也高度互信息的特征可能是一个强预测因子但也可能携带了大量冗余信息。你可以结合相关性/互信息矩阵做出更明智的取舍。4.3 策略整合一个实战工作流在实际项目中我通常采用一个分层的工作流来处理冗余特征第一层业务清洗。基于领域知识直接移除明显无意义或重复的业务指标如同时存在“销售额”和“销售额万元”。第二层高相关过滤。计算斯皮尔曼相关系数矩阵对数据分布假设更宽松设定一个较高的阈值如0.95使用改进的筛选策略如在每个高相关簇中保留与目标变量相关性最高或互信息最大的那个进行初步剔除。第三层多重共线性诊断。对剩余特征计算VIF迭代移除VIF值最高的特征直到所有特征VIF10。第四层模型导向精筛。将处理后的特征送入一个简单的基线模型如Lasso回归或带特征重要性的随机森林。Lasso的系数收缩可以自动将一些冗余特征的系数压到0。树模型的特征重要性可以帮你确认哪些特征在预测中真正有用。结合这些结果进行最终的手动调整。5. 相关性分析在机器学习流程中的定位看了热搜词很多人关心“机器学习应用流程”。消除冗余特征属于特征工程的核心环节通常发生在数据清洗和探索性数据分析之后在模型训练之前。一个简化的流程如下数据收集 - 数据清洗处理缺失值、异常值- 探索性数据分析EDA包括相关性分析、分布可视化- 特征工程构造新特征、转换特征、特征选择- 模型训练与评估 - 模型部署相关性分析是EDA和特征选择之间的桥梁。它不仅能指导我们删除冗余特征还能带来其他好处发现潜在关系高相关可能暗示着有趣的业务逻辑值得深入分析。辅助特征构造如果两个特征A和B都与目标相关但彼此不相关那么构造一个交互特征如A*B可能会带来效果提升。降低维度灾难风险对于样本量有限的数据集减少特征数量能有效缓解过拟合。最后记住一点特征选择没有银弹。相关性分析是一个强大而基础的起点但它必须与业务理解、模型特性和其他统计工具如VIF、互信息结合使用。每次做完特征筛选一定要在验证集或通过交叉验证评估模型性能的变化。有时候扔掉一个“冗余”特征可能会损失掉模型捕捉复杂模式的微弱能力这需要反复的实验和权衡。我个人的习惯是建立一个特征筛选的“流水线”将相关性阈值、VIF阈值作为参数方便快速实验不同严格度下的模型表现。毕竟我们的终极目标不是得到一个最“干净”的特征集而是得到一个最“好用”的模型。
返回列表