SHAP для бинарных и многоклассовых целевых переменных

SHAP for binary and multiclass target variables.

Руководство по коду и интерпретации графиков SHAP, когда ваша модель прогнозирует категориальную целевую переменную

Фото Ники Бенедиктовой на Unsplash

SHAP-значения отражают вклад признака модели в прогнозирование. Для бинарных целевых переменных мы интерпретируем эти значения в терминах логарифма шансов. Для мультиклассовых целей мы используем софтмакс. Мы будем:

  • Более подробно обсудим эти интерпретации
  • Предоставим код для отображения графиков SHAP
  • Исследуем новые способы агрегирования SHAP-значений для мультиклассовых целей

Вы также можете посмотреть видео на эту тему:

Предыдущий учебник SHAP

Мы продолжаем с предыдущего учебника SHAP. В нем подробно рассматриваются графики SHAP для непрерывной целевой переменной. Вы увидите, что эти графики и их идеи схожи для категориальных целевых переменных. Вы также можете найти полный проект на GitHub.

Введение в SHAP с использованием Python

Как создавать и интерпретировать графики SHAP: водопад, силовой, средний SHAP, рассеивание и зависимость

towardsdatascience.com

Подведем итоги: мы использовали SHAP для объяснения модели, построенной на основе набора данных об абалоне. В нем содержится 4,177 экземпляров, и вы можете увидеть примеры функций ниже. Мы используем 8 функций для прогнозирования y — количества колец в раковине абалона. Кольца связаны с возрастом абалона. В этом учебнике мы разделим y на разные группы, чтобы создать бинарные и мультиклассовые целевые переменные.

Матрица признаков X (исходник: репозиторий машинного обучения UCI) (лицензия: CC0: общественное достояние)

Бинарная целевая переменная

Для непрерывной целевой переменной мы видели, что у каждого экземпляра есть 8 SHAP-значений — по одному для каждого признака модели. Как видно на Рисунке 1, если мы их суммируем и усредненный прогноз E[f(x)], мы получаем прогноз для этого экземпляра f(x). Для бинарных целевых переменных у нас есть такое же свойство. Разница в том, что мы интерпретируем значения в терминах логарифма шансов положительного прогноза.

Рисунок 1: интерпретация SHAP-значений в терминах логарифма шансов (источник: автор)

Чтобы понять это, давайте рассмотрим график SHAP. Мы начинаем с создания бинарной целевой переменной (строка 2). Мы создаем две группы на основе y:

  • 1, если количество колец у абалона выше среднего
  • 0 в противном случае
#Бинарная целевая переменнаяy_bin = [1 if y_>10 else 0 for y_ in y]

Мы используем эту целевую переменную и 8 функций для обучения классификатора XGBoost (строки 2–3). У этой модели точность составляет 96,6%.

#Обучение моделиmodel_bin = xgb.XGBClassifier(objective="binary:logistic")model_bin.fit(X, y_bin)

Теперь мы вычисляем значения SHAP (строки 2–3). Мы выводим форму этого объекта (строка 5), которая дает (4177, 8). Таким образом, как и для непрерывной целевой переменной, у нас есть одно SHAP-значение на прогноз и признак. Позже мы увидим, как это отличается для мультиклассовой цели.

# Получение значений SHAP
explainer = shap.Explainer(model_bin)
shap_values_bin = explainer(X)
print(shap_values_bin.shape) # вывод: (4177, 8)

Мы отображаем график водопада для первого экземпляра (строка 6). Результат можно увидеть на Рисунке 2. Обратите внимание, что код аналогичен для непрерывной переменной. Кроме чисел, график водопада также выглядит похожим.

# график водопада для первого экземпляра
shap.plots.waterfall(shap_values_bin[0])

Теперь E[f(x)] = -0.789 представляет средний предсказанный логарифм шансов для всех 4 177 абалонов. Это логарифм шансов положительного (1) предсказания. Для этого конкретного абалона модель предсказала вероятность 0.3958, что у него выше-среднее количество колец (т.е. P = 0.3958). Это дает нам предсказанный логарифм шансов f(x) = ln(0.3958/(1–0.3958)) = -0.423.

Рисунок 2: график водопада для бинарной целевой переменной (источник: автор)

