машинное обучение
February 23, 2023

Оценка влияния признаков на модель, о которой незаслуженно забывают

Ключам никогда не сообщают, какую дверь им предстоит открыть (Сергей Лукьяненко). Для оценки влияния признаков на модель машинного обучения следует использовать разные способы. Одним из ключевых является обучение модели на парах признаков и визуализация границ принятия решений. Конечно, хотелось бы подавать для этого все признаки, тем более, что именно так они включаются в итоговую модель (а не отдельными парами). Однако ввиду ограниченности человеческого восприятия приходится чем-то пренебрегать. В качестве демонстрационного примера посмотрим на границы логистической регрессии при классификации цветков Ириса:

from sklearn.datasets import load_iris
iris_df = load_iris(as_frame=True)['frame']
iris_df.head()

Для визуализации воспользуемся классом DecisionBoundaryDisplay:

from sklearn.linear_model import LogisticRegression
from sklearn.inspection import DecisionBoundaryDisplay

features = iris_df.drop(columns='target').columns.tolist()[:2]
model = LogisticRegression(random_state=0, max_iter=1000).fit(iris_df[features], iris_df.target)

display = DecisionBoundaryDisplay.from_estimator(model, X=iris_df[features], response_method="predict")
display.ax_.scatter(iris_df[features[0]], iris_df[features[1]], c=iris_df.target, edgecolor="k")

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

import numpy as np

X,Y = np.meshgrid(np.linspace(iris_df[features[0]].min(), iris_df[features[0]].max(), 100), 
                  np.linspace(iris_df[features[1]].min(), iris_df[features[1]].max(), 100))

display = DecisionBoundaryDisplay(xx0=X, xx1=Y, response=np.reshape(model.predict(np.column_stack([X.ravel(), Y.ravel()])), X.shape))
display.plot()
display.ax_.scatter(iris_df[features[0]], iris_df[features[1]], c=iris_df.target, edgecolor="black")

Для оценки всех пар признаков на модель следует чуть расширить код:

import matplotlib.pyplot as plt
from itertools import combinations

features_all = iris_df.drop(columns='target').columns.tolist()
feat_pairs = [[feat1, feat2] for (feat1,feat2) in combinations(features_all, 2)]

fig, axs = plt.subplots(2,3, figsize=(15,7))
    
for features, ax in zip(feat_pairs, axs.flatten()):
  model = LogisticRegression(random_state=0, max_iter=1000).fit(iris_df[features], iris_df.target)
  
  display = DecisionBoundaryDisplay.from_estimator(model, X=iris_df[features], response_method="predict", ax=ax)
  display.ax_.scatter(iris_df[features[0]], iris_df[features[1]], c=iris_df.target, edgecolor="k")

plt.suptitle("DecisionBoundaryDisplay for pairs of features")