处理多重共线性特征的重要性评估

在本例中,使用乳腺癌威斯康星诊断数据集来计算一个训练有素的随机森林分类器的特征排列重要性。该模型在测试数据集上可以轻松达到约97%的准确率。由于这个数据集包含多重共线性特征,排列重要性显示没有特征是重要的,这与高测试准确率相矛盾。演示了一种可能的处理多重共线性的方法,该方法包括对特征的斯皮尔曼等级相关性进行层次聚类,选择一个阈值,并从每个聚类中保留一个单一特征。

首先,定义一个函数来简化绘图过程:

import matplotlib from sklearn.inspection import permutation_importance from sklearn.utils.fixes import parse_version def plot_permutation_importance(clf, X, y, ax): result = permutation_importance(clf, X, y, n_repeats=10, random_state=42, n_jobs=2) perm_sorted_idx = result.importances_mean.argsort() # 处理matplotlib版本差异 tick_labels_parameter_name = ( "tick_labels" if parse_version(matplotlib.__version__) >= parse_version("3.9") else "labels" ) tick_labels_dict = { tick_labels_parameter_name: X.columns[perm_sorted_idx] } ax.boxplot(result.importances[perm_sorted_idx].T, vert=False, **tick_labels_dict) ax.axvline(x=0, color="k", linestyle="--") return ax

接下来,在乳腺癌威斯康星诊断数据集上训练一个随机森林分类器,并在测试集上评估其准确性:

from sklearn.datasets import load_breast_cancer from sklearn.ensemble import RandomForestClassifier from sklearn.model_selection import train_test_split X, y = load_breast_cancer(return_X_y=True, as_frame=True) X_train, X_test, y_train, y_test = train_test_split(X, y, random_state=42) clf = RandomForestClassifier(n_estimators=100, random_state=42) clf.fit(X_train, y_train) print(f"测试数据上的基线准确率:{clf.score(X_test, y_test):.2f}")

然后,绘制基于树的特征重要性和排列重要性。排列重要性是在训练集上计算的,以显示模型在训练期间对每个特征的依赖程度。

import matplotlib.pyplot as plt import numpy as np import pandas as pd mdi_importances = pd.Series(clf.feature_importances_, index=X_train.columns) tree_importance_sorted_idx = np.argsort(clf.feature_importances_) fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 8)) mdi_importances.sort_values().plot.barh(ax=ax1) ax1.set_xlabel("Gini重要性") plot_permutation_importance(clf, X_train, y_train, ax2) ax2.set_xlabel("准确率分数的下降") fig.suptitle("多重共线性特征上的基于不纯度与排列重要性(训练集)") fig.tight_layout()

左侧的图表显示了模型的Gini重要性。由于scikit-learn实现的RandomForestClassifier在每个分割点使用随机子集的$\sqrt{n_\text{features}}$特征,它能够稀释任何单一相关特征的主导地位。因此,相关特征之间的个体特征重要性可能更加均匀分布。由于特征具有大基数且分类器没有过拟合,相对可以信任这些值。

右侧的排列重要性图表显示,排列一个特征最多会使准确率下降0.012,这将表明没有特征是重要的。这与计算出的高测试准确率基线相矛盾:一些特征必须是重要的。同样,在测试集上计算的准确率分数的变化似乎是由偶然因素驱动的:

fig, ax = plt.subplots(figsize=(7, 6)) plot_permutation_importance(clf, X_test, y_test, ax) ax.set_title("多重共线性特征上的排列重要性\n(测试集)") ax.set_xlabel("准确率分数的下降") ax.figure.tight_layout()

尽管如此,在存在相关特征的情况下,仍然可以计算出有意义的排列重要性,如下一节所示。

处理多重共线性特征

当特征共线时,排列一个特征对模型性能的影响很小,因为它可以从相关特征中获得相同的信息。请注意,并非所有预测模型都是如此,这取决于它们的底层实现。

处理多重共线性特征的一种方法是对斯皮尔曼等级相关性进行层次聚类,选择一个阈值,并从每个聚类中保留一个单一特征。首先,绘制相关特征的热图:

from scipy.cluster import hierarchy from scipy.spatial.distance import squareform from scipy.stats import spearmanr fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(12, 8)) corr = spearmanr(X).correlation # 确保相关性矩阵是对称的 corr = (corr + corr.T) / 2 np.fill_diagonal(corr, 1) # 将相关性矩阵转换为距离矩阵,然后使用Ward链接进行层次聚类。 distance_matrix = 1 - np.abs(corr) dist_linkage = hierarchy.ward(squareform(distance_matrix)) dendro = hierarchy.dendrogram(dist_linkage, labels=X.columns.tolist(), ax=ax1, leaf_rotation=90) dendro_idx = np.arange(0, len(dendro["ivl"])) ax2.imshow(corr[dendro["leaves"], :][:, dendro["leaves"]]) ax2.set_xticks(dendro_idx) ax2.set_yticks(dendro_idx) ax2.set_xticklabels(dendro["ivl"], rotation="vertical") ax2.set_yticklabels(dendro["ivl"]) fig.tight_layout()

接下来,通过观察树状图手动选择一个阈值,将特征分组到聚类中,并从每个聚类中选择一个特征来保留,从数据集中选择这些特征,并训练一个新的随机森林。与在完整数据集上训练的随机森林相比,新的随机森林的测试准确率没有太大变化。

from collections import defaultdict cluster_ids = hierarchy.fcluster(dist_linkage, 1, criterion="distance") cluster_id_to_feature_ids = defaultdict(list) for idx, cluster_id in enumerate(cluster_ids): cluster_id_to_feature_ids[cluster_id].append(idx) selected_features = [v[0] for v in cluster_id_to_feature_ids.values()] selected_features_names = X.columns[selected_features] X_train_sel = X_train[selected_features_names] X_test_sel = X_test[selected_features_names] clf_sel = RandomForestClassifier(n_estimators=100, random_state=42) clf_sel.fit(X_train_sel, y_train) print("移除特征后的测试数据基线准确率:", f"{clf_sel.score(X_test_sel, y_test):.2f}")

最后,探索所选特征子集的排列重要性:

fig, ax = plt.subplots(figsize=(7, 6)) plot_permutation_importance(clf_sel, X_test_sel, y_test, ax) ax.set_title("所选特征子集上的排列重要性\n(测试集)") ax.set_xlabel("准确率分数的下降") ax.figure.tight_layout() plt.show()
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485