特征选择综合指南(附代码)

来源:小Z的科研日常


引言

在现代机器学习应用中,我们经常面临着处理大量数据和特征的挑战。但并非所有特征都对模型构建有用。

实际上,一些无关的或冗余的特征可能会降低模型的性能,导致过拟合,增加计算负担,甚至使模型解释变得困难。因此,特征选择成为了优化机器学习模型的关键步骤。

而特征选择的意义:

减少计算资源:减少特征数量意味着模型训练需要更少的计算资源,这对于处理大型数据集尤为重要。

避免过拟合:过多的特征可能导致模型过度适应训练数据(过拟合),从而降低其在新数据上的表现。

提高模型可解释性:简化的模型通常更易于理解和解释,这对于机器学习模型的实际应用至关重要。

目录:
1. 过滤方法
2. 包装方法
3. 嵌入方法
4. 如何选择合适的特征选择方法
5. 特征选择的技巧和窍门
6. 结论


▎过滤方法

过滤方法通常被用作预处理步骤,在特征选择过程中,它们不依赖于任何机器学习算法。相反,这些方法基于特征与结果变量的相关性在各种统计测试中的得分来选择特征。这些方法的特点如下:

  • 依赖于数据的特征(特征的特性)。
  • 不使用机器学习算法。
  • 模型不可知(与模型无关)
  • 计算成本相对较低。
  • 通常比包装方法提供更低的预测性能。
  • 非常适合快速筛选和移除无关特征。

过滤方法包含以下几种技术:

1. 基本方法:移除在数据集中不变或几乎不变的特征。

2. 单变量特征选择:基于单变量统计测试选择特征。

3. 信息增益:评估特征提供多少信息以预测结果变量。

4. 费舍尔得分:根据费舍尔评分(Fisher Score)选择特征。

5. 方差分析(ANOVA)F值特征选择:使用ANOVA F值来评估特征的重要性。

6. 相关性矩阵与热图:利用相关性矩阵和热图识别和选择相关特征。

过滤方法可以通过以下图解进行说明:

特征选择综合指南(附代码)


1. 基本方法

移除常数特征

常数特征是指在数据集的所有观测中都显示相同的值,即数据集的所有行都具有相同的数值。这些特征不提供任何信息,无法让机器学习模型进行区分或预测目标。

识别并移除常数特征是进行特征选择和更容易解释的机器学习模型的第一步。要识别常数特征,我们可以使用sklearn中的VarianceThreshold函数。

本文将演示如何使用客户满意度数据集来识别常数特征,代码如下:

import numpy as np  
import pandas as pd  
import matplotlib.pyplot as plt
import seaborn as sns
import warnings
warnings.filterwarnings('ignore')

X_train = pd.read_csv('train.csv', nrows=35000)
X_test = pd.read_csv('test.csv', nrows=15000)

# 从X_train中删除TARGET标签
X_train.drop(labels=['TARGET'], axis=1, inplace=True)

# 检查训练集和测试集的形状
X_train.shape, X_test.shape

在所有特征选择过程中,一种良好的做法是仅通过检查训练集来选择特征。这是为了避免过拟合。

使用sklearn中的方差阈值,sklearn中的方差阈值是一种简单的基线特征选择方法。它会移除所有方差不符合某个阈值的特征。默认情况下,它会移除所有零方差的特征,即在所有样本中具有相同值的特征。

# 使用sklearn的VarianceThreshold来查找常数特征
from sklearn.feature_selection import VarianceThreshold

sel = VarianceThreshold(threshold=0)
sel.fit(X_train)  # fit用于查找方差为零的特征

# get_support是一个布尔向量,指示保留哪些特征
# 如果我们对get_support求和,我们将得到不是常数的特征数量
print(sum(sel.get_support()))

# 寻找非常数特征的另一种方法
len(X_train.columns[sel.get_support()])
# 打印常数特征
print(
    len([
        x for x in X_train.columns
        if x not in X_train.columns[sel.get_support()]
    ]))

[x for x in X_train.columns if x not in X_train.columns[sel.get_support()]]

我们可以看到有 51 列/变量是常数。这意味着这 51 个变量在训练集的所有观测中都显示相同的值,只有一个值。

然后,我们使用 transform 函数来减少训练集和测试集的特征。

