Крутой способ оценки влияния признака на модель, о котором мало кто знает
Для моделей машинного обучения методы диагностики влияния признаков на прогноз представляют исключительную важность.
Одним из ключевых способов является разбиение значений признака на интервалы и подсчет для каждого из них усредненного прогноза для всех объектов датасета. То есть фактически для всех точек подсчитывается значение прогноза при подмене признака на заданное значение, затем усредняется, и процедура повторяется для всех бинов признака. Для моделей, основанных на деревьях решений, оценка влияния признака может быть подсчитана еще путем определения разницы в прогнозах модели при наличии признака и удалении его из деревьев.
Загрузим экспериментальный датасет 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')