特征选择是识别和选择与目标变量最相关的输入变量子集的过程。
特征选择最简单的情况可能是存在数字输入变量和用于回归预测建模的数字目标的情况。这是因为可以计算出每个输入变量与目标之间的关系强度,称为相关性,并进行相对比较。
本文中我们将展示如何使用数值输入数据执行特征选择以进行回归预测建模。
本文分为四部分:
我们将使用综合回归数据集作为本文的基础。
回归问题是我们要预测数值的问题。在这种情况下,我们需要一个具有数字输入变量的数据集。
scikit-learn库中的make_regression()函数可用于定义数据集。它提供了对样本数量,输入特征数量以及重要的是相关和冗余输入特征数量的控制。这一点至关重要,因为我们特别希望我们知道的数据集具有一些冗余输入特征。
在这种情况下,我们将定义一个包含1000个样本的数据集,每个样本具有100个输入特征,其中10个是信息性的,其余90个是冗余的。
# generate regression dataset
X, y = make_regression(n_samples=1000, n_features=100, n_informative=10, noise=0.1, random_state=1)
希望特征选择技术可以识别与目标相关的那些特征中的一些或全部,或者至少识别并除去某些冗余输入特征。
定义后,我们可以将数据分为训练集和测试集,以便我们拟合和评估学习模型。
我们将使用scikit-learn形式的train_test_split()函数,并将67%的数据用于训练,并将33%的数据用于测试。
...
# split into train and test sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=1)
1
2
3
...
# split into train and test sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=1)
将这些特征结合在一起,下面列出了定义,拆分和汇总原始回归数据集的完整示例。
# load and summarize the dataset
from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split
# generate regression dataset
X, y = make_regression(n_samples=1000, n_features=100, n_informative=10, noise=0.1, random_state=1)
# split into train and test sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=1)
# summarize
print('Train', X_train.shape, y_train.shape)
print('Test', X_test.shape, y_test.shape)
运行示例将报告训练和测试集的输入和输出特征的大小。
我们可以看到,我们有670个训练示例和330个测试示例。
现在,我们已经加载并准备了数据集,我们可以探索特征选择。
有两种流行的特征选择技术,可用于数字输入数据和数字目标变量。
他们是:
相关性是两个变量如何一起变化的度量。也许最常见的相关度量是Pearson相关,它假设每个变量的高斯分布并报告它们的线性关系。
对于数字预测变量,量化与结果之间的每个关系的经典方法是使用样本相关性统计量。
线性相关分数通常是介于-1和1之间的值,0表示没有关系。对于特征选择,我们通常对正值感兴趣,正值越大,关系越大,并且应该更可能选择特征进行建模。这样,线性相关可以转换为仅具有正值的相关统计量。
scikit-learn库在f_regression()函数中提供了相关统计的实现。此函数可用于特征选择策略中,例如通过SelectKBest类选择前k个最相关的特征(最大值)。
例如,我们可以定义SelectKBest类以使用f_regression()函数并选择所有特征,然后转换训练和测试集。
# configure to select all features
fs = SelectKBest(score_func=f_regression, k='all')
# learn relationship from training data
fs.fit(X_train, y_train)
# transform train input data
X_train_fs = fs.transform(X_train)
# transform test input data
X_test_fs = fs.transform(X_test)
return X_train_fs, X_test_fs, fs
然后,我们可以输出每个变量的分数(越大越好),并将每个变量的分数绘制为条形图,以了解应该选择多少个特征。
for i in range(len(fs.scores_)):
print('Feature %d: %f' % (i, fs.scores_[i]))
# plot the scores
pyplot.bar([i for i in range(len(fs.scores_))], fs.scores_)
pyplot.show()
将其与上一节中数据集的数据准备一起进行绑定,下面列出了完整的示例。
from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import f_regression
from matplotlib import pyplot
# feature selection
def select_features(X_train, y_train, X_test):
# configure to select all features
fs = SelectKBest(score_func=f_regression, k='all')
# learn relationship from training data
fs.fit(X_train, y_train)
# transform train input data
X_train_fs = fs.transform(X_train)
# transform test input data
X_test_fs = fs.transform(X_test)
return X_train_fs, X_test_fs, fs
# load the dataset
X, y = make_regression(n_samples=1000, n_features=100, n_informative=10, noise=0.1, random_state=1)
# split into train and test sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=1)
# feature selection
X_train_fs, X_test_fs, fs = select_features(X_train, y_train, X_test)
# what are scores for the features
for i in range(len(fs.scores_)):
print('Feature %d: %f' % (i, fs.scores_[i]))
# plot the scores
pyplot.bar([i for i in range(len(fs.scores_))], fs.scores_)
pyplot.show()
首先运行示例将输出为每个输入特征和目标变量计算的分数。
请注意,您的特定结果可能会有所不同。尝试运行该示例几次。
我们可以看到,某些变量的分数高于其他变量,而其他变量的分数则大得多,例如Feature 9的分数为101。
为每个输入特征创建一个特征重要性评分的条形图。
该图清楚地表明8至10个特征比其他重要得多。
在配置SelectKBest时,我们可以设置k=10来选择这些最重要的特征。
输入特征(x)与关联特征重要性(y)的条形图
来自信息理论领域的相关信息是信息增益(通常用于决策树的构建)在特征选择中的应用。
在两个变量之间计算相关信息,并在给定另一个变量的已知值的情况下测量一个变量的不确定性降低。
考虑两个离散(分类或有序)变量(例如分类输入和分类输出数据)的分布时,相关信息很简单。尽管如此,它仍可适用于数字输入和输出变量。
scikit-learn库中的common_info_regression()函数通过数字输入和输出变量提供了用于信息选择的相关信息实现。
与f_regression()一样,它可以用于SelectKBest特征选择策略(和其他策略)中。
# configure to select all features
fs = SelectKBest(score_func=mutual_info_regression, k='all')
# learn relationship from training data
fs.fit(X_train, y_train)
# transform train input data
X_train_fs = fs.transform(X_train)
# transform test input data
X_test_fs = fs.transform(X_test)
return X_train_fs, X_test_fs, fs
我们可以使用数据集上的相关信息来执行特征选择。
下面列出了使用相关信息进行数字特征选择的完整示例。
from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import mutual_info_regression
from matplotlib import pyplot
# feature selection
def select_features(X_train, y_train, X_test):
# configure to select all features
fs = SelectKBest(score_func=mutual_info_regression, k='all')
# learn relationship from training data
fs.fit(X_train, y_train)
# transform train input data
X_train_fs = fs.transform(X_train)
# transform test input data
X_test_fs = fs.transform(X_test)
return X_train_fs, X_test_fs, fs
# load the dataset
X, y = make_regression(n_samples=1000, n_features=100, n_informative=10, noise=0.1, random_state=1)
# split into train and test sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=1)
# feature selection
X_train_fs, X_test_fs, fs = select_features(X_train, y_train, X_test)
# what are scores for the features
for i in range(len(fs.scores_)):
print('Feature %d: %f' % (i, fs.scores_[i]))
# plot the scores
pyplot.bar([i for i in range(len(fs.scores_))], fs.scores_)
pyplot.show()
首先运行示例将输出为每个输入特征和目标变量计算的分数。
请注意,您的特定结果可能会有所不同。
我们可以看到许多特征的分数为0.0,而这已经确定了许多可能与目标有关的特征。
为每个输入特征创建一个特征重要性评分的条形图。
与相关特征选择方法相比,我们可以清楚地看到更多的特征被标记为相关。这可能是由于我们在其构造中添加到数据集中的统计噪声。
输入特征(x)与相关信息特征重要性(y)的条形图
既然我们知道如何针对回归预测建模问题对数字输入数据执行特征选择,那么我们可以尝试使用选定的特征开发模型并比较结果。
有许多不同的技术可用来对特征评分和根据分数选择特征。您怎么知道要使用哪个?
一种可靠的方法是使用不同的特征选择方法(和特征数量)评估模型,然后选择能够产生最佳性能的模型的方法。
在本节中,我们将评估具有所有特征的线性回归模型,并将其与根据相关统计选择的特征和通过互信息选择的特征构建的模型进行比较。
线性回归是测试特征选择方法的良好模型,因为如果从模型中删除了不相关的特征,则线性回归性能会更好。
第一步,我们将使用所有可用特征来评估LinearRegression模型。
该模型适合训练数据集,并在测试数据集上进行评估。
下面列出了完整的示例。
from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_error
# load the dataset
X, y = make_regression(n_samples=1000, n_features=100, n_informative=10, noise=0.1, random_state=1)
# split into train and test sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=1)
# fit the model
model = LinearRegression()
model.fit(X_train, y_train)
# evaluate the model
yhat = model.predict(X_test)
# evaluate predictions
mae = mean_absolute_error(y_test, yhat)
print('MAE: %.3f' % mae)
运行示例将在训练数据集上输出模型的平均绝对误差(MAE)。
由于学习算法的随机性,结果可能会有所不同。
在这种情况下,我们可以看到该模型实现了约0.086的误差。
我们可以使用相关方法对特征进行评分,然后选择10个最相关的特征。
下面的select_features()函数已更新以实现此目的。
# feature selection
def select_features(X_train, y_train, X_test):
# configure to select a subset of features
fs = SelectKBest(score_func=f_regression, k=10)
# learn relationship from training data
fs.fit(X_train, y_train)
# transform train input data
X_train_fs = fs.transform(X_train)
# transform test input data
X_test_fs = fs.transform(X_test)
return X_train_fs, X_test_fs, fs
下面列出了使用此特征选择方法评估线性回归模型拟合和对数据进行评估的完整示例。
from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import f_regression
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_error
# feature selection
def select_features(X_train, y_train, X_test):
# configure to select a subset of features
fs = SelectKBest(score_func=f_regression, k=10)
# learn relationship from training data
fs.fit(X_train, y_train)
# transform train input data
X_train_fs = fs.transform(X_train)
# transform test input data
X_test_fs = fs.transform(X_test)
return X_train_fs, X_test_fs, fs
# load the dataset
X, y = make_regression(n_samples=1000, n_features=100, n_informative=10, noise=0.1, random_state=1)
# split into train and test sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=1)
# feature selection
X_train_fs, X_test_fs, fs = select_features(X_train, y_train, X_test)
# fit the model
model = LinearRegression()
model.fit(X_train_fs, y_train)
# evaluate the model
yhat = model.predict(X_test_fs)
# evaluate predictions
mae = mean_absolute_error(y_test, yhat)
print('MAE: %.3f' % mae)
该示例仅报告了使用相关统计数据选择的100个输入特征中的10个的模型性能。
在这种情况下,我们看到该模型实现了约2.7的误差分,这比使用所有特征并获得0.086的MAE的基线模型要大得多。
这表明,尽管该方法对选择哪些特征有很强的想法,但是仅从这些特征构建模型并不能带来更熟练的模型。这可能是因为对目标很重要的特征被忽略了。
让我们采用另一种方法,尝试使用该方法删除一些冗余特征,而不是所有冗余特征。
为此,我们可以将所选特征的数量设置为一个更大的值(在本例中为88),希望它可以找到并丢弃90个冗余特征中的12个。
下面列出了完整的示例。
该示例报告了使用相关统计量选择的100个输入特征中的88个的模型性能。
在本例中,我们可以看到,删除一些冗余特征使性能有了小幅提升,误差约为0.085,而基线的误差约为0.086。
下面列出了实现此目的的select_features()函数的更新版本。
def select_features(X_train, y_train, X_test):
# configure to select a subset of features
fs = SelectKBest(score_func=mutual_info_regression, k=88)
# learn relationship from training data
fs.fit(X_train, y_train)
# transform train input data
X_train_fs = fs.transform(X_train)
# transform test input data
X_test_fs = fs.transform(X_test)
return X_train_fs, X_test_fs, fs
下面列出了使用相关信息进行特征选择以拟合线性回归模型的完整示例。
from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import mutual_info_regression
from sklearn.linear_model import LinearRegression
from sklearn.metrics import mean_absolute_error
# feature selection
def select_features(X_train, y_train, X_test):
# configure to select a subset of features
fs = SelectKBest(score_func=mutual_info_regression, k=88)
# learn relationship from training data
fs.fit(X_train, y_train)
# transform train input data
X_train_fs = fs.transform(X_train)
# transform test input data
X_test_fs = fs.transform(X_test)
return X_train_fs, X_test_fs, fs
# load the dataset
X, y = make_regression(n_samples=1000, n_features=100, n_informative=10, noise=0.1, random_state=1)
# split into train and test sets
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=1)
# feature selection
X_train_fs, X_test_fs, fs = select_features(X_train, y_train, X_test)
# fit the model
model = LinearRegression()
model.fit(X_train_fs, y_train)
# evaluate the model
yhat = model.predict(X_test_fs)
# evaluate predictions
mae = mean_absolute_error(y_test, yhat)
print('MAE: %.3f' % mae)
运行这个例子符合该模型在前88个选择使用相关信息特征选择。
在这种情况下,我们可以看到与相关统计量相比误差的进一步减少,在这种情况下,相对于上一节的0.085,MAE达到了大约0.084。
在上一个示例中,我们选择了88个特征,但是如何知道要选择的特征数量是否是最佳。
无需猜测,我们可以系统地测试一系列不同数量的所选特征,并发现哪种结果会产生最佳性能的模型。这称为网格搜索,其中可以调整SelectKBest类的k参数。
使用重复分层k-fold cross-validation来评估回归任务上的模型配置是一种好习惯。我们将通过RepeatedKFold类使用三个重复的10倍交叉验证。
cv = RepeatedKFold(n_splits=10, n_repeats=3, random_state=1)
我们可以定义一条通道,以正确准备训练集上的特征选择变换,并将其应用于交叉验证的每一折的训练集和测试集。
在这种情况下,我们将使用相关信息统计方法来选择特征。
# define the pipeline to evaluate
model = LinearRegression()
fs = SelectKBest(score_func=mutual_info_regression)
pipeline = Pipeline(steps=[('sel',fs), ('lr', model)])
然后,我们可以定义值的网格以评估为80到100。
请注意,网格是要搜索的参数到值的字典映射,并且假设我们使用的是Pipeline,则可以通过给我们提供的名称“ sel ”和参数名称“ k ” 来访问SelectKBest对象。两个下划线或“ sel__k ”。
# define the grid
grid = dict()
grid['sel__k'] = [i for i in range(X.shape[1]-20, X.shape[1]+1)]
然后,我们可以定义并运行搜索。
在这种情况下,我们将使用负平均绝对误差(neg_mean_absolute_error)评估模型。它是负数,因为scikit-learn需要使分数最大化,因此MAE变为负数,这意味着分数从-infinity到0(最佳)。
# define the grid search
search = GridSearchCV(pipeline, grid, scoring='neg_mean_absolure_error', n_jobs=-1, cv=cv)
# perform the search
results = search.fit(X, y)
下面列出了完整的示例
from sklearn.datasets import make_regression
from sklearn.model_selection import RepeatedKFold
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import mutual_info_regression
from sklearn.linear_model import LinearRegression
from sklearn.pipeline import Pipeline
from sklearn.model_selection import GridSearchCV
# define dataset
X, y = make_regression(n_samples=1000, n_features=100, n_informative=10, noise=0.1, random_state=1)
# define the evaluation method
cv = RepeatedKFold(n_splits=10, n_repeats=3, random_state=1)
# define the pipeline to evaluate
model = LinearRegression()
fs = SelectKBest(score_func=mutual_info_regression)
pipeline = Pipeline(steps=[('sel',fs), ('lr', model)])
# define the grid
grid = dict()
grid['sel__k'] = [i for i in range(X.shape[1]-20, X.shape[1]+1)]
# define the grid search
search = GridSearchCV(pipeline, grid, scoring='neg_mean_squared_error', n_jobs=-1, cv=cv)
# perform the search
results = search.fit(X, y)
# summarize best
print('Best MAE: %.3f' % results.best_score_)
print('Best Config: %s' % results.best_params_)
# summarize all
means = results.cv_results_['mean_test_score']
params = results.cv_results_['params']
for mean, param in zip(means, params):
print(">%.3f with: %r" % (mean, param))
运行示例选择不同数量的网格搜索功能使用相关信息统计,其中每个建模评估通道使用重复交叉验证。
在这种情况下,我们可以看到,选择的特征的最佳数量是81,这使得MAE达到大约0.082(忽略符号)。
鉴于学习算法和评估程序的随机性,您的具体结果可能会有所不同。尝试运行该示例几次。
我们可能希望查看所选特征的数量与MAE之间的关系。在这种关系中,我们可以预期到更多的特征会带来更好的性能。
通过手动评估SelectKBest从81到100 的k的每个配置,收集MAE分数样本,并使用箱型图和须状图并排绘制结果来探索结果。这些箱形图的分布和均值将显示所选特征的数量与管道的MAE之间任何有趣的关系。
请注意,由于k = 80的MAE分数的分布远大于所考虑的k的所有其他值,因此我们从81而不是80开始了k值的传播。
下面列出了实现此目的的完整示例。
from numpy import mean
from numpy import std
from sklearn.datasets import make_regression
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedKFold
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import mutual_info_regression
from sklearn.linear_model import LinearRegression
from sklearn.pipeline import Pipeline
from matplotlib import pyplot
# define dataset
X, y = make_regression(n_samples=1000, n_features=100, n_informative=10, noise=0.1, random_state=1)
# define number of features to evaluate
num_features = [i for i in range(X.shape[1]-19, X.shape[1]+1)]
# enumerate each number of features
results = list()
for k in num_features:
# create pipeline
model = LinearRegression()
fs = SelectKBest(score_func=mutual_info_regression, k=k)
pipeline = Pipeline(steps=[('sel',fs), ('lr', model)])
# evaluate the model
cv = RepeatedKFold(n_splits=10, n_repeats=3, random_state=1)
scores = cross_val_score(pipeline, X, y, scoring='neg_mean_absolute_error', cv=cv, n_jobs=-1)
results.Append(scores)
# summarize the results
print('>%d %.3f (%.3f)' % (k, mean(scores), std(scores)))
# plot model performance for comparison
pyplot.boxplot(results, labels=num_features, showmeans=True)
pyplot.show()
运行这个示例,显示了每个选定特征数量的平均值和标准差MAE。
在这种情况下,报告MAE的均值和标准差不是很有趣,除了80的k值比90的k值更好。
并排创建了箱形图和须状图,显示了k与MAE 的趋势,其中绿色三角形代表平均值,橙色线代表分布的中值。
在本文中,您发现了如何使用数字输入数据执行特征选择以进行回归预测建模。