前言
嵌入式方法是一种在构建预测模型后,可以利用模型结构进行特征选择的算法。换句话说,特征选择阶段被嵌入到模型训练中。在这方面,一旦模型训练完成,特征的重要性就可以通过学习器的属性来判断。例如,在归一化数据上训练的线性模型中,系数的大小可以被视为它们对目标变量的影响。现在,如果在相同的场景中一个特征的系数为零,这意味着在给定数据中该特征对目标变量没有影响。嵌入式方法分为两种类型:1) 非迭代型;2) 迭代型
非迭代型嵌入式方法 :
非迭代型嵌入式方法不需要迭代搜索策略来选择特征。在这里,“迭代”并不指的是用于在给定一组固定特征的情况下构建基础预测模型的迭代优化方法。相反,指的是一种用于选择特征的迭代过程。一个非迭代型嵌入式方法的例子是基于l1正则化(Lasso)的逻辑回归,这个分类器具有在高维环境中选择特征的属性,因为它将许多特征的系数设为零。一些其他模型,如决策树和随机森林,也有内部机制来定义特征的“重要性”
迭代型嵌入式方法:
与计算添加或删除特征子集的多个性能评分的包装器相比,迭代型嵌入式方法通过与学习算法或训练好的预测模型的结构相关的某些标准来引导搜索。例如其中一种前向选择过程,适用于使用梯度下降来优化对数损失的模型。在每次迭代中,该方法识别并添加在减小损失函数方面速度最快的特征(即相对于特征权重的损失函数梯度具有最大幅度的特征)。其中最流行的迭代型嵌入式特征选择方法之一是递归特征消除(Recursive Feature Elimination, RFE)。在这种方法中,使用与训练好的预测模型结构相关的特征排名标准,以递归方式删除排名最低的特征
RFE(Recursive Feature Elimination)属于迭代型的特征选择方法。在RFE中,特征的选择是通过反复的迭代过程完成的
具体而言,RFE的工作流程如下:
初始阶段:使用所有特征训练模型
评估特征重要性:通过模型的特征权重或系数,识别对模型性能贡献较小的特征
剔除特征:剔除这些对模型贡献较小的特征
迭代:重复上述步骤,直到达到指定的特征数目或其他停止条件
这个迭代过程使得模型在每一轮迭代中都会选择对模型性能影响最小的特征并进行剔除,从而逐步降低特征数量
RFE-CV(Recursive Feature Elimination with Cross-Validation)是RFE的变体,引入了交叉验证来更可靠地评估特征子集的性能。虽然在评估上使用了交叉验证,但整体的特征选择过程仍然是迭代的
代码实现
Scikit -learn对RFE的实现 : Scikit-learn实现了两个用于RFE的类: RFE和RFECV,它们都位于sklearn.feature_selection模块中。 在RFE中,需要设置n_features_to_select参数,该参数指示RFE选择的特征数量。 目前,该参数的默认值为None,将返回特征数量的一半。 另一方面,RFECV将从交叉验证中选择特征数量。 例如,对于step=1,将计算p(特征数量)个CV分数,并选择导致最高分数的特征子集。 RFE和RFECV类的另一个方便且有用的特性是它们都实现了predict和score方法,使用这些方法可以将提供的测试数据的特征减少到所选的特征数量,并进行预测和计算得分
import numpy as np
import matplotlib.pyplot as plt
# 从Scikit-Learn库中导入make_classification函数,用于生成分类问题的合成数据集
from sklearn.datasets import make_classification
# 从Scikit-Learn库中导入递归特征消除(RFE)方法
from sklearn.feature_selection import RFE
# 从Scikit-Learn库中导入递归特征消除交叉验证(RFECV)方法
from sklearn.feature_selection import RFECV
# 从Scikit-Learn库中导入StratifiedKFold,用于生成分层折叠的交叉验证数据集
from sklearn.model_selection import StratifiedKFold
from sklearn.model_selection import train_test_split
# 从Scikit-Learn库中导入线性判别分析(LDA)模型
from sklearn.discriminant_analysis import LinearDiscriminantAnalysis as LDA
# 设置随机种子以确保结果的可重复性
np.random.seed(42)
# 定义特征的数量
n_features = 100
# 定义有信息性特征的数量
n_informative = 10
# 初始化线性判别分析(LDA)模型
lda = LDA()
# 生成分类问题的合成数据集
X, y = make_classification(
n_samples=1000,
n_features=n_features,
n_informative=n_informative,
n_redundant=0,
n_repeated=0,
n_classes=2,
n_clusters_per_class=1,
class_sep=1,
shuffle=False
)
# 划分数据集为训练集和测试集
X_train_full, X_test_full, y_train, y_test = train_test_split(
X,
y,
stratify=y, # 使用分层抽样以保持类别分布的一致性
train_size=0.2
)
import time
# 记录代码执行开始时间
start = time.time()
# 初始化递归特征消除(RFE)对象,选择要保留的特征数量为10
rfe = RFE(lda, n_features_to_select=10)
rfe.fit(X_train_full, y_train)
print("RFE选择的特征掩码:", rfe.get_support())
print("RFE选择的真正有信息的特征数量:", sum(rfe.get_support()[:10]))
end = time.time()
print("RFE执行时间(秒):{:.4f}".format(end - start))
lda_score = rfe.score(X_test_full, y_test)
# 打印使用选定特征的LDA模型在测试数据上的得分
print("使用选定特征的LDA模型在测试数据上的得分:{:.4f}".format(lda_score))
plt.rcParams['font.sans-serif'] = 'SimHei' # 设置中文显示
plt.rcParams['axes.unicode_minus'] = False
selected_features = np.array(range(1, len(rfe.support_) + 1))[rfe.support_]
plt.figure(figsize=(10, 6))
plt.title('RFE 特征选择结果')
plt.xlabel('特征索引')
plt.ylabel('是否被选中')
plt.scatter(selected_features, np.ones(len(selected_features)), marker='o', label='选中特征', color='blue')
plt.scatter(np.setdiff1d(range(1, len(rfe.support_) + 1), selected_features), np.zeros(len(np.setdiff1d(range(1, len(rfe.support_) + 1), selected_features))), marker='x', label='未选中特征', color='red')
plt.legend()
plt.grid(True)
plt.show()
# 初始化StratifiedKFold对象,用于生成分层折叠的交叉验证数据集
strkfold = StratifiedKFold(n_splits=5, shuffle=True)
import time
start = time.time()
# 初始化递归特征消除交叉验证(RFECV)对象,使用StratifiedKFold进行交叉验证,n_jobs=-1表示使用所有可用的CPU核心进行并行计算
rfecv = RFECV(lda, cv=strkfold, n_jobs=-1)
rfecv.fit(X, y)
print("RFECV选择的特征掩码:", rfecv.get_support())
# 打印RFECV选择的特征数量
print("RFECV选择的特征数量:", rfecv.n_features_)
print("RFECV选择的真正有信息的特征数量:", sum(rfecv.get_support()[:10]))
end = time.time()
print("RFECV执行时间(秒):{:.4f}".format(end - start))
# 使用RFECV选择的特征进行LDA模型的评估
lda_score = rfecv.score(X_test_full, y_test)
# 打印使用选定特征的LDA模型在测试数据上的得分
print("使用选定特征的LDA模型在测试数据上的得分:{:.4f}".format(lda_score))
在整个数据集上进行RFECV模型拟合的确可能导致过拟合的问题,为了避免过拟合,最好将特征选择过程嵌套在交叉验证的内部。这意味着在每个交叉验证折叠中,使用训练数据进行特征选择,而不是在整个数据集上进行。这样可以确保模型在每个折叠中都是基于相对独立的训练数据进行评估的,代码中RFECV的cv参数使用了strkfold,这是一个使用分层抽样的StratifiedKFold对象,有助于确保每个交叉验证折叠中都保持了类别的分布
plt.figure(figsize=(10, 6))
plt.title('RFECV 特征排名变化曲线')
plt.xlabel('特征数量')
plt.ylabel('交叉验证分数')
plt.plot(range(1, len(rfecv.cv_results_['mean_test_score']) + 1), rfecv.cv_results_['mean_test_score'])
plt.grid(True)
plt.show()
总结:嵌入法作为特征选择的一种重要方法,通过迭代优化的方式,帮助模型更好地理解数据,提高了模型的解释性和泛化能力。读者可以通过本文学习如何灵活运用RFE和RFECV,并在实际项目中应用这些技术,以优化模型的性能
如果你对类似于这样的文章感兴趣。
欢迎关注、点赞、转发~