X_train = sel.transform(X_train)
X_test = sel.transform(X_test)
# 检查训练集和测试集的形状
print("训练集形状:", X_train.shape)
print("测试集形状:", X_test.shape)

通过移除常数特征,我们可以看到成功地减少了特征空间的大小。

移除准常数特征

准常数特征是指在数据集的大多数观测中都显示相同的值。通常情况下,这些特征提供的信息很少,几乎无法让机器学习模型进行区分或预测目标。但也可能存在例外情况,因此在移除这些类型的特征时,我们应该谨慎。识别并移除准常数特征是进行特征选择和更易于解释的机器学习模型的第一步。

要识别准常数特征,我们可以再次使用sklearn中VarianceThreshold函数。

本小节将演示如何使用Santander客户满意度数据集来识别准常数特征。

# 从Kaggle导入Santander客户满意度数据集

X_train = pd.read_csv('train.csv', nrows=35000)
X_test = pd.read_csv('test.csv', nrows=15000)

# 从X_train中删除TARGET标签

X_train.drop(labels=['TARGET'], axis=1, inplace=True)

# 检查训练集和测试集的形状

print("训练集形状:", X_train.shape)
print("测试集形状:", X_test.shape)

使用sklearn的方差阈值,sklearn中的方差阈值是一种简单的特征选择基线方法。它会移除所有方差不符合某个阈值的特征。默认情况下,它会移除所有零方差的特征,即在所有样本中具有相同值的特征。

在这里,我将更改默认阈值以移除准常数特征。

sel = VarianceThreshold(threshold=0.01)  # 0.01 表示约占 99% 观测数据

sel.fit(X_train)  # fit 用于查找方差较低的特征

# get_support 是一个布尔向量,指示保留哪些特征
# 如果我们对 get_support 求和,我们得到不是准常数特征的数量
sum(sel.get_support())

# 执行上述操作的另一种方法:
len(X_train.columns[sel.get_support()])

# 最后,我们可以打印准常数特征
print(
    len([
        x for x in X_train.columns
        if x not in X_train.columns[sel.get_support()]
    ]))

[x for x in X_train.columns if x not in X_train.columns[sel.get_support()]]

我们可以看到有 107 列/变量几乎是常数。这意味着这 107 个变量在训练集的观测中占据了约 99% 的比例,主要显示一个值。

# 每个不同数值显示的观测百分比
(X_train['ind_var31'].value_counts() / np.float(len(X_train)))

我们可以看到超过 99% 的观测显示一个值,即 0。因此,这个特征几乎是常数。

X_train = sel.transform(X_train)
X_test = sel.transform(X_test)

print(X_train.shape)
print(X_test.shape)

通过移除常数和准常数特征,我们将特征空间从370减少到263。可以看到,从当前数据集中移除了100多个特征。

2. 单变量选择方法

单变量特征选择方法通过基于单变量统计测试(如ANOVA)来选择最佳特征。它可以被视为估计器的预处理步骤。Scikit-learn将特征选择例程公开为实现transform方法的对象。

基于F-测试的方法估计两个随机变量之间的线性依赖程度。它们假定特征和目标之间存在线性关系。这些方法还假定变量遵循高斯分布。

在这个类别下有4种方法:

  • SelectKBest
  • SelectPercentile
  • SelectFpr、SelectFdr或错误率SelectFwe
  • GenericUnivariateSelection

在这里,我将限制讨论到SelectKBest和SelectPercentile,因为这两种方法在实际中最常被使用。

SelectKBest

这个方法根据最高分数选择特征。例如,我们可以对鸢尾花数据集的样本进行卡方检验,以仅检索出最佳的两个特征,如下所示:

from sklearn.datasets import load_iris
from sklearn.feature_selection import SelectKBest, chi2

# 加载鸢尾花数据集
X, y = load_iris(return_X_y=True)
print(X.shape)
# 选择最佳的两个特征
X_new = SelectKBest(chi2, k=2).fit_transform(X, y)
print(X_new.shape)

因此,我们从鸢尾花数据集中选择了最佳的两个特征。

SelectPercentile

根据最高分数的百分位数来选择特征。

from sklearn.datasets import load_digits
from sklearn.feature_selection import SelectPercentile, chi2

X, y = load_digits(return_X_y=True)
# 根据前10%选择特征
X_new = SelectPercentile(chi2, percentile=10).fit_transform(X, y)

