Skip to content

1.13. 特征选择(Feature selection)

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

1.13.1. 去除方差比较低的特征

VarianceThreshold是特征选择的一种简单的基准方法。它删除方差未达到某个阈值的所有样本。默认情况下,它将删除所有零方差特征(zero-variance features),即在所有样本中具有相同值的特征。

举例来说,假设我们有一个具有布尔特征的数据集,并且我们希望删除所有在超过80%的样本中取值都为1或0(on or off)的特征。布尔特征是伯努利随机变量,这些变量的方差由下式给出: $$ \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已经将第一列删除了,因为第一列包含0值的概率(样本比例)是$p = 5/6 >.8$,超过了给定的阈值0.8。

1.13.2. 单变量特征选择

单变量特征选择是通过选择那些基于单变量统计检验(univariate statistical tests)得出的最优特征来实现的。它可以看作是估计器的一个预处理步骤。Scikit-learn将一系列特征选择程序作为不同的类提供给我们,这些类都实现了 transform 方法:

  • SelectKBest 选择得分最高的$k$个特征,其余的特征都删除。
  • SelectPercentile 删除除用户指定的最高得分百分比以外的所有特征
  • 对每个特征使用通用的单变量统计检验:假正率(false positive rate)SelectFpr,伪发现率(false discovery rate) SelectFdr或多重比较谬误(family wise error)SelectFwe
  • GenericUnivariateSelect允许使用可配置的策略来执行单变量特征选择。这允许使用超参数搜索估计器(hyper-parameter search estimator)选择最佳的单变量选择策略。

例如,我们可以对样本执行一个$χ^2$测试来挑选出两个最好的特征

>>> from sklearn.datasets import load_iris
>>> from sklearn.feature_selection import SelectKBest
>>> from sklearn.feature_selection import chi2
>>> X, y = load_iris(return_X_y=True)
>>> X.shape
(150, 4)
>>> X_new = SelectKBest(chi2, k=2).fit_transform(X, y)
>>> X_new.shape
(150, 2)

这些对象把输入当做计分函数,返回单变量分数(univariate scores)和p值(或对于SelectKBestSelectPercentile来说,仅仅返回分数):

基于F-检验的方法可以估计两个随机变量之间的线性依赖度(linear dependency)。另一方面,基于互信息(mutual information)的方法可以捕捉任何类型的统计依赖性(statistical dependency),但由于互信息方法是无参数的,他们需要更多的样本才能进行准确的估计。

稀疏数据的特征选择

如果使用的是稀疏数据(即,表示为稀疏矩阵的数据)则mutual_info_regressionchi2mutual_info_classif可以在不用把数据变为稠密矩阵的前提下使用这些稀疏矩阵。

警告:

小心不要把回归评分函数用在分类问题上,您将会得到无意义的结果。

案例:

1.13.3. 递归特征消除

如果给定一个可以对特征向量赋予对应权重向量(比如线性模型的相关系数)的外部估计器,RFE(ecursive feature elimination)就可以通过递归地考虑越来越小的特征集合来选择特征。 首先,估计器在初始的特征集合上训练并且每一个特征的重要程度是通过一个coef_ 属性或者feature_importances_属性来获得。 然后,从当前的特征集合中移除最不重要的特征。在特征集合上不断的重复递归这个步骤,直到最终达到所需要的特征数量为止。RFECV在一个交叉验证的循环中执行 RFE 来找到最优的特征数量。

RFECV在一个交叉验证的循环中执行RFE来找到最优的特征数量。

案例:

1.13.4. 使用SelectFromModel来选取特征

SelectFromModel是一个元转换器(meta-transformer),可以和任意拟合后具有属性 coef_feature_importances_的估计器一起使用。如果与某个特征对应的coef_feature_importances_的值小于某个给定的阈值参数threshold,则认为该特征是不重要的,应该被去除。除了以数字的方式指定阈值,还有一些内建的启发式方法可以用来寻找合适的阈值,这些方法用一个字符串做为参数来指定具体的启发式策略。现在可用的启发式策略有 “mean”, “median” 以及用浮点数乘以字符串的方式,比如 “0.1*mean”。

关于该类的具体使用方法的案例,请参阅以下各章节。

案例:

1.13.4.1. 基于 L1 的特征选取

用 L1范数的线性模型可以获得稀疏解:用 L1范数估计出的模型的很多系数都是0。当我们的目标是使用另一个分类器对数据进行维数约简的时候,这样的分类器就可以和类feature_selection.SelectFromModel 一起使用来选择非零系数。特别地,可以用做这种用途的稀疏估计器(sparse estimators)有这些: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和logistic回归,参数 C 是用来控制稀疏性的:越小的 C 会导致越少的特征被选择。在Lasso中,参数 alpha 的值越大,越少的特征会被选择。

案例:

L1恢复(L1-recovery)和压缩感知(compressive sensing)

当选择了正确的 alpha 值以后,Lasso可以仅通过少量观察点便完整的恢复准确的非零变量集合,假设特定的条件可以被满足的话。特别的,数据量需要 “足够大(sufficiently large)” ,不然L1模型的表现将充满不确定性。“足够大” 的定义取决于非零系数的个数、特征数量的对数值、噪音的数量、非零系数的最小绝对值、以及设计矩阵(design maxtrix) X 的结构。另外,设计矩阵必须有某些特定的性质,如数据不能过度相关。

关于如何选择 alpha 的值来恢复非零系数并没有通用的规则。alpha值可以通过交叉验证来确定 (LassoCVLassoLarsCV),尽管这可能会导致欠惩罚的模型:包括少量的无关变量对于预测值来说并非致命的。相反的,BIC( LassoLarsIC ) 倾向于给定高 alpha 值。

参考文献 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模块中的由树构成的森林那一章节)可用于计算特征重要性,而特征重要性又可用于丢弃不相关的特征(与预测不相关的特征值)(当与sklearn.feature_selection.SelectFromModel元转换器(meta-transformer)结合使用时):

>>> 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. 把特征选择作为管道(pipeline)的一部分

在进行实际训练之前,通常会将特征选择作为预处理步骤。在scikit-learn中,推荐使用sklearn.pipeline.Pipeline管道类来执行此操作:

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

在上面的代码段中,我们将sklearn.svm.LinearSVCsklearn.feature_selection.SelectFromModel结合使用来评估特征的重要性并选择最相关的特征。然后sklearn.ensemble.RandomForestClassifier就紧跟在特征选择的输出端接收数据进行训练(既,随机森林分类器只在最相关的特征上训练)。在上面的代码段中你可以选择其他的特征选择器类,也可以选择别的分类器进行特征重要性评估。请参考sklearn.pipeline.Pipeline类的更多案例。

©2007-2019,scikit-learn开发人员(BSD许可证)。 显示此页面源码