特征选择是识别和选择与目标变量最相关的输入特征子集的过程。
当处理实值输入和输出数据时,特征选择通常很简单,例如使用皮尔逊相关系数,但在处理数值输入数据和分类目标变量时可能会很困难。
当输入数据为数值型,目标变量为分类型时(例如分类预测建模),最常用的两种特征选择方法是方差分析 f 检验统计量和互信息统计量。
在本教程中,您将学习如何对分类问题进行数值输入数据的特征选择。
完成本教程后,您将了解:
- 糖尿病预测建模问题,包含数值输入和二分类目标变量。
- 如何使用方差分析 f 检验和互信息统计量评估数值特征的重要性。
- 在拟合和评估分类模型时,如何为数值数据进行特征选择。
开启您的项目,请阅读我的新书《机器学习数据准备》,其中包含分步教程和所有示例的Python源代码文件。
让我们开始吧。

如何使用数值输入数据进行特征选择
照片由 Susanne Nilsson 拍摄,保留部分权利。
教程概述
本教程分为四个部分;它们是
- 糖尿病数值数据集
- 数值特征选择
- 方差分析 f 检验特征选择
- 互信息特征选择
- 使用选定特征进行建模
- 使用所有特征构建的模型
- 使用方差分析 f 检验特征构建的模型
- 使用互信息特征构建的模型
- 调整选定的特征数量
糖尿病数值数据集
作为本教程的基础,我们将使用自1990年以来被广泛研究的机器学习数据集,即所谓的“糖尿病”数据集。
该数据集将患者数据分类为五年内患有糖尿病或未患有糖尿病。有768个样本和八个输入变量。这是一个二分类问题。
一个朴素模型在此数据集上可以达到约65%的准确率。一个好的分数约为77% +/- 5%。我们将以此为目标,但请注意,本教程中的模型未经优化;它们旨在演示特征选择方案。
您可以下载该数据集,并将文件另存为“pima-indians-diabetes.csv”,保存在当前工作目录中。
查看数据,我们可以看到所有九个输入变量都是数值型的。
1 2 3 4 5 6 |
6,148,72,35,0,33.6,0.627,50,1 1,85,66,29,0,26.6,0.351,31,0 8,183,64,0,0,23.3,0.672,32,1 1,89,66,23,94,28.1,0.167,21,0 0,137,40,35,168,43.1,2.288,33,1 ... |
我们可以使用 Pandas 库将此数据集加载到内存中。
1 2 3 4 5 |
... # 将数据集加载为 pandas DataFrame data = read_csv(filename, header=None) # 检索 numpy 数组 dataset = data.values |
加载后,我们可以将列分为输入(X)和输出(y)以进行建模。
1 2 3 4 |
... # 分割为输入 (X) 和输出 (y) 变量 X = dataset[:, :-1] y = dataset[:,-1] |
我们可以将所有这些内容整合到一个有用的函数中,以便以后重复使用。
1 2 3 4 5 6 7 8 9 10 |
# 加载数据集 def load_dataset(filename): # 将数据集加载为 pandas DataFrame data = read_csv(filename, header=None) # 检索numpy数组 dataset = data.values # 分割为输入 (X) 和输出 (y) 变量 X = dataset[:, :-1] y = dataset[:,-1] return X, y |
加载后,我们可以将数据划分为训练集和测试集,以便拟合和评估学习模型。
我们将使用scikit-learn的train_test_split()函数,其中67%的数据用于训练,33%用于测试。
1 2 3 4 5 |
... # 加载数据集 X, y = load_dataset('pima-indians-diabetes.csv') # 拆分为训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=1) |
将所有这些元素结合起来,加载、分割和汇总原始分类数据集的完整示例列在下面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
# 加载并汇总数据集 from pandas import read_csv from sklearn.model_selection import train_test_split # 加载数据集 def load_dataset(filename): # 将数据集加载为 pandas DataFrame data = read_csv(filename, header=None) # 检索numpy数组 dataset = data.values # 分割为输入 (X) 和输出 (y) 变量 X = dataset[:, :-1] y = dataset[:,-1] 返回 X, y # 加载数据集 X, y = load_dataset('pima-indians-diabetes.csv') # 拆分为训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=1) # 总结 print('Train', X_train.shape, y_train.shape) print('Test', X_test.shape, y_test.shape) |
运行示例报告了训练集和测试集的输入和输出元素的尺寸。
我们可以看到,我们有514个样本用于训练,254个样本用于测试。
1 2 |
训练 (514, 8) (514, 1) 测试 (254, 8) (254, 1) |
现在我们已经加载并准备好了糖尿病数据集,我们可以开始探索特征选择。
想开始学习数据准备吗?
立即参加我为期7天的免费电子邮件速成课程(附示例代码)。
点击注册,同时获得该课程的免费PDF电子书版本。
数值特征选择
对于数值输入数据和分类(类别)目标变量,有两种流行的特征选择技术。
它们是
- 方差分析 f 检验统计量。
- 互信息统计量。
让我们依次仔细看看每一个。
方差分析 f 检验特征选择
ANOVA是“方差分析”的缩写,是一种参数统计假设检验,用于确定来自两个或多个样本(通常是三个或更多)的数据均值是否来自同一分布。
F 统计量或 F 检验是一类统计检验,它计算方差值之间的比率,例如两个不同样本的方差,或者像 ANOVA 这样的统计检验所解释和未解释的方差。ANOVA 方法是 F 统计量的一种类型,此处称为 ANOVA f 检验。
重要的是,ANOVA 用于一个变量是数值型而另一个变量是分类型的情况,例如分类任务中的数值输入变量和分类目标变量。
此测试的结果可用于特征选择,其中可以从数据集中移除与目标变量无关的特征。
当结果是数值型,[...] 预测变量有三个或更多级别时,可以计算传统的 ANOVA F 统计量。
— 第242页,《特征工程与选择》,2019年。
scikit-learn 机器学习库在f_classif()函数中提供了 ANOVA f 检验的实现。该函数可用于特征选择策略,例如通过SelectKBest类选择最相关的 k 个特征(值越大越好)。
例如,我们可以配置SelectKBest类使用f_classif()函数并选择所有特征,然后转换训练集和测试集。
1 2 3 4 5 6 7 8 9 |
... # 配置以选择所有特征 fs = SelectKBest(score_func=f_classif, k='all') # 从训练数据中学习关系 fs.fit(X_train, y_train) # 转换训练输入数据 X_train_fs = fs.transform(X_train) # 转换测试输入数据 X_test_fs = fs.transform(X_test) |
然后,我们可以打印每个变量的分数(分数越高越好),并将每个变量的分数绘制成条形图,以了解应该选择多少特征。
1 2 3 4 5 6 7 |
... # 特征得分是多少 for i in range(len(fs.scores_)): print('Feature %d: %f' % (i, fs.scores_[i])) # 绘制分数 pyplot.bar([i for i in range(len(fs.scores_))], fs.scores_) pyplot.show() |
结合上一节中糖尿病数据集的数据准备,完整的示例列在下面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# 用于数值数据的方差分析 f 检验特征选择示例 from pandas import read_csv from sklearn.model_selection import train_test_split 从 sklearn.特征选择 导入 SelectKBest from sklearn.feature_selection import f_classif from matplotlib import pyplot # 加载数据集 def load_dataset(filename): # 将数据集加载为 pandas DataFrame data = read_csv(filename, header=None) # 检索numpy数组 dataset = data.values # 分割为输入 (X) 和输出 (y) 变量 X = dataset[:, :-1] y = dataset[:,-1] 返回 X, y # 特征选择 def select_features(X_train, y_train, X_test): # 配置以选择所有特征 fs = SelectKBest(score_func=f_classif, k='all') # 从训练数据中学习关系 fs.fit(X_train, y_train) # 转换训练输入数据 X_train_fs = fs.transform(X_train) # 转换测试输入数据 X_test_fs = fs.transform(X_test) return X_train_fs, X_test_fs, fs # 加载数据集 X, y = load_dataset('pima-indians-diabetes.csv') # 拆分为训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=1) # 特征选择 X_train_fs, X_test_fs, fs = select_features(X_train, y_train, X_test) # 特征得分是多少 for i in range(len(fs.scores_)): print('Feature %d: %f' % (i, fs.scores_[i])) # 绘制分数 pyplot.bar([i for i in range(len(fs.scores_))], fs.scores_) pyplot.show() |
运行该示例首先打印为每个输入特征和目标变量计算的分数。
注意:由于算法或评估程序的随机性,或数值精度的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
在这种情况下,我们可以看到某些特征比其他特征更相关,具有更高的检验统计量值。
也许特征1、5和7是最相关的。
1 2 3 4 5 6 7 8 |
特征 0: 16.527385 特征 1: 131.325562 特征 2: 0.042371 特征 3: 1.415216 特征 4: 12.778966 特征 5: 49.209523 特征 6: 13.377142 特征 7: 25.126440 |
创建了一个显示每个输入特征的特征重要性分数的条形图。
这清楚地表明,特征1可能是最相关的(根据检验),并且也许八个输入特征中有六个是更相关的。
在配置SelectKBest时,我们可以将 k 设置为6,以选择这四个最相关的特征。