我们可以看到只有7个特征位于前10%的百分位数,因此我们相应地选择了它们。

这些对象以一个返回单变量得分和p值(或仅对于SelectKBest和SelectPercentile的得分)的评分函数作为输入:

  • 对于回归任务:f_regression, mutual_info_regression
  • 对于分类任务:chi2, f_classif, mutual_info_classif

基于F-测试的方法估计两个随机变量之间的线性依赖程度。另一方面,互信息方法可以捕捉任何类型的统计依赖性,但由于是非参数的,因此需要更多的样本进行准确估计。

稀疏数据的特征选择,如果使用稀疏数据(即表示为稀疏矩阵的数据),chi2、mutual_info_regression和mutual_info_classif将处理数据而不会使其变得密集。

注意,不要在分类问题中使用回归评分函数,否则将得到无用的结果。

3. 信息增益

信息增益或互信息测量了特征的存在/缺失对正确预测目标的贡献有多大。

互信息测量了X和Y之间共享的信息:它衡量了知道其中一个变量如何降低对另一个变量的不确定性。例如,如果X和Y是独立的,那么知道X不会提供任何关于Y的信息,反之亦然,因此它们的互信息为零。

在另一个极端情况下,如果X是Y的确定性函数,而Y是X的确定性函数,那么X传递的所有信息与Y共享:知道X确定了Y的值,反之亦然。因此,在这种情况下,互信息与仅包含在Y(或X)中的不确定性相同,即Y(或X)的熵。

此外,这种互信息与X的熵和Y的熵相同。(这是当X和Y是相同的随机变量的一个非常特殊的情况。)

mutual_info_classif

  • 它用于估计离散目标变量的互信息。
  • 两个随机变量之间的互信息(MI)是一个非负值,它衡量了变量之间的依赖关系。只有当两个随机变量是独立的时,它才等于零,而较高的值意味着较高的依赖性。
  • 该函数依赖于基于k最近邻距离的熵估计的非参数方法。
  • 它可以用于单变量特征选择。

mutual_info_regression

  • 用于估计连续目标变量的互信息。
  • 两个随机变量之间的互信息(MI)是一个非负值,它衡量了变量之间的依赖关系。只有当两个随机变量是独立的时,它才等于零,而较高的值意味着较高的依赖性。
  • 该函数依赖于基于k最近邻距离的熵估计的非参数方法。
  • 它可以用于单变量特征选择。

4. Fisher分数(卡方实现)

这是scikit-learn中的卡方实现。它计算每个非负特征与类之间的卡方统计量。

这个分数应该用于评估分类任务中的分类变量。它将目标Y的不同类别在特征的不同类别之间的观察分布与目标类别的期望分布进行比较,而不考虑特征的类别。

# 导入库
from sklearn.datasets import load_iris
from sklearn.feature_selection import SelectKBest, chi2

# 加载数据
# 加载鸢尾花数据
iris = load_iris()

# 创建特征和目标
X = iris.data
y = iris.target

# 将数据转换为整数以转换为分类数据
X = X.astype(int)

# 比较卡方统计量
# 选择两个具有最高卡方统计量的特征
chi2_selector = SelectKBest(chi2, k=2)
X_kbest = chi2_selector.fit_transform(X, y)

# 查看结果
print('原始特征数量:', X.shape[1])
print('减少后的特征数量:', X_kbest.shape[1])

我们可以看到,上面的代码帮助我们基于Fisher分数选择了两个最佳特征。

5. 特征选择的ANOVA F值

计算提供的样本的ANOVA F值。

如果特征是分类的,我们将计算每个特征与目标向量之间的卡方统计量。然而,如果特征是定量的,我们将计算每个特征与目标向量之间的ANOVA F值。

F值分数检查了当我们根据目标向量对数值特征进行分组时,每个组的均值是否显著不同。

# 导入库
from sklearn.datasets import load_iris
from sklearn.feature_selection import SelectKBest
from sklearn.feature_selection import f_classif

# 加载鸢尾花数据
iris = load_iris()

# 创建特征和目标
X = iris.data
y = iris.target

# 选择具有最佳ANOVA F-值的特征

# 创建一个SelectKBest对象来选择具有两个最佳ANOVA F-值的特征
fvalue_selector = SelectKBest(f_classif, k=2)

