为何决策树模型每次运行特征重要性与准确率有差异?10折CV如何选?
决策树+Stratified K-Fold交叉验证的常见问题解答
一、特征重要性与准确率存在差异的原因
- 训练子集的差异:Stratified K-Fold会将数据集拆分为10个不同的训练/测试子集,每折的训练数据分布存在细微差别。决策树是贪心算法,初始节点的分裂选择会被训练数据的细节影响,最终导致整个树结构、特征重要性和预测准确率出现波动。
- 决策树的固有随机性:默认的
DecisionTreeClassifier未固定random_state,当多个特征在节点分裂时能带来相同的增益(比如基尼系数下降幅度一致),模型会随机选择其中一个特征。这种随机选择会直接改变树的结构,进而造成特征重要性和准确率的差异。 - 原代码的潜在问题:你当前先对整个数据集做特征缩放再拆分,相当于测试集的信息被提前用到了缩放过程中(数据泄露),这会导致每折的测试结果存在偏差,但不是每次运行差异的核心原因。
二、10折CV中特征重要性的选择策略
- 不要单独选用某一折的结果:单折的特征重要性仅反映该折训练数据下的局部特征贡献,不具备全局代表性,无法体现特征在整个数据集上的真实价值。
- 推荐计算各折的平均特征重要性:记录每折训练后模型的特征重要性,再取所有折的平均值,这样得到的结果是特征在不同训练子集下的综合贡献,稳定性和代表性更强,是行业通用做法。
- 无需取特征重要性的交集:交集会过滤掉那些在部分折中有重要贡献但并非所有折都突出的特征,这类特征可能在特定数据分布下仍有价值。取平均值能保留所有特征的综合表现,结果更全面。
代码优化建议
针对你的代码,调整后可以稳定计算平均特征重要性,同时修正数据泄露问题:
from statistics import mean, stdev from sklearn import preprocessing from sklearn.model_selection import StratifiedKFold from sklearn import datasets from sklearn import tree import pandas as pd import numpy as np # 加载乳腺癌数据集 cancer = datasets.load_breast_cancer() x = cancer.data y = cancer.target # 初始化10折分层交叉验证 skf = StratifiedKFold(n_splits=10, shuffle=True, random_state=1) fold_accuracies = [] fold_feature_importances = [] for train_idx, test_idx in skf.split(x, y): # 拆分当前折的训练/测试集 x_train, x_test = x[train_idx], x[test_idx] y_train, y_test = y[train_idx], y[test_idx] # 特征缩放:仅在训练集拟合scaler,避免数据泄露 scaler = preprocessing.MinMaxScaler() x_train_scaled = scaler.fit_transform(x_train) x_test_scaled = scaler.transform(x_test) # 初始化决策树并固定random_state,减少随机性 dt_model = tree.DecisionTreeClassifier(criterion="gini", random_state=1) dt_model.fit(x_train_scaled, y_train) # 记录当前折的准确率和特征重要性 fold_accuracies.append(dt_model.score(x_test_scaled, y_test)) fold_feature_importances.append(dt_model.feature_importances_) # 计算平均特征重要性 avg_importances = np.mean(fold_feature_importances, axis=0) # 输出结果 print('各折准确率:', fold_accuracies) print('\n最高准确率: {:.2f}%'.format(max(fold_accuracies)*100)) print('\n最低准确率: {:.2f}%'.format(min(fold_accuracies)*100)) print('\n平均准确率: {:.2f}%'.format(mean(fold_accuracies)*100)) print('\n准确率标准差:', stdev(fold_accuracies)) # 关联特征名称输出更直观的结果 feature_names = cancer.feature_names importance_df = pd.DataFrame({ '特征名称': feature_names, '平均重要性': avg_importances }).sort_values(by='平均重要性', ascending=False) print('\n特征重要性排序:\n', importance_df)
内容的提问来源于stack exchange,提问作者roudan
相关产品推荐
相关产品推荐

