十九、自动特征选择
我们经常收集许多可能与监督预测任务相关的特征,但我们不知道它们中的哪一个实际上是预测性的。 为了提高可解释性,有时还提高泛化表现,我们可以使用自动特征选择来选择原始特征的子集。 有几种可用的特征选择方法,我们将按照复杂性的升序来解释。
对于给定的监督模型,最佳特征选择策略是尝试每个可能的特征子集,并使用该子集评估泛化表现。 但是,特征子集是指数级,因此这种详尽的搜索通常是不可行的。 下面讨论的策略可以被认为是这种不可行计算的替代。
单变量统计
选择要素的最简单方法是使用单变量统计,即通过单独查看每个特征并运行统计检验,来查看它是否与目标相关。 这种检验也称为方差分析(ANOVA)。
我们创建了一个人造数据集,其中包含乳腺癌数据和另外 50 个完全随机的特征。
from sklearn.datasets import load_breast_cancer, load_digits
from sklearn.model_selection import train_test_split
cancer = load_breast_cancer()
# get deterministic random numbers
rng = np.random.RandomState(42)
noise = rng.normal(size=(len(cancer.data), 50))
# add noise features to the data
# the first 30 features are from the dataset, the next 50 are noise
X_w_noise = np.hstack([cancer.data, noise])
X_train, X_test, y_train, y_test = train_test_split(X_w_noise, cancer.target,
random_state=0, test_size=.5)
我们必须在统计检验的 p 值上定义一个阈值,来决定要保留多少特征。 在 scikit-learn 中实现了几种策略,一种直接的策略是SelectPercentile
,它选择原始特征的百分位数(下面我们选择 50%):
from sklearn.feature_selection import SelectPercentile
# use f_classif (the default) and SelectPercentile to select 50% of features:
select = SelectPercentile(percentile=50)
select.fit(X_train, y_train)
# transform training set:
X_train_selected = select.transform(X_train)
print(X_train.shape)
print(X_train_selected.shape)
我们还可以直接使用检验统计量,来查看每个特征的相关性。 由于乳腺癌数据集是一项分类任务,我们使用f_classif
,F 检验用于分类。 下面我们绘制 p 值,与 80 个特征中的每一个相关(30 个原始特征和 50 个噪声特征)。 低 p 值表示信息性特征。
from sklearn.feature_selection import f_classif, f_regression, chi2
F, p = f_classif(X_train, y_train)
plt.figure()
plt.plot(p, 'o')
显然,前 30 个特征中的大多数具有非常小的 p 值。
回到SelectPercentile
转换器,我们可以使用get_support
方法获得所选特征:
mask = select.get_support()
print(mask)
# 展示掩码。黑色是真,白色是假
plt.matshow(mask.reshape(1, -1), cmap='gray_r')
几乎所有最初的 30 个特征都被还原了。 我们还可以通过在数据上训练监督模型,来分析特征选择的效果。 仅在训练集上学习特征选择非常重要!
from sklearn.linear_model import LogisticRegression
# 转换测试数据
X_test_selected = select.transform(X_test)
lr = LogisticRegression()
lr.fit(X_train, y_train)
print("Score with all features: %f" % lr.score(X_test, y_test))
lr.fit(X_train_selected, y_train)
print("Score with only selected features: %f" % lr.score(X_test_selected, y_test))
基于模型的特征选择
用于特征选择的稍微复杂的方法,是使用监督机器学习模型,并基于模型认为它们的重要性来选择特征。 这要求模型提供某种方法,按重要性对特征进行排名。 这适用于所有基于树的模型(实现get_feature_importances
)和所有线性模型,系数可用于确定特征对结果的影响程度。
任何这些模型都可以制作成变换器,通过使用SelectFromModel
类包装它,用于特征选择:
from sklearn.feature_selection import SelectFromModel
from sklearn.ensemble import RandomForestClassifier
select = SelectFromModel(RandomForestClassifier(n_estimators=100, random_state=42), threshold="median")
select.fit(X_train, y_train)
X_train_rf = select.transform(X_train)
print(X_train.shape)
print(X_train_rf.shape)
mask = select.get_support()
# 展示掩码。黑色是真,白色是假
plt.matshow(mask.reshape(1, -1), cmap='gray_r')
X_test_rf = select.transform(X_test)
LogisticRegression().fit(X_train_rf, y_train).score(X_test_rf, y_test)
此方法构建单个模型(在本例中为随机森林)并使用此模型中的特征重要性。 我们可以通过在数据子集上训练多个模型,来进行更精细的搜索。 一种特殊的策略是递归特征消除:
递归特征消除
递归特征消除在整个特征集上构建模型,类似于上述方法,选择模型认为最重要的特征子集。 但是,通常只会从数据集中删除单个要素,并使用其余要素构建新模型。 重复删除特征和模型构建的过程,直到只剩下预定数量的特征:
from sklearn.feature_selection import RFE
select = RFE(RandomForestClassifier(n_estimators=100, random_state=42), n_features_to_select=40)
select.fit(X_train, y_train)
# 可视化所选特征
mask = select.get_support()
plt.matshow(mask.reshape(1, -1), cmap='gray_r')
X_train_rfe = select.transform(X_train)
X_test_rfe = select.transform(X_test)
LogisticRegression().fit(X_train_rfe, y_train).score(X_test_rfe, y_test)
select.score(X_test, y_test)
练习
创建“XOR”数据集,如下面的第一个单元格: 添加随机特征,并使用随机森林,在还原原始特征时,比较单变量选择与基于模型的选择。
import numpy as np
rng = np.random.RandomState(1)
# 在 [0,1] 范围内生成 400 个随机整数
X = rng.randint(0, 2, (200, 2))
y = np.logical_xor(X[:, 0] > 0, X[:, 1] > 0) # XOR creation
plt.scatter(X[:, 0], X[:, 1], c=plt.cm.tab10(y))
# %load solutions/19_univariate_vs_mb_selection.py