在特征选择中,最适合在探索阶段使用的就是过滤式的方法,比如方差阈值法和单变量选择法。在上一篇文章中我们介绍了如何在sklearn中使用方差阈值法,那么今天我们就进一步介绍更加实用、有效的单变量选择法。
sklearn中的单变量选择法
单变量选择法的主要思路是根据某些统计检验的方法分别对每个变量进行检验,得到一组分数、p-value数据,然后我们排序选择分数最高(或p-value最小等)的那些特征。在sklearn中,分别针对不同的统计检验方法和不同的排序选择标准提供了不同的工具,比如用于回归问题的f_regression、mutual_info_regression分数,用于分类问题的f_classif、chi2、mutual_info_classf分数;以及用于特征排序和选择的SelectKBest、SelectPercentile、SelectFpr等。把这两类工具相结合,就可以完成特征选择的任务了。
今天我们先学习一下选择方法,在下一篇文章中,我们再介绍不同的评分方法。
我们来看一个简单的例子。
from sklearn.datasets import load_boston
from sklearn.feature_selection import SelectKBest, f_regression
from sklearn.metrics import r2_score, mean_squared_error
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
boston = load_boston()
X = boston.data
y = boston.target
print(X.shape)
print(y.shape)
输出为:
(506, 13)
(506,)
这里我们导入了波士顿房价的数据集,该数据集是一个回归问题,共有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)
这里我们使用 R 2 R^2 R2和均方误差MSE来作为回归模型效果的评估标准,输出如下:
R2: 0.6783942923302058
MSE: 29.824006898863182
从 R 2 R^2 R2来看效果尚可。那么接下来我们就从这13个特征中选择最好的10个特征来训练一个新的模型。
我们先生成新的特征子集:
selector = SelectKBest(f_regression, k=10)
X_new = selector.fit_transform(X, y)
mask = selector.get_support()
new_features = []
for bool, feature in zip(mask, boston.feature_names):
if bool:
new_features.append(feature)
print(‘newfeature’,new_features) # bool true, feature被选中
dataframe = pd.DataFrame(X_new, columns=new_features)
print(X_new.shape)
输出为:
(506, 10)
可以看到,样本数不变,仍为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.682644378927699
MSE: 29.429876418646238
可以使用迭代方法,找到R2 最大值
r_list=[]
for i in range(1,14):
selector = SelectKBest(f_regression, k=i)
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)
r_list.append(r2)
mse = mean_squared_error(y_test, y_pred)
print('R2: ', r2)
print('MSE: ', mse)
对比刚才的 R 2 R^2 R2和MSE,我们发现在我们删掉了3个特征以后, R 2 R^2 R2略有提升,均方误差MSE略有下降,也就是说我们模型的效果反而更好了。
其使用的评分标准与SelectKBest没有什么不同,按需传入所需的评分方法即可。需要注意的是,percentile接受的是一个0到100的整数。
from sklearn.feature_selection import SelectPercentile
selector = 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.6831525503257939
MSE: 29.382751299529424
可以看到,我们选择了前70%的特征(9个),模型的 R 2 R^2 R2进一步提升。
不想深究的同学可以直接简单粗暴地记住,这里就是按照p-value来筛选特征的,p-value越小越好,所有p-value低于我们设定的阈值 α \alpha α的特征都会被选择。由于这个例子中每个特征的p-value都很小,为了演示效果,我们把 α \alpha α设定到十万分之一:
from sklearn.feature_selection import SelectFpr
selector = 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.6935405006596658
MSE: 28.419427903725335
可以看到,f_regression检验中,p值在十万分之一以下的特征(第四个)被剔除了。
sklearn中的SelectFdr方法使用Benjamini-Hochberg过程对p值进行修正,并筛选出修正后的p值在 α \alpha α水平以下的特征。这一过程的公式很简单,但是其原理较复杂,感兴趣的可以阅读这篇维基百科。
下边这张图是sklearn中的源码,也可以帮助你理解这一过程。
在这个例子中,SelectFdr和SelectFpr的结果是一样的。
from sklearn.feature_selection import SelectFdr
selector = 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.6935405006596658
MSE: 28.419427903725335
5. SelectFwe
在多重假设检验中,总体错误率(族系误差率,FWER,family-wise error rate)是另一个常用的错误控制指标,它与FDR的区别在于,FWER是指至少出现一次一类错误的概率,而FDR则是关注预测为正的样本中一类错误发生的比例。
举个例子来帮助大家理解FPR、FDR和FWER之间的区别,共有20个样本,我们做了100次多重检验,其中有30次出现了一类错误,这30次中,平均每次检验中我们会拒绝$H_0$10次,其中2次是错误拒绝。这时:
[公式]
当然,在sklearn中是直接使用特定的过程对p值进行修正,并不是真正地去进行多次模拟并计算各项指标。我们可以再看下sklearn中的源码,来加深对FWER过程的理解:
我们来看下sklearn中的SelectFwe的使用:
from sklearn.feature_selection import SelectFwe
selector = 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.690619122897421
MSE: 28.69034097665137
在不同的 α \alpha α下,SelectFpr、SelectFdr和SelectFwe所选取的特征会出现不同,大家可以自行测试一下。
from sklearn.feature_selection import GenericUnivariateSelect
selector = 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.6935405006596658
MSE: 28.419427903725335
注意这里的参数名称有所变化。
好,今天就介绍到这里,下次我们会进一步探索不同的评分方法的原理和实践。