Полное руководство по «важности функций», одному из самых полезных (и все же скользких) понятий в ML.

Важность функций — фундаментальная концепция для специалистов по машинному обучению.

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

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

Цель этой статьи — пролить свет на различные подходы, которые можно использовать для расчета важности признаков.

Но что мы подразумеваем под «важностью функции»?

Прежде чем углубляться в тему, желательно договориться о том, что мы подразумеваем под «важностью функции».

Предположим, что у нас есть фрейм данных с именем X, где каждая строка представляет собой «наблюдение», а каждый столбец представляет собой характеристику (или «признак»). У нас также есть явление (или «целевая переменная»), которое мы хотели бы предсказать по каждому наблюдению.

Важность функции — это оценка от 0 до 100, присвоенная каждому столбцу (или функции), которая говорит насколько эффективна эта функция в прогнозировании целевой переменной.

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

Таксономия важности признаков

Начнем с концептуальной карты типов важности признаков.

Первое различие, с которым мы сталкиваемся, — это различие между одномерной и многомерной важностью. Разница между ними в том, что:

  • Одномернаяважность рассматривает каждую функцию отдельно.
  • Многомернаяважность измеряет вклад каждой функции условно во все остальные функции.

Эти два типа важности придают друг другу совершенно разное значение, можно сказать, «ортогональное». Вот почему я лично предпочитаю обеспечивать 2 типа важности: одномерную и многомерную. Таким образом, конечный пользователь может получить более полную картину.

1. Одномерный

Целью одномерной важности является получение оценки того, насколько целевая переменная зависит от каждой функции, игнорируя все остальные функции. Оценка может быть получена из закрытой формулы (например, корреляция Пирсона) или из прогностической модели (например, кривая под ROC-кривой случайного леса). Затем оценки всех признаков нормализуются до суммы 100.

В целом, это основные плюсы и минусы однофакторной важности:

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

Позвольте мне объяснить, что я имею в виду, когда говорю, что одномерная важность не страдает от коррелированных признаков.

Предположим, у вас есть 2 функции, рост и возраст, и вы используете случайный лес для предсказания целевой переменной. В этом их важность:

Они одинаково важны, оба близки к 50%.

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

Поскольку три признака возраста несут одинаковую информацию, общая значимость возраста (примерно 50%) практически распределяется между ними.

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

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

– F-статистика

Самый простой тип одномерной важности задается F-статистикой (также называемой ANOVA) для категориальных целевых переменных. В этом случае вы хотите предсказать целевую переменную, состоящую из 2 или более классов, на основе значения числового признака. Затем рассчитывается F-статистика как отношение межгрупповой изменчивости и внутригрупповой изменчивости признака.

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

Scikit-learn имеет две функции для обработки как категориального, так и непрерывного случая, соответственно f_classif и f_regression , поэтому вы можете использовать его через:

from sklearn.feature_selection import f_classif
f = pd.Series(f_classif(X, y)[0], index = X.columns)
fimpo = f / f.sum() * 100

если y является категоричным, или

from sklearn.feature_selection import f_regression
f = pd.Series(f_regression(X, y)[0], index = X.columns)
fimpo = f / f.sum() * 100

если y непрерывно.

- Максимальный информационный коэффициент

F-статистика или корреляция Пирсона очень просты. Фактически, первый касается только различий между средними значениями, а второй — только линейных отношений.

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

Однако основным недостатком MI является то, что он требует, чтобы функция была дискретной, что часто не так. Максимальный информационный коэффициент предназначен для преодоления этой проблемы, потому что он автоматически объединяет для нас непрерывные функции таким образом, чтобы МИ между ними был максимальным.

В Python вы можете найти реализацию MIC в библиотеке Minepy (которую вы можете установить через pip install minepy). Предположим, что X — это кадр данных Pandas, а y — это серия Pandas, вы можете получить MIC каждой функции с помощью:

from minepy import MINE
def get_mic(x, y):
    mine = MINE()
    mine.compute_score(x, y)
    return mine.mic()
