Py学习  »  机器学习算法

机器学习实战 | 特征选择

生信菜鸟团 • 5 年前 • 1080 次点击  

数据预处理完成后,我们通常需要选择有意义的特征输入机器学习的算法和模型进行训练。一般从两个方面考虑来选择特征:

  • 特征是否发散:如果一个特征不发散,方差接近于 0,也就是说样本在这个特征上基本上没有差异,这个特征对于样本的区分并没有什么用。

  • 特征与目标的相关性:这点比较显见,与目标相关性高的特征,应当优选选择。

根据特征选择的形式又可以将特征选择方法分为3种:

  • Filter:过滤法,按照发散性或者相关性对各个特征进行评分,设定阈值或者待选择阈值的个数,选择特征。

  • Wrapper:包装法,根据目标函数(通常是预测效果评分),每次选择若干特征,或者排除若干特征。

  • Embedded:嵌入法,先使用某些机器学习的算法和模型进行训练,得到各个特征的权值系数,根据系数从大到小选择特征。类似于过滤法,但是是通过训练来确定特征的优劣。

我们使用 sklearn 中的 feature_selection 库来进行特征选择。数据来自 Kaggle 的 digit recognizer 数据集:https://www.kaggle.com/c/digit-recognizer/data

#导入数据
import pandas as pd
import numpy as np
data = pd.read_csv(r"./digit-recognizer/train.csv")
X = data.iloc[:,1:]
y = data.iloc[:,0]
X.shape

Filter 过滤法

过滤方法通常用作预处理步骤,特征选择完全独立于任何机器学习算法。它是根据各种统计检验中的分数以及相关性的各项指标来选择特征。在文章中最常见的例子就是选择使用显著差异的特征进行建模,例如,在宏基因组数据中,我们可以先使用秩和检验找出差异菌株,然后选择 P 值较小的点来建立模型。但在这里我只介绍机器学习中一些常见的过滤法。

方差过滤

方差过滤是通过特征本身的方差来进行筛选,移除低方差的特征。比如一个特征本身的方差很小,就表示样本在这个特征上基本没有差异,说明特征对于区分样本没有很大贡献,就可以选择删除它们。

使用feature_selection库的VarianceThreshold 类来进行方差过滤。参数threshold,表示方差的阈值,舍弃所有方差小于threshold的特征,不填默认为 0,即删除所有的记录都相同的特征。

from sklearn.feature_selection import VarianceThreshold
# 实例化,留下一半的特征(threshold 为方差的中值)
selector = VarianceThreshold(np.median(X.var().values))
# 获取删除不合格特征之后的新特征矩阵
X_fsvar = selector.fit_transform(X)
# 也可以直接写成 X = VairanceThreshold().fit_transform(X)
X_fsvar.shape

方差过滤完后,我们就需要考虑下特征与标签的相关性了。显而易见,如果选取相关性较高的特征,则有助于模型的构建,反之,如果特征与标签无关,那只会白白浪费我们的计算内存,可能还会给模型带来噪音。在 sklearn 当中,我们有三种常用的方法来评判特征与标签之间的相关性:卡方,F检验,互信息。

相关性过滤

卡方(Chi2)检验

卡方检验适用于分类问题,feature_selection.chi2会计算每个非负特征和标签之间的卡方统计量,并依照卡方统计量由高到低为特征排名。再结合feature_selection.SelectKBest这个可以输入「评分标准」来选出前 K 个分数最高的特征的类。代码如下:

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

# 假设我们需要300个特征
X_fschi = SelectKBest(chi2, k=300).fit_transform(X_fsvar, y)
X_fschi.shape

看看效果如何:

cross_val_score(RFC(n_estimators=10,random_state=0),X_fschi,y,cv=5).mean()

除此之外,还可以根据 P 值进行筛选:




    
chivalue, pvalues_chi = chi2(X_fsvar,y)
# 计算所有p值大于 0.05 的特征数量
k = chivalue.shape[0] - (pvalues_chi > 0.05).sum()

可得 k=392 :

X_fschi = SelectKBest(chi2, k=392).fit_transform(X_fsvar, y)
cross_val_score(RFC(n_estimators=10,random_state=0),X_fschi,y,cv=5).mean()

F 检验

F 检验是用来捕捉每个特征与标签之间的线性关系的过滤方法。它既可以做回归也可以做分类,因此包含feature_selection.f_classif( F 检验分类)和feature_selection.f_regression( F 检验回归)两个类。其中 F 检验分类用于标签是离散型变量的数据,而 F 检验回归用于标签是连续型变量的数据。和卡方检验一样,这两个类需要和类SelectKBest连用。

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()

互信息法

互信息法是用来捕捉每个特征与标签之间的任意关系(包括线性和非线性关系)的过滤方法。和 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)
X_fsmic = SelectKBest(MIC, k=填写具体的k).fit_transform(X_fsvar, y)
cross_val_score(RFC(n_estimators=10,random_state=0),X_fsmic,y,cv=5).mean()

Embedded 嵌入法

嵌入法则是让算法自己决定使用哪些特征,即特征选择和算法训练同时进行。
在使用嵌入法时,我们一般选取能对特征进行打分的模型,比如 RandomForest 或 Logistic Regression,这样就可以根据权值系数从大到小来选择特征。但由于嵌入法使用算法来进行特征筛选,所以其计算速度与算法有很大的关系,如果选取一些计算量较大的算法,则会非常消耗计算资源。

from sklearn.feature_selection import SelectFromModel
from sklearn.ensemble import RandomForestClassifier as RFC
# 选用随机森林模型
RFC_ = RFC(n_estimators =10,random_state=0)
# 取 threshold=0.005
X_embedded = SelectFromModel(RFC_,threshold=0.005).fit_transform(X,y)
X_embedded.shape

可以看到,当我们卡了 0.005 这个阈值之后只剩下 47 个特征了。而具体我们应该卡多少阈值合适,那就需要画学习曲线了:

import numpy as


    
 np
import matplotlib.pyplot as plt

# 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.xticks(threshold)
plt.show()

从图上看,我们可以选取 0.00067 来构建模型,测试结果:

X_embedded = SelectFromModel(RFC_,threshold=0.00067).fit_transform(X,y)
cross_val_score(RFC_,X_embedded,y,cv=5).mean()

当然,我们也可以根据自己的需求进一步细化学习曲线来找到模型的最佳值。

Wrapper 包装法

包装法的核心思想在于,给定了某种模型及预测效果评价的方法,然后针对特征空间中的不同子集,计算每个子集的预测效果,把效果最好的作为最终被挑选出来的特征子集。需要注意的是集合的子集往往是一个指数的量级,故此类方法计算量较大。故而针对如何高效搜索特征空间子集,就产生了不同的算法。

最典型的算法是递归特征消除法(Recursive feature elimination, 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()

参考

  • https://blog.csdn.net/whether_you/article/details/81080281

  • 菜菜的 scikit-learn 课堂 | sklearn中的数据预处理和特征工程

猜你喜欢

机器学习实战 | 机器学习性能指标

机器学习实战 | 数据预处理

机器学习实战 | Adaboost

支持向量机 sklearn 参数详解

一文带你了解什么是支持向量机

机器学习实战 | 逻辑回归

机器学习实战 | 朴素贝叶斯

机器学习实战 | 决策树

机器学习实战 | k-邻近算法

一起来学习机器学习吧~

Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/40855
 
1080 次点击