输入特征 (x) 与方差分析 f 检验特征重要性 (y) 的条形图
互信息特征选择
信息论领域的互信息是将信息增益(通常用于决策树的构建)应用于特征选择。
互信息在两个变量之间计算,并衡量给定另一个变量已知值时,一个变量不确定性的减少量。
您可以在以下教程中了解更多关于互信息的信息。
当考虑两个离散(分类或有序)变量的分布时,互信息很简单,例如分类输入和分类输出数据。尽管如此,它可以改编用于数值输入和分类输出。
有关如何实现此目的的技术细节,请参阅2014年题为“离散和连续数据集之间的互信息”的论文。
scikit-learn 机器学习库通过mutual_info_classif()函数提供了用于数值输入和分类输出变量特征选择的互信息实现。
与f_classif()一样,它也可以用于SelectKBest特征选择策略(以及其他策略)。
1 2 3 4 5 6 7 8 9 |
... # 配置以选择所有特征 fs = SelectKBest(score_func=mutual_info_classif, k='all') # 从训练数据中学习关系 fs.fit(X_train, y_train) # 转换训练输入数据 X_train_fs = fs.transform(X_train) # 转换测试输入数据 X_test_fs = fs.transform(X_test) |
我们可以使用互信息对糖尿病数据集进行特征选择,并打印和绘制分数(分数越高越好),就像我们在上一节中所做的那样。
用于数值特征选择的互信息的完整示例将在下面列出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# 用于数值输入数据互信息特征选择的示例 from pandas import read_csv from sklearn.model_selection import train_test_split 从 sklearn.特征选择 导入 SelectKBest from sklearn.feature_selection import mutual_info_classif from matplotlib import pyplot # 加载数据集 def load_dataset(filename): # 将数据集加载为 pandas DataFrame data = read_csv(filename, header=None) # 检索numpy数组 dataset = data.values # 分割为输入 (X) 和输出 (y) 变量 X = dataset[:, :-1] y = dataset[:,-1] 返回 X, y # 特征选择 def select_features(X_train, y_train, X_test): # 配置以选择所有特征 fs = SelectKBest(score_func=mutual_info_classif, k='all') # 从训练数据中学习关系 fs.fit(X_train, y_train) # 转换训练输入数据 X_train_fs = fs.transform(X_train) # 转换测试输入数据 X_test_fs = fs.transform(X_test) return X_train_fs, X_test_fs, fs # 加载数据集 X, y = load_dataset('pima-indians-diabetes.csv') # 拆分为训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=1) # 特征选择 X_train_fs, X_test_fs, fs = select_features(X_train, y_train, X_test) # 特征得分是多少 for i in range(len(fs.scores_)): print('Feature %d: %f' % (i, fs.scores_[i])) # 绘制分数 pyplot.bar([i for i in range(len(fs.scores_))], fs.scores_) pyplot.show() |
运行该示例首先打印为每个输入特征和目标变量计算的分数。
注意:由于算法或评估程序的随机性,或数值精度的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
在这种情况下,我们可以看到一些特征的分数相当低,这表明它们可能可以被移除。
也许特征1和5是最相关的。
1 2 3 4 5 6 7 |
特征 1: 0.118431 特征 2: 0.019966 特征 3: 0.041791 特征 4: 0.019858 特征 5: 0.084719 特征 6: 0.018079 特征 7: 0.033098 |
创建了一个显示每个输入特征的特征重要性分数的条形图。
重要的是,一种不同的特征组合得到了推广。

