1.13. 特征选择#

sklearn.feature_selection 模块中的类可用于样本集的特征选择/降维,既能提高估计器的准确率分数,也能提升其在超高维数据集上的性能。

1.13.1. 移除低方差特征#

VarianceThreshold 是一种简单的特征选择基准方法。它会移除所有方差未达到阈值的特征。默认情况下,它会移除所有零方差特征,即在所有样本中具有相同值的特征。

举个例子,假设我们有一个包含布尔特征的数据集,我们想要移除所有在超过 80% 的样本中均为 1 或 0(开启或关闭)的特征。布尔特征是伯努利随机变量,此类变量的方差公式为

\[\mathrm{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\)

1.13.2. 单变量特征选择#

单变量特征选择通过单变量统计检验来选择最佳特征。它可以被视为估计器的一个预处理步骤。Scikit-learn 将特征选择程序暴露为实现 transform 方法的对象

  • SelectKBest 移除除得分最高的 \(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 值(或者对于 SelectKBestSelectPercentile 仅返回得分)的评分函数作为输入

基于 F 检验的方法估计两个随机变量之间的线性相关程度。另一方面,互信息方法可以捕获任何类型的统计依赖关系,但由于是非参数化的,它们需要更多的样本才能进行准确估计。请注意,\(\chi^2\) 检验只能应用于非负特征,例如频率。

警告

注意不要将回归评分函数用于分类问题,否则会得到无用的结果。

注意

SelectPercentileSelectKBest 也支持无监督特征选择。您需要提供一个 y=Nonescore_func。该 score_func 应在内部使用 X 来计算得分。

示例

1.13.3. 递归特征消除#

给定一个为特征分配权重的外部估计器(例如线性模型的系数),递归特征消除(RFE)的目标是通过递归考虑越来越小的特征集来选择特征。首先,在初始特征集上训练估计器,并通过特定的属性(如 coef_, feature_importances_)或可调用对象获得每个特征的重要性。然后,从当前特征集中修剪掉最不重要的特征。该过程在修剪后的集合上递归重复,直到最终达到所需的特征数量。

RFECV 在交叉验证循环中执行 RFE,以找到最佳特征数量。更详细地说,通过在不同的交叉验证拆分(由 cv 参数提供)上拟合 RFE 选择器来自动调整选择的特征数量。RFE 选择器的性能是使用 scorer 针对不同数量的选中特征进行评估并聚合在一起的。最后,对各折得分进行平均,并将最终选定的特征数量设置为使交叉验证得分最大化的特征数量。

示例

1.13.4. 使用 SelectFromModel 进行特征选择#

SelectFromModel 是一种元转换器,可以与任何在拟合后通过特定属性(如 coef_, feature_importances_)或通过 importance_getter 可调用对象为每个特征分配重要性的估计器一起使用。如果特征值的对应重要性低于提供的 threshold 参数,则认为这些特征不重要并将其移除。除了以数值方式指定阈值外,还可以使用字符串参数通过内置启发式方法找到阈值。可用的启发式方法包括 “mean”、“median” 以及诸如 “0.1*mean” 之类的浮点乘法。结合 threshold 标准,可以使用 max_features 参数来限制要选择的特征数量。

有关其使用方法,请参考以下各节。

示例

1.13.4.1. 基于 L1 的特征选择#

线性模型 若使用 L1 范数惩罚会产生稀疏解:其许多估计系数为零。当目标是将数据维度降低以供其他分类器使用时,它们可以与 SelectFromModel 结合使用以选择非零系数。特别是,适用于此目的的稀疏估计器包括用于回归的 Lasso,以及用于分类的 LogisticRegressionLinearSVC

>>> 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 参数越高,选择的特征越少。

示例

L1 恢复与压缩感知#

对于 alpha 的良好选择,Lasso 可以在满足特定条件的情况下,仅使用少量观测值就能完全恢复非零变量的精确集合。特别地,样本数量应当“足够大”,否则 L1 模型将随机执行。其中“足够大”取决于非零系数的数量、特征数量的对数、噪声量、非零系数的最小绝对值以及设计矩阵 X 的结构。此外,设计矩阵必须表现出某些特定属性,例如不能过度相关。关于 Lasso 在稀疏信号恢复中的应用,请参阅以下压缩感知示例:压缩感知:使用 L1 先验(Lasso)进行层析成像重建

对于恢复非零系数,选择 alpha 参数没有通用规则。它可以由交叉验证(LassoCVLassoLarsCV)设置,尽管这可能导致模型惩罚不足:包含少量不相关的变量对预测分数并无害处。相反,BIC(LassoLarsIC)倾向于设置较高的 alpha 值。

References

Richard G. Baraniuk “Compressive Sensing”, IEEE Signal Processing Magazine [120] July 2007 http://users.isr.ist.utl.pt/~aguiar/CS_notes.pdf

1.13.4.2. 基于树的特征选择#

基于树的估计器(请参阅 sklearn.tree 模块以及 sklearn.ensemble 模块中的树森林)可用于计算基于杂质(impurity-based)的特征重要性,进而可以用于丢弃不相关特征(当与 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)

示例

1.13.5. 序列特征选择#

序列特征选择 [sfs] (SFS) 在 SequentialFeatureSelector 转换器中可用。SFS 可以是向前或向后的。

向前 SFS 是一种贪婪程序,它迭代地找到添加到所选特征集中的最佳新特征。具体而言,我们最初从零特征开始,找到当估计器在该单个特征上训练时最大化交叉验证得分的那个特征。一旦选择了第一个特征,我们就重复该过程,向所选特征集中添加新特征。该程序在达到由 n_features_to_select 参数确定的所需特征数量时停止。

向后 SFS 遵循相同的思路但在相反的方向上工作:我们从*所有*特征开始,贪婪地从集合中*移除*特征,而不是从零特征开始并贪婪地添加特征。direction 参数控制使用向前还是向后 SFS。

序列特征选择的详细信息#

通常,向前和向后选择不会产生等效的结果。此外,根据所请求的选中特征数量,一种方法可能比另一种快得多:如果我们有 10 个特征并要求选中 7 个,向前选择需要执行 7 次迭代,而向后选择仅需执行 3 次。

SFS 与 RFESelectFromModel 的区别在于,它不需要底层模型暴露 coef_feature_importances_ 属性。然而,考虑到需要评估更多的模型,它可能比其他方法更慢。例如,在向后选择中,使用 k 折交叉验证从 m 个特征减少到 m - 1 个特征的迭代需要拟合 m * k 个模型,而 RFE 只需要单次拟合,SelectFromModel 总是只做单次拟合且不需要任何迭代。

References

示例

1.13.6. 作为流水线(Pipeline)一部分的特征选择#

特征选择通常用作实际学习之前的预处理步骤。在 scikit-learn 中推荐的做法是使用 Pipeline

clf = Pipeline([
  ('feature_selection', SelectFromModel(LinearSVC(penalty="l1"))),
  ('classification', RandomForestClassifier())
])
clf.fit(X, y)

在这个代码片段中,我们利用 LinearSVC 结合 SelectFromModel 来评估特征重要性并选择最相关的特征。然后,在转换后的输出(即仅使用相关特征)上训练 RandomForestClassifier。当然,您可以使用其他特征选择方法以及提供特征重要性评估方式的分类器执行类似操作。有关更多详细信息,请参阅 Pipeline 示例。