sklearn.feature_selection
模块中的类可用于对样本集进行特征选择/降维,以提高估计器的准确性或在高维数据集上提高性能。
去除低方差的特征
VarianceThreshold
是一种简单的基准方法用于特征选择。它会删除所有方差不满足某个阈值的特征。默认情况下,它会删除所有方差为零的特征,即在所有样本中具有相同值的特征。
例如,假设我们有一个具有布尔特征的数据集,并且我们想要删除在超过80%的样本中为1或0(开或关)的所有特征。布尔特征是伯努利随机变量,其方差由以下公式给出:
V a r [ X ] = p ( 1 − p ) \mathrm{Var}[X] = p(1 - p) 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]])
正如预期的那样,VarianceThreshold
已删除了第一列,该列具有概率
p
=
5
/
6
>
.
8
p = 5/6 > .8
p=5/6>.8 包含零的特征。
单变量特征选择
单变量特征选择通过基于单变量统计测试选择最佳特征。它可以看作是估计器的预处理步骤。Scikit-learn 将特征选择例程作为实现 transform
方法的对象公开:
SelectKBest
保留得分最高的 k k k 个特征SelectPercentile
保留得分最高的用户指定百分比的特征- 使用常见的单变量统计测试对每个特征进行选择:假阳性率
SelectFpr
,假发现率SelectFdr
或家族误差SelectFwe
。 GenericUnivariateSelect
允许使用可配置的策略进行单变量特征选择。这允许使用超参数搜索估计器选择最佳的单变量选择策略。
例如,我们可以使用 F 检验来检索数据集的两个最佳特征,如下所示:
>>> 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_y=True)
>>> X.shape
(150, 4)
>>> X_new = SelectKBest(f_classif, k=2).fit_transform(X, y)
>>> X_new.shape
(150, 2)
这些对象以输入形式接受一个返回单变量得分和 p 值(或仅对于 SelectKBest
和 SelectPercentile
的得分)的评分函数:
- 对于回归:
r_regression
,f_regression
,mutual_info_regression
- 对于分类:
chi2
,f_classif
,mutual_info_classif
基于 F 检验的方法估计了两个随机变量之间的线性依赖程度。另一方面,互信息方法可以捕捉任何类型的统计依赖关系,但由于是非参数的,它们需要更多的样本才能进行准确估计。注意, χ 2 \chi^2 χ2-检验只能应用于非负特征,例如频率。
使用稀疏数据进行特征选择
如果使用稀疏数据(即表示为稀疏矩阵的数据),chi2
,mutual_info_regression
,mutual_info_classif
将处理数据而不使其变为稠密。
[!WARNING]
注意不要在分类问题中使用回归评分函数,否则将得到无用的结果。
[!NOTE]
SelectPercentile
和SelectKBest
也支持无监督特征选择。需要提供一个 y=None 的 score_func。score_func 应在内部使用 X 计算得分。
示例:
sphx_glr_auto_examples_feature_selection_plot_feature_selection.py
sphx_glr_auto_examples_feature_selection_plot_f_test_vs_mi.py
递归特征消除
给定一个为特征分配权重的外部估计器(例如线性模型的系数),递归特征消除(RFE
)的目标是通过逐渐考虑越来越小的特征集来选择特征。首先,估计器在初始特征集上进行训练,并通过任何特定属性(例如 coef_
,feature_importances_
)或可调用函数获得每个特征的重要性。然后,将最不重要的特征从当前特征集中剪枝。该过程在修剪后的集合上递归重复,直到最终达到所需的特征数。
RFECV
在交叉验证循环中执行 RFE,以找到最佳特征数。更详细地说,通过在不同的交叉验证拆分上拟合 RFE
选择器来自动调整选择的特征数(由 cv 参数提供)。使用评分器评估 RFE
选择器的性能,评分器使用不同数量的选择特征,并将其聚合在一起。最后,得分在交叉验证折叠中平均,并将选择的特征数设置为最大化交叉验证得分的特征数。
示例:
sphx_glr_auto_examples_feature_selection_plot_rfe_digits.py
:一个递归特征消除的示例,展示了在数字分类任务中像素的相关性。sphx_glr_auto_examples_feature_selection_plot_rfe_with_cross_validation.py
:一个带有交叉验证的递归特征消除示例,自动调整选择的特征数。
使用 SelectFromModel 进行特征选择
SelectFromModel
是一种元转换器,可与通过特定属性(例如 coef_
,feature_importances_
)或在拟合后通过 importance_getter 可调用函数为每个特征分配重要性的任何估计器一起使用。如果特征值的相应重要性低于提供的 threshold
参数,则认为这些特征不重要并将其删除。除了通过数值方式指定阈值外,还可以使用字符串参数来查找阈值的内置启发式方法。可用的启发式方法有 “mean”、“median” 和这些的浮点倍数,如 “0.1*mean”。结合阈值条件,可以使用 max_features 参数来设置选择的特征数的限制。
有关如何使用的示例,请参见下面的部分。
示例
sphx_glr_auto_examples_feature_selection_plot_select_from_model_diabetes.py
基于 L1 的特征选择
sklearn
使用 L1 范数进行惩罚的线性模型具有稀疏解:它们的估计系数中有很多为零。当目标是减少数据的维度以便与另一个分类器一起使用时,可以将它们与 ~feature_selection.SelectFromModel
一起使用以选择非零系数。特别地,用于回归的稀疏估计器有 ~linear_model.Lasso
,用于分类的有 ~linear_model.LogisticRegression
和 ~svm.LinearSVC
:
>>> from sklearn.svm import LinearSVC
>>> from sklearn.datasets import load_iris
>>> from sklearn.feature_selection import SelectFromModel
>>> X, y = load_iris(return_X_y=True)
>>> X.shape
(150, 4)
>>> lsvc = LinearSVC(C=0.01, penalty="l1", dual=False).fit(X, y)
>>> model = SelectFromModel(lsvc, prefit=True)
>>> X_new = model.transform(X)
>>> X_new.shape
(150, 3)
对于 SVM 和逻辑回归,参数 C 控制稀疏性:C 越小,选择的特征越少。对于 Lasso,alpha 参数越高,选择的特征越少。
示例:
sphx_glr_auto_examples_linear_model_plot_lasso_dense_vs_sparse_data.py
。
L1 恢复和压缩感知
选择非零系数恢复的 alpha 参数没有通用规则。可以通过交叉验证(~sklearn.linear_model.LassoCV
或 ~sklearn.linear_model.LassoLarsCV
)来设置,但这可能会导致未惩罚的模型:包含少量不相关的变量对预测得分没有不利影响。相反,BIC(~sklearn.linear_model.LassoLarsIC
)倾向于设置较高的 alpha 值。
参考文献
Richard G. Baraniuk “Compressive Sensing”, IEEE Signal Processing Magazine [120] July 2007 http://users.isr.ist.utl.pt/~aguiar/CS_notes.pdf
基于树的特征选择
可以使用基于树的估计器(参见 sklearn.tree
模块和 sklearn.ensemble
模块中的树集合)来计算基于不纯度的特征重要性,然后可以与 ~feature_selection.SelectFromModel
元转换器一起用于丢弃不相关的特征:
from sklearn.ensemble import ExtraTreesClassifier
from sklearn.datasets import load_iris
from sklearn.feature_selection import SelectFromModel
X, y = load_iris(return_X_y=True)
X.shape
# (150, 4)
clf = ExtraTreesClassifier(n_estimators=50)
clf = clf.fit(X, y)
clf.feature_importances_
# array([ 0.04..., 0.05..., 0.4..., 0.4...])
model = SelectFromModel(clf, prefit=True)
X_new = model.transform(X)
X_new.shape
# (150, 2)
示例:
sphx_glr_auto_examples_ensemble_plot_forest_importances.py
:使用合成数据的示例,展示了实际有意义特征的恢复。sphx_glr_auto_examples_ensemble_plot_forest_importances_faces.py
:使用人脸识别数据的示例。
顺序特征选择
顺序特征选择(Sequential Feature Selection,[sfs]())可在 ~sklearn.feature_selection.SequentialFeatureSelector
转换器中使用。SFS 可以是前向或后向的:
前向 SFS 是一种贪婪的过程,它迭代地找到要添加到所选特征集的最佳新特征。具体来说,我们最初从零特征开始,并找到在此单个特征上训练估计器时最大化交叉验证得分的特征。选择了第一个特征后,我们通过将新特征添加到所选特征集中重复该过程。当达到所需的所选特征数量时,由 n_features_to_select 参数确定,该过程停止。
后向 SFS 遵循相同的思路,但工作方向相反:我们从所有特征开始,并贪婪地从集合中删除特征。direction 参数控制使用前向还是后向 SFS。
关于顺序特征选择的详细信息
一般来说,前向和后向选择不会产生等效的结果。此外,根据所需的所选特征数量,其中一个可能比另一个快得多:如果我们有 10 个特征并要求选择 7 个特征,前向选择需要执行 7 次迭代,而后向选择只需要执行 3 次。
SFS 与 ~sklearn.feature_selection.RFE
和 ~sklearn.feature_selection.SelectFromModel
的不同之处在于,它不需要底层模型公开 coef_ 或 feature_importances_ 属性。然而,考虑到需要评估更多的模型,与其他方法相比,它可能会更慢。例如,在后向选择中,从 m 个特征到 m - 1 个特征的迭代使用 k 折交叉验证需要拟合 m * k 个模型,而 ~sklearn.feature_selection.RFE
只需要进行一次拟合,~sklearn.feature_selection.SelectFromModel
只需进行一次拟合,无需迭代。
参考文献
示例
sphx_glr_auto_examples_feature_selection_plot_select_from_model_diabetes.py
特征选择作为流水线的一部分
特征选择通常用作实际学习之前的预处理步骤。在 scikit-learn 中推荐的方法是使用 ~pipeline.Pipeline
:
clf = Pipeline([
('feature_selection', SelectFromModel(LinearSVC(dual="auto", penalty="l1"))),
('classification', RandomForestClassifier())
])
clf.fit(X, y)
在这个片段中,我们使用 ~svm.LinearSVC
结合 ~feature_selection.SelectFromModel
来评估特征重要性并选择最相关的特征。然后,使用转换后的输出训练 ~ensemble.RandomForestClassifier
,即仅使用相关特征。您可以使用其他特征选择方法和提供评估特征重要性方式的分类器执行类似的操作。有关更多详细信息,请参阅 ~pipeline.Pipeline
示例。