输入特征 (x) 与互信息特征重要性 (y) 的条形图
现在我们知道如何为分类预测建模问题对数值输入数据进行特征选择,我们可以尝试使用选定的特征来开发模型,并比较结果。
使用选定特征进行建模
有许多不同的技术可以用于特征评分和基于分数选择特征;您如何知道应该使用哪一种?
一种稳健的方法是使用不同的特征选择方法(和特征数量)评估模型,并选择能够产生最佳性能模型的方法。
在本节中,我们将评估一个逻辑回归模型,比较使用所有特征的模型与使用方差分析 f 检验选定的特征以及使用互信息选定的特征构建的模型。
逻辑回归是测试特征选择方法的良好模型,因为如果从模型中移除不相关的特征,它可以表现得更好。
使用所有特征构建的模型
作为第一步,我们将评估一个LogisticRegression模型,该模型使用所有可用特征。
该模型在训练数据集上进行拟合,并在测试数据集上进行评估。
完整的示例如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
# 使用所有输入特征评估模型 from pandas import read_csv from sklearn.model_selection import train_test_split from sklearn.linear_model import LogisticRegression from sklearn.metrics import accuracy_score # 加载数据集 def load_dataset(filename): # 将数据集加载为 pandas DataFrame data = read_csv(filename, header=None) # 检索numpy数组 dataset = data.values # 分割为输入 (X) 和输出 (y) 变量 X = dataset[:, :-1] y = dataset[:,-1] 返回 X, y # 加载数据集 X, y = load_dataset('pima-indians-diabetes.csv') # 拆分为训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=1) # 拟合模型 model = LogisticRegression(solver='liblinear') model.fit(X_train, y_train) # 评估模型 yhat = model.predict(X_test) # 评估预测 accuracy = accuracy_score(y_test, yhat) print('Accuracy: %.2f' % (accuracy*100)) |
运行示例将打印模型在训练数据集上的准确率。
注意:由于算法或评估程序的随机性,或数值精度的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
在这种情况下,我们可以看到模型达到了约77%的分类准确率。
我们更希望使用一个特征子集,该子集能够达到与此相当或更好的分类准确率。
1 |
准确率: 77.56 |
使用方差分析 f 检验特征构建的模型
我们可以使用方差分析 f 检验对特征进行评分,并选择四个最相关的特征。
下面的 select_features() 函数已更新以实现此目的。
1 2 3 4 5 6 7 8 9 10 11 |
# 特征选择 def select_features(X_train, y_train, X_test): # 配置以选择特征子集 fs = SelectKBest(score_func=f_classif, k=4) # 从训练数据中学习关系 fs.fit(X_train, y_train) # 转换训练输入数据 X_train_fs = fs.transform(X_train) # 转换测试输入数据 X_test_fs = fs.transform(X_test) return X_train_fs, X_test_fs, fs |
使用此特征选择方法拟合和评估的逻辑回归模型的完整示例将在下面列出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
# 使用方差分析 f 检验选择的 4 个特征的模型评估 from pandas import read_csv from sklearn.model_selection import train_test_split 从 sklearn.特征选择 导入 SelectKBest from sklearn.feature_selection import f_classif from sklearn.linear_model import LogisticRegression from sklearn.metrics import accuracy_score # 加载数据集 def load_dataset(filename): # 将数据集加载为 pandas DataFrame data = read_csv(filename, header=None) # 检索numpy数组 dataset = data.values # 分割为输入 (X) 和输出 (y) 变量 X = dataset[:, :-1] y = dataset[:,-1] 返回 X, y # 特征选择 def select_features(X_train, y_train, X_test): # 配置以选择特征子集 fs = SelectKBest(score_func=f_classif, k=4) # 从训练数据中学习关系 fs.fit(X_train, y_train) # 转换训练输入数据 X_train_fs = fs.transform(X_train) # 转换测试输入数据 X_test_fs = fs.transform(X_test) return X_train_fs, X_test_fs, fs # 加载数据集 X, y = load_dataset('pima-indians-diabetes.csv') # 拆分为训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=1) # 特征选择 X_train_fs, X_test_fs, fs = select_features(X_train, y_train, X_test) # 拟合模型 model = LogisticRegression(solver='liblinear') model.fit(X_train_fs, y_train) # 评估模型 yhat = model.predict(X_test_fs) # 评估预测 accuracy = accuracy_score(y_test, yhat) print('Accuracy: %.2f' % (accuracy*100)) |
运行示例将报告模型在方差分析 f 检验统计量选择的八个输入特征中的四个上的性能。
注意:由于算法或评估程序的随机性,或数值精度的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
在这种情况下,我们发现模型的准确率为 78.74%,比基线模型的 77.56% 提高了性能。
1 |
准确率: 78.74 |
使用互信息特征构建的模型
我们可以重复实验,并使用互信息统计量选择排名前四的特征。
下面列出了更新后的 *select_features()* 函数以实现此目的。
1 2 3 4 5 6 7 8 9 10 11 |
# 特征选择 def select_features(X_train, y_train, X_test): # 配置以选择特征子集 fs = SelectKBest(score_func=mutual_info_classif, k=4) # 从训练数据中学习关系 fs.fit(X_train, y_train) # 转换训练输入数据 X_train_fs = fs.transform(X_train) # 转换测试输入数据 X_test_fs = fs.transform(X_test) return X_train_fs, X_test_fs, fs |
使用互信息进行特征选择以拟合逻辑回归模型的完整示例将在下面列出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
# 使用互信息选择的 4 个特征的模型评估 from pandas import read_csv from sklearn.model_selection import train_test_split 从 sklearn.特征选择 导入 SelectKBest from sklearn.feature_selection import mutual_info_classif from sklearn.linear_model import LogisticRegression from sklearn.metrics import accuracy_score # 加载数据集 def load_dataset(filename): # 将数据集加载为 pandas DataFrame data = read_csv(filename, header=None) # 检索numpy数组 dataset = data.values # 分割为输入 (X) 和输出 (y) 变量 X = dataset[:, :-1] y = dataset[:,-1] 返回 X, y # 特征选择 def select_features(X_train, y_train, X_test): # 配置以选择特征子集 fs = SelectKBest(score_func=mutual_info_classif, k=4) # 从训练数据中学习关系 fs.fit(X_train, y_train) # 转换训练输入数据 X_train_fs = fs.transform(X_train) # 转换测试输入数据 X_test_fs = fs.transform(X_test) return X_train_fs, X_test_fs, fs # 加载数据集 X, y = load_dataset('pima-indians-diabetes.csv') # 拆分为训练集和测试集 X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.33, random_state=1) # 特征选择 X_train_fs, X_test_fs, fs = select_features(X_train, y_train, X_test) # 拟合模型 model = LogisticRegression(solver='liblinear') model.fit(X_train_fs, y_train) # 评估模型 yhat = model.predict(X_test_fs) # 评估预测 accuracy = accuracy_score(y_test, yhat) print('Accuracy: %.2f' % (accuracy*100)) |
运行示例将在使用互信息选择的前四个特征上拟合模型。
注意:由于算法或评估程序的随机性,或数值精度的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
在这种情况下,我们发现与基线模型相比没有差异。这一点很有趣,因为我们知道该方法选择了与前一种方法不同的四个特征。
1 |
准确率: 77.56 |
调整选定的特征数量
在前面的示例中,我们选择了四个特征,但我们如何知道这是一个好的或最佳的特征选择数量呢?
与其猜测,不如系统地测试一系列不同的选定特征数量,并找出哪种特征能带来最佳的模型性能。这称为网格搜索,其中SelectKBest类中的 k 参数可以进行调整。
评估分类任务上的模型配置的最佳实践是使用重复分层 k-折叠交叉验证。我们将通过RepeatedStratifiedKFold类使用10折交叉验证的3次重复。
1 2 3 |
... # 定义评估方法 cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1) |
我们可以定义一个Pipeline,它可以在每个交叉验证折叠中正确地在训练集上准备特征选择转换,并将其应用于训练集和测试集。
在这种情况下,我们将使用方差分析 f 检验统计方法来选择特征。
1 2 3 4 5 |
... # 定义要评估的管道 model = LogisticRegression(solver='liblinear') fs = SelectKBest(score_func=f_classif) pipeline = Pipeline(steps=[('anova',fs), ('lr', model)]) |
然后,我们可以定义要评估的值网格,从1到8。
请注意,网格是参数到值的字典,并且由于我们使用的是Pipeline,我们可以通过我们给它起的名称“anova”来访问SelectKBest对象,然后是参数名称“k”,用双下划线分隔,即“anova__k”。
1 2 3 4 |
... # 定义网格 grid = dict() grid['anova__k'] = [i+1 for i in range(X.shape[1])] |
然后,我们可以定义并运行搜索。
1 2 3 4 5 |
... # 定义网格搜索 search = GridSearchCV(pipeline, grid, scoring='accuracy', n_jobs=-1, cv=cv) # 执行搜索 results = search.fit(X, y) |
将这些结合起来,完整的示例列在下面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
# 比较使用方差分析 f 检验选择的不同特征数量 from pandas import read_csv from sklearn.model_selection import RepeatedStratifiedKFold 从 sklearn.特征选择 导入 SelectKBest from sklearn.feature_selection import f_classif from sklearn.linear_model import LogisticRegression from sklearn.pipeline import Pipeline from sklearn.model_selection import GridSearchCV from matplotlib import pyplot # 加载数据集 def load_dataset(filename): # 将数据集加载为 pandas DataFrame data = read_csv(filename, header=None) # 检索numpy数组 dataset = data.values # 分割为输入 (X) 和输出 (y) 变量 X = dataset[:, :-1] y = dataset[:,-1] 返回 X, y # 定义数据集 X, y = load_dataset('pima-indians-diabetes.csv') # 定义评估方法 cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1) # 定义要评估的管道 model = LogisticRegression(solver='liblinear') fs = SelectKBest(score_func=f_classif) pipeline = Pipeline(steps=[('anova',fs), ('lr', model)]) # 定义网格 grid = dict() grid['anova__k'] = [i+1 for i in range(X.shape[1])] # 定义网格搜索 search = GridSearchCV(pipeline, grid, scoring='accuracy', n_jobs=-1, cv=cv) # 执行搜索 results = search.fit(X, y) # 总结最佳结果 print('Best Mean Accuracy: %.3f' % results.best_score_) print('Best Config: %s' % results.best_params_) |
运行示例使用方差分析 f 检验对不同数量的选定特征进行网格搜索,其中使用重复交叉验证评估每个模型管道。
注意:由于算法或评估程序的随机性,或数值精度的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
在这种情况下,我们可以看到最佳的选定特征数量是七个;这可以达到约77%的准确率。
1 2 |
最佳平均准确率: 0.770 最佳配置: {'anova__k': 7} |
我们可能想看看选定的特征数量与分类准确率之间的关系。在这种关系中,我们期望更多的特征会带来更好的性能,但有一个临界点。
可以通过手动评估SelectKBest从1到8的每个k配置来探索这种关系,收集准确率得分样本,并使用并列的箱须图绘制结果。这些箱图的分布和均值应能显示选定特征数量与管道分类准确率之间的任何有趣关系。
实现此目的的完整示例将在下面列出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 |
# 比较使用方差分析 f 检验选择的不同特征数量 from numpy import mean from numpy import std from pandas import read_csv from sklearn.model_selection import cross_val_score from sklearn.model_selection import RepeatedStratifiedKFold 从 sklearn.特征选择 导入 SelectKBest from sklearn.feature_selection import f_classif from sklearn.linear_model import LogisticRegression from sklearn.pipeline import Pipeline from matplotlib import pyplot # 加载数据集 def load_dataset(filename): # 将数据集加载为 pandas DataFrame data = read_csv(filename, header=None) # 检索numpy数组 dataset = data.values # 分割为输入 (X) 和输出 (y) 变量 X = dataset[:, :-1] y = dataset[:,-1] 返回 X, y # 使用交叉验证评估给定模型 def evaluate_model(model, X, y): cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1) scores = cross_val_score(model, X, y, scoring='accuracy', cv=cv, n_jobs=-1, error_score='raise') 返回 分数 # 定义数据集 X, y = load_dataset('pima-indians-diabetes.csv') # 定义要评估的特征数量 num_features = [i+1 for i in range(X.shape[1])] # 枚举特征数量 results = list() for k in num_features: # 创建管道 model = LogisticRegression(solver='liblinear') fs = SelectKBest(score_func=f_classif, k=k) pipeline = Pipeline(steps=[('anova',fs), ('lr', model)]) # 评估模型 scores = evaluate_model(pipeline, X, y) results.append(scores) # 总结结果 print('>%d %.3f (%.3f)' % (k, mean(scores), std(scores))) # 绘制模型性能以供比较 pyplot.boxplot(results, labels=num_features, showmeans=True) pyplot.show() |
运行示例首先报告每个选定特征数量的平均准确率和标准差。
注意:由于算法或评估程序的随机性,或数值精度的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
在这种情况下,选择五个和七个特征的准确率似乎差不多。
1 2 3 4 5 6 7 8 |
>1 0.748 (0.048) >2 0.756 (0.042) >3 0.761 (0.044) >4 0.759 (0.042) >5 0.770 (0.041) >6 0.766 (0.042) >7 0.770 (0.042) >8 0.768 (0.040) |
创建并排的箱形图,显示平均准确率随选定特征数量增加的趋势,直到五个特征,之后可能会变得不太稳定。
在这种情况下,选择五个特征可能是一个合适的配置。

