特征工程-特征选择

特征选择 feature_selection

  • 特征工程包括三大部分:

  • 特征提取(feature extraction):从文字,图像,声音等其他非结构化数据中提取新信息作为特征。比如说,从淘宝宝贝的名称中提取出产品类别,产品颜色,是否是网红产品等等。

  • 特征创造(feature creation):把现有特征进行组合,或互相计算,得到新的特征。比如说,我们有一列特征是速度,一列特征是距离,我们就可以通过让两列相处,创造新的特征:通过距离所花的时间。

  • 特征选择(feature selection):从所有的特征中,选择出有意义,对模型有帮助的特征,以避免必须将所有特征都导入模型去训练的情况。

  • 四种方法可以用来选择特征:过滤法,嵌入法,包装法,和降维算法

import pandas as pd
data = pd.read_csv(r"F:\计算机学习资料\机器学习b站菜菜\【机器学习】菜菜的sklearn课堂(1-12全课)\03数据预处理和特征工程\digit_recognizor.csv")
data.head()

在这里插入图片描述

x = data.iloc[:,1:]
y = data.iloc[:,0]
x.shape

在这里插入图片描述

一:Filter过滤法

  • 过滤方法通常用作预处理步骤,特征选择完全独立于任何机器学习算法。它是根据各种统计检验中的分数以及相关性的各项指标来选择特征
  • 全部特征》最佳特征子集》算法》模型评估

1.1、方差过滤

1.1.1、VarianceThreshold
  • 这是通过特征本身的方差来筛选特征的类。比如一个特征本身的方差很小,就表示样本在这个特征上基本没有差异,可能特征中的大多数值都一样,甚至整个特征的取值都相同,那这个特征对于样本区分没有什么作用。所以无论接下来的特征工程要做什么,都要优先消除方差为0的特征
  • VarianceThreshold有重要参数threshold,表示方差的阈值,表示舍弃所有方差小于threshold的特征,不填默认为0,即删除所有的记录都相同的特征
from sklearn.feature_selection import VarianceThreshold

selector = VarianceThreshold()  # 实例化,不填参数默认方差为0
x_var0 = selector.fit_transform(x)  # 获取删除不合格特征之后的特征矩阵
                # x_var0 = VarianceThreshold().fit_transform(x)
x_var0.shape

在这里插入图片描述

pd.DataFrame(x_var0).head()

在这里插入图片描述

  • 如果知道需要多少个特征,方差也可以将特征选择一步到位。
  • 比如说,希望留下一半的特征,那可以设定一个让特征总数减半的方差阈值,只要找到特征方差的中位数,再将这个中位数作为参数threshold的值输入就好
import numpy as np
# np.median(x.var().values)   # x.var() 返回Series数据  1352.286703180131
x_fsvar = VarianceThreshold(np.median(x.var().values)).fit_transform(x)
x_fsvar.shape

在这里插入图片描述

  • 若特征是伯努利随机变量(0,1),假设p=0.8,即二分类特征中某种分类占到80%以上的时候删除特征
X_bvar = VarianceThreshold(.8 * (1 - .8)).fit_transform(x)
X_bvar.shape

在这里插入图片描述

1.1.2、方差过滤对模型的影响
from sklearn.ensemble import RandomForestClassifier as RFC
from sklearn.neighbors import KNeighborsClassifier as KNN
from sklearn.model_selection import cross_val_score
import numpy as np

x = data.iloc[:,1:]    # 方差过滤前的特征矩阵
y = data.iloc[:,0]

x_fsvar = VarianceThreshold(np.median(x.var().values)).fit_transform(x)   # 方差过滤后的特征矩阵
  • KNN 方差过滤前
#======【TIME WARNING:35mins +】======#
cross_val_score(KNN(),x,y,cv=5).mean()    # 0.96585697