# 将SelectKBest对象应用于特征和目标
X_kbest = fvalue_selector.fit_transform(X, y)

# 查看结果
print('原始特征数量:', X.shape[1])
print('减少后的特征数量:', X_kbest.shape[1])

我们可以看到,上述代码可以帮助我们根据方差分析 F 值选出 2 个最佳特征

6. 热力图的相关矩阵

相关性是衡量2个或更多变量之间线性关系的一种方法。通过相关性,我们可以从一个变量预测另一个变量。

良好的变量与目标高度相关。

相关的预测变量提供冗余信息。

变量应与目标相关,但在彼此之间不相关。

相关特征选择根据以下假设评估特征子集:

"好的特征子集包含与目标高度相关但彼此不相关的特征"。

在本节中,我将演示如何基于两个特征之间的相关性来选择特征。我们可以找到彼此相关的特征。通过识别这些特征,我们可以决定要保留哪些特征,要删除哪些特征。

使用皮尔逊相关性,我们返回的系数值将在-1和1之间变化。

如果两个特征之间的相关性为0,这意味着改变这两个特征中的任何一个都不会影响另一个。

如果两个特征之间的相关性大于0,这意味着增加一个特征中的值也会增加另一个特征中的值(相关系数越接近1,两个不同特征之间的联系越强)。

如果两个特征之间的相关性小于0,这意味着增加一个特征中的值会减少另一个特征中的值(相关系数越接近-1,两个不同特征之间的关系越强)。

在此分析中,我们将检查所选变量是否彼此高度相关。如果是这样,我们需要保留其中一个相关的变量并删除其他变量。

# 加载鸢尾花数据
from sklearn.datasets import load_iris
iris = load_iris()

# 创建特征和目标
X = iris.data
y = iris.target

# 将特征矩阵转换为DataFrame
df = pd.DataFrame(X)

# 查看数据框
print(df)

# 创建相关矩阵
corr_matrix = df.corr()
print(corr_matrix)

# 创建相关性热力图
plt.figure(figsize=(8,6))
plt.title('鸢尾花数据集的相关热力图')
a = sns.heatmap(corr_matrix, square=True, annot=True, fmt='.2f', linecolor='black')
a.set_xticklabels(a.get_xticklabels(), rotation=30)
a.set_yticklabels(a.get_yticklabels(), rotation=30)           
plt.show()

特征选择综合指南

# 选择相关矩阵的上三角部分
upper = corr_matrix.where(np.triu(np.ones(corr_matrix.shape), k=1).astype(np.bool))

# 找到相关性大于0.9的特征列的索引
to_drop = [column for column in upper.columns if any(upper[column] > 0.9)]
print(to_drop)

# 删除标记的特征
df1 = df.drop(df.columns[to_drop], axis=1)
print(df1)

我们可以看到,我们从原始数据集中删除了第三列。


▎包装方法

在包装方法中,我们尝试使用特征的子集并使用它们训练模型。根据我们从先前模型中得出的推断,我们决定向子集中添加或删除特征。问题本质上被缩小为一个搜索问题。这些方法通常在计算上非常昂贵。

一些常见的包装方法示例包括:

  • 正向选择
  • 后向消除
  • 详尽的特征选择
  • 递归特征消除
  • 具有交叉验证的递归特征消除

以下图形来解释包装方法:

1. 正向选择

# 步骤前进特征选择

from sklearn.model_selection import train_test_split
from sklearn.ensemble import RandomForestRegressor
from sklearn.metrics import r2_score
from mlxtend.feature_selection import SequentialFeatureSelector as SFS

# 加载数据集
data = pd.read_csv('/train.csv')

# 为简单起见,只使用数值变量
# 选择数值列:

numerics = ['int16', 'int32', 'int64', 'float16', 'float32', 'float64']
numerical_vars = list(data.select_dtypes(include=numerics).columns)
data = data[numerical_vars]

# 分离训练和测试集
X_train, X_test, y_train, y_test = train_test_split(
    data.drop(labels=['SalePrice'], axis=1),
    data['SalePrice'],
    test_size=0.3,
    random_state=0)

