在特征选择中,最适合在探索阶段使用的就是过滤式的方法,比如方差阈值法和单变量选择法。在上一篇文章中我们介绍了如何在sklearn中使用方差阈值法,那么今天我们就进一步介绍更加实用、有效的单变量选择法。
sklearn中的单变量选择法
单变量选择法的主要思路是根据某些统计检验的方法分别对每个变量进行检验,得到一组分数、p-value数据,然后我们排序选择分数最高(或p-value最小等)的那些特征。在sklearn中,分别针对不同的统计检验方法和不同的排序选择标准提供了不同的工具,比如用于回归问题的f_regression、mutual_info_regression分数,用于分类问题的f_classif、chi2、mutual_info_classf分数;以及用于特征排序和选择的SelectKBest、SelectPercentile、SelectFpr等。把这两类工具相结合,就可以完成特征选择的任务了。
今天我们先学习一下选择方法,在下一篇文章中,我们再介绍不同的评分方法。
1. SelectKBest
顾名思义,SelectKBest方法就是从所有特征中挑选出最好的K个特征组成新的特征集。如何来定义最好呢?这要取决于我们传入的评分函数,该方法默认会使用f_classif,这种方法适用于为分类模型评估特征,与之对应的是f_regression,它适用于回归模型的特征评估。
我们来看一个简单的例子。
from sklearn.datasets import load_bostonfrom sklearn.feature_selection import SelectKBest, f_regressionfrom sklearn.metrics import r2_score, mean_squared_errorfrom sklearn.linear_model import LinearRegressionfrom sklearn.model_selection import train_test_splitboston = load_boston()X = boston.datay = boston.targetprint(X.shape)print(y.shape)
输出为:
(506, 13)(506,)
![e111120fd2d3c989e574b1a3c54bfa90.png](https://i-blog.csdnimg.cn/blog_migrate/decea3386d47769a52f0a3e7cfb6b25f.jpeg)
这里我们导入了波士顿房价的数据集,该数据集是一个回归问题,共有506个样本、13个特征,我们的任务是根据这13个特征来预测房价。我们先使用所有的特征数据来训练一个模型,作为一个基准。
X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=1001)model = LinearRegression()model.fit(X_train, y_train)y_pred = model.predict(X_test)r2 = r2_score(y_test, y_pred)mse = mean_squared_error(y_test, y_pred)print('R2: ', r2)print('MSE: ', mse)
这里我们使用R2和均方误差MSE来作为回归模型效果的评估标准,输出如下:
R2: 0.6783942923302058MSE: 29.824006898863182
![43e602d88a15d9470ce9e5c741fecbd0.png](https://i-blog.csdnimg.cn/blog_migrate/943ea4fcd58d0f95cfa7f3b99acf8db5.jpeg)
从R2来看效果尚可。那么接下来我们就从这13个特征中选择最好的10个特征来训练一个新的模型。
我们先生成新的特征子集:
selector = SelectKBest(f_regression, k=10)X_new = selector.fit_transform(X, y)print(X_new.shape)
输出为:
(506, 10)
![3f61e101be3b6af9978110f45b229ca0.png](https://i-blog.csdnimg.cn/blog_migrate/9c43c574b12a779c4a6ccbf619899c91.jpeg)
可以看到,样本数不变,仍为506个,特征则被删减到了10个。接下来我们用这10个特征的数据来训练一个新的模型。注意,在使用train_test_split方法分割测试集和训练集时,我们要保证random_state参数的取值是固定的,这样才能确保不同的模型训练和测试过程中,训练集和测试集的样本是一致的。
X_train, X_test, y_train, y_test = train_test_split(X_new, y, random_state=1001)model = LinearRegression()model.fit(X_train, y_train)y_pred = model.predict(X_test)r2 = r2_score(y_test, y_pred)mse = mean_squared_error(y_test, y_pred)print('R2: ', r2)print('MSE: ', mse)
输出为:
R2: 0.682644378927699MSE: 29.429876418646238
![45e7a4f741ba2559353b3f0dbc5e5fe6.png](https://i-blog.csdnimg.cn/blog_migrate/f93b6282554f587719e97fbd1cdf84a2.jpeg)
对比刚才的R2和MSE,我们发现在我们删掉了3个特征以后,R2略有提升,均方误差MSE略有下降,也就是说我们模型的效果反而更好了。
2. SelectPercentile
SelectPercentile的选取方式与SelectKBest略有不同,后者如我们之前所示,是选取在某种标准下最好的K个特征;而前者,则是在给定一个0到1之间的百分比x%的情况下,选择最好的x%的特征。比如10个特征,前20%的特征就是最好的两个特征,以此类推。
其使用的评分标准与SelectKBest没有什么不同,按需传入所需的评分方法即可。需要注意的是,percentile接受的是一个0到100的整数。
from sklearn.feature_selection import SelectPercentileselector = SelectPercentile(f_regression, percentile=70)X_new = selector.fit_transform(X, y)print(X_new.shape)X_train, X_test, y_train, y_test = train_test_split(X_new, y, random_state=1001)model = LinearRegression()model.fit(X_train, y_train)y_pred = model.predict(X_test)r2 = r2_score(y_test, y_pred)mse = mean_squared_error(y_test, y_pred)print('R2: ', r2)print('MSE: ', mse)
输出为:
(506, 9)R2: 0.6831525503257939MSE: 29.382751299529424
![759af2b2aef1bf9170e8cdedf0b0a9dc.png](https://i-blog.csdnimg.cn/blog_migrate/616baceef604d5ce5cddda02df2c2905.jpeg)
可以看到,我们选择了前70%的特征(9个),模型的R2进一步提升。
3. SelectFpr
前两种方法都很好理解,而SelectFpr方法则要求对假设检验有一定的认识。这种方法基于FPR测试,即False Positive Rate,其含义为假阳性率,是指被我们预测为正但实际为负的样本的比例,也就是假设检验中的一类错误发生的比例。
不想深究的同学可以直接简单粗暴地记住,这里就是按照p-value来筛选特征的,p-value越小越好,所有p-value低于我们设定的阈值α的特征都会被选择。由于这个例子中每个特征的p-value都很小,为了演示效果,我们把α设定到十万分之一:
from sklearn.feature_selection import SelectFprselector = SelectFpr(f_regression, alpha=0.00001)X_new = selector.fit_transform(X, y)print(X_new.shape)X_train, X_test, y_train, y_test = train_test_split(X_new, y, random_state=1001)model = LinearRegression()model.fit(X_train, y_train)y_pred = model.predict(X_test)r2 = r2_score(y_test, y_pred)mse = mean_squared_error(y_test, y_pred)print('R2: ', r2)print('MSE: ', mse)
输出为:
(506, 12)R2: 0.6935405006596658MSE: 28.419427903725335
![ec9b1c4f98d7da78420752fae07dac02.png](https://i-blog.csdnimg.cn/blog_migrate/7dcc75792d5619413344dd3254821ebd.jpeg)
可以看到,f_regression检验中,p值在十万分之一以下的特征(第四个)被剔除了。
![bc80b42f706533226c607e96a4c99240.png](https://i-blog.csdnimg.cn/blog_migrate/095ed28c4426f6b152f22cf2fccdf556.jpeg)
4. SelectFdr
SelectFdr则是根据错误发现率(FDR,False Discovery Rate)来为特征排序。错误发现率与假阳性率非常相似,但是却有着本质的不同。假阳性率是在所有样本中一类错误发生的概率,而错误发现率则仅仅关注在我们拒绝原假设(预测为正)的样本中,有多大比例是犯了一类错误的(即在"发现"的样本里,有多少是错误的"发现")。
sklearn中的SelectFdr方法使用Benjamini-Hochberg过程对p值进行修正,并筛选出修正后的p值在α水平以下的特征。这一过程的公式很简单,但是其原理较复杂,感兴趣的可以阅读这篇维基百科。
下边这张图是sklearn中的源码,也可以帮助你理解这一过程。
![7b06f2711ad3c3cf4c4b02e35182ac76.png](https://i-blog.csdnimg.cn/blog_migrate/96be2f681c735654c746c3c47cd09f75.jpeg)
在这个例子中,SelectFdr和SelectFpr的结果是一样的。
from sklearn.feature_selection import SelectFdrselector = SelectFdr(f_regression, alpha=0.00001)X_new = selector.fit_transform(X, y)print(X_new.shape)X_train, X_test, y_train, y_test = train_test_split(X_new, y, random_state=1001)model = LinearRegression()model.fit(X_train, y_train)y_pred = model.predict(X_test)r2 = r2_score(y_test, y_pred)mse = mean_squared_error(y_test, y_pred)print('R2: ', r2)print('MSE: ', mse)
输出为:
(506, 12)R2: 0.6935405006596658MSE: 28.419427903725335
5. SelectFwe
在多重假设检验中,总体错误率(族系误差率,FWER,family-wise error rate)是另一个常用的错误控制指标,它与FDR的区别在于,FWER是指至少出现一次一类错误的概率,而FDR则是关注预测为正的样本中一类错误发生的比例。
举个例子来帮助大家理解FPR、FDR和FWER之间的区别,共有20个样本,我们做了100次多重检验,其中有30次出现了一类错误,这30次中,平均每次检验中我们会拒绝H010次,其中2次是错误拒绝。这时:
![120eda3af70fb19dd9885821734cb864.png](https://i-blog.csdnimg.cn/blog_migrate/d53a141098b1806a36d32c01de66f526.jpeg)
当然,在sklearn中是直接使用特定的过程对p值进行修正,并不是真正地去进行多次模拟并计算各项指标。我们可以再看下sklearn中的源码,来加深对FWER过程的理解:
![1a06bc3f997f715af0ce73a3298a027d.png](https://i-blog.csdnimg.cn/blog_migrate/56af296cdbf5d04f49ff174334b59928.jpeg)
我们来看下sklearn中的SelectFwe的使用:
from sklearn.feature_selection import SelectFweselector = SelectFwe(f_regression, alpha=0.0000001)X_new = selector.fit_transform(X, y)print(X_new.shape)X_train, X_test, y_train, y_test = train_test_split(X_new, y, random_state=1001)model = LinearRegression()model.fit(X_train, y_train)y_pred = model.predict(X_test)r2 = r2_score(y_test, y_pred)mse = mean_squared_error(y_test, y_pred)print('R2: ', r2)print('MSE: ', mse)
输出为:
(506, 11)R2: 0.690619122897421MSE: 28.69034097665137
![61dacaed42fc4ee58a58c32bca5ef6f2.png](https://i-blog.csdnimg.cn/blog_migrate/c21b456e40e9f5e1fa7c8ff7a88249c1.jpeg)
在不同的α下,SelectFpr、SelectFdr和SelectFwe所选取的特征会出现不同,大家可以自行测试一下。
6. GenericUnivariateSelect
这种方法将上述五种方法集成到了一起,然后我们将评分方法、选择方法以参数的形式传递进来即可。
from sklearn.feature_selection import GenericUnivariateSelectselector = GenericUnivariateSelect(f_regression, mode='fpr', param=0.0000001)X_new = selector.fit_transform(X, y)print(X_new.shape)X_train, X_test, y_train, y_test = train_test_split(X_new, y, random_state=1001)model = LinearRegression()model.fit(X_train, y_train)y_pred = model.predict(X_test)r2 = r2_score(y_test, y_pred)mse = mean_squared_error(y_test, y_pred)print('R2: ', r2)print('MSE: ', mse)
输出为:
(506, 12)R2: 0.6935405006596658MSE: 28.419427903725335
![20ad0b1bf15ab987c576bef992d586ac.png](https://i-blog.csdnimg.cn/blog_migrate/d66ff7e91b51043e08b9eeb773f437e2.jpeg)
注意这里的参数名称有所变化。
好,今天就介绍到这里,下次我们会进一步探索不同的评分方法的原理和实践。