Любая мысль, возникшая в вашем мозгу и укрепившаяся там, окажет, воздействие на вашу жизнь (Джон Кехо). Для моделей машинного обучения методы диагностики влияния признаков на прогноз представляют исключительную важность.
Одним из ключевых способов является разбиение значений признака на интервалы и подсчет для каждого из них усредненного прогноза для всех объектов датасета. То есть фактически для всех точек подсчитывается значение прогноза при подмене признака на заданное значение, затем усредняется, и процедура повторяется для всех бинов признака. Для моделей, основанных на деревьях решений, оценка влияния признака может быть подсчитана еще путем определения разницы в прогнозах модели при наличии признака и удалении его из деревьев.
Загрузим экспериментальный датасет california_housing_train (расположен в Colab-е по адресу /content/sample_data/):
import pandas as pd
import numpy as np
df = pd.read_csv('/content/sample_data/california_housing_train.csv')
df.head()
Обучим модель HistGradientBoostingRegressor:
from sklearn.ensemble import HistGradientBoostingRegressor
model = HistGradientBoostingRegressor().fit(df.drop(columns='median_house_value'), \
df['median_house_value'])
Теперь оценим влияние признака на прогноз модели с методом PartialDependenceDisplay.from_estimator модуля sklearn.inspection. Первый и второй параметры - обученная модель и матрица с точками, на которых делается прогноз, grid_resolution - размерность сетки (бинов) для исследования, features - список исследуемых признаков, method - способ подсчета прогноза (brute - общий способ, recursion - для деревянных моделей, о которых писал выше):
%%time
from sklearn.inspection import PartialDependenceDisplay
PartialDependenceDisplay.from_estimator(model, df.drop(columns='median_house_value'),
n_jobs=-1, grid_resolution=20,
features=['median_income'], method='brute')
Теперь применим метод recursion:
%%time
PartialDependenceDisplay.from_estimator(model, df.drop(columns='median_house_value'),
n_jobs=-1, grid_resolution=20,
features=['median_income'], method='recursion')
Второй метод (для деревянных моделей по умолчанию) намного быстрее. Также примечательна разница в области прогноза, которая обусловлена тем, что бустинговые модели при методе recursion первое дерево (константу равную среднему) не учитывают. Функция partial_dependence является неграфическим аналогом вышеуказанного способа, которая позволяет вывести значения цели (ключ average результата) и сетки признаков (ключ values):
from sklearn.inspection import partial_dependence
res = partial_dependence(model, df.drop(columns='median_house_value'),
grid_resolution=20,
features=['median_income'], method='brute')
res['average'][0][:2], res['values'][0][:2]
Убедимся, что величины "значимости" получаются путем усреднения прогноза по всем точкам с подменой значений колонки признака на заданные (для первых двух точек):
(model.predict(df.drop(columns='median_house_value').assign(median_income=res['values'][0][0])).mean(),
model.predict(df.drop(columns='median_house_value').assign(median_income=res['values'][0][1])).mean()
)
Если признак категориальный, то укажите его в параметре categorical_features, чтобы он не разбивался по бинам при анализе:
bins = np.linspace(df['median_income'].min()-1, df['median_income'].max()+1, 10)
df['median_income_cat'] = pd.cut(df['median_income'], bins=bins, labels=False)
categorical = ['median_income_cat']
model.fit(df.drop(columns='median_house_value'), df['median_house_value'])
PartialDependenceDisplay.from_estimator(model, df.drop(columns='median_house_value'),
n_jobs=-1, grid_resolution=20,
features=['median_income', 'median_income_cat'],
categorical_features=categorical, method='brute')
В функции PartialDependenceDisplay также предусмотрена возможность отображения изменения прогноза от пары признаков, а не только одного (для этого укажите их в кортеже в параметре features):
PartialDependenceDisplay.from_estimator(model, df.drop(columns='median_house_value'),
features= [('longitude', 'latitude')],
n_jobs=-1, grid_resolution=20, method='brute')
Построенные уже графики выводили усредненный прогноз по всем точкам датасета при фиксации значения признака в неком интервале. Однако можно выводить и индивидуальные графики для отдельных точек. Это регулируется параметром kind: kind='individual' - активирует режим отображения частных графиков, kind='both'- оба режима (+ усреднение). При визуализации графиков для точек также важны параметры subsample, который определяет их количество или долю, random_state - инициализатор счетчика случайных чисел, centered - флаг центрирования прогнозов графиков:
PartialDependenceDisplay.from_estimator(model, df.drop(columns='median_house_value'), centered=True,
n_jobs=-1, grid_resolution=20, subsample = 0.1, random_state = 0,
features=['median_income'], kind='both', method='brute')
Полезные ссылки: