Выбор функций для машинного обучения (2/2)

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

Первая часть этой серии доступна здесь.

Борута: когда леса помогают найти дорогу

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

Еще одна интересная вещь о Boruta из ее оригинального FAQ.

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

Отличное (лучше не придумаешь + слишком много копипастинга в блоге просто несправедливо) объяснение того, как работает Boruta, как правильно его использовать и даже почему такое название, можно найти на его wiki/ Страница часто задаваемых вопросов здесь. Если вы собираетесь использовать его, проверьте ссылку.

Лучше всего то, что он также доступен на Python, здесь. Будучи частью группы пакетов scikit-learn-contrib, его интерфейс представляет собой всем знакомые методы fit_transform/fit+transform. Он более гибкий, чем первоначальная реализация, с возможностью использовать ваш оценщик (если он имеет атрибут feature_importances_) и настраивать порог для сравнения между теневыми и реальными объектами. Вот пример кода из проекта:

import pandas as pd
from sklearn.ensemble import RandomForestClassifier
from boruta import BorutaPy
# load X and y
# NOTE BorutaPy accepts numpy arrays only, hence the .values attribute
X = pd.read_csv('examples/test_X.csv', index_col=0).values
y = pd.read_csv('examples/test_y.csv', header=None, index_col=0).values
y = y.ravel()
# define random forest classifier, with utilising all cores and
# sampling in proportion to y labels
rf = RandomForestClassifier(n_jobs=-1, class_weight='balanced', max_depth=5)
# define Boruta feature selection method
feat_selector = BorutaPy(rf, n_estimators='auto', verbose=2, random_state=1)
# find all relevant features - 5 features should be selected
feat_selector.fit(X, y)
# check selected features - first 5 features are selected
feat_selector.support_
# check ranking of features
feat_selector.ranking_
# call transform() on X to filter it down to selected features
X_filtered = feat_selector.transform(X)

Взаимная информация: что это такое?

Взаимная информация — это мера того, насколько присутствие/отсутствие некоторой переменной повлияет на правильную классификацию целевой переменной.

Даны две дискретные случайные величины X и Y с массовыми функциями вероятности p( x), p( y) и совместной функции массы вероятности p( x, y) имеем следующую формулу для корреляции

Cov(X, Y) = E(XY) − E(X)E(Y) = ∑p(x, y)xy − ∑p(x)x ⋅ ∑p(y)y ⇒ Cov(X, Y) = ∑[p(x, y)−p(x)p(y)]xy

и для взаимной информации

I(X, Y) = H(XY) — H(X)H(Y) = E(lnp(x, y)p(x)p(y))
⇒ I(X, Y) = ∑p(x, y)[lnp(x,y)−lnp(x)p(y)]

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

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

Итак, это компромисс, при котором люди обычно предпочитают методы, основанные на корреляции.

Алгоритмы MI для выбора признаков

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

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

argmax I(xi; y) — [αI(xi; xs)-βI(xi; xs|y)], где i не принадлежит S

Левая часть формулы ищет максимальную релевантность, а правая — минимальную избыточность. Различные алгоритмы выбора признаков на основе MI изменяют параметры α и β, тем самым изменяя некоторые другие предположения алгоритма. Например, JMI, также известный как алгоритм совместной взаимной информации, присваивает 1/#selected feature-1 как α, так и β. И тогда мы приходим к mRMR.

mRMR (минимальная-избыточность-максимальная-релевантность) — это класс алгоритмов, которые пытаются идентифицировать функции с наибольшей взаимной информацией с целевой переменной, но с минимальным перекрытием между ними. Он присваивает 1/#selected features-1 параметру α и 0 параметру β по приведенной выше формуле. В оригинальной статье описаны 2 метода, MID и MIQ, то есть минимальное информационное расстояние и минимальный информационный фактор соответственно.

Хороший пакет с интерфейсом, совместимым со scikit-learn, можно найти здесь. Вот пример кода из них:

import pandas as pd
import mifs
# load X and y
X = pd.read_csv('my_X_table.csv', index_col=0).values
y = pd.read_csv('my_y_vector.csv', index_col=0).values
# define MI_FS feature selection method
feat_selector = mifs.MutualInformationFeatureSelector(method="MRMR")
# find all relevant features
feat_selector.fit(X, y)
# check selected features
feat_selector.support_
# check ranking of features
feat_selector.ranking_
# call transform() on X to filter it down to selected features
X_filtered = feat_selector.transform(X)

Для более глубокого обзора и большего количества теории см. этот удивительный пост в блоге. Кроме того, просто чтобы вы знали, вы можете использовать функции mutual_info_classif или mutual_info_regression sklearn в сочетании с классами SelectKBest или SelectPercentile, но использование пакета MIFS быстрее и гибче.

Выбор методов выбора признаков

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

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

  • При наличии небольшого набора данных (менее пары тысяч) отдавайте предпочтение выборке на основе модели, например, с использованием деревьев или лесов. Это мощные методы, требующие больших вычислительных ресурсов, поэтому их применение для больших наборов данных будет непомерно высоким. Кроме того, вы могли бы позволить себе перекрестную проверку в такой обстановке.
  • При наличии очень большого и разреженного набора данных выберите модель на основе SGDClassifier со штрафом L1.
  • Для небольших выборок и наборов данных большой размерности лучшим вариантом будет использование чего-то вроде алгоритма Лассо.
  • Для быстрого рабочего процесса, подобного EDA, для наборов данных низкой размерности (‹100 объектов), не забудьте найти коэффициенты корреляции между всеми функциями в вашем наборе данных.
  • Выбор признаков на основе MI лучше всего подходит при наличии больших (это обязательно для оценки распределения) и плотных (необязательно, неплохо работает и с разреженными данными) наборов данных.
  • Выбор статистических признаков (chi2, корреляция и т. д.) лучше всего подходит, когда важно работать дешево и быстро, а набор данных не слишком велик (‹100 тыс. или около того).

Эпилог

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

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

Кроме того, помните, бесплатных обедов не бывает — подумайте, на какие компромиссы вы готовы пойти, выберите пару методов, поэкспериментируйте, выберите лучший для вашей проблемы.

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

Учитывая мою историю публикаций, следующий пост будет где-то в январе 😃 Постараюсь сделать лучше.