f = X.apply(lambda feature: get_mic(feature, y))
fimpo = f / f.sum() * 100

- Прогнозируемый показатель мощности

Поскольку мы хотим оценить взаимосвязь между одним признаком и одной целевой переменной, почему бы не применить прогностическую модель, такую ​​как дерево решений, к этому единственному признаку? Это идея Predictive Power Score (PPS), которая впервые была представлена ​​в статье На пути к науке о данных.

Хитрость в том, что модель нелинейна, поэтому она способна улавливать и нелинейные отношения. Кроме того, PPS обладает некоторыми другими удобными свойствами, такими как несимметричность и возможность реализации для каждой возможной комбинации категориального/числового признака/цели.

Выделенную библиотеку Python можно установить через pip install ppscore.

import ppscore
f = ppscore.predictors(pd.concat([X, y], axis = 1),
  column_target).set_index(‘x’)[‘ppscore’]
fimpo = f / f.sum() * 100

2. Многовариантность

Многофакторная важность призвана ответить на вопрос: какова общая важность функции, принимая во внимание то, что мы знаем обо всех других функциях? Другими словами, он также учитывает взаимодействие между функциями.

Давайте посмотрим на плюсы и минусы многомерной важности.

  • Плюсы: он более полный, поскольку учитывает взаимодействия. Более того, полезно демистифицировать прогностические модели.
  • Минусы. Как показано в абзаце выше, высокая корреляция функций может вводить в заблуждение.

Давайте возьмем пример из этой статьи Лоуренса Гамильтона, чтобы объяснить, почему взаимодействия важны. Уровень образования может мало что сказать о том, насколько человек обеспокоен повышением уровня моря. Но, если совместить эту особенность с политической идеологией, она вдруг становится актуальной. Таким образом, одномерная значимость уровня образования для этой темы низкая, а многомерная – высокая.

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

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

Давайте рассмотрим каждый из них отдельно.

3. Время подгонки

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

Это также называется «внутренней» важностью, потому что метод расчета зависит от модели.

  • Плюсы: это быстро, так как его можно экстраполировать непосредственно из обученной модели, поэтому кроме обучения модели ничего не требуется. Это также удобно тем, что встроено в некоторые модели Python. Например, в Scikit-learn достаточно вызвать model.feature_importances_.
  • Минусы: может придаваться большое значение функциям, которые плохо работают с невидимыми данными. Более того, он зависит от модели: для некоторых моделей его трудно, если вообще возможно, рассчитать. Более того, важность признаков на основе примесей для деревьев сильно смещена в пользу признаков с высокой кардинальностью (см. Документацию по Scikit-learn).

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

- Сокращение примесей

В деревьях решений выбираются разбиения, чтобы уменьшить меру примеси (например, Джини, энтропия или MSE) в группах наблюдений. Поэтому наиболее важными естественно считать особенности, отвечающие за наибольшее уменьшение примеси.

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

Это значение по умолчанию, которое вы получаете, вызывая model.feature_importances_ для моделей Scikit-learn.

Например,

from sklearn.ensemble import RandomForestClassifier
rf = RandomForestClassifier().fit(X, y)
fimpo = pd.Series(rf.feature_importances_ * 100, index = X.columns)

- Разделить счет

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

Этот метод изначально доступен в библиотеке XGBoost:

from xgboost import XGBClassifier
xgb = XGBClassifier().fit(X, y)
f = pd.Series(xgb.get_booster().get_score(importance_type='weight'))
fimpo = f / f.sum() * 100

- Покрытие

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

Этот метод изначально доступен в библиотеке XGBoost:

from xgboost import XGBClassifier
f = pd.Series(xgb.get_booster().get_score(importance_type='cover'))
fimpo = f / f.sum() * 100

4. Время предсказания

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

