目录
在机器学习和统计学中,特征选择(英语:feature selection)也被称为变量选择、属性选择 或变量子集选择 。它是指:为了构建模型而选择相关特征(即属性、指标)子集的过程。使用特征选择技术有三个原因:
- 简化模型,使之更易于被研究人员或用户理解
- 缩短训练时间
- 改善通用性、降低过拟合(即降低方差)
要使用特征选择技术的关键假设是:训练数据包含许多冗余 或无关 的特征,因而移除这些特征并不会导致丢失信息。冗余 或无关 特征是两个不同的概念。如果一个特征本身有用,但如果这个特征与另一个有用特征强相关,且那个特征也出现在数据中,那么这个特征可能就变得多余。
由此可见,无论是传统的基础算法,还是时下最流行的深度学习,特征的选择与提取,对于模型最终的预测性能至关重要。另一方面,优选的特征集合相比原始特征集合,只需更少的数据量即可得到同样性能的模型,从系统的角度看,特征选择对机器学习执行性能的优化具有重大意义。
(一)过滤类
主要思想是:对每一维特征“打分”,即给每一维特征赋予权重,这样权重就代表该维特征的重要性,然后依据权重排序。即,按照特征的发散性或者相关性指标对各个特征进行评分,设定评分阈值或待选择阈值的个数,选择合适特征。具体方法实现如下:
1、皮尔逊、斯皮尔曼、肯德尔相关系数
统计学中的三大相关性系数:pearson, spearman, kendall,他们反应的都是两个变量之间变化趋势的方向以及程度,其值范围为-1到+1。0表示两个变量不相关,正值表示正相关,负值表示负相关,值越大表示相关性越强。
(1)、皮尔逊相关系数
“皮尔逊相关系数( Pearson correlation coefficient),又称皮尔逊积矩相关系数(Pearson product-moment correlation coefficient,简称 PPMCC或PCCs),是用于度量两个变量X和Y之间的相关(线性相关),其值介于-1与1之间。函数介绍在自然科学领域中,皮尔逊相关系数广泛用于度量两个变量之间的相关程度,其值介于-1与1之间。”
皮尔逊相关系数适用场景是呈正态分布的连续变量,当数据集的数量超过500时,可以近似认为数据呈正态分布,因为按照中心极限定理,当数据量足够大时,可以认为数据是近似正态分布的。如果关系是非线性的,即便两个变量具有一一对应的关系,Pearson相关性也可能会接近0
适用范围整体概括如下:
- 两个变量之间是线性关系,都是连续数据。
- 两个变量的总体是正态分布,或接近正态的单峰分布。
- 两个变量的观测值是成对
(2)、斯皮尔曼相关系数
在 统计学中, 以查尔斯·爱德华·斯皮尔曼命名的斯皮尔曼等级相关系数,即spearman相关系数。经常用希腊字母ρ表示。 它是衡量两个变量的依赖性的 非参数 指标。 它利用单调方程评价两个统计变量的相关性。 如果数据中没有重复值, 并且当两个变量完全单调相关时,斯皮尔曼相关系数则为+1或−1。
Spearman等级相关系数衡量两个变量之间秩次(排序的位置)的相关程度,通常用于计算离散型数据、分类变量或等级变量之间的相关性;
Spearman等级相关系数对数据条件的要求没有皮尔逊相关系数严格,只要两个变量的观测值是成对的等级评定资料,或者是由连续变量观测资料转化得到的等级资料,不论两个变量的总体分布形态、样本容量的大小如何,都可以用斯皮尔曼等级相关系数来进行研究。对于服从Pearson相关系数的数据亦可计算Spearman相关系数,但统计效能要低一些。
(3)、肯德尔相关系数
Kendall(肯德尔)系数的定义:n个同类的统计对象按特定属性排序,其他属性通常是乱序的。 同序对(concordant pairs)和异序对(discordant pairs)之差与总对数(n*(n-1)/2)的比值定义为Kendall(肯德尔)系数。
Kendall等级相关系数用于计算有序的分类变量之间的相关系数,对于有序的分类变量,例如,评委对选手的评级,优、中、差等。
Kendall相关系数与斯皮尔曼相关系数对数据条件的要求相同。用于反映分类变量相关性的指标,适用于两个分类变量均为有序分类的情况。
三大相关性系数python代码实现参考(六)、过滤类代码
2、方差阀值
使用方差法,要先计算各个特征的方差,然后根据阈值,选择方差大于阈值的特征
实现代码如下:
from sklearn.feature_selection import VarianceThreshold
from sklearn.datasets import load_iris
iris = load_iris()
selector = VarianceThreshold(threshold=(.7 * (1 - .7)))
selector.fit_transform(iris.data)
print("各个特征的方差:",selector.variances_)
print("获取估计器参数",selector.get_params())
print("各个特征的结果",selector.get_support())
返回结果信息如下:
3、卡方检验
卡方检验是用途非常广的一种假设检验方法,它在分类资料统计推断中的应用,包括:两个率或两个构成比比较的卡方检验;多个率或多个构成比比较的卡方检验以及分类资料的相关分析等。
卡方检验就是统计样本的实际观测值与理论推断值之间的偏离程度,实际观测值与理论推断值之间的偏离程度就决定卡方值的大小,如果卡方值越大,二者偏差程度越大;反之,二者偏差越小;若两个值完全相等时,卡方值就为0,表明理论值完全符合。
注意:卡方检验针对分类变量。经典的卡方检验是检验定性自变量对定性因变量的相关性。
卡方检验类feature_selection.chi2计算每个非负 特征和标签之间的卡方统计量,并依照卡方统计量由高到低为特征排名。再结合feature_selection.SelectKBest 这个可以输入”评分标准“来选出前 K 个分数最高的特征的类,我们可以借此除去最可能独立于标签,与我们分类目的无关的特征
feature_selection库的SelectKBest类结合卡方检验选择特征的代码示例参考(六)、过滤类代码
4、方差分析(F检验)
方差分析(Analysis of Variance,简称ANOVA),又称“变异数分析”或“F检验”,是R.A.Fisher发明的,用于两个及两个以上样本均数差别的显著性检验。 由于各种因素的影响,研究所得的数据呈现波动状。 造成波动的原因可分成两类,一是不可控的随机因素,另一是研究中施加的对结果形成影响的可控因素。方差分析的基本思想是:通过分析研究不同来源的变异对总变异的贡献大小,从而确定可控因素对研究结果影响力的大小。
方差分析是用来捕捉每个特征与标签之间的线性关系的过滤方法。它即可以做回归也可以做分类,因此包含feature_selection.f_classif(F 检验分类)和feature_selection.f_regression(F 检验回归)两个类。其中 F 检验分类用于标签是离散型变量的数据,而 F 检验回归用于标签是连续型变量的数据。
feature_selection库的SelectKBest类结合方差分析选择特征的代码示例参考(六)、过滤类代码
5、互信息
互信息法是用来捕捉每个特征与标签之间的任意关系(包括线性和非线性关系)的过滤方法。和 F 检验相似,它既可以做回归也可以做分类,并且包含两个类 feature_selection.mutual_info_classif(互信息分类)和 feature_selection.mutual_info_regression(互信息回归)。这两个类的用法和参数都和 F 检验一模一样,不过互信息法比 F 检验更加强大,F 检验只能够找出线性关系,而互信息法可以找出任意关系。
互信息法不返回 p 值或 F 值类似的统计量,它返回“每个特征与目标之间的互信息量的估计”,这个估计量在[0,1]之间 取值,为 0 则表示两个变量独立,为 1 则表示两个变量完全相关。
feature_selection库的SelectKBest类结合互信息选择特征的代码示例参考(六)、过滤类代码
6、最大互信息系数
最大信息系数(The Maximal Information Coefficient,MIC)是在互信息的基础上发展起来的,MIC方法能快速通过给不同类型的关联关系进行评估,从而发现广泛范围的关系类型,此算法的作者来自哈佛大学,并在生物学等数据上进行了成功的实验,相关成果公布在Science杂志上。
根据MIC的性质,MIC具有普适性、公平性和对称性。所谓普适性,是指在样本量足够大(包含了样本的大部分信息)时,能够捕获各种各样的有趣的关联,而不限定于特定的函数类型(如线性函数、指数函数或周期函数),或者说能均衡覆盖所有的函数关系。一般变量之间的复杂关系不仅仅是通过单独一个函数就能够建模的,而是需要叠加函数来表现。所谓公平性,是指在样本量足够大时能为不同类型单噪声程度相似的相关关系给出相近的系数。例如,对于一个充满相同噪声的线性关系和一个正弦关系,一个好的评价算法应该给出相同或相近的相关系数。
需要注意的是:MIC的统计能力遭到了一些质疑,当零假设不成立时,MIC的统计就会受到影响。在有的数据集上不存在这个问题,但有的数据集上就存在这个问题。
MIC算法与其它算法的优劣对比:
feature_selection库的SelectKBest类结合最大互信息选择特征的代码示例参考(六)、过滤类代码
(二)包装类
1、递归特征消除
说简单点,递归式特征消除的主要思路是反复建立多种模型,每一次根据系数的不挑出差的特征,并去除挑出来的特征,然后在剩余的特征上重复该过程,直到遍历了所有的特征。 所以递归式特征消除效果如果很看选用的模型
递归特征消除(RFE)的目标是通过递归地考虑越来越小的特征集来选择特征。首先,估计器在初始特征集上进行训练,每个特征的重要性通过任何特定属性(例如coef_
,feature_importances_
)或可调用的属性获得。然后,从当前的特征集中修剪最不重要的特征。该过程在修剪后的集合上递归重复,直到最终达到要选择的所需特征数量。
基于交叉验证的RFECV:在数据训练的过程中,我们经常采用K折交叉验证来进行调参,RFE也可以进化,这个叫做RFECV,其实简单理解就是:采用交叉验证的递归特征消除,RFECV,这样做的目的只有一个,选择最佳数量的特征。
具体代码实现如下:
from sklearn.datasets import load_iris
from sklearn.svm import SVC
from sklearn.feature_selection import RFE
from sklearn.feature_selection import RFECV
from sklearn.model_selection import StratifiedKFold
iris = load_iris()
X = iris.data
y = iris.target
print("————————————————————————RFE特征选择结果——————————————————————————")
svc = SVC(kernel="linear")
rfe = RFE(estimator = svc, # 基分类器
n_features_to_select = 2, # 选择特征个数
step = 3, # 每次迭代移除的特征个数
verbose = 1 # 显示中间过程
).fit(X,y)
X_RFE = rfe.transform(X)
print("RFECV保留的特征数 : %d" % rfe.n_features_)
print("RFECV重要程度排名 : %s" % list(rfe.ranking_))
print("RFECV是否保留",rfe.get_support())
print("—————————————————————————RFECV特征选择结果—————————————————————————")
svc_rfecv = SVC(kernel="linear")
rfecv = RFECV(estimator=svc_rfecv, # 学习器
step=2, # 移除特征个数
cv=StratifiedKFold(2), # 交叉验证次数
# scoring='accuracy', # 学习器的评价标准
verbose = 1,
min_features_to_select = 1
# n_jobs = 1
).fit(X, y)
X_RFECV = rfecv.transform(X)
print("RFECV保留的特征数 : %d" % rfecv.n_features_)
print("RFECV重要程度排名 : %s" % list(rfecv.ranking_))
print("RFECV是否保留",rfecv.get_support())
结果如下:
(三)嵌入类
嵌入法先使用某些机器学习的算法和模型进行训练,得到各特征的权值系数,根据权值系数从大到小来选择特征。与过滤法类似,但它是通过机器学习训练来确定特征的优劣,而不是直接从特征的统计学指标来确定特征优劣。主要思想是:在模型既定的情况下学习出对提高模型准确性最好的属性,即,在确定模型的过程中,挑选出那些对模型的训练有重要意义的属性。包括:基于惩罚项的特征选择法、基于树模型的特征选择法。
1、基于惩罚项的特征选择法
2、基于树模型的特征选择法
(四)、降维
机器学习领域中所谓的降维就是指采用某种映射方法,将原高维空间中的数据点映射到低维度的空间中。 降维的本质是学习一个映射函数f : x->y,其中x是原始数据点的表达,目前最多使用向量表达形式。 y是数据点映射后的低维向量表达,通常y的维度小于x的维度(当然提高维度也是可以的),主要有以下两种方式:
1、主成分分析(PCA)
2、线性判别分析法(LDA)
(五)、sklearn数据包相关类功能整理
Python类 | 特征方式 | 类说明 |
VarianceThreshold | Filter | 方差选择法 |
SelectKBest | Filter | 可选关联系数、卡方校验、最大信息系数作为得分计算的方法 |
RFE | Wrapper | 递归地训练基模型,将权值系数较小的特征从特征集合中消除 |
SelectFromModel | Embedded | 训练基模型,选择权值系数较高的特征 |
(六)、过滤类代码
from pandas import DataFrame
from numpy import *
import numpy as np
from sklearn.datasets import load_iris
from sklearn.feature_selection import SelectKBest
from scipy.stats import pearsonr
from scipy.stats import spearmanr
from scipy.stats import kendalltau
from sklearn.feature_selection import chi2
from sklearn.feature_selection import f_classif
from sklearn.feature_selection import f_regression
from sklearn.feature_selection import mutual_info_classif
from sklearn.feature_selection import mutual_info_regression
from minepy import MINE
#mic方法
def mic(x, y):
mine = MINE(alpha=0.6, c=15, est="mic_approx")
mine.compute_score(x, y)
return (mine.mic(), 0.5)
def get_filter_model(algorithm_type,feature_limit):
model = None
if algorithm_type == 'pearsonr':
f = lambda X, Y: np.array(list(map(lambda x: pearsonr(x, Y)[0], X.T))).T
model = SelectKBest(score_func=f, k=feature_limit)
elif algorithm_type == 'spearmanr':
f = lambda X, Y: np.array(list(map(lambda x: spearmanr(x, Y)[0], X.T))).T
model = SelectKBest(score_func=f, k=feature_limit)
elif algorithm_type == 'kendalltau':
f = lambda X, Y: np.array(list(map(lambda x: kendalltau(x, Y)[0], X.T))).T
model = SelectKBest(score_func=f, k=feature_limit)
elif algorithm_type == 'chi2':
model = SelectKBest(score_func=chi2, k=feature_limit)
elif algorithm_type == 'f_classif':
model = SelectKBest(score_func=f_classif, k=feature_limit)
elif algorithm_type == 'f_regression':
model = SelectKBest(score_func=f_regression, k=feature_limit)
elif algorithm_type == 'mutual_info_classif':
model = SelectKBest(score_func=mutual_info_classif, k=feature_limit)
elif algorithm_type == 'mutual_info_regression':
model = SelectKBest(score_func=mutual_info_regression, k=feature_limit)
elif algorithm_type == 'mic':
f = lambda X, Y: tuple(map(tuple,array(list(map(lambda x:mic(x, Y), X.T))).T))
model = SelectKBest(f,k=feature_limit)
return model
def getScores(X,y,algorithm_type,feature_limit):
model = get_filter_model(algorithm_type,feature_limit) # 获取模型
model.fit_transform(X,y.ravel()) # 训练
scores = model.scores_ # 得分越高,特征越重要
p_values = model.pvalues_ # p-values 越小,置信度越高,特征越重要
# 按重要性排序,选出最重要的 k 个
indices = np.argsort(scores)[::-1] #所有index列
k_best_features = indices[0:feature_limit]
arrayKey = []
arrayScoresValue = []
arraypValues = []
arrayBest = []
arrayType = []
for index in range(len(indices)):
arrayKey.append(iris.feature_names[index])
arrayScoresValue.append(scores[index])
if p_values is None :
arraypValues.append(None)
else:
arraypValues.append(p_values[index])
if index in k_best_features:
arrayBest.append(True) #选中的特征
else:
arrayBest.append(False)
arrayType.append(algorithm_type)
resultData = {'name':arrayKey, 'scores':arrayScoresValue, 'p_values':arraypValues, 'best_features':arrayBest, 'type':arrayType}
resultDf = DataFrame(resultData)
print(resultDf)
print(algorithm_type,"k_best_features",k_best_features)
if __name__ == '__main__':
print()
iris = load_iris()
X = iris.data
y = iris.target
feature_limit = 2 #选取两个重要的特征
algorithm_type = 'pearsonr'
getScores(X,y,algorithm_type,feature_limit)
print('------------------------------------------------------------------')
algorithm_type = 'spearmanr'
getScores(X,y,algorithm_type,feature_limit)
print('------------------------------------------------------------------')
algorithm_type = 'kendalltau'
getScores(X,y,algorithm_type,feature_limit)
print('------------------------------------------------------------------')
algorithm_type = 'f_classif'
getScores(X,y,algorithm_type,feature_limit)
print('------------------------------------------------------------------')
algorithm_type = 'f_regression'
getScores(X,y,algorithm_type,feature_limit)
print('------------------------------------------------------------------')
algorithm_type = 'mutual_info_classif'
getScores(X,y,algorithm_type,feature_limit)
print('------------------------------------------------------------------')
algorithm_type = 'mutual_info_regression'
getScores(X,y,algorithm_type,feature_limit)
print('------------------------------------------------------------------')
algorithm_type = 'mic'
getScores(X,y,algorithm_type,feature_limit)
结果信息:
参考文档:
1.13. Feature selection — scikit-learn 1.0 documentation