"Сбор данных"

Применение метода передискретизации синтетического меньшинства (SMOTe) для несбалансированных наборов данных

В Data Science несбалансированные наборы данных не удивляют. Если наборы данных, предназначенные для задач классификации, таких как анализ настроений, медицинская визуализация или другие проблемы, связанные с дискретной прогнозной аналитикой (например, прогнозирование задержки полета), имеют неодинаковое количество экземпляров (образцов или точек данных) для разных классов, то эти наборы данных считаются быть несбалансированным. Это означает, что существует дисбаланс между классами в наборе данных из-за большой разницы между количеством экземпляров, принадлежащих каждому классу. Класс, имеющий сравнительно меньшее количество экземпляров, чем другой, известен как меньшинство по сравнению с классом, имеющим сравнительно большее количество образцов (известный как большинство). Пример несбалансированного набора данных приведен ниже:

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

Чтобы решить проблему классового дисбаланса, Чавла и др. Представили технику избыточной выборки синтетического меньшинства (SMOTe). [3] в 2002 году.

Краткое описание SMOTe

  1. SMOTe - это метод, основанный на ближайших соседях, оцененных по Евклидову расстоянию между точками данных в пространстве признаков.
  2. Существует процент передискретизации, который указывает количество синтетических семплов, которые должны быть созданы, и этот процентный параметр передискретизации всегда кратен 100. Если процент передискретизации равен 100, то для каждого экземпляра создается новый образец будет создан. Следовательно, количество экземпляров класса меньшинства удвоится. Точно так же, если процент избыточной выборки равен 200, то общее количество выборок из класса меньшинств будет утроено.

В SMOTe,

  • Для каждого экземпляра меньшинства найдено k ближайших соседей, так что они также принадлежат к тому же классу, где,

  • Обнаружена разница между вектором признаков рассматриваемого экземпляра и векторами признаков k ближайших соседей. Таким образом, получается k разностных векторов.
  • Каждый k векторов разности умножается на случайное число от 0 до 1 (исключая 0 и 1).
  • Теперь векторы разности после умножения на случайные числа добавляются к вектору признаков рассматриваемого экземпляра (исходный экземпляр меньшинства) на каждой итерации.

Реализация SMOTe в Python с нуля следует ниже -

import numpy as np
def nearest_neighbour(X, x, K):
    euclidean = np.ones(X.shape[0]-1)
    k = 0
    for j in range(0,X.shape[0]):
        if np.array_equal(X[j], x) == False:
            euclidean[k] = sqrt(sum((X[j]-x)**2))
            k = k + 1
    indices=list(sorted(range(len(euclidean)), key=lambda j: euclidean[j]))
    difference = []
    for j in range(0, K):
        difference.append((abs(x-X[indices[j]])))
    weight = random.random()
    while(weight == 0):
        weight = random.random()
    additive = np.multiply(difference,weight)
    return additive
def SMOTE(X, K):
    K = int(K/100)
    new = [None]*(K*X.shape[0]*X.shape[1])
    new = np.array(new).reshape(K*X.shape[0],X.shape[1])
    k = 0
    for i in range(0,X.shape[0]):
        additive = nearest_neighbour(X, X[i], K)
        for j in range(0, K):
            new[k] = X[i] + additive[j]
            k = k + 1
    return new

Применение SMOTe на практике

Давайте рассмотрим Набор данных прогнозирования доходов взрослого населения из UCI, содержащий 48 842 экземпляра и 14 атрибутов / функций.

Предварительная обработка данных с помощью Python:

  1. Кодирование метки выполняется для категориальных (нечисловых) характеристик, упомянутых в таблице 1 (приведенной ниже), и метки доход.
  2. Выбор функций выполняется на основе оценок важности функций, полученных классификатором дополнительных деревьев для всего набора данных (см. таблицу 1). Поскольку раса и страна происхождения дают самые низкие оценки важности функций, эти 2 функции исключаются при разработке модели.
  3. Быстрое кодирование выполняется для категориальных функций, имеющих более двух категорий. В One-Hot Encoding категориальная функция разделяется на подфункции, каждая из которых соответствует одной из ее категорий (основной категориальной функции), принимая двоичные значения 0/1. Здесь категориальные особенности, рабочий класс, образование, семейное положение, род занятий и отношения - это One-Hot Encoded. Поскольку пол - это функция, имеющая только 2 подкатегории (мужской и женский), он не кодируется в дальнейшем One-Hot Encoded, чтобы избежать проклятия. размерности.

Реализация «горячего» кодирования в Python после выбора функции….

import numpy as np
import pandas as pd
from sklearn.preprocessing import OneHotEncoder
# Label Encoding and Feature Selection is over ....
# 1. Loading the modified dataset after Label Encoding
df = pd.read_csv('adult.csv') 
# Loading of Selected Features into X
X = df.iloc[:,[0,1,2,3,4,5,6,7,9,10,11,12]].values
# Loading of the Label into y
y = df.iloc[:,14].values
# 2. One Hot Encoding ....
onehotencoder = OneHotEncoder(categorical_features = [1,3,5,6,7])
X = onehotencoder.fit_transform(X).toarray()

