scikit-learn 特征选择完全指南:从 VarianceThreshold 到递归消除与顺序选择的工程实践

发布时间:2026/9/18 1:35:24
scikit-learn 特征选择完全指南:从 VarianceThreshold 到递归消除与顺序选择的工程实践 scikit-learn 特征选择完全指南从 VarianceThreshold 到递归消除与顺序选择的工程实践【免费下载链接】scikit-learnscikit-learn: machine learning in Python项目地址: https://gitcode.com/gh_mirrors/sc/scikit-learnsklearn.feature_selection是 scikit-learn 中专门用于特征选择与降维的模块它既可以提升模型的准确率也可以显著加速超高维数据集上的训练与推理。本文基于官方文档 feature_selection.rst 整理并结合模块源码sklearn/feature_selection深入讲解每一类选择器的实现原理与参数细节。读完本文你将掌握低方差过滤、单变量统计检验、递归特征消除RFE、基于模型重要性的选择SelectFromModel以及顺序特征选择SFS的完整用法并能将特征选择器正确嵌入 Pipeline 工作流。模块总览特征选择器的统一接口sklearn.feature_selection中的所有选择器都实现了统一的 Transformer 接口通过fit学习一个“保留哪些特征”的布尔掩码通过transform输出只含保留特征的子集。这种统一约定来自 SelectorMixin它提供了开箱可用的get_support(indicesFalse)、transform、inverse_transform和get_feature_names_out方法子类只需实现一个_get_support_mask即可get_support()返回布尔掩码indicesTrue时返回整数索引数组用于查看究竟保留了哪些特征transform(X)按掩码对特征轴做索引若一个特征都没选中会给出 UserWarninginverse_transform(X)把降维后的数据还原到原始维度被剔除的特征位置填 0在解释模型系数时非常有用get_feature_names_out(input_features)返回保留特征的名称配合 DataFrame 输入与set_output使用。模块的公开 API 清单定义在init.py共包含 7 个选择器VarianceThreshold、SelectKBest、SelectPercentile、SelectFpr/SelectFdr/SelectFwe、GenericUnivariateSelect、RFE/RFECV、SelectFromModel、SequentialFeatureSelector以及 6 个评分函数chi2、f_classif、f_regression、r_regression、mutual_info_classif、mutual_info_regression。一、移除低方差特征VarianceThreshold原理与使用VarianceThreshold 是最简单的基线式特征选择方法它只查看特征本身X完全不依赖目标 y因此天然支持无监督场景。其规则是移除所有“训练集方差低于阈值”的特征默认阈值threshold0意味着移除所有零方差特征即在所有样本上取值完全相同的常量列。文档给出了一个布尔特征的经典示例若我们希望剔除“在超过 80% 的样本中恒为 0 或恒为 1”的特征由于布尔特征是伯努利随机变量其方差为Var[X] p(1 - p)取阈值.8 * (1 - .8)即可实现目标 from sklearn.feature_selection import VarianceThreshold X [[0, 0, 1], [0, 1, 0], [1, 0, 0], [0, 1, 1], [0, 1, 0], [0, 1, 1]] sel VarianceThreshold(threshold(.8 * (1 - .8))) sel.fit_transform(X) array([[0, 1], [1, 0], [0, 0], [1, 1], [1, 0], [1, 1]])第一列在 6 个样本中有 5 个为 0p 5/6 .8方差不足因此被移除输出从 3 列变为 2 列。源码级细节fit时对稠密矩阵用np.nanvar(X, axis0)计算方差对稀疏矩阵接受csr/csc则走mean_variance_axis不会把数据转稠密见 _variance_threshold.py当threshold 0时为了规避浮点数值精度问题源码会额外计算峰峰值peak-to-peak最终取“方差与峰峰值中较小者”作为比较基准_variance_threshold.py若没有任何特征满足阈值要求fit会抛出ValueError: No feature in X meets the variance threshold ...单样本输入时错误信息还会额外提示X contains only one sample_variance_threshold.py该选择器允许输入含 NaNtagallow_nan True且threshold参数约束为非负实数Interval(Real, 0, None, closedleft)。二、单变量特征选择基于统计检验打分单变量特征选择的核心思想是对每一个特征独立地做统计检验得到分数score与 p 值再按分数筛选。它通常作为 estimator 之前的预处理步骤。文档中列出了 4 种策略选择器行为关键参数SelectKBest保留分数最高的 k 个特征kint 或all默认 10SelectPercentile保留分数最高的百分比特征percentile0~100默认 10SelectFpr / SelectFdr / SelectFwe按 p 值阈值筛选分别控制假阳性率、错误发现率、族错误率alpha默认 5e-2GenericUnivariateSelect可配置策略的统一入口便于配合超参搜索modeparam用 F 检验选取前 k 个特征 from sklearn.datasets import load_iris from sklearn.feature_selection import SelectKBest from sklearn.feature_selection import f_classif X, y load_iris(return_X_yTrue) X.shape (150, 4) X_new SelectKBest(f_classif, k2).fit_transform(X, y) X_new.shape (150, 2)评分函数回归与分类各取所需这些选择器接收一个评分函数函数返回每特征的分数与 p 值SelectKBest与SelectPercentile也接受只返回分数的函数回归任务r_regressionPearson 相关系数、f_regressionF 统计量与 p 值、mutual_info_regression分类任务chi2卡方检验、f_classifANOVA F 值、mutual_info_classif。各类方法的定位差异值得注意F 检验类f_classif/f_regression估计的是两个随机变量之间的线性依赖程度。其中f_classif内部调用的是模块自实现的单因素方差分析f_oneway基于 Heiman 教材的算法见 _univariate_selection.pyf_regression则先通过r_regression计算 Pearson 相关再转换为 F 分数_univariate_selection.py互信息方法mutual_info_*是非参数的能够捕获任意类型的统计依赖包括非线性代价是需要更多样本才能准确估计卡方检验chi2只适用于非负特征如词频源码中会直接校验if np.any((X.data if issparse(X) else X) 0): raise ValueError(Input X must be non-negative.)_univariate_selection.py。若特征是连续值文档建议先用KBinsDiscretizer分箱。三条重要注意事项稀疏数据友好chi2、mutual_info_regression、mutual_info_classif在稀疏矩阵输入时不会将其转稠密可直接高效处理切勿混用文档明确警告不要把回归评分函数用在分类问题上否则会得到无意义的结果支持无监督选择SelectPercentile与SelectKBest支持score_func中yNone的无监督用法——评分函数只需内部基于X计算分数即可源码中_BaseFilter.fit在y is None时只校验X见 _univariate_selection.py。源码细节阈值与平局处理SelectKBest在kall时保留全部特征k0时全部剔除排序使用稳定排序np.argsort(scores, kindmergesort)_univariate_selection.py且当k n_features时给出 UserWarning 并保留全部特征SelectPercentile通过np.percentile(scores, 100 - percentile)求阈值并对分数恰等于阈值的“平局”特征按需保留_univariate_selection.pySelectFpr保留pvalues_ alpha的特征SelectFdr采用 Benjamini-Hochberg 过程控制错误发现率上界SelectFwe执行 Bonferroni 式校正pvalues_ alpha / n_features_univariate_selection.pyGenericUnivariateSelect的mode支持{percentile, k_best, fpr, fdr, fwe}五种模式param对应被代理选择器的参数因此可以用它作为超参搜索空间_univariate_selection.py评分结果中的 NaN 会被_clean_nans统一替换为数据类型最小值避免排序与比较出错_univariate_selection.py。可参考的官方示例plot_f_test_vs_mi.py对比 F 检验与互信息、plot_feature_selection.py单变量选择综合示例。三、递归特征消除RFE 与 RFECVRFE按重要性递归剪枝给定一个能为特征赋予权重的外部估计器如线性模型的coef_、树的feature_importances_或任意可调用对象RFE 的目标是递归地考虑越来越小的特征集先在当前特征集上训练估计器得到每个特征的重要性然后剪掉最不重要的若干特征再在剩余集上重复直到达到目标特征数。from sklearn.svm import SVR from sklearn.datasets import make_friedman1 from sklearn.feature_selection import RFE X, y make_friedman1(n_samples50, n_features10, random_state0) selector RFE(SVR(kernellinear), n_features_to_select5, step1).fit(X, y) selector.support_ # 布尔掩码 selector.ranking_ # 排名rank 1 为最终保留的特征关键参数源码 _rfe.pyn_features_to_selectNone时默认保留一半特征整数表示绝对个数(0,1)区间的浮点数表示比例step 1的整数表示每轮移除的特征个数(0,1)浮点数表示每轮移除的比例向下取整importance_getter与SelectFromModel相同auto时自动探测coef_或feature_importances_也可传字符串路径如named_steps.clf.feature_importances_或可调用对象。拟合后可从ranking_属性读取完整排名1 表示最终保留support_给出保留掩码。RFECV用交叉验证自动确定特征个数RFECV 在交叉验证循环中执行 RFE从而自动找出最优特征数。其流程是由cv参数划分出不同的交叉验证折对每个折拟合一个 RFE 选择器用scorer评估不同特征数下的性能并跨折聚合平均最终把特征数设为使交叉验证得分最大化的数值。实现上每个折都会记录各步骤的得分、掩码与排名见_rfe_single_fit_rfe.py因此RFECV在计算上比单个RFE更重但免去了人工调特征数的麻烦。参考示例plot_rfe_digits.py数字分类中像素点相关性、plot_rfe_with_cross_validation.py用交叉验证自动调特征数。四、SelectFromModel基于模型重要性的元变换器阈值机制SelectFromModel 是一个元变换器meta-transformer可与任何在拟合后通过属性coef_、feature_importances_或importance_getter可调用对象暴露特征重要性的估计器配合。特征重要性绝对值低于threshold的特征被视为不重要而被移除。threshold有三种指定方式源码_calculate_threshold_from_model.py数值直接给出阈值内置启发式字符串mean、median以及它们的浮点倍数如0.1*mean、1.25*median——字符串会在拟合时动态解析为对应统计量或缩放值None默认若估计器显式或隐式使用了 L1 惩罚如Lasso、penaltyl1的线性模型默认阈值取1e-5近似“保留非零系数”否则回退到mean。max_features参数可设置最大保留特征数int 或可调用对象max_features(X)与阈值机制联合使用若只想按max_features截断而不做阈值过滤可将threshold设为-np.inf。其余参数包括prefit直接传入已拟合的估计器fit时会深拷贝、norm_ordercoef_为二维时用于聚合系数的范数阶数默认 1。重要性的提取由_get_feature_importances完成_base.pyauto模式依次探测coef_与feature_importances_两者都不存在时抛出明确报错。场景一L1 稀疏化特征选择带 L1 惩罚的线性模型天然具有稀疏解——大量估计系数为 0。当目标是为另一个分类器降维时可配合SelectFromModel直接筛出非零系数。文档推荐三类稀疏估计器回归用 Lasso分类用 LogisticRegression 与 LinearSVC from sklearn.svm import LinearSVC from sklearn.datasets import load_iris from sklearn.feature_selection import SelectFromModel X, y load_iris(return_X_yTrue) X.shape (150, 4) lsvc LinearSVC(C0.01, penaltyl1, dualFalse).fit(X, y) model SelectFromModel(lsvc, prefitTrue) X_new model.transform(X) X_new.shape (150, 3)稀疏度控制规律对 SVM 与逻辑回归参数C越小选中的特征越少对 Lassoalpha越大选中的特征越少。关于 L1 恢复与压缩感知文档补充了以下要点详见doc/modules/feature_selection.rst的 “L1-recovery and compressive sensing” 一节在alpha取值恰当时Lasso 可以仅用少量观测完全恢复出真实的非零变量集合但前提是满足特定条件——样本数需“足够大”取决于非零系数个数、特征数的对数、噪声量、非零系数的最小绝对值以及设计矩阵 X 的结构且设计矩阵不能过度相关。目前没有选择恢复用alpha的通用规则交叉验证LassoCV/LassoLarsCV可能偏欠惩罚混入少量无关变量对预测分数影响不大而LassoLarsICBIC则倾向于给出较大的alpha。相关示例见 plot_tomography_l1_reconstruction.py更多参考可阅读 plot_lasso_dense_vs_sparse_data.py。场景二基于树的特征选择树模型sklearn.tree 模块与 sklearn.ensemble 中的森林可以计算基于不纯度的特征重要性再交由SelectFromModel丢弃无关特征 from sklearn.ensemble import ExtraTreesClassifier from sklearn.datasets import load_iris from sklearn.feature_selection import SelectFromModel X, y load_iris(return_X_yTrue) clf ExtraTreesClassifier(n_estimators50) clf clf.fit(X, y) clf.feature_importances_ array([ 0.04, 0.05, 0.4, 0.4]) model SelectFromModel(clf, prefitTrue) X_new model.transform(X) X_new.shape (150, 2)需要提醒的是基于不纯度的重要性并不等价于“真实相关性”其结论可能受特征间共线性等因素影响相关局限性讨论见 plot_permutation_importance.py 与 plot_forest_importances.py。五、顺序特征选择SequentialFeatureSelectorSequentialFeatureSelectorSFS不依赖模型的coef_/feature_importances_属性而是基于交叉验证得分贪心地挑选特征子集分为两种方向由direction控制前向 SFSforward初始为空集每轮评估“加入哪个新特征能使交叉验证得分最大”加入后继续直到达到n_features_to_select后向 SFSbackward初始为全量特征每轮贪心地移除对得分影响最小的特征。from sklearn.feature_selection import SequentialFeatureSelector from sklearn.neighbors import KNeighborsClassifier from sklearn.datasets import load_iris X, y load_iris(return_X_yTrue) sfs SequentialFeatureSelector( KNeighborsClassifier(n_neighbors3), n_features_to_select3, directionbackward, ) sfs.fit(X, y) sfs.get_support() # 布尔掩码 sfs.transform(X).shape # (150, 3)主要参数_sequential.pyn_features_to_selectauto、整数或(0,1)浮点数。auto时若指定了tol则按容忍度停止否则默认选一半特征tol连续两次增删特征后得分提升不足tol即停止后向时可取负值仅在n_features_to_selectauto时生效可用来在“牺牲少量得分”的前提下大幅减少特征directionforward/backwardscoring交叉验证评分默认使用估计器自身的默认评估准则cv默认 5 折整数/None时分类任务自动用StratifiedKFold其余用KFoldn_jobs评估候选特征时跨折并行。几个从源码与文档中可以确认的关键认知前后向结果通常不等价而且两者的速度可能相差很大——若有 10 个特征要选 7 个前向需要 7 轮后向只需 3 轮_sequential.py 对应实现逻辑见文档说明不需要重要性属性这是 SFS 相对RFE/SelectFromModel的核心优势任何带fit的估计器都能用计算开销更大以后向为例从 m 个特征降到 m-1 个特征的迭代k 折交叉验证需要拟合m * k个模型而RFE每轮只需一次拟合SelectFromModel永远只拟合一次、无需迭代。因此 SFS 更适合特征维度适中、模型单次训练较快的场景。六、把特征选择嵌入 Pipeline特征选择通常作为学习之前的预处理步骤使用scikit-learn 推荐的做法是封装进 Pipeline保证“选择”与“建模”共享同一份数据切分避免信息泄漏from sklearn.pipeline import Pipeline from sklearn.feature_selection import SelectFromModel from sklearn.svm import LinearSVC from sklearn.ensemble import RandomForestClassifier clf Pipeline([ (feature_selection, SelectFromModel(LinearSVC(penaltyl1))), (classification, RandomForestClassifier()), ]) clf.fit(X, y)这里先用带 L1 惩罚的LinearSVC评估特征重要性并通过SelectFromModel选出相关特征再让RandomForestClassifier只基于变换后的特征子集训练。同样的模式适用于前文提到的所有特征选择方法以及任何能提供特征重要性评估的分类器。另外SelectFromModel的importance_getter也可以指向 Pipeline 内部步骤的属性如named_steps.clf.feature_importances_使其能在嵌套场景下正确取到重要性。总结如何选型结合文档与源码可以给出如下选型建议极大规模、低质特征先用VarianceThreshold做基线清洗零方差/近零方差特征几乎不可能有信息量且会拖慢后续步骤维度极高、要求可解释优先单变量选择SelectKBest/SelectPercentilef_classif/mutual_info_*/chi2速度快、无模型耦合追求最优特征子集、模型暴露了重要性用RFE/RFECV有明确的特征排名输出或SelectFromModel阈值/max_features灵活可控L1 模型和树模型尤其常用模型不暴露重要性属性用SequentialFeatureSelector按交叉验证得分贪心搜索但要为更高的拟合次数做好预算需要防泄漏与端到端调参一律放入Pipeline配合GridSearchCV可同时搜索特征选择器与分类器的超参数。【免费下载链接】scikit-learnscikit-learn: machine learning in Python项目地址: https://gitcode.com/gh_mirrors/sc/scikit-learn创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考