Чтобы решить эту проблему, существует набор методов, которые можно вычислять во время прогнозирования, т. е. после того, как модель завершила фазу обучения. Это также означает, что эти методы можно применять к наборам данных, отличным от набора данных для обучения.

  • Плюсы: не зависит от модели. Более того, поскольку это делается во время прогнозирования, его можно рассчитать для разных наборов данных, что оказывается очень полезным в реальных приложениях.
  • Минусы: вычисление может быть медленным, поскольку может потребоваться выполнение итераций (важность перестановки) или использование сложных методов (SHAP).

В категории методов Predict-time существует два основных типа алгоритмов:

  • Требуется цель, помимо входных данных для модели, вам также необходимо знать целевую переменную;
  • Цель не требуется: вы можете использовать эти методы, даже если не знаете целевую переменную.

Давайте рассмотрим каждый из них отдельно.

5. Требуется цель

В этой категории основным алгоритмом является «Важность перестановки». Это его плюсы и минусы:

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

- Важность перестановки

Предположим, вы уже обучили прогностическую модель M.

Как следует из названия, этот алгоритм случайным образом перемешивает по одной функции за раз и делает прогноз модели M на наборе данных, содержащем перетасованный столбец. Затем он вычисляет оценку производительности (например, площадь под кривой ROC) для прогноза.

Это повторяется для всех объектов в наборе данных. Идея состоит в том, что ухудшение оценки производительности (по сравнению с производительностью M в «неиспорченном» наборе данных) пропорционально важности функции. Обратите внимание, что эта процедура выполняется на тестовом наборе данных, а не на наборе данных, на котором обучалась модель.

Зачем перетасовывать функцию, а не, например, вводить фиксированное значение? Потому что перестановка позволяет сохранить исходный дистрибутив. И это безопаснее, поскольку мы хотим использовать модель, которая была на исходной функции.

Важность перестановки легко доступна в Scikit-learn:

from sklearn.inspection import permutation_importance
f = permutation_importance(model, X, y)[‘importances_mean’]
fimpo = f / f.sum() * 100

6. Цель не требуется

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

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

Методы, которые мы увидим, основаны на значениях Shap. Shap можно рассчитать через одноименную библиотеку Python. Предположим, что вы уже обучили модель, вот как вы можете получить соответствующие значения Shap:

import shap
explainer = shap.Explainer(model)
shap_values = pd.DataFrame(explainer(X).values, columns = X.columns)

- Абсолютная важность

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

f = shap_values.abs().sum()
fimpo = f / f.sum() * 100

- Главный фактор

Нам может быть интересно узнать, что является наиболее релевантной характеристикой для каждого наблюдения, а не общими эффектами. Этот подход интересен тем, что важность функции можно интерпретировать напрямую в процентах, поэтому ее гораздо легче понять. Например, если бы важность «Возраст» составляла 25%, это означало бы, что для 25% наблюдений «Возраст» был наиболее важным признаком.

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

fimpo = shap_values.abs().idxmax(axis = 1).value_counts(normalize=True) * 100

- Главный фактор со знаком

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

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

Например, если вы хотите знать, какие функции наиболее важны для повышения окончательного прогноза, вам следует установить нижний предел на 0:

fimpo = shap_values.clip(lower = 0).abs().idxmax(axis = 1).value_counts(normalize = True) * 100

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

fimpo = shap_values.clip(upper = 0).abs().idxmax(axis = 1).value_counts(normalize = True) * 100

Сравнение важности

Давайте посмотрим на результаты методов, которые мы видели выше, на самом известном наборе данных всех времен:

Как видите, оценки по разным методам сильно различаются.

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

Также интересно отметить, насколько отличаются «основной положительный фактор» и «основной отрицательный фактор» от «основного фактора». Например, глядя на секс, тот факт, что «главный фактор отрицательный» выше, чем «главный фактор положительный», говорит о том, что секс чаще является решающим фактором в «осуждении» жизни пассажира, а не в спасении ее.

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

Спасибо, что прочитали! Надеюсь, вам понравилась эта статья. Если хотите, добавьте меня в Linkedin!