Теперь метка класса в этой задаче двоичная. Это означает, что метка класса принимает 2 значения, т.е. существует 2 класса. Итак, это проблема двоичной классификации.

Визуализация распределения классов

# Getting the no. of instances with Label 0
n_class_0 = df[df['income']==0].shape[0]
# Getting the no. of instances with label 1
n_class_1 = df[df['income']==1].shape[0]
# Bar Visualization of Class Distribution
import matplotlib.pyplot as plt # required library
x = ['0', '1']
y = np.array([n_class_0, n_class_1])
plt.bar(x, y)
plt.xlabel('Labels/Classes')
plt.ylabel('Number of Instances')
plt.title('Distribution of Labels/Classes in the Dataset')

Итак, в данном наборе данных существует общий дисбаланс между двумя классами с меткой класса, «1» в качестве меньшинства и меткой класса, «0» в качестве большинства.

Теперь есть 2 возможных подхода:

  1. Перемешивание и разделение набора данных на обучающий и проверочный наборы и применение SMOTe к обучающему набору данных. (1-й подход)
  2. Применение SMOTe к данному набору данных в целом, а затем перемешивание-разбиение набора данных на обучающий и проверочный наборы. (2-й подход)

Во многих веб-источниках, таких как Stack Overflow, и во многих личных блогах, второй подход был заявлен как неправильный метод избыточной выборки. В частности, я видел Личный блог Ника Беккера [1], где он упомянул второй подход как неправильный, указав следующую причину:

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

Он также доказал это с помощью практического примера из реальной жизни, рассмотрев набор данных. Он использовал набор инструментов для несбалансированного обучения [2] для применения SMOTe. По правде говоря, я сам никогда не смогу правильно разобраться в документации набора инструментов. Поэтому я предпочитаю реализовывать алгоритм SMOTe с нуля, как показано выше. В этой статье я собираюсь продемонстрировать, что второй подход НЕ неправильный !!!

Давайте следовать 1-му подходу, поскольку он повсеместно широко применяется.

Чтобы продемонстрировать, что второй подход не ошибочен, я буду в случайном порядке разделить весь набор данных на наборы Train-Validation и Test. Набор тестов будет храниться отдельно как неизвестный набор экземпляров. Реализация того же самого следует -

from sklearn.model_selection import train_test_split 
X_train, X_test, y_train, y_test = train_test_split(X, y,
                                   test_size=0.2, random_state=1234)
# X_train and y_train is the Train-Validation Set
# X_test and y_test is the Test Set separated out
  1. Теперь в наборе Обучающая проверка 1-й и 2-й подходы будут применяться в зависимости от случая.
  2. Затем анализ производительности будет выполнен на одном и том же отдельном наборе неизвестных экземпляров (Набор тестов) для обеих моделей (разработанных в соответствии с 1-м подходом и 2-м подходом).

После первого подхода к использованию SMOTe после разделения

= ›Разделение набора Обучение-проверка на наборы Обучение и Проверка. Реализация того же самого следует -

X_train, X_v, y_train, y_v = train_test_split(X_train, y_train,
                             test_size=0.2, random_state=2341)
# X_train and y_train is the Training Set
# X_v and y_v is the Validation Set

= ›Применение SMOTe только на обучающем наборе

# 1. Getting the number of Minority Class Instances in Training Set
import numpy as np # required library
unique, counts = np.unique(y_train, return_counts=True)
minority_shape = dict(zip(unique, counts))[1]
# 2. Storing the minority class instances separately
x1 = np.ones((minority_shape, X_train.shape[1]))
k=0
for i in range(0,X_train.shape[0]):
    if y_train[i] == 1.0:
        x1[k] = X_train[i]
        k = k + 1
# 3. Applying 100% SMOTe
sampled_instances = SMOTE(x1, 100)
# Keeping the artificial instances and original instances together
X_f = np.concatenate((X_train,sampled_instances), axis = 0)
y_sampled_instances = np.ones(minority_shape)
y_f = np.concatenate((y_train,y_sampled_instances), axis=0)
# X_f and y_f are the Training Set Features and Labels respectively 

Обучение модели с использованием классификатора повышения градиента

Классификатор повышения градиента используется для обучения модели машинного обучения. Grid-Search используется в Gradient Boosting Classifier для получения наилучшего набора гиперпараметров, которые представляют собой количество оценок и max_depth.

from sklearn.ensemble import GradientBoostingClassifier
from sklearn.model_selection import GridSearchCV
parameters = {'n_estimators':[100,150,200,250,300,350,400,450,500],
              'max_depth':[3,4,5]}
clf= GradientBoostingClassifier()
grid_search = GridSearchCV(param_grid = parameters, estimator = clf,
                           verbose = 3)
grid_search_1 = grid_search.fit(X_f,y_f)

После второго подхода к использованию SMOTe перед разделением

= ›Применение SMOTe ко всему набору Train-Validation:

# 1. Getting the number of Minority Class Instances in Training Set
unique, counts = np.unique(y_train, return_counts=True)
minority_shape = dict(zip(unique, counts))[1]
# 2. Storing the minority class instances separately
x1 = np.ones((minority_shape, X_train.shape[1]))
k=0
for i in range(0,X_train.shape[0]):
    if y_train[i] == 1.0:
        x1[k] = X_train[i]
        k = k + 1
# 3. Applying 100% SMOTe
sampled_instances = SMOTE(x1, 100)
# Keeping the artificial instances and original instances together
X_f = np.concatenate((X_train,sampled_instances), axis = 0)
y_sampled_instances = np.ones(minority_shape)
y_f = np.concatenate((y_train,y_sampled_instances), axis=0)
# X_f and y_f are the Train-Validation Set Features and Labels respectively

= ›Разделение набора Обучение-проверка на наборы Обучение и Проверка. Реализация того же самого следует -

X_train, X_v, y_train, y_v = train_test_split(X_f, y_f,
                             test_size=0.2, random_state=9876)
# X_train and y_train is the Training Set
# X_v and y_v is the Validation Set

Обучение модели с использованием классификатора повышения градиента

Аналогичным образом поиск по сетке применяется к классификатору повышения градиента.

from sklearn.ensemble import GradientBoostingClassifier
from sklearn.model_selection import GridSearchCV
parameters = {'n_estimators':[100,150,200,250,300,350,400,450,500],
              'max_depth':[3,4,5]}
clf= GradientBoostingClassifier()
grid_search = GridSearchCV(param_grid = parameters, estimator = clf,
                           verbose = 3)
grid_search_2 = grid_search.fit(X_train,y_train)

АНАЛИЗ И СРАВНЕНИЕ ЭФФЕКТИВНОСТИ

Для сравнения и анализа используются следующие показатели производительности:

  1. Точность на тестовом наборе (точность теста)
  2. Точность на тестовом наборе
  3. Напомним о тестовом наборе
  4. F1-Score по тесту

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

# MODEL 1 PERFORMANCE ANALYSIS
model1 = GradientBoostingClassifier(n_estimators = 250, max_depth = 5).fit(X_f, y_f) # best hyperparameters obtained from grid_search_1
# 1. Training Accuracy for Model 1 (following Approach 1)
print(grid_search_1.score(X_f, y_f))
# 2. Validation Accuracy on Validation Set for Model 1 
print(grid_search_1.score(X_v, y_v))
# 3. Test Accuracy on Test Set for Model 1
print(grid_search_1.score(X_test, y_test))
# 4. Precision, Recall and F1-Score on the Test Set for Model 1
from sklearn.metrics import classification_report
predictions=grid_search_1.predict(X_test)
print(classification_report(y_test,predictions))
# MODEL 2 PERFORMANCE ANALYSIS
model2 = GradientBoostingClassifier(n_estimators = 300, max_depth = 4).fit(X_train, y_train) # best hyperparameters obtained from grid_search_2
# 5. Training Accuracy for Model 2(following Approach 2)
print(grid_search_2.score(X_train, y_train))
# 6. Validation Accuracy on Validation Set for Model 2
print(grid_search_2.score(X_v, y_v))
# 3. Test Accuracy on Test Set for Model 2
print(grid_search_2.score(X_test, y_test))
# 4. Precision, Recall and F1-Score on the Test Set for Model 2
from sklearn.metrics import classification_report
predictions=grid_search_2.predict(X_test)
print(classification_report(y_test,predictions))

Точность набора для обучения и проверки для модели 1 и модели 2:

  1. Точность обучения (Модель 1): 91,65025296412158%
  2. Точность обучения (Модель 2): 92,55866094553327%
  3. Точность проверки (модель 1): 86,73704414587332%
  4. Точность проверки (модель 2): 89,5021645021645%

Итак, отсюда очевидно, что 2-й подход показывает более высокую точность проверки, но без тестирования на совершенно неизвестном и том же самом наборе тестов нельзя сделать никаких выводов. В Таблице 2 показана сравнительная таблица характеристик 2 моделей на тестовом наборе.

Таким образом, очевидно, что доказано, что каким бы незначительным ни было различие, Подход 2 явно более успешен, чем Подход 1 и, следовательно, не может быть заявлен Ником Беккером как неправильный по вышеупомянутым причинам [1], потому что

«Хотя SMOTe создает похожие экземпляры, с другой стороны, это свойство требуется не только для уменьшения дисбаланса классов и увеличения данных, но и для поиска лучшего обучающего набора, подходящего для обучения модели. Если обучающий набор не универсален, как можно повысить производительность модели? Что касается утечки информации из проверочного набора в обучающий набор, даже если оно происходит, оно способствует улучшению обучающего набора и помогает в разработке надежной модели машинного обучения, поскольку доказано, что для совершенно неизвестных случаев Подход 2 эффективнее, чем подход 1 »

ИСПОЛЬЗОВАННАЯ ЛИТЕРАТУРА

[1] https://beckernick.github.io/oversampling-modeling/

[2] https://imbalanced-learn.readthedocs.io/en/stable/

[3] Чавла, Нитеш В. и др. «SMOTE: метод передискретизации синтетического меньшинства». Журнал исследований искусственного интеллекта 16 (2002): 321–357.

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