# python中的魔法命令,可以直接使用%%timeit来计算运行这个cell中的代码所需的时间
# 为了计算所需的时间,需要将这个cell中的代码运行很多次(通常是7次)后求平均值,因此运行%%timeit的时间会
# 远远超过cell中的代码单独运行的时间

#======【TIME WARNING:4 hours】======#
%%timeit
cross_val_score(KNN(),x,y,cv=5).mean()   
  • KNN 方差过滤后
#======【TIME WARNING:20 mins+】======#
cross_val_score(KNN(),x_fsvar,y,cv=5).mean()   # 0.96599974

#======【TIME WARNING:2 hours】======#
%%timeit
cross_val_score(KNN(),x,y,cv=5).mean()
  • 对于KNN,过滤后的效果十分明显:准确率稍有提升,但平均运行时间减少了10分钟,特征选择过后算法的效率上升了1/3
  • 随机森林方差过滤前
cross_val_score(RFC(n_estimators=10,random_state=0),x,y,cv=5).mean()   # 0.93800038

#======【TIME WARNING:2 hours】======#
%%timeit
cross_val_score(RFC(n_estimators=10,random_state=0),x,y,cv=5).mean()   # 11.5s
  • 随机森林方差过滤后
cross_val_score(RFC(n_estimators=10,random_state=0),x,y,cv=5).mean()   # 0.93880981

#======【TIME WARNING:2 hours】======#
%%timeit
cross_val_score(RFC(n_estimators=10,random_state=0),x,y,cv=5).mean()   # 11.1s
  • 由于两种算法的原理中涉及到的计算量不同。最近邻算法KNN,单棵决策树,支持向量机SVM,神经网络,回归算法,都需要遍历特征或升维来进行运算,所以他们本身的运算量就很大,需要的时间就很长,因此方差过滤这样的特征选择对他们来说就尤为重要。但对于不需要遍历特征的算法,比如随机森林,它随机选取特征进行分枝,本身运算就非常快速,因此特征选择对它来说效果平平。这其实很容易理解,无论过滤法如何降低特征的数量,随机森林也只会选取固定数量的特征来建模;而最近邻算法就不同了,特征越少,距离计算的维度就越少,模型明显会随着特征的减少变得轻量。
  • 过滤法的主要对象是:需要遍历特征或升维的算法们
  • 而过滤法的主要目的是:在维持算法表现的前提下,帮助算法们降低计算成本
1.1.3、选取超参数threshold
  • 每个数据集不一样,只能自己去尝试。这里的方差阈值,其实相当于是一个超参数,要选定最优的超参数,可以画学习曲线,找模型效果最好的点。但现实中,往往不会这样去做,因为这样会耗费大量的时间。只会使用阈值为0或者阈值很小的方差过滤,来优先消除一些明显用不到的特征,然后选择更优的特征选择方法继续削减特征数量

1.2、相关性过滤

  • 方差挑选完毕之后,就要考虑下一个问题:相关性。希望选出与标签相关且有意义的特征,因为这样的特征能够提供大量信息。如果特征与标签无关,那只会白白浪费我们的计算内存,可能还会给模型带来噪音。在sklearn当中,有三种常用的方法来评判特征与标签之间的相关性:卡方,F检验,互信息。
1.2.1、卡方过滤 feature_selection.chi2
  • 卡方过滤是专门针对离散型标签(即分类问题)的相关性过滤。卡方检验类feature_selection.chi2计算每个非负特征和标签之间的卡方统计量,并依照卡方统计量由高到低为特征排名。(卡方值越大,表明两者之间的影响关系越大)
  • 再结合feature_selection.SelectKBest这个可以输入”评分标准“来选出前K个分数最高的特征的类,可以借此除去最可能独立于标签,与分类目的无关的特征。另外,如果卡方检验检测到某个特征中所有的值都相同,会提示使用方差先进行方差过滤。当使用方差过滤筛选掉一半的特征后,模型的表现是提升的。
  • 因此在这里,使用threshold=中位数时完成的方差过滤的数据来做卡方检验(如果方差过滤后模型的表现反而降低了,那就不会使用方差过滤后的数据,而是使用原数据):
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
import numpy as np