# 查找并删除相关特征
def correlation(dataset, threshold):
    col_corr = set()  # 相关列的名称集合
    corr_matrix = dataset.corr()
    for i in range(len(corr_matrix.columns)):
        for j in range(i):
            if abs(corr_matrix.iloc[i, j]) > threshold: # 我们感兴趣的是绝对系数值
                colname = corr_matrix.columns[i]  # 获取列的名称
                col_corr.add(colname)
    return col_corr

corr_features = correlation(X_train, 0.8)
print('相关特征数量: ', len(set(corr_features)) )

# 删除相关的特征
X_train.drop(labels=corr_features, axis=1, inplace=True)
X_test.drop(labels=corr_features, axis=1, inplace=True)

X_train.fillna(0, inplace=True)

# 步骤前进特征选择

sfs1 = SFS(RandomForestRegressor(), 
           k_features=10, 
           forward=True, 
           floating=False, 
           verbose=2,
           scoring='r2',
           cv=3)

sfs1 = sfs1.fit(np.array(X_train), y_train)

X_train.columns[list(sfs1.k_feature_idx_)]

我们可以看到,前向特征选择的结果是从所有给定列中选择出上述列。

2. 后向消除

在后向消除中,我们从所有特征开始,并在每次迭代中移除最不显著的特征,从而改善模型性能。我们重复这个过程,直到移除特征后不再观察到性能改善。

该过程始于所有属性的完整集合。在每一步中,它会移除剩余集合中最差的属性。

# 步骤后向特征消除

sfs1 = SFS(RandomForestRegressor(), 
           k_features=10, 
           forward=False, 
           floating=False, 
           verbose=2,
           scoring='r2',
           cv=3)

sfs1 = sfs1.fit(np.array(X_train), y_train)

3. 全特征穷举选择

在全特征穷举选择中,通过优化某个特定机器学习算法的指定性能度量,从所有可能的特征子集中选择最佳特征子集。例如,如果分类器是逻辑回归,数据集包含4个特征。

这是另一种贪婪算法,因为它评估所有可能的特征组合。它在计算上相当昂贵,有时,如果特征空间很大,甚至不可行。

有一个专门的Python包实现了这种类型的特征选择:mlxtend。

在mlxtend的穷举特征选择实现中,停止标准是任意设置的特征数量。因此,当我们达到所选特征数量时,搜索将结束。

这在某种程度上是任意的,因为我们可能选择了一个次优数量的特征,或者同样,一个较高数量的特征。

4. 递归特征消除

递归特征消除是一种贪婪的优化算法,旨在找到性能最佳的特征子集。它反复创建模型,并在每次迭代中保留最佳或最差性能的特征。它使用剩余特征构建下一个模型,直到所有特征都用完。然后,它根据它们的消除顺序对特征进行排名。

递归特征消除执行贪婪搜索以找到性能最佳的特征子集。它反复创建模型,并确定每次迭代中性能最佳或最差的特征。它使用剩余特征构建后续模型,直到所有特征都被探索。然后,它根据它们的消除顺序对特征进行排名。在最坏的情况下,如果一个数据集包含N个特征,RFE 将对 2N 个特征组合进行贪婪搜索。

5. 带交叉验证的递归特征消除

具有交叉验证的递归特征消除(RFECV)特征选择技术通过迭代递归特征消除的方式选择估计器的最佳特征子集,从0到N个特征逐个删除。

然后,它根据模型的准确度、交叉验证分数或 ROC-AUC 来选择最佳子集。递归特征消除技术通过多次拟合模型并在每个步骤中删除最弱的特征来消除n个特征。


▎嵌入式方法

嵌入式方法是迭代的,因为它们关心模型训练过程的每一次迭代,并仔细提取那些对特定迭代的训练贡献最大的特征。正则化方法是最常用的嵌入式方法,它们根据一个系数阈值对特征进行惩罚。

这就是为什么正则化方法也被称为惩罚方法,它们引入了额外的约束条件到预测算法的优化中(比如回归算法),使模型倾向于更低复杂度(更少的系数)。

其中一些最流行的示例是 LASSO 和 RIDGE 回归,它们具有内置的惩罚函数以减少过拟合。

嵌入式方法可以通过以下图示来解释

特征选择综合指南

1. Lasso回归

Lasso回归执行L1正则化,它添加的惩罚项等于系数的绝对值的大小。

正则化是指向机器学习模型的不同参数添加惩罚项,以减少模型的自由度,换句话说,以避免过拟合。在线性模型的正则化中,惩罚应用于乘以每个预测变量的系数。在不同类型的正则化中,Lasso或l1具有将某些系数缩小为零的特性。因此,该特征可以从模型中移除。