Таким образом, значения SHAP показывают разницу между предсказанными логарифмами шансов и средним предсказанным логарифмом шансов. Положительные значения SHAP увеличивают логарифм шансов. Например, вес мяса увеличил логарифм шансов на 1.32. Другими словами, данная характеристика увеличила вероятность того, что модель предскажет выше-среднее количество колец. Аналогично, отрицательные значения уменьшают логарифм шансов.

Мы также можем агрегировать эти значения так же, как и раньше. Хорошая новость состоит в том, что интерпретации графиков, таких как пчелиный рой или среднее значение SHAP, будут такими же. Просто помните, что мы имеем дело с логарифмами шансов. Теперь давайте посмотрим, как меняется эта интерпретация для многоклассовых целевых переменных.

Многоклассовая целевая переменная

Мы начинаем с создания новой целевой переменной (y_cat) с 3 категориями — молодой (0), VoAGI (1) и старый (2). Как и ранее, мы обучаем классификатор XGBoost для предсказания этой целевой переменной (строки 5–6).

# Категориальная целевая переменная
y_cat = [2 if y_>12 else 1 if y_>8 else 0 for y_ in y]
# Обучение модели
model_cat = xgb.XGBClassifier(objective="binary:logistic")
model_cat.fit(X, y_cat)

Для этой модели мы больше не можем говорить о “положительном предсказании”. Мы можем увидеть это, если вывести предсказанную вероятность для первого экземпляра (строка 2). Получаем [0.2562, 0.1571, 0.5866]. В этом случае третья вероятность самая высокая, поэтому абалон предсказывается как старый (2). То, что это означает для SHAP, заключается в том, что мы больше не можем рассматривать только значения для положительного класса.

# Получение вероятностных предсказаний
model_cat.predict_proba(X)[0]

Мы можем увидеть это, когда вычисляем значения SHAP (строки 2–3). Код аналогичен для бинарной модели. Однако, когда мы выводим форму (строка 5), мы получаем (4177, 8, 3). Теперь у нас есть одно значение SHAP для каждого экземпляра, признака и класса.

# Получение значений SHAP
explainer = shap.Explainer(model_cat)
shap_values_cat = explainer(X)
print(np.shape(shap_values_cat))

В результате нам нужно отображать значения SHAP для каждого класса в отдельных графиках водопада. Мы делаем это для первого экземпляра в следующем коде.

# График водопада для класса 0
shap.plots.waterfall(shap_values_cat[0,:,0])
# График водопада для класса 1
shap.plots.waterfall(shap_values_cat[0,:,1])
# График водопада для класса 2
shap.plots.waterfall(shap_values_cat[0,:,2])

Рисунок 3 показывает график водопада для класса 0. Значения объясняют, как каждый признак внес свой вклад в предсказание модели для этого класса. Это сравнивается с средним предсказанием для этого класса. Мы видели, что вероятность для этого класса была относительно низкой (т.е. 0.2562). Мы видим, что характеристика веса мяса внесла наиболее значительный вклад в эту низкую вероятность.

Рисунок 3: диаграмма водопада для класса 0 (источник: автор)

Рисунок 4 показывает результаты для других классов. Вы заметите, что f(x) = 1.211 является наибольшим для класса 2. Это имеет смысл, так как мы видели, что вероятность для этого класса также была наибольшей (0.5866). При анализе значений SHAP для этого экземпляра может быть разумным обратить внимание на эту диаграмму водопада. Это предсказание класса для этого абалона.

Рисунок 4: диаграмма водопада для классов 1 и 2 (источник: автор)

Интерпретация значений с помощью Softmax

Поскольку мы сейчас имеем дело с несколькими классами, f(x) задается в терминах softmax. Мы можем преобразовать значения softmax в вероятности, используя следующую функцию. fx дает три значения f(x) на приведенных выше диаграммах водопада. Результатом является [0.2562, 0.1571, 0.5866]. Те же предсказанные вероятности, которые мы видели для экземпляра 0!

def softmax(x):    """Вычислить значения softmax для каждого набора оценок в x"""    e_x = np.exp(x - np.max(x))    return e_x / e_x.sum(axis=0)# преобразование softmax в вероятностьfx = [0.383,-0.106,1.211]softmax(fx)

Агрегация значений SHAP для многоклассовых задач

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

Первый подход – это версия графика среднего значения SHAP. Мы вычисляем абсолютное среднее значение значений SHAP для каждого класса отдельно (строки 2-4). Затем мы создаем столбчатую диаграмму с одним столбцом для каждого класса и признака.

