Анализ данных

Ликвидация классового дисбаланса в данных. Удаление некоторого числа примеров мажоритарного класса

Время прочтения: 4 мин.

Алгоритмы машинного обучения работают лучше всего, когда различные классы, содержащиеся в наборе данных, присутствуют более или менее одинаково. Если примеров одного из классов немного, то данных, чтобы научиться идентифицировать его может не хватить. Данное явление называется классовым дисбалансом, и эта проблема в дальнейшем усложняет обучение нейронных сетей. Рассмотрим различные методы в рамках стратегии удаления некоторого числа примеров мажоритарного класса, позволяющие решить эту проблему. Возьмём данные о транзакциях по кредитным картам. Целевой столбец “Class” содержит информацию о том, является ли транзакция мошеннической.  Исследуем этот набор данных и рассмотрим проблему дисбаланса классов.

occ = df['Class'].value_counts()
ratio_cases  =  occ / len ( df.index ) 
print ('Коэффициент случаев мошенничества: %f \nКоэффициент случаев без мошенничества: %f' % ( ratio_cases [1], ratio_cases [0] ))
Коэффициент случаев мошенничества: 0.009901
Коэффициент случаев без мошенничества: 0.990099

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

Рассмотрим различные подходы к устранению проблемы. Один из них – удаление некоторого числа примеров мажоритарного (большего) класса.

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

from imblearn.under_sampling import RandomUnderSampler 
x = df.iloc[:, 2:30].values
y = df.Class.values
rus = RandomUnderSampler ()
x_res, y_res = rus.fit_sample(x, y)

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

Например, поиск связей Томека. Этот метод устраняет нежелательное перекрытие между классами, при этом экземпляры мажоритарного класса удаляются до тех пор, пока все пары ближайших соседей, минимально удаленных друг от друга, не будут одного класса. Связь Томека определяется следующим образом:  и     — экземпляры, принадлежащие к различным классам, d( , ) – расстояние между ними. Пара ( , ) является связью Томека, если не существует   такого, что:

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

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

from imblearn.under_sampling import TomekLinks 
x = df.iloc[:, 2:30].values
y = df.Class.values
print('Original dataset shape %s' % Counter(y))
tl = TomekLinks ()
x_res, y_res =  tl.fit_sample(x, y)
print('Resampled dataset shape %s' % Counter(y_resampled))
Original dataset shape Counter({0: 5000, 1: 50})
Resampled dataset shape Counter({0: 4997, 1: 50}

Как можно увидеть, данный алгоритм удалил только 3 примера мажоритарного класса.

Если нам необходимо находить различие в схожих примерах, но из разных классов, можно применять правило сосредоточенного ближайшего соседа. Из изначального набора данных создаём новый набор, куда добавляем все примеры миноритарного класса и один из мажоритарного класса, выбранный случайным образом. Далее, все элементы из изначального набора классифицируем по правилу ближайшего соседа (k = 1). Находим записи с ошибочной меткой и добавляем в новый набор.

from imblearn.under_sampling import CondensedNearestNeighbour 
x = df.iloc[:, 2:30].values
y = df.Class.values
cnn = CondensedNearestNeighbour()
x_res, y_res =  cnn.fit_sample(x, y)

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

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

Советуем почитать