Spec-Zone.ru › scikit-learn

Примечание

Перейти к концу, чтобы загрузить весь пример кода. или запустить этот пример в браузере через JupyterLite или Binder

Важность перестановки с многоколлинэрными или коррелированными признаками

В этом примере мы вычисляем permutation_importance признаков для обученного RandomForestClassifier с использованием набора данных Рак молочной железы Висконсин (диагностический). Модель легко достигает точности около 97% на тестовом наборе данных. Поскольку этот набор данных содержит многоколлинэрные признаки, важность перестановки показывает, что ни один из признаков не является важным, что противоречит высокой точности на тестовом наборе.

Мы демонстрируем возможный подход к обработке многоколлинэрности, который заключается в иерархической кластеризации по корреляциям рангов Спермана признаков, выборе порога и сохранении одного признака из каждого кластера.

Примечание

См. также Важность перестановки против важности признаков случайного леса (MDI)

# Authors: The scikit-learn developers
# SPDX-License-Identifier: BSD-3-Clause

Важность признаков случайного леса для данных о раке молочной железы

Сначала мы определим функцию для упрощения построения графиков:

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()

    # `labels` argument in boxplot is deprecated in matplotlib 3.9 and has been
    # renamed to `tick_labels`. The following code handles this, but as a
    # scikit-learn user you probably can write simpler code by using `labels=...`
    # (matplotlib < 3.9) or `tick_labels=...` (matplotlib >= 3.9).
    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

Затем мы обучим RandomForestClassifier на наборе данных Рак молочной железы Висконсин (диагностический) и оценим его точность на тестовом наборе:

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):.2}")
Baseline accuracy on test data: 0.97

Далее, мы построим важность признаков, основанную на дереве, и важность перестановки. Важность перестановки вычисляется на обучающем наборе, чтобы показать, насколько модель полагается на каждый признак во время обучения.

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 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()
Impurity-based vs. permutation importances on multicollinear features (train set)

График слева показывает важность Джини модели. Поскольку реализация 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("Permutation Importances on multicollinear features\n(test set)")
ax.set_xlabel("Decrease in accuracy score")
_ = ax.figure.tight_layout()
Permutation Importances on multicollinear features (test set)

Тем не менее, можно вычислить осмысленную важность перестановки в присутствии коррелированных признаков, как показано в следующем разделе.

Обработка многоколлинэрных признаков

Когда признаки коллинэрны, перестановка одного признака оказывает малое влияние на производительность модели, потому что она может получить ту же информацию от коррелированного признака. Обратите внимание, что это не относится ко всем предсказательным моделям и зависит от их внутренней реализации.

Один из способов обработки многоколлинэрных признаков заключается в выполнении иерархической кластеризации по корреляциям рангов Спермана, выборе порога и сохранении одного признака из каждого кластера. Сначала мы строим тепловую карту коррелированных признаков:

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

# Ensure the correlation matrix is symmetric
corr = (corr + corr.T) / 2
np.fill_diagonal(corr, 1)

# We convert the correlation matrix to a distance matrix before performing
# hierarchical clustering using Ward's linkage.
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()
plot permutation importance multicollinear

Далее, мы вручную выбираем порог по визуальному осмотру дендрограммы, чтобы сгруппировать наши признаки в кластеры и выбрать признак из каждого кластера для сохранения, выбираем эти признаки из нашего набора данных и обучаем новый случайный лес. Точность тестового набора нового случайного леса не сильно изменилась по сравнению со случайным лесом, обученным на полном наборе данных.

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:"
    f" {clf_sel.score(X_test_sel, y_test):.2}"
)
Baseline accuracy on test data with features removed: 0.97

Наконец, мы можем изучить важность перестановки выбранного подмножества признаков:

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\n(test set)")
ax.set_xlabel("Decrease in accuracy score")
ax.figure.tight_layout()
plt.show()
Permutation Importances on selected subset of features (test set)

Общее время выполнения скрипта: (0 минут 5,585 секунды)

Launch binder
Launch JupyterLite

Download Jupyter notebook: plot_permutation_importance_multicollinear.ipynb

Download Python source code: plot_permutation_importance_multicollinear.py

Download zipped: plot_permutation_importance_multicollinear.zip

Связанные примеры

Важность перестановки против важности признаков случайного леса (MDI)

Важность признаков с лесом деревьев

Регрессия с градиентным бустингом

Влияние изменяющегося порога на самообучение

© 2007–2025 The scikit-learn developers
Licensed under the 3-clause BSD License.
https://scikit-learn.org/1.6/auto_examples/inspection/plot_permutation_importance_multicollinear.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API