本小节将演示如何在Kaggle的House Price数据集上使用Lasso正则化来选择特征。

# 导入所需库
from sklearn.model_selection import train_test_split
from sklearn.linear_model import Lasso
from sklearn.feature_selection import SelectFromModel
from sklearn.preprocessing import StandardScaler

# 加载数据集
data = pd.read_csv('train.csv')

# 选择数值型变量
numerics = ['int16', 'int32', 'int64', 'float16', 'float32', 'float64']
numerical_vars = list(data.select_dtypes(include=numerics).columns)
data = data[numerical_vars]

# 分离训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(
    data.drop(labels=['SalePrice'], axis=1),
    data['SalePrice'],
    test_size=0.3,
    random_state=0)

# 房屋数据集中的特征在不同的尺度上,所以对于回归来说,将它们进行标准化有助于提高模型性能
scaler = StandardScaler()
scaler.fit(X_train.fillna(0))

# 在这里,我将再次训练一个Lasso线性回归模型,并选择非零特征。
# 请注意,sklearn的线性回归对象不允许正则化。因此,如果要创建正则化的线性回归,
# 需要特别导入"Lasso",它是线性回归的l1正则化版本。
# alpha是惩罚参数,我设置它很高以强制算法缩小一些系数

sel_ = SelectFromModel(Lasso(alpha=100))
sel_.fit(scaler.transform(X_train.fillna(0)), y_train)

# 创建包含所选特征的列表并打印输出
selected_feat = X_train.columns[(sel_.get_support())]

print('总特征数: {}'.format((X_train.shape[1])))
print('选择的特征数: {}'.format(len(selected_feat)))
print('系数收缩为零的特征数: {}'.format(
    np.sum(sel_.estimator_.coef_ == 0)))

可以看到,Lasso正则化有助于从数据集中移除不重要的特征。因此,增加惩罚参数会导致移除更多特征。因此,我们需要密切关注并监测,以确保不设置过高的惩罚,从而移除甚至重要的特征,也不要设置过低的惩罚,以免移除不重要的特征。

如果惩罚太高,导致重要特征被移除,我们会注意到算法性能下降,然后意识到需要降低正则化。

2. 随机森林重要性

随机森林是最流行的机器学习算法之一。它们之所以如此成功,是因为它们通常具有良好的预测性能、低过拟合和易于解释性。这种可解释性是因为可以轻松地推导出每个变量对树决策的影响。换句话说,可以轻松计算每个变量对决策的贡献。

随机森林包括4-12百棵决策树,每棵树都是基于从数据集中随机抽取的观测值和随机抽取的特征构建的。并非每棵树都看到所有特征或所有观测值,这保证了树是不相关的,因此不容易过拟合。每棵树还是基于单个或多个特征的一系列是/否问题。在每个节点(即每个问题),树将数据集分成两个桶,每个桶都包含彼此更相似且与另一个桶中的观测值不同的观测值。因此,每个特征的重要性是通过每个桶的“纯度”来衡量的。

对于分类问题,不纯度的度量可以是基尼不纯度或信息增益/熵。对于回归问题,不纯度的度量是方差。因此,在训练树时,可以计算每个特征降低不纯度的程度。特征降低不纯度的越多,特征就越重要。在随机森林中,可以将每个特征的不纯度减少平均在树之间,以确定变量的最终重要性。

为了更好地理解,通常在树的顶部选择的特征通常比在树的末节点选择的特征更重要,因为通常顶部的分割会带来更大的信息增益。

# 导入所需库
from sklearn import preprocessing
from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from sklearn import tree
from sklearn.ensemble import RandomForestClassifier

# 加载数据集
df = pd.read_csv('mushrooms.csv')

# 声明特征向量和目标变量
X = df.drop(['class'], axis=1)
y = df['class']

# 编码分类变量
X = pd.get_dummies(X, prefix_sep='_')
y = LabelEncoder().fit_transform(y)

# 标准化特征向量
X2 = StandardScaler().fit_transform(X)

# 分割数据集
X_train, X_test, y_train, y_test = train_test_split(X2, y, test_size=0.30, random_state=0)