使用 ANOVA f 检验的每个选定特征数量的分类准确率的箱形图
进一步阅读
如果您想深入了解,本节提供了更多关于该主题的资源。
教程
书籍
- 特征工程与选择, 2019.
论文
- 离散和连续数据集之间的互信息, 2014.
API
- 特征选择,Scikit-Learn 用户指南.
- sklearn.feature_selection.f_classif API.
- sklearn.feature_selection.mutual_info_classif API.
- sklearn.feature_selection.SelectKBest API.
文章
数据集
总结
在本教程中,您学习了如何对分类任务的数值输入数据执行特征选择。
具体来说,你学到了:
- 糖尿病预测建模问题,包含数值输入和二分类目标变量。
- 如何使用方差分析 f 检验和互信息统计量评估数值特征的重要性。
- 在拟合和评估分类模型时,如何为数值数据进行特征选择。
你有什么问题吗?
在下面的评论中提出你的问题,我会尽力回答。
给我们很大的动力。您会为一些 NSE/BSE 的股票预测做吗?如何为某些股票应用特征选择并预测未来价格。
谢谢。
可能不会
https://machinelearning.org.cn/faq/single-faq/can-you-help-me-with-machine-learning-for-finance-or-the-stock-market
这太棒了,非常有帮助……我正在从事特征工程和学习,这涉及的范围很广。
谢谢!
Jason,精彩的帖子。您的代码总是非常简洁,而且结果可以快速涵盖,即使它非常技术性。我读了它,因为我现在正在努力解决同一个问题:找出分类器的最佳特征数量,主要是为了提高计算时间。问题:您已经为 LogisticRegression(solver='liblinear') 做了这个。我们似乎找到了最佳特征数量,但据我所理解,这仅适用于此特定估计器。但是现在,我们可以推广这个经验吗?
换句话说,我们如何将此信息推断到其他分类器上,可能是在同一数据集上?我认为答案很大程度上取决于分类器的类型。我认为没有简短的答案(或者有吗?),所以也许您想在一段时间后考虑一个后续帖子。无论如何,以上内容已经非常有用,谢谢。
谢谢!
很好的问题。
不。您可能需要测试不同的表示形式与不同的模型,以发掘对您的数据集来说效果好/最好的内容。如果您在模型之间看到相同的特征/结果,那么您可以说这些发现对您的数据集来说是跨模型通用的。
非常感谢您的教程,它们始终是信息和帮助的绝佳来源。
谢谢!
老师,您的帖子很有帮助,而且您提到的每个点都易于理解。谢谢
谢谢,很高兴听到这个。
嗨,Jason,
非常有用的教程。
我们如何最终知道在应用 Grid SearchCV 和 RepeatedStratifiedKFold 类后选择了哪些特征?
我的意思是,如何打印它们?
谢谢!!
我们不这样做。
了解哪些特征能带来最佳模型性能,与为了了解数据集而进行的特征选择是不同的问题。
例如,在后一种情况下,您可以使用完整数据集和所选配置,以及独立的建模管道来列出该过程中选定的特征。
嗨,Jason,
精彩的教程!谢谢。
说实话,我认为有很多选项(和您一样多的教程)需要测试,才能为每个特定问题获得最终的特征重要性、降维或选择。您有该主题的完整教程列表吗?
无论如何,关于“SelectKBest”,我对 Sklearn 函数描述中阅读的示例应用感到惊喜
https://scikit-learn.cn/stable/modules/generated/sklearn.feature_selection.SelectKBest.html
即该函数也可以应用于图像数据集(而不仅仅是像本例这样的表格数据)……例如,我在“load_digits”示例中看到 SelectKBest 可以计算每张图像的 64 个像素中的最佳 20 个像素……所以我的问题是
1) SelectKbest 也是计算机视觉的有效方法吗?
2) 在示例中,他们最终选择从 64 个像素中选择 20 个像素 = 8x8 原始像素……我如何知道最终选择的是哪些像素?
3) 您有将 SelectKBest 应用于图像的教程吗?
谢谢你,Jason。
谢谢。
不确定您在问什么,您是指所有数据准备教程吗?
https://machinelearning.org.cn/category/data-preparation/
我很惊讶人们在像素上使用特征选择,乍一看似乎很疯狂。
我没有这方面的例子,我需要考虑一下。
嗨 Jason
我自己通过“SelectKBest()”上的几个简单代码行进行了实验。
关于上述思考
from sklearn.datasets import load_digits
from sklearn.feature_selection import SelectKBest, chi2
X, y = load_digits(return_X_y=True)
X.shape
(1797, 64)
X_new = SelectKBest(chi2, k=25).fit_transform(X, y)
X_new.shape
(1797, 25)
我决定应用几个 Sklearn 模型,例如(Dummy()、logisticRegression()、SVC()、ExtraTreesClassifier()、RandomForestClassifier()、XGBClassifier()、GradientBoostingClassifier() 和 BaggingClassifier()),用于 Sklear 数字数据集问题(我认为它是 MNIST 28x28 的 8x8 像素图像的简单版本)。
当然,我与缩减后的图像进行了比较(而不是 64 个像素,我决定选择最佳的 25 个像素 - 5x5),最后我将准确率作为指标进行比较,并且对于仅 25 个像素而不是 64 个像素的缩减图像,我只损失了不到 1% 的准确率。
因此,结论是我们可以减少图像像素以获得简单且更快的训练数据集,并且仅损失 0.5% 的准确率……所以它有效!
我使用了数据集像素归一化(0 到 1 之间),但没有对 Sklearn 模型进行任何标签编码……例如,我以 SVC 模型作为图像数字多分类的最佳模型获得了 98.9% 的准确率。我甚至绘制了仅 5x5 像素 = 25 个像素的缩减图像,并获得了机器可以很好地解释但我不行的缩减特征(我无法识别其中的数字 :-))。
我相信您会通过新教程扩展这个强大的图像缩减工具,使用特征降维或选择技术……同时,我很高兴能试验这些技术,并为您的 ML 社区中的每个人打开这扇门……
所以再一次,您激励我们通过您出色的教程进行许多实验。谢谢 Jason!
非常棒,谢谢分享!
嗨,Jason,
当使用 ANOVA f-test 统计量选择特征数量时,如果我的目标变量不平衡,我是否应该先选择特征数量,然后转换我的输入数据(因为它们不是正态分布的),然后再对少数类进行过采样(例如使用 smote),而目标变量不平衡?或者我应该按其他顺序进行?提前感谢!😉
是的,理想情况下,您应该在对基于 knn 的行重采样使用之前,应用数据准备,如特征选择和更改概率分布。
谢谢你,Jason 🙂
不客气。
当数据集包含数值和分类数据时,我们能否使用这些方法?我的数据集中包含一些数值特征和一些分类数据,这些数据已被标签编码特征替换。应该使用哪些特征选择方法更好?
以上适用于数值变量。
如果您同时拥有这两种类型的输入数据,您可以使用 RFE
https://machinelearning.org.cn/rfe-feature-selection-in-python/
或者 ColumnTransformer
https://machinelearning.org.cn/columntransformer-for-numerical-and-categorical-data/
在计算连续变量的互信息之前,我们需要标准化吗?
也许可以评估一下在选择之前和之后对模型进行缩放的效果,并比较结果。
谢谢!我听说 selectkbest 通常只对正态分布的特征有用。我正在使用基于树的模型。因此,我不会进行标准化/归一化,但现在我想知道当我的数据不符合正态假设时,使用 selectKbest 是否是最佳选择。
这正确吗?
我可以在 sklearn 中使用哪些非参数特征选择方法?
Hi Zjo……以下是关于“selectkbest”用法的绝佳资源。
https://www.datatechnotes.com/2021/02/seleckbest-feature-selection-example-in-python.html
您引用的论文提到了基于最近邻的算法修改,用于数值和连续变量的任务。您在此处描述的方法不同。
感谢您的反馈 Anjali!
非常感谢您的教程,我获得了关于特征选择的大量信息。
我想问一个关于特征选择的问题
我的数据集包含两种数据类型:分类和数值。目标是分类(0 和 1)。我对数值数据类型进行了 ANOVA,对分类数据进行了卡方检验。但是,当我测试我的数值数据时,没有选择任何特征。所以,当我查看评论中的建议时,我看到了关于使用 RFE 的建议。但我无法对其进行操作,因为我使用的是 ANN – 反向传播算法进行建模。为了解决这个问题,您是否有参考资料或教程?
谢谢你。
Hi Dede……以下资源可能有助于决定特征选择方法
https://machinelearning.org.cn/feature-selection-with-real-and-categorical-data/
嗨,Jason,
感谢这篇精彩的文章。
我有一个关于 anova ftest 先决条件的问题。
“为了使用 ANOVA F-Test,每个组都必须是正态分布的”。
我们如何确保数据是正态分布的?
此示例中的哪个步骤使数据呈正态分布?
如果数据集不具有正态分布,我们能使用 anova 吗?
我们在这里使用的是单向 anova 还是双向 anova?如果我问的是基础问题,我很抱歉……。