# calculate mean SHAP values for each classmean_0 = np.mean(np.abs(shap_values_cat.values[:,:,0]),axis=0)mean_1 = np.mean(np.abs(shap_values_cat.values[:,:,1]),axis=0)mean_2 = np.mean(np.abs(shap_values_cat.values[:,:,2]),axis=0)df = pd.DataFrame({'small':mean_0,'VoAGI':mean_1,'large':mean_2})# plot mean SHAP valuesfig,ax = plt.subplots(1,1,figsize=(20,10))df.plot.bar(ax=ax)ax.set_ylabel('Mean SHAP',size = 30)ax.set_xticklabels(X.columns,rotation=45,size=20)ax.legend(fontsize=30)

Мы можем увидеть результат на Рисунке 5. Одно замечание заключается в том, что каждый столбец дает среднее значение для всех предсказаний. Однако фактический предсказанный класс будет отличаться в каждом случае. Таким образом, вы можете исказить средние значения значениями SHAP, которые не объясняют предсказанный класс. Возможно, поэтому мы видим меньшие значения для класса VoAGI.

Рисунок 5: среднее значение SHAP для каждого класса в многоклассовой целевой переменной (источник: автор)

Чтобы обойти это, мы можем сосредоточиться на значениях SHAP для предсказанного класса. Мы начинаем с получения предсказанного класса для каждого экземпляра (строка 2). Мы создаем новый набор значений SHAP (new_shap_values). Это делается путем циклического прохода по исходным значениям и выбора только набора, который соответствует предсказанию для этого экземпляра (строки 5–7).

# получаем предсказания моделиpreds = model_cat.predict(X)new_shap_values = []for i, pred in enumerate(preds):    # получаем значения SHAP для предсказанного класса    new_shap_values.append(shap_values_cat.values[i][:,pred])

Затем мы заменяем значения SHAP в исходном объекте (строка 2). Теперь, если мы выводим форму, мы получаем (4177, 8). Другими словами, мы вернулись к одному набору значений SHAP для каждого экземпляра.

# заменяем значения SHAPshap_values_cat.values = np.array(new_shap_values)print(shap_values_cat.shape)

Преимущество этого подхода заключается в том, что он легко использовать встроенные графики SHAP. Например, средний график SHAP на Рисунке 6. Мы можем интерпретировать эти значения как средний вклад функции в предсказанный класс.

shap.plots.bar(shap_values_cat)
Рисунок 6: средний SHAP для предсказанного класса в многоклассовой целевой переменной (источник: автор)

Мы также можем использовать beeswarm. Однако заметьте, что мы не видим четкой связи между значениями SHAP и значениями признаков. Это связано с тем, что связь признаков будет различаться в зависимости от предсказанного класса. Более старые абалоны будут больше. Так, например, большие веса раковины приведут к большей вероятности предсказания старого (2) класса. Обратное верно для предсказания молодого (0) класса.

shap.plots.beeswarm(shap_values_cat)
Рисунок 6: beeswarm для многоклассовой целевой переменной (источник: автор)

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

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

Узнайте больше о shap:

Новые графики SHAP: скрипичная диаграмма и тепловая карта

Что графики в SHAP версии 0.42.1 могут рассказать вам о вашей модели

towardsdatascience.com

Ограничения SHAP

Как SHAP зависит от зависимостей признаков, причинно-следственной связи и человеческих предубеждений

towardsdatascience.com

Использование SHAP для отладки модели регрессии изображений PyTorch

Использование DeepShap для понимания и улучшения модели автономного автомобиля

towardsdatascience.com

Надеюсь, вам понравилась эта статья! Вы можете поддержать меня, став одним из моих рекомендованных пользователей 🙂

Как участник VoAGI, часть вашей членской платы идет писателям, которых вы читаете, и вы получаете полный доступ ко всем историям…

conorosullyds.medium.com

| Twitter | YouTube | Newsletter – подпишитесь на бесплатный доступ к курсу Python SHAP

Ссылки

Stackoverflow Как интерпретировать базовое значение проблемы многоклассовой классификации при использовании SHAP?https://stackoverflow.com/questions/65029216/how-to-interpret-base-value-of-multi-class-classification-problem-when-using-sha/65034362#65034362