特征排列重要性与随机森林分类器

在机器学习中,了解模型中各个特征的重要性对于模型的解释性和特征选择至关重要。本文将探讨如何利用排列重要性来评估训练好的随机森林分类器中特征的重要性。将使用乳腺癌威斯康星诊断数据集来演示这一过程。尽管该数据集包含多重共线性特征,模型在测试集上仍能轻松达到约97%的准确率。然而,排列重要性却显示没有特征是重要的,这与高测试准确率相矛盾。

为了处理多重共线性问题,提出了一种可能的方法,即对特征的斯皮尔曼等级相关性进行层次聚类,选择一个阈值,并从每个聚类中保留一个特征。这种方法可以帮助更准确地评估特征的重要性。

排列重要性与随机森林特征重要性的比较

首先,定义了一个函数来简化绘图过程。该函数使用排列重要性来评估模型对每个特征的依赖程度。在matplotlib 3.9及以上版本中,需要注意`labels`参数已被重命名为`tick_labels`。

import matplotlib.pyplot as plt 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() 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"Baseline accuracy on test data: {clf.score(X_test, y_test):.2f}")

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

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 importance") plot_permutation_importance(clf, X_train, y_train, ax2) ax2.set_xlabel("Decrease in accuracy score") fig.suptitle("Impurity-based vs. permutation importances on multicollinear features (train set)") fig.tight_layout()

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

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

fig, ax = plt.subplots(figsize=(7, 6)) plot_permutation_importance(clf, X_test, y_test, ax) ax.set_title("Permutation Importances on multicollinear features (test set)") ax.set_xlabel("Decrease in accuracy score") 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) distance_matrix = 1 - np.abs(corr) dist_linkage = hierarchy.ward(squareform(distance_matrix)) dendro = hierarchy.dendrogram(dist_linkage, labels=X.columns.to_list(), 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("Baseline accuracy on test data with features removed:", 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("Permutation Importances on selected subset of features (test set)") ax.set_xlabel("Decrease in accuracy score") ax.figure.tight_layout() plt.show()
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485