x = data.iloc[:,1:]    # 方差过滤前的特征矩阵
y = data.iloc[:,0]

x_fsvar = VarianceThreshold(np.median(x.var().values)).fit_transform(x)   # 方差过滤后的特征矩阵
# 假设选取300个特征   SelecKBest 实例化  里面填的参数为选择的统计量和要保留的特征数
x_fschi = SelectKBest(chi2,k=300).fit_transform(x_fsvar,y)
x_fschi.shape

在这里插入图片描述

# 使用随机森林交叉验证模型的效果
cross_val_score(RFC(n_estimators=10,random_state=10),x_fschi,y,cv=5).mean()

在这里插入图片描述

  • 可以看出,模型的效果降低了,这说明在设定k=300的时候删除了与模型相关且有效的特征,K值设置得太小,要么需要调整K值,要么必须放弃相关性过滤。
  • 当然,如果模型的表现提升,则说明的相关性过滤是有效的,是过滤掉了模型的噪音的,这时候就保留相关性过滤的结果
1.2.2、选取超参数k
  • 在现实数据中,数据量很大,模型很复杂的时候,也许不能先去跑一遍模型看看效果,而是希望最开始就能够选择一个最优的超参数k。
  • 第一个方法,就是学习曲线
%matplotlib inline
import matplotlib.pyplot as plt

score = []
for i in range(350,200,-10):
    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=5).mean()
    score.append(once)
    
plt.plot(range(350,200,-10),score)
plt.show()

在这里插入图片描述

  • 通过这条曲线,可以观察到,随着K值的不断增加,模型的表现不断上升,这说明,K越大越好,数据中所有的特征都是与标签相关的。但是运行这条曲线的时间同样也是非常地长
  • 第二个方法看p值选择k,更好的选择K值的方法
    • 卡方检验的本质是推测两组数据之间的差异,其检验的原假设是”两组数据是相互独立的”。卡方检验返回卡方值和P值两个统计量,
      其中卡方值很难界定有效的范围,而p值,一般使用0.01或0.05作为显著性水平,即p值判断的边界
  • 从特征工程的角度,选取卡方值很大,p值小于0.05的特征(落入拒绝域),即和标签是相关联的特征。而调用SelectKBest之前,可以直接从chi2实例化后的模型中获得各个特征所对应的卡方值和P值。
chivalue, pvalues_chi = chi2(x_fsvar,y)
chivalue   # 卡方值

在这里插入图片描述

pvalues_chi   # P值

在这里插入图片描述

#k取多少?我们想要消除所有p值大于设定值,比如0.05或0.01的特征:
k = chivalue.shape[0] - (pvalues_chi > 0.05).sum()

#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()
1.2.2、F检验
  • F检验,又称ANOVA,方差齐性检验,是用来捕捉每个特征与标签之间的线性关系的过滤方法。它即可以做回归也可以做分类,因此包含feature_selection.f_classif(F检验分类)和feature_selection.f_regression(F检验回归)两个类。
  • F检验分类用于标签是离散型变量的数据,而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, pvalues_f = f_classif(x_fsvar,y)
F

在这里插入图片描述

pvalues_f
# k = F.shape[0] - (pvalues_f > 0.05).sum()
# x_fsF = SelectKBest(f_classif, k=填写具体的k).fit_transform(x_fsvar, y)
# cross_val_score(RFC(n_estimators=10,random_state=0),x_fsF,y,cv=5).mean()

在这里插入图片描述

1.2.3、互信息法
  • 互信息法是用来捕捉每个特征与标签之间的任意关系(包括线性和非线性关系)的过滤方法。和F检验相似,它既可以做回归也可以做分类,并且包含两个类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_fsvar,y)    # 得到互信息量的估计
