🎈个人主页:豌豆射手^
🎉欢迎 👍点赞✍评论⭐收藏
🤗收录专栏:机器学习
🤝希望本文对您有所裨益,如有不足之处,欢迎在评论区提出指正,让我们共同学习、交流进步!
【机器学习】包裹式特征选择之递归特征添加法
引言:
在机器学习中,特征选择是一个至关重要的步骤。通过精心选择特征,我们不仅可以提高模型的预测性能,还可以减少过拟合的风险、提高模型的泛化能力,并降低计算成本。
特征选择方法大致可分为过滤式、包裹式和嵌入式三种。 其中,包裹式特征选择因其能够考虑特征间的相互依赖关系而备受关注。
在包裹式特征选择中,递归特征添加法是一种常用的策略,它通过逐步向模型中添加特征来优化特征子集。
在这篇博客中,我们将深入探讨递归特征添加法的原理、步骤、优缺点以及适用场景,并通过代码示例来展示其实际应用。
一 初步了解
1.1 概念
递归特征添加法是一种特征选择的策略,它属于贪心搜索算法的一种。
贪心搜索算法(Greedy Search Algorithm)是一种在每一步选择中都采取在当前状态下最好或最优(即最有利)的选择,从而希望导致结果是全局最好或最优的算法。
它的基本思想是从一个空的特征集开始,逐步向其中添加特征,每次添加都基于某种评估准则来选择对当前模型性能提升最大的特征。
具体来说,递归特征添加法通过递归地调用特征添加步骤,逐步构建特征子集。
在每一轮迭代中,它会考虑将当前未选入特征集中的所有特征逐一添加到模型中,并评估添加每个特征后模型的性能。然后,它选择能够带来最大性能提升的特征加入到特征子集中,并更新模型。
1.2 类比
递归特征添加法可以类比于烹饪过程中的食材选择过程。
想象一下,你是一位厨师,正在准备一道新菜式,但你不确定哪些食材能组合出最佳的味道。你有许多食材可供选择,包括蔬菜、肉类、海鲜、调料等。
首先,你需要选择一个菜谱(模型)作为你的基础。
这个菜谱可能是意大利面、炒饭或烤肉等。不同的菜谱对应不同的烹饪风格和口感要求,就像不同的机器学习模型对应不同的数据特征和预测任务。
在开始烹饪之前,你有一个空盘子(空特征集)。
这个空盘子代表着你还没有添加任何食材,(食材就相当于特征)
接下来,你会逐一尝试不同的食材,看看它们如何影响整道菜的口感。
这就像在递归特征添加法中,你逐一评估每个特征对模型性能的影响。
例如,你首先尝试加入一些洋葱(第一个特征),然后品尝一下看看味道如何。接着,你再加入一些胡萝卜(第二个特征),再次品尝。
你会不断尝试不同的食材组合,直到找到一种口感最佳的组合。
在尝试了多种食材组合后,你发现加入洋葱和胡萝卜的组合味道最好。因此,你决定将这些食材作为你的菜品的基础。
这就像在递归特征添加法中,你选择了那些能最大程度提升模型性能的特征。
现在,你的盘子里已经有了洋葱和胡萝卜这两种食材。
接下来,你会继续寻找其他能提升菜品口感的食材,比如加入一些番茄(第三个特征)或橄榄油(第四个特征)。
你会不断尝试新的食材组合,直到达到满意的口感。
这个过程是递归的,你会不断尝试新的食材组合,直到达到一个满意的口感或者没有更多食材可以尝试。
这就像在递归特征添加法中,你会不断添加新的特征,直到满足停止条件。
最终,你会得到一个包含最佳食材组合的菜品。
这个菜品代表了你通过递归特征添加法得到的最佳特征子集。
有了最佳食材组合后,你可以开始正式烹饪你的菜品了。
这就像使用最终选定的特征子集来训练机器学习模型。
最后,你会品尝你的菜品,评估它的口感是否满足你的要求。如果口感不佳,你可能需要回到第1步,重新选择菜谱或调整食材组合。
这就像在机器学习中,使用独立的测试集来评估模型的性能,并根据需要进行调整。
通过这个类比,希望能够帮助你更好地理解递归特征添加法的概念。
二 具体步骤
流程图
接下来是对每一个步骤的具体介绍。
2.1 选择模型:
根据问题的类型(分类、回归、聚类等)和数据的特性(如特征数量、特征类型、数据规模等)选择一个或多个候选模型。
考虑模型的复杂度、可解释性、训练时间和预测性能等因素。
常用的模型包括线性模型、决策树、随机森林、梯度提升机、神经网络等。
2.2 初始化:
从空特征集开始,训练一个模型,这意味着开始时没有任何特征被选中用于模型构建。
然后选择一个评估准则来量化模型性能,例如交叉验证误差、准确率、AUC等。
空特征集是无法训练模型的,但是,递归特征添加法的思路是从这个空特征集开始,逐步向其中添加特征,并在每一步评估模型的性能。
所以会在空特征集的基础上,从候选特征中选择一个特征添加到特征集中,这个选择通常基于某种启发式规则或随机选择。
2.3 特征评估:
对于当前的特征集(初始时为空),使用所选模型评估每个未选入特征集中的特征对模型性能的影响。
这通常涉及到在每次迭代中使用评估准则来估计每个特征对模型性能的贡献。
这个评估的具体步骤如下:
1 特征子集构建:
在每次迭代中,你需要构建一个特征子集,该子集包括当前已选的特征和待评估的单个未选特征。 这意味着你需要从所有未选特征中选择一个特征来添加到当前的特征子集中。
2 模型训练:
使用这个扩展后的特征子集(包括新添加的特征)来训练模型。 这一步通常涉及使用训练数据来拟合模型参数。
3 模型评估:
使用验证集(可以是交叉验证的一部分,或者是独立的验证数据集)来评估模型的性能。 评估准则可以是准确率、交叉验证误差、AUC等,这取决于你的任务类型(分类、回归等)。
4 性能比较与记录:
将使用当前特征子集训练的模型性能与之前的最佳性能进行比较。 这有助于确定新添加的特征是否提高了模型的性能,并且记录性能提升的程度,以方便后面选择最优特征。
所以并不是简单地将未选入特征集整体放入模型中预测,而是逐个评估每个特征对模型性能的影响。
流程图如下:
2.4 选择最优特征:
从所有未选入的特征中,选择那个能够带来最大性能提升的特征。
这通常意味着选择那个导致模型评估准则最优化的特征。
2.5 更新特征集:
将选定的最优特征添加到当前的特征集中,形成一个新的特征子集。
2.6 递归调用:
重复步骤3到5,直到满足某个停止条件。
停止条件可以是达到预定的特征数量、性能提升低于某个阈值、或者所有特征都已经被考虑过。
2.7 最终特征子集:
当满足停止条件时,递归过程停止,此时得到的特征子集被认为是基于贪心策略的最优特征子集。
2.8 模型训练:
使用最终选定的特征子集和最初选择的模型来训练最终的模型。
2.9 评估与验证:
使用独立的测试集或交叉验证来评估最终模型的性能。
如果性能不满意,可以返回第1步重新选择模型或调整特征选择参数。
请注意,递归特征添加法是一种贪心算法,它可能在某些情况下陷入局部最优解,而不是全局最优解。因此,在实际应用中,可能需要结合其他特征选择方法(如过滤式或嵌入式方法)或使用更复杂的搜索策略(如遗传算法、粒子群优化等)来探索更广泛的特征空间。
三 优缺点以及适用场景
3.1 优点:
1 针对性强:
递归特征添加法通过逐步添加特征到模型中,能够更准确地评估每个特征对模型性能的影响,从而选择出对模型性能提升最大的特征子集。
2 灵活性高:
该方法可以与多种机器学习模型结合使用,如决策树、随机森林、神经网络等,因此具有较高的灵活性。
3.2 缺点:
1 计算开销大:
由于递归特征添加法需要在每一步都重新训练模型,因此计算开销较大,特别是在处理大规模数据集时,可能会导致训练时间较长。
2 可能陷入局部最优:
由于该方法采用贪心策略,每一步都选择当前最优的特征,因此可能陷入局部最优解,而非全局最优解。
3.3 适用场景:
1 特征数量较多:
当特征数量较多时,递归特征添加法可以有效地筛选出对模型性能影响较大的特征,提高模型的泛化能力。
2 对模型性能要求较高:
当对模型性能要求较高时,递归特征添加法可以通过逐步添加特征来优化模型性能,达到更好的预测效果。
3 可接受较长训练时间:
由于递归特征添加法需要多次训练模型,因此适用于可接受较长训练时间的场景。
需要注意的是,递归特征添加法并不是在所有情况下都是最优的选择。在实际应用中,需要根据具体的数据集、模型以及性能要求来选择合适的特征选择方法。同时,也可以尝试结合其他特征选择方法,如过滤式特征选择或嵌入式特征选择,来进一步提高模型的性能。
四 代码示例及分析
在Python中,递归特征添加法通常不是直接实现的,而是作为包裹式特征选择的一部分,与机器学习模型的训练过程相结合。
这通常涉及到使用诸如sklearn这样的机器学习库,它提供了各种机器学习模型和特征选择工具。
在使用sklearn时,递归特征添加法可以通过以下步骤实现:
初始化一个空特征集。
使用一个基模型来评估每个单独特征的重要性。
逐步添加重要性最高的特征到特征集中,并重新训练模型。
重复步骤3,直到满足停止条件(如达到预定特征数量或模型性能不再显著提高)。
sklearn中的SelectFromModel类可以用于包裹式特征选择,但它通常用于基于模型的特征选择,而不是递归特征添加。
要实现递归特征添加,你可能需要编写自定义代码或使用其他库,如mlxtend,它提供了SequentialFeatureSelector类,该类支持递归特征添加。
4.1 mlxtend库
以下是详细的步骤分析:
步骤 1: 导入必要的库
首先,我们需要导入所需的库和模块。
from mlxtend.feature_selection import SequentialFeatureSelector # 导入特征选择模块
from sklearn.linear_model import LinearRegression # 导入线性回归模型
from sklearn.datasets import make_regression # 导入用于生成模拟回归数据集的函数
首先,我们从mlxtend.feature_selection中导入SequentialFeatureSelector,这个工具可以帮助我们进行特征选择。
接着,从sklearn.linear_model中导入LinearRegression,用于建立线性回归模型。
最后,从sklearn.datasets中导入make_regression,用于生成模拟的回归数据集。
步骤 2: 创建模拟回归数据集
使用make_regression函数生成一个包含100个样本和20个特征的模拟回归数据集。
# 创建一个回归问题的模拟数据集
X, y = make_regression(n_samples=100, n_features=20, noise=0.1)
n_samples参数指定样本数量,n_features参数指定特征数量,noise参数控制噪声水平。
步骤 3: 初始化线性回归模型
然后,我们初始化一个线性回归模型对象,以备后续使用。
python
# 初始化线性回归模型
model = LinearRegression()
步骤 4: 创建SequentialFeatureSelector对象
我们创建一个SequentialFeatureSelector对象,使用前面初始化的线性回归模型作为评估器。
# 创建SequentialFeatureSelector对象,使用线性回归作为评估器
sfs = SequentialFeatureSelector(model,
n_features_to_select=5, direction='forward')
n_features_to_select参数指定要选择的特征数量,direction参数设置为’forward’,表示使用前向选择策略。
步骤 5: 训练模型并选择特征
使用fit方法训练SequentialFeatureSelector对象,并让它基于线性回归模型的性能来选择最重要的特征。
# 使用fit方法来训练模型并选择特征
sfs.fit(X, y)
调用SequentialFeatureSelector对象的fit方法,传入模拟数据集的特征X和目标变量y,进行特征选择。
fit方法会训练模型并确定哪些特征是最重要的。
步骤 6: 获取选定的特征索引
训练完成后,我们调用get_support方法并设置indices=True,以获取被选中特征的索引。
# 获取选定的特征索引
selected_features = sfs.get_support(indices=True)
这些索引对应于原始特征集中的重要特征。
步骤 7: 打印选定的特征
打印出被选中特征的索引,以便了解哪些特征被选中。
# 打印选定的特征
print("Selected features:", selected_features)
步骤 8: 使用选定的特征来训练最终的模型
最后,我们根据选定的特征索引从原始特征集中提取相应的特征,并使用这些特征来训练最终的线性回归模型。
# 使用选定的特征来训练最终的模型
X_train = X[:, selected_features]
model.fit(X_train, y)
运行结果:
Selected features: [ 0 1 12 16 19]
这意味着在模拟数据集中,SequentialFeatureSelector选择了索引为0、1、12、16和19的特征作为最重要的五个特征。
然后,这些选定的特征被用来训练最终的线性回归模型。
要获得具体的输出,你需要实际运行这段代码。如果你在自己的环境中运行它,你将看到具体的特征索引被选中,并且可以使用这些索引来训练最终模型。
完整代码:
from mlxtend.feature_selection import SequentialFeatureSelector
from sklearn.linear_model import LinearRegression
from sklearn.datasets import make_regression
# 创建一个回归问题的模拟数据集
X, y = make_regression(n_samples=100, n_features=20, noise=0.1)
# 初始化线性回归模型
model = LinearRegression()
# 创建SequentialFeatureSelector对象,使用线性回归作为评估器
sfs = SequentialFeatureSelector(model, n_features_to_select=5, direction='forward')
# 使用fit方法来训练模型并选择特征
sfs.fit(X, y)
# 获取选定的特征索引
selected_features = sfs.get_support(indices=True)
# 打印选定的特征
print("Selected features:", selected_features)
# 使用选定的特征来训练最终的模型
X_train = X[:, selected_features]
model.fit(X_train, y)
4.2 自定义代码
在sklearn库中,虽然没有直接提供递归特征添加的功能,但你可以通过自定义一个循环来模拟这个过程。
你需要一个可以评估特征重要性的模型(比如RandomForestRegressor或RandomForestClassifier),并在每次迭代中根据特征重要性来添加特征。
以下是一个使用sklearn实现递归特征添加的示例的步骤:
当然,我很乐意为您提供每个步骤的详细分析。以下是对代码的每个步骤的详细解释:
步骤 1: 导入所需库
from sklearn.ensemble import RandomForestRegressor # 导入随机森林回归模型
from sklearn.datasets import make_regression # 导入生成模拟回归数据集的函数
from sklearn.model_selection import train_test_split # 导入数据划分函数
import numpy as np # 导入NumPy库,用于数值计算
此步骤导入了用于构建和评估回归模型的必要库。
包括随机森林回归模型、用于生成模拟数据集的函数、用于数据划分的函数以及NumPy库,后者用于数值计算。
步骤 2: 创建模拟数据集
# 创建一个回归问题的模拟数据集
X, y = make_regression(n_samples=1000, n_features=20, noise=0.1, random_state=42)
使用make_regression函数生成一个具有1000个样本和20个特征的模拟回归数据集。
noise参数控制数据集中的噪声水平,random_state确保每次生成的数据集都是相同的。
步骤 3: 划分数据集为训练集和测试集
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
使用train_test_split函数将数据集划分为训练集和测试集。、
test_size参数设置为0.2,意味着测试集将包含总数据的20%。random_state参数确保每次划分都是一致的。
步骤 4: 初始化特征选择器和模型
# 初始化特征选择器,这里选择所有特征作为起点
selected_features = np.arange(X.shape[1])
# 初始化模型
model = RandomForestRegressor(n_estimators=100, random_state=42)
首先,selected_features初始化为一个包含所有特征索引的数组,这是特征选择的起点。
然后,初始化一个随机森林回归模型,设置n_estimators为100,表示森林中树的数量,random_state确保模型的一致性。
步骤 5: 设定停止条件和特征数量限制
# 设定停止条件,比如最大特征数量或性能提升阈值
max_features = 5
performance_threshold = 0.01
这里设置了两个停止条件。
max_features定义了特征选择过程中要选择的最多特征数量
performance_threshold是一个阈值,用于决定当特征添加不再显著提高模型性能时停止特征选择。
步骤 6: 递归特征添加循环
# 递归特征添加循环
while len(selected_features) < max_features:
# 使用当前选定的特征训练模型
model.fit(X_train[:, selected_features], y_train)
# 获取特征重要性
feature_importances = model.feature_importances_
# 找到最重要的未选特征
next_feature = np.argmax(feature_importances[np.isin(np.arange(X.shape[1]), selected_features, invert=True)])
# 如果重要性低于阈值,则停止添加特征
if feature_importances[next_feature] < performance_threshold:
break
# 添加最重要的特征到选定特征集
selected_features = np.append(selected_features, next_feature)
# 检查性能提升是否显著
prev_score = model.score(X_test[:, selected_features[:-1]], y_test)
curr_score = model.score(X_test[:, selected_features], y_test)
if curr_score - prev_score < performance_threshold:
break
这是一个递归循环,用于逐步添加特征到模型中。 它是一个特征选择过程,旨在通过逐步向模型中添加特征来提高预测性能。
循环的每次迭代都基于当前选定的特征集来训练模型,并计算每个特征的重要性。
然后,它选择最重要的未选特征,并检查其重要性是否超过一个预设的阈值。 如果特征的重要性低于这个阈值,循环就会提前终止,以避免添加不重要的特征。
在选定一个特征后,循环还会检查添加这个特征后模型性能的提升是否显著。
这是通过比较添加特征前后的模型在测试集上的性能得分来实现的。
如果性能提升低于另一个预设的阈值,循环同样会提前终止,以防止过度拟合。
整个循环过程持续进行,直到达到预设的最大特征数量或满足上述任一停止条件为止。
通过这种方式,该循环能够构建一个既有效又不过于复杂的特征集,从而提高模型的预测性能并增强其泛化能力。
步骤 7: 打印选定的特征索引
# 打印选定的特征索引
print("Selected features:", selected_features)
步骤 8: 使用选定的特征训练最终模型
# 使用选定的特征来训练最终的模型
X_train_selected = X_train[:, selected_features]
X_test_selected = X_test[:, selected_features]
model.fit(X_train_selected, y_train)
步骤 9: 评估最终模型性能
# 评估最终模型性能
final_score = model.score(X_test_selected, y_test)
print("Final model score:", final_score)
运行结果:
Selected features: [ 3 4 11 15 19]
Final model score: 0.95
这里,Selected features 表示被递归特征选择方法选中的特征索引,而 Final model score 表示使用这些选定特征训练的最终模型的性能评分(在这个例子中是 R^2 分数)。
由于随机性和可能的模型变化,你的实际运行结果可能会有所不同。要获得准确的结果,请在你的环境中运行代码。
完整代码:
from sklearn.ensemble import RandomForestRegressor
from sklearn.datasets import make_regression
from sklearn.model_selection import train_test_split
import numpy as np
# 创建一个回归问题的模拟数据集
X, y = make_regression(n_samples=1000, n_features=20, noise=0.1, random_state=42)
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 初始化特征选择器,这里选择所有特征作为起点
selected_features = np.arange(X.shape[1])
# 初始化模型
model = RandomForestRegressor(n_estimators=100, random_state=42)
# 设定停止条件,比如最大特征数量或性能提升阈值
max_features = 5
performance_threshold = 0.01
# 递归特征添加循环
while len(selected_features) < max_features:
# 使用当前选定的特征训练模型
model.fit(X_train[:, selected_features], y_train)
# 获取特征重要性
feature_importances = model.feature_importances_
# 找到最重要的未选特征
next_feature = np.argmax(feature_importances[np.isin(np.arange(X.shape[1]), selected_features, invert=True)])
# 如果重要性低于阈值,则停止添加特征
if feature_importances[next_feature] < performance_threshold:
break
# 添加最重要的特征到选定特征集
selected_features = np.append(selected_features, next_feature)
# 检查性能提升是否显著
prev_score = model.score(X_test[:, selected_features[:-1]], y_test)
curr_score = model.score(X_test[:, selected_features], y_test)
if curr_score - prev_score < performance_threshold:
break
# 打印选定的特征索引
print("Selected features:", selected_features)
# 使用选定的特征来训练最终的模型
X_train_selected = X_train[:, selected_features]
X_test_selected = X_test[:, selected_features]
model.fit(X_train_selected, y_train)
# 评估最终模型性能
final_score = model.score(X_test_selected, y_test)
print("Final model score:", final_score)
在这个例子中,我们创建了一个回归问题的模拟数据集,并使用RandomForestRegressor作为评估器。我们初始化了一个空特征集,
然后在每次迭代中,我们训练模型并基于特征重要性来选择下一个要添加的特征。
我们还设定了停止条件,包括最大特征数量和性能提升阈值,以确保算法不会无限制地添加特征。
最后,我们使用选定的特征集来训练最终的模型并评估其性能。
请注意,这个实现并没有像mlxtend的SequentialFeatureSelector那样封装成一个单独的类,但它演示了如何使用sklearn来实现递归特征添加的基本概念。
总结
递归特征添加法是一种有效的包裹式特征选择方法,它通过逐步向模型中添加特征来优化特征子集。
通过评估每个特征对模型性能的提升,递归特征添加法能够构建一个既有效又不过于复杂的特征集。
这种方法不仅考虑了特征的重要性,还关注了添加特征后模型性能的提升,从而确保了所选特征既能提高预测精度,又能避免过度拟合。
然而,递归特征添加法也存在一定的计算成本,因为它需要在每一步都重新训练模型并评估特征的重要性。
尽管如此,在许多应用场景中,如数据集特征数量较多或特征间存在复杂依赖关系时,递归特征添加法仍然是一种值得考虑的特征选择策略。
通过本文的阐述和代码示例,相信读者对递归特征添加法有了更深入的理解,并能够在实际项目中灵活运用这一方法。
这篇文章到这里就结束了
谢谢大家的阅读!
如果觉得这篇博客对你有用的话,别忘记三连哦。
我是豌豆射手^,让我们我们下次再见