# 实例化分类器,n_estimators设置为100
clf = RandomForestClassifier(n_estimators=100, random_state=0)

# 拟合训练集
clf.fit(X_train, y_train)

# 在测试集上进行预测
y_pred = clf.predict(X_test)

特征重要性是了解模型如何进行预测以及哪些特征对模型的预测最重要的关键信息之一。决策树模型以及基于集成的模型(例如Extra Trees和Random Forest)可以用来排名不同特征的重要性。通过了解模型赋予最高重要性的特征,我们可以更好地理解模型的预测过程,从而使模型更具解释性。同时,我们还可以去除对模型没有益处的特征,从而提高模型的效率和性能。

在上一段代码中,我们使用了Random Forest分类器来拟合数据,并可以通过以下方式获取特征的重要性:

plt.figure(num=None, figsize=(10,8), dpi=80, facecolor='w', edgecolor='k')
feat_importances = pd.Series(clf.feature_importances_, index= X.columns)
feat_importances.nlargest(7).plot(kind='barh')

特征选择综合指南

现在我们知道了Random Forest模型中哪些特征最重要,我们可以仅使用这些特征来训练我们的模型。

可以按照以下步骤来实现这一操作:

  • ​根据特征重要性,选择最重要的特征。
  • ​使用选定的重要特征创建新的特征矩阵。
  • ​使用新的特征矩阵来训练Random Forest模型。

如何选择在正确的特征选择方法

选择合适的特征选择方法取决于您的问题类型和数据类型。

以下是一些指导原则:

数值输入,数值输出:如果您的问题是回归问题,并且都是数值型输入变量,通常可以使用相关系数,如皮尔逊相关系数(线性相关)或斯皮尔曼秩相关系数(非线性相关)。

数值输入,分类输出:如果您的问题是分类问题,输入变量是数值型的,可以考虑使用与分类目标相关的特征选择方法,如ANOVA相关系数(线性相关)或肯德尔秩相关系数(非线性相关)。肯德尔秩相关系数要求分类变量是有序的。

分类输入,数值输出:虽然不常见,但如果您的问题是回归问题,输入变量是分类型的,可以使用与“数值输入,分类输出”相反的方法。

分类输入,分类输出:如果您的问题是分类问题,并且输入变量是分类型的,通常可以使用卡方检验(列联表)或互信息(信息增益)等特征选择方法。互信息是一种强大的方法,可以用于分类和数值数据,不受数据类型限制。

选择合适的特征选择方法需要根据您的具体问题和数据类型进行决策。考虑问题类型和数据类型,然后选择适合的特征选择方法,以提高模型性能并减少不必要的特征。


特征选择的技巧和敲门

在使用特征选择时,以下是一些额外的考虑因素和技巧:

相关性统计:scikit-learn库提供了许多有用的统计量的实现,如皮尔逊相关系数、ANOVA、卡方检验、互信息等。您可以根据问题类型选择适当的统计方法。

选择方法:一旦计算了每个输入变量与目标的统计信息,scikit-learn库提供了许多不同的过滤方法。其中两种流行的方法是SelectKBest(选择前k个变量)和SelectPercentile(选择前百分位数的变量)。

变量转换:考虑对变量进行转换以使用不同的统计方法。例如,您可以将分类变量转换为有序变量,即使它们不是有序的,然后查看是否会出现有趣的结果。您还可以将数值变量离散化(例如,分箱),尝试基于分类的测量方法。某些统计测量方法假设变量具有特定的属性,例如皮尔逊相关系数假设观察值具有高斯概率分布和线性关系。您可以转换数据以满足测试的预期,或者忽略预期并进行测试,然后比较结果。

没有最佳方法:没有最佳的特征选择方法,就像没有最佳的输入变量集或最佳的机器学习算法一样。相反,您需要通过谨慎的系统实验来发现适用于您特定问题的最佳方法。尝试使用不同的统计测量方法选择不同特征子集上适合的一系列不同模型,并发现对于您的特定问题哪种方法效果最好。

总结起来,特征选择是一个灵活的过程,需要根据问题和数据的特点来选择适当的方法。尝试不同的方法,实验并比较结果,以找到最适合您问题的特征选择方法。


本文转自:小Z的科研日常,转载此文目的在于传递更多信息,版权归原作者所有。如不支持转载,请联系小编demi@eetrend.com删除。

最新文章