k = result.shape[0] - sum(result <= 0)
result

在这里插入图片描述

  • 通常来说,我会建议,先使用方差过滤,然后使用互信息法来捕捉相关性

二、Embedded嵌入法

  • 嵌入法是一种让算法自己决定使用哪些特征的方法

  • 先使用某些机器学习的算法和模型进行训练,得到各个特征的权值系数,根据权值系数从大到小选择特征

  • 模型权值系数是超参数

  • 在选择完毕之后,需要自己来评估模型

  • feature_selection.SelectFromModel

  • class sklearn.feature_selection.SelectFromModel (estimator, threshold=None, prefit=False, norm_order=1,max_features=None)
    在这里插入图片描述

from sklearn.feature_selection import SelectFromModel
from sklearn.ensemble import RandomForestClassifier as RFC

RFC_ = RFC(n_estimators =10,random_state=0)   # 实例化后放入SelectFromModel
x_embedded = SelectFromModel(RFC_,threshold=0.005).fit_transform(x,y)
# 这里只想取出来有限的特征。
# 0.005这个阈值对于有780个特征的数据来说,是非常高的阈值,因为平均每个特征只能够分到大约0.001的feature_importances_
x_embedded.shape
#模型的维度明显被降低

在这里插入图片描述

#同样的,也可以画学习曲线来找最佳阈值
import numpy as np
import matplotlib.pyplot as plt
RFC_.fit(x,y).feature_importances_

在这里插入图片描述

threshold = np.linspace(0,(RFC_.fit(x,y).feature_importances_).max(),20)
score = []
for i in threshold:
    x_embedded = SelectFromModel(RFC_,threshold=i).fit_transform(x,y)
    once = cross_val_score(RFC_,x_embedded,y,cv=5).mean()
    score.append(once)
plt.plot(threshold,score)
plt.show()

在这里插入图片描述

  • 从图像上来看,随着阈值越来越高,模型的效果逐渐变差,被删除的特征越来越多,信息损失也逐渐变大。
  • 但是在0.002之前,模型的效果都可以维持在0.93以上,因此我们可以从中挑选一个数值来验证一下模型的效果
x_embedded = SelectFromModel(RFC_,threshold=0.00067).fit_transform(x,y)
x_embedded.shape

在这里插入图片描述

cross_val_score(RFC_,x_embedded,y,cv=5).mean()

在这里插入图片描述

# 细化学习曲线
score2 = []
for i in np.linspace(0,0.002,20):
    x_embedded = SelectFromModel(RFC_,threshold=i).fit_transform(x,y)
    once = cross_val_score(RFC_,x_embedded,y,cv=5).mean()
    score2.append(once)
plt.figure(figsize=[20,5])
plt.plot(np.linspace(0,0.002,20),score2)
plt.xticks(np.linspace(0,0.002,20))
plt.show()

在这里插入图片描述

三、Wrapper包装法

  • feature_selection.RFE
    • 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 =10,random_state=0)
selector = RFE(RFC_, n_features_to_select=340, step=50).fit(x, y)

selector.support_.sum()

在这里插入图片描述

selector.ranking_

在这里插入图片描述

x_wrapper = selector.transform(x)
cross_val_score(RFC_,x_wrapper,y,cv=5).mean()

在这里插入图片描述

四、总结

  • 降维之外的所有特征选择的方法。
  • 过滤法更快速,但更粗糙。
  • 包装法和嵌入法更精确,比较适合具体到算法去调整,但计算量比较大,运行时间长。
  • 当数据量很大的时候,优先使用方差过滤和互信息法调整,再上其他特征选择方法。
  • 使用逻辑回归时,优先使用嵌入法。
  • 使用支持向量机时,优先使用包装法。
  • 迷茫的时候,从过滤法走起,看具体数据具体分析
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值