机器学习之特征选择方法
特征选择是一个重要的数据预处理过程,在现实机器学习任务中,获得数据之后通常先进行特征选择,此后在训练学习器,如下图所示:
进行特征选择有两个很重要的原因:
- 避免维数灾难:能剔除不相关(irrelevant)或冗余(redundant )的特征,从而达到减少特征个数,提高模型精确度,减少运行时间的目的
- 降低学习任务的难度:选取出真正相关的特征简化模型,协助理解数据产生的过程
如流程图所示,特征选择包括两个环节:
-
子集搜索 (subset search)
-
子集评价 (subset evaluation)
《机器学习》将特征选择分为了三种方法:分别是过滤式(filter) 、包裹式(wrapper)和嵌入式(embedded)。下面依据sklearn中的特征选择文档来叙述特征选择的几个方法。
过滤式(filter)
这类方法先对数据机进行特征选择,然后再训练学习器,特征选择的过程与后续学习器无关。
方差选择法
计算各个特征的方差,然后根据阈值选择方差大于阈值的特征,或者指定待选择的特征数 k,然后选择 k 个最大方差的特征。
相关系数法
计算各个特征对目标值的相关系数及相关系数的 P 值。
相关系数:如何通俗易懂地解释「协方差」与「相关系数」的概念?知乎这篇回答浅显易懂地介绍了协方差以及相关系数,十分推荐。
在机器学习中我们一般采用皮尔逊相关系数来测量两个序列的线性关系,也就是说皮尔逊相关系数只能检测出线性关系,那么对于分类问题的适用性就远低于回归问题,因此相关系数法常用于回归问题。
卡方检验法
检验定性自变量对定性因变量的相关性。关于卡方检验的介绍可参考这篇文章 卡方检验原理及应用。需要注意的是,卡方检验适用于分类问题。卡方值越大,表明特征与预测结果的相关性也越大,同时 p 值也相应较小,因此我们优先选择卡方值大的特征。
互信息法
互信息法与卡方检验法相同,都是评价定性自变量对定性因变量的相关性。互信息用以计算两个特征或自变量与因变量之间所共有的信息。
【区别】:
相关性:与相关性不同,互信息计算的不是数据序列,而是数据的分布,因此互信息可以用于检测特征间的非线性关系。
基于模型的特征排序
该方法的思路同包裹式选择,直接使用后续要用的机器学习算法,针对每个单独的特征和预测值建立预测模型。
【步骤】:
-
-
- 判断特征和预测值之间的关系,若为线性则考虑线性算法;若为非线性,则考虑非线性算法,例如基于树模型的方法;
- 单独采用每个特征进行建模,并进行交叉验证;
- 选择指定个数评分最高的特征,组成特征子集。
-
Relief
Relief(Relevant Features)是一种著名的过滤式特征选择方法,该方法设计了一个“相关统计量”来度量特征的重要性。该统计量是一个向量,其每个分量分别对应于一个初始特征,而特征子集的重要性则是由子集中每个特征所对应的相关统计量分量之和来决定。
【选择方式】:
-
-
- 指定一个阈值 r,然后选择比 r 大的相关统计量分量所对应的特征即可;
- 指定要选取的特征个数 k,然后选择相关统计量分量最大的 k 个特征。
-
卡方检验具体可参考这篇博客:卡方分布与卡方检验
包裹式(wrapper)
这类方法选择直接把最终将要使用学习期的性能作为特征子集的评价准则。
递归式特征消除(RFE)
给定一个外部的估计器,该估计起对特征赋予一定的权重(比如,线性模型的系数),recursive feature elimination ( RFE )
通过处理越来越少的特征集合来递归的选择特征。 首先,评估器在初始的特征集合上面进行训练并且每一个特征的重要程度是通过一个诸如sklearn里的 coef_
属性 或者 feature_importances_
属性来获得。 然后,从当前的特征集合中移除最不重要的特征。在特征集合上不断的重复递归这个步骤,直到最终达到所需要的特征数量为止。
下列代码使用RFE
抽取5个最informative的特征:
>>> from sklearn.datasets import make_friedman1
>>> from sklearn.feature_selection import RFE
>>> from sklearn.svm import SVR
>>> X, y = make_friedman1(n_samples=50, n_features=10, random_state=0)
>>> estimator = SVR(kernel="linear")
>>> selector = RFE(estimator, 5, step=1)
>>> selector = selector.fit(X, y)
>>> selector.support_
array([ True, True, True, True, True,
False, False, False, False, False], dtype=bool)
>>> selector.ranking_
array([1, 1, 1, 1, 1, 6, 4, 3, 2, 5])
从最终的学习器性能来看,包裹式特征选择比过滤式特征选择更好。但是另一方面,由于在特征选择过程中需多次训练学习期,因此包裹式特征选择的计算开销通常要大得多
嵌入式(embedded)
SelectFromModel
选取特征
sklearn.feature_selection.SelectFromModel(estimator, threshold=None, prefit=False, norm_order=1)
SelectFromModel
是一个 meta-transformer(元转换器) ,它可以用来处理任何带有 coef_
或者 feature_importances_
属性的训练之后的评估器。 如果相关的coef_
或者 feature_importances_
属性值低于预先设置的阈值,这些特征将会被认为不重要并且移除掉。除了指定数值上的阈值之外,还可以通过给定字符串参数来使用内置的启发式方法找到一个合适的阈值。可以使用的启发式方法有 mean 、 median 以及使用浮点数乘以这些(例如,0.1*mean)
Linear models 使用 L1 正则化的线性模型会得到稀疏解:他们的许多系数为 0。 当目标是降低使用另一个分类器的数据集的维度, 它们可以与 feature_selection.SelectFromModel
一起使用来选择非零系数。
>>> from sklearn.svm import LinearSVC
>>> from sklearn.datasets import load_iris
>>> from sklearn.feature_selection import SelectFromModel
>>> iris = load_iris()
>>> X, y = iris.data, iris.target
>>> 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)
其实用包装好的库看不出嵌入式的两者兼顾,实际上在fit后,得到coef的过程中,相当于已经做出了特征选择。
另外,基于树的 estimators 也可以用来计算特征的重要性,然后可以消除不相关的特征(当与 sklearn.feature_selection.SelectFromModel 等元转换器一同使用时)
以下是一个使用随机森林进行特征选择的例子:
from sklearn.ensemble import RandomForestClassifier
feat_labels = df_wine.columns[1:]
forest = RandomForestClassifier(n_estimators=500,
random_state=1)
forest.fit(X_train, y_train)
importances = forest.feature_importances_
indices = np.argsort(importances)[::-1]
for f in range(X_train.shape[1]):
print("%2d) %-*s %f" % (f + 1, 30,
feat_labels[indices[f]],
importances[indices[f]]))