特征选择的原因:
- 降低维度,减少特征数量,使得模型的泛化能力增加,减少过拟合
- 加强对特征的理解,知道特征对模型的影响是怎样,大白话说就是从很多证据中找出最关键的信息
特征工程是机器学习两大玄学之一。具体问题要具体分析。
Caution
- 特征选择的过程必须确保不丢失重要的特征,否则在后续的学习过程中会因为重要特征的缺失而导致无法获得好的机器学习的效果
- 给定数据集,如果学习任务不一样,那么相关特征很可能不一样,所以特征选择中所谓的无关特征指的是当前学习任务无关。
特征选择
特征选择时首先要去除冗余特征。
它是由其他其他的特征中推演出来的。比如,一个球的体积,那么半径这个特征就是冗余的,因为我们可以由球的体积推算半径。冗余特征在很多时候都是不起作用的。
filter过滤法
过滤法通常用于预处理的步骤,而且它选择特征独立于机器学习算法,它是通过相关统计量来进行筛选
方差过滤VarianceThreshold
通过特征的方差来筛选特征的。比如一个特征的方差很小,则表示这个样本在特征上是基本没什么差异的,可能特征中有很多样本的该特征的值都是一样的,甚至该特征的所有值都是一样的。那么这个特征对于样本区分没有什么作用。所以我们的方差过滤就是优先消除方差为0的特征。
VarianceThreshold有个重要参数threshold,表示方差的阈值。低于阈值的特征会被舍弃。
from sklearn.feature_selection import VarianceThreshold
selector = VarianceThreshold(threshold=0)
X = selector.fit_transform(X)
相关性过滤
方差过滤完成后就要考虑特征之间是否有相关性了。
卡方过滤
卡方过滤是专门针对离散型标签的相关型过滤。借此我们可以除去最有可能独立于标签的特征。
from sklearn.ensemble import RandomForestClassifier as RFC
from sklearn.model_selection import cross_val_score
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import chi2
#假设我需要20个特征
X_chi = SelectKBest(chi2, k=20).fit_transform(X, y)
X_chi.shape
选取超参数k
我该选择保留多少个特征呢?
我们不能拿着模型去跑这些特征,往往数据量大,模型复杂时,是很难跑完的。所以希望最开始就能够选择一个最优的超参数k。那第一个方法,就是我们之前提过的学习曲线,利用随机森林来试。
%matplotlib inline
import matplotlib.pyplot as plt
score = []
for i in range(380,180,-20):
X_fschi = SelectKBest(chi2, k=i).fit_transform(X_fsvar, y)
once = cross_val_score(RFC(n_estimators=10,random_state=0),X_fschi,y,cv=4).mean()
score.append(once)
plt.plot(range(380,180,-20),score)
plt.show()
通过这条曲线,我们可以观察到,随着K值的不断增加,模型的表现不断上升,这说明,K越大越好,数据中所有的特征都是与标签相关的。但是运行这条曲线的时间同样也是非常地长,接下来我们就来介绍一种更好的选择k的方 法:看p值选择k。 卡方检验的本质是推测两组数据之间的差异,其检验的原假设是”两组数据是相互独立的”。卡方检验返回卡方值和 P值两个统计量,其中卡方值很难界定有效的范围,而p值,我们一般使用0.01或0.05作为显著性水平
chivalue, pvalues_chi = chi2(X_fsvar,y)
print(chivalue.shape)
print(pvalues_chi.shape)
print(pvalues_chi)
#k取多少?我们想要消除所有p值大于设定值,比如0.05或0.01的特征:
k = chivalue.shape[0] - (pvalues_chi > 0.05).sum()
k
#X_fschi = SelectKBest(chi2, k=填写具体的k).fit_transform(X_fsvar, y)
#cross_val_score(RFC(n_estimators=10,random_state=0),X_fschi,y,cv=5).mean()
算出每个特征的pvalue都为0
通过这条曲线,我们可以观察到,随着K值的不断增加,模型的表现不断上升,这说明,K越大越好,数据中所有的特征都是与标签相关的。但是运行这条曲线的时间同样也是非常地长,接下来我们就来介绍一种更好的选择k的方 法:看p值选择k。 卡方检验的本质是推测两组数据之间的差异,其检验的原假设是”两组数据是相互独立的”。卡方检验返回卡方值和 P值两个统计量,其中卡方值很难界定有效的范围,而p值,我们一般使用0.01或0.05作为显著性水平
p值 | 0.05< or 0.01< | >0.05 or >0.01 |
---|---|---|
相关性 | 相关 | 不相关 |
算出每个特征的pvalue都为0,则拒绝原假设接受备择假设,则特征与标签相关,算出k的值为392,每个特征都相关
F检验
F检验,方差齐性检验,是用来捕捉每个特征与标签之间的线性关系的过滤方法。它即可以做回归也 可以做分类。
- feature_selection.f_classif(F检验分类)用于标签是离散型变量的数据
- feature_selection.f_regression(F检验回 归)用于标签是连续型变量的数据。
和卡方检验一样,这两个类需要和类SelectKBest连用,并且也可以直接通过输出的统计量来判断我们到底要 设置K值。需要注意的是,F检验在数据服从正态分布时效果会非常稳定,因此如果使用F检验过滤,会先将数据转换成服从正态分布的方式。
F检验的本质是寻找两组数据之间的线性关系,其原假设是”数据不存在显著的线性关系“。它返回F值和p值两个统 计量。我们希望选取p值小于0.05或0.01的特征,这些特征与标签时显著线性相关的,而p值大于 0.05或0.01的特征则被我们认为是和标签没有显著线性关系的特征,应该被删除。以F检验的分类为例,我们继续 在数字数据集上来进行特征选择:
from sklearn.feature_selection import f_classif
F, f_pvalues = f_classif(X_fsvar,y)
k = F.shape[0] - (f_pvalues > 0.05).sum()
此时我们得出它的k为392,这表示所以的特征都是显著线性相关的
互信息法
互信息法是用来捕捉每个特征与标签之间的任意关系(包括线性和非线性关系)的过滤方法。
它包含两个类:
- feature_selection.mutual_info_classif(互信息分类)
- feature_selection.mutual_info_regression(互信息回归)
这两个类的用法和参数都和F检验一模一样。但是互信息法比F检验更强大,F检验只能够找出线性关系,而互信息法可以找出任意关系。
互信息法不返回p值或F值类似的统计量,它返回“每个特征与目标之间的互信息量的估计”,这个估计量在[0,1]之间取值,为0则表示两个变量独立,为1则表示两个变量完全相关。
from sklearn.feature_selection import mutual_info_classif as MIC
result = MIC(X, y)
k = result.shape[0] - sum(result <=0)
得出k值为多少,我的代码中得出k值为392,也就是说所有的特征的户型西量都大于0,因此所有特征都与标签相关
summary
无论是采用f检验还是卡方检验还是互信息法,只能说然筛选更加高效。当用这些方法无法把维度降下来时,那再进行删除特征只会减低数据的信息量,影响模型表现。
Embedded
嵌入式特征选择是将特征选择过程与学习器训练过程融合一体,两者在同一个优化过程中完成。使用嵌入法时,我们先使 用某些机器学习的算法和模型进行训练,得到各个特征的权值系数,根据权值系数从大到小选择特征。这些权值系 数往往代表了特征对于模型的某种贡献或某种重要性。例如决策树和随机森林中的feature_importances_属性就可以表示对模型的某种贡献或某种重要性。
由于考虑到特征对模型的贡献率,那么无关的特征(相关性为零的特征)或无区分度的特征(方差低的特征)都会过滤掉。
嵌入法缺点:
- 计算量大时,使用耗时的算法,费时费力
- 嵌入法中的权值系数没有范围可依据,当然权值系数为零的特征是对模型没有任何作用,但是有很多特征对模型有贡献,而且还贡献不一,那我们怎么去界定小于多少的权值系数才给去掉呢?这时这个threshold就成为我们的超参数需要去画学习曲线,或者是看模型只是的性质来判断这个超参数该为多少。
feature_selection.SelectFromModel
class sklearn.feature_selection.SelectFromModel (
estimator
,threshold=None
, prefit=False
, norm_order=1
, max_features=None)
SelectFromModel是一个元变换器,可以与任何在拟合后具有coef_,feature_importances_属性或参数中可选惩 罚项的评估器一起使用(比如随机森林和树模型就具有属性feature_importances_,逻辑回归就带有l1和l2惩罚 项,线性支持向量机也支持l2惩罚项)。
使用惩罚项的模型的嵌入法
对于有惩罚项的模型来说正则化惩罚项越大,特征在模型中的系数就会越小
l1正则化和l2正则化的区别
l1正则化惩罚力度越大,数据中某些特征会变成零
l2正则化惩罚力度越大,数据中某些特征会趋近零
参数 | 解释 |
---|---|
estimator | 评估器 |
threshold | 特征权值系数阈值,低于此的特征会被删除 |
prefit | 默认为False,如果为True要分别调用fit,transform,不能直接使用fit_transform |
norm_order | 在评估器coef属性高于一维时,用于过滤低于阈值的向量的范式阶数(正则化阶数) |
max_features | 在阈值设定下选择最大特征数。若要禁用阈值,只根据max_features来选择,需要将threshold设为-np.inf |
from sklearn.feature_selection import SelectFromModel
from sklearn.ensemble import RandomForestClassifier as RFC
estimator = RFC(n_estimators=100, random_state=0)
X_embedded = SelectFromModel(estimator=estimator
,threshold=0.002
).fit_transform(X,y)
# 0.002这个阈值应该能筛掉很多特征了,我这个数据集是手写字体数据,有780个特征,每个特征平均下来也就能分到0.001的feature_importances_
print(X_embedded.shape)
当然你也可以用学习曲线来看评估器在数据上的效果,这里就不画了。
Wrapper
filter过滤式的选择特征是不考虑后续学习器的,而Wrapper包装法选择特征的评价准则是要使用的学习器的性能。也就是说包装法的特征选取的目的就是给定学习器选择最有利其性能的“量身定制”的特征子集。
包装法在初始特征集上训练评估器,并且通过 coef_属性或通过feature_importances_属性获得每个特征的重要性
从学习器性能来看,wrapper包装法特征选择是要比filter好。但是wrapper在特征选择时需要多次训练学习器,因此包装式特征选择开销很大,远远大于filter.
然后,从当前的一组特征中修剪最不重要的 特征。在修剪的集合上递归地重复该过程,直到最终到达所需数量的要选择的特征。区别于过滤法和嵌入法的一次 训练解决所有问题,包装法要使用特征子集进行多次训练,因此它所需要的计算成本是最高的.
包装法的效果是所有特征选择方法中最利于提升模型 表现的,它可以使用很少的特征达到很优秀的效果。除此之外,在特征数目相同时,包装法和嵌入法的效果能够匹 敌,不过它比嵌入法算得更见缓慢,所以也不适用于太大型的数据。相比之下,包装法是最能保证模型效果的特征 选择方法。
如下是sklearn中wrapper方法的类
class sklearn.feature_selection.RFE (estimator, n_features_to_select=None, step=1, verbose=0)
参数:
estimator:实例化后的评估器
n_features_to_select:想要选择的特征个数
step:希望每次迭 代中移除的特征个数。
RFE类有两个很重要的属性:
support_:返回所有的特征的是否最后被选 中的布尔矩阵,
ranking_:返回特征的按数次迭代中综合重要性的排名。
类feature_selection.RFECV会在交叉 验证循环中执行RFE以找到最佳数量的特征,增加参数cv,其他用法都和RFE一模一样。
from sklearn.feature_selection import RFE
RFC_ = RFC(n_estimators =100, random_state=7)
selector = RFE(RFC_, n_features_to_select=200, step=30).fit(X, y) selector.support_.sum()
selector.ranking_
X_wrapper = selector.transform(X)
# 验证一下分数有多高
cross_val_score(RFC_,X_wrapper,y,cv=5).mean()
--------- 上述内容有来自各方大神的博客,加上西瓜书,数据挖掘导论 后个人糅合而成,不足之处多多指教---------