"Начиная"

Машинное обучение: проблемы с дисбалансом меток целевых функций и решения

Оглавление

Часть 1: Концепции

  1. "Вступление"
  2. Что такое данные поезда?
  3. Зачем нужно балансировать данные поездов?
  4. Как на самом деле выглядят« данные балансировки
  5. Нам также нужно сбалансировать тестовые данные?
  6. Метрики оценки несбалансированных данных испытаний

Часть 2: Код

  1. "Настраивать"
  2. Проектирование начальных характеристик
  3. Трансформация цели модели
  4. Обращение к категориальным признакам
  5. Поезд тестовый сплит
  6. Балансировка данных
  7. Модели
  8. Что, если мы уравновесим тестовые данные?
  9. "Заключение"
  10. "Ресурсы"

Часть 1: Концепции

1. Введение

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

"Оглавление"

2. Что такое данные поезда?

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

"Оглавление"

3. Зачем нужно балансировать данные поездов?

Я твердо верю в использование крайних примеров для подтверждения своих взглядов. В этом случае я хотел бы привести следующий пример, чтобы продемонстрировать, почему баланс данных имеет значение в алгоритмах классификации: скажем, вы пытаетесь предсказать, может ли определенное заболевание возникнуть у данного человека. Давайте сосредоточимся на выходных данных, то есть на значении y, которое мы пытаемся предсказать с помощью наших многочисленных функций. Составьте все функции, которые вы хотите использовать, поскольку они не будут иметь значения за секунду. Если бы у нас было 500 точек данных, и 499 показывали бы здоровых людей, в то время как 1 показывал бы больных, и наша модель имела бы 100% точность… ну, это не доказывает многого. Фактически, это доказывает такую ​​же точность, как модель с точностью 99,99%, в которой единственная ошибка была у больного человека, которого считали здоровым. Распределение точек данных 499: 1 просто не сокращает его, поскольку у нас нет необходимого уровня данных для понимания того, что может привести к заболеванию. Балансировка данных дает нам одинаковый объем информации для предсказания каждого класса и, следовательно, дает лучшее представление о том, как реагировать на тестовые данные.

"Оглавление"

4. Как на самом деле выглядят «данные балансировки»?

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

Примеры данных:

10 строк данных с меткой A.

12 строк данных с меткой B.

14 строк данных с меткой C.

Метод 1: недостаточная выборка; Удалите некоторые данные из строк данных из большинства классов. В этом случае удалите 2 строки с меткой B и 4 строки с меткой C.

Ограничение: это сложно использовать, когда у вас нет значительного (и относительно равного) количества данных от каждого целевого класса.

Метод 2. Скопируйте строки данных, полученные в результате ярлыков меньшинства. В этом случае скопируйте 4 строки с меткой A и 2 строки с меткой B, чтобы добавить в набор данных всего 6 новых строк.

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

Метод 3: (SMOTE - Техника передискретизации синтетического меньшинства) Синтетическое создание новых данных на основе старых данных. По сути, вместо удаления или копирования данных вы используете текущие входные данные для создания новых входных строк, которые являются уникальными, но будут иметь метку, основанную на том, что подразумевают исходные данные. В приведенном выше случае простой способ подумать об этой идее - добавить 4 строки с меткой A к данным, где входные данные представляют полное или частичное сходство значений с текущими входными функциями. Повторите этот процесс также для 2-х рядов этикетки B.

Ограничение: если две разные метки классов имеют общие соседние примеры, может быть сложно сгенерировать точные данные, представляющие, как каждая уникальная метка может выглядеть со стороны ввода, и поэтому SMOTE борется с данными более высокой размерности (Lusa, Л, Благус, Р, 2013).

"Оглавление"

5. Нужно ли нам также балансировать тестовые данные?

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

"Оглавление"

6. Метрики оценки несбалансированных данных испытаний.

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

Точность

Точность - это мера, которая показывает, насколько значимым является положительный результат (целевой класс = 1). Это достигается делением количества правильных положительных прогнозов на общее количество положительных прогнозов (истинное положительное, деленное на сумму ложных и истинных положительных результатов). Допустим, вы прогнозируете болезнь и имеете 400 здоровых и 100 больных. Если вы предсказываете, что все 500 случаев будут заболеванием, вы правильно определили каждый случай заболевания, но ваша модель довольно бессмысленна, поскольку у вас всегда один и тот же результат, и вы неверно определили 80% входных данных.

Напомнить

Напоминание очень похоже на точность. Однако знаменатель при отзыве состоит из истинно положительных и ложно отрицательных результатов, в то время как числитель остается истинно положительным. Это смещает наше внимание с того, насколько на самом деле важна положительная оценка, на понимание того, насколько эффективна наша модель при выявлении любого положительного случая, который присутствует (потому что ложноотрицательный результат на самом деле является положительным). По мере того, как количество ложноотрицательных результатов растет, числитель истинных положительных результатов не увеличивается, а отзыв становится все меньше и меньше. Давайте снова воспользуемся примером точности. Если вы предсказываете, что 500 пациентов заболеют, тогда как на самом деле заболели только 100, у вас будет высокий уровень отзыва, поскольку нет ложноотрицательных результатов. Однако, если вы впадете в другую крайность и скажете, что все здоровы, то внезапно у вас будет 80% точности и нет ложных срабатываний, но вы не смогли идентифицировать 100% больных. Вы можете заметить, что в случае, когда прогнозируется, что каждый заболеет, точность будет низкой, а отзыв - высоким. На самом деле существует умеренный компромисс между оптимизацией точности и отзывом.

F1-Score

F1-Score - это идеальный способ получить лучшее представление о производительности модели при несбалансированных данных, поскольку сама по себе точность не является хорошим показателем. F1-Score - это гармоническое среднее (максимальное значение - это среднее арифметическое) оценки точности и отзыва. Сочетание точности и запоминания дает нам четкое представление о том, насколько хорошо на самом деле работает модель.

"Оглавление"

Часть 2: Код

Данные, которые я буду использовать для демонстрации, можно найти на сайте kaggle.com по этой ссылке, и они относятся к прогнозированию цен на алмазы.

Приведенный ниже код проведет вас через весь процесс; от начала импорта и подготовки данных до моделирования

1. Настройка

  • Установить библиотеки
!pip install -U scikit-learn
!pip install -U imbalanced-learn
!pip install xgboost
  • Импортировать необходимые библиотеки
# remove warnings
import warnings
warnings.filterwarnings('ignore')
# standard imports and setup
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
import seaborn as sns
from scipy import stats
# model evaluation
from sklearn.model_selection import train_test_split, cross_val_score, GridSearchCV, ShuffleSplit
from sklearn.metrics import *
# pipelines
from sklearn.compose import make_column_transformer
from sklearn.pipeline import Pipeline, make_pipeline
# data preparation
from sklearn.preprocessing import *
from sklearn.decomposition import PCA
from sklearn.feature_selection import RFE, RFECV
from sklearn.utils import resample
from imblearn.datasets import make_imbalance
from imblearn.over_sampling import SMOTE
# machine learning
from sklearn.linear_model import *
from sklearn.neighbors import KNeighborsClassifier
from sklearn.svm import SVC, LinearSVC
from sklearn.naive_bayes import GaussianNB
from sklearn.ensemble import RandomForestClassifier, RandomForestRegressor
from sklearn.tree import DecisionTreeClassifier
from xgboost import XGBClassifier, XGBRegressor
  • Чтение и предварительный просмотр данных
df=pd.read_csv('diamonds.csv')
print(df.shape)
df.head()

  • Отбросьте «Безымянный: 0», поскольку это бесполезно, и ищите нулевые значения.
  • При поиске нулевых значений почувствуйте типы данных
df.drop('Unnamed: 0', axis=1, inplace=True)
df.info()

  • Кажется, нет нулевых значений

"Оглавление"

2. Первоначальная разработка функций

  • Далее мы быстро внесем некоторые изменения в функции.
  • Вы можете заметить, что произведение x, y и z (согласно ссылке kaggle) соответствует объему. Мы можем добавить функцию, называемую объемом, чтобы уравнять этот продукт и удалить x, y и z. Я хотел бы поблагодарить Чинмэя Рэйна за эту идею, поскольку я видел ее в его записной книжке kaggle, которую можно найти по этой ссылке (Rane, 2018)
df['volume'] = df.x*df.y*df.z
df.drop(['x','z','y'],axis=1,inplace=True)
  • Как выглядят новые данные?

"Оглавление"

3. Преобразование цели модели

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

  • В Pandas есть встроенная функция для разбиения вещей на ячейки, и вы даже можете иметь соотношение 1: 1 от каждого размера ячейки к следующему, используя pd.qcut
  • У нас все еще есть проблема, потому что смотреть через интервалы немного больно.
  • К счастью, Pandas позволяет пользователям создавать ярлыки по умолчанию, как я покажу ниже.
df['price_bin']=pd.cut(df.price,bins=4,labels=[0,1,2,3])
  • Теперь посмотрим на данные из первых двух строк ниже.
  • Вы можете заметить, что наиболее распространенная метка выше - 0, и это отражено ниже.

  • Для получения дополнительной информации о встроенных функциях, использованных выше, вы можете просмотреть документацию для pandas.cut здесь и документация для pandas.qcut здесь

"Оглавление"

4. Работа с категориальными данными

  • Сейчас мы рассматриваем проблему классификации, но по-прежнему существует проблема вырезания, цвета и четкости элементов, которые не имеют числовых значений.
  • Чтобы решить эту проблему, мы будем использовать кодировку меток, которая представляет собой метод присвоения числовой метки уникальному значению категориальной переменной.
  • На самом деле я предпочитаю целевую кодировку, но не считаю, что целевая кодировка является наиболее «справедливым» приближением с очень небольшим количеством входных функций.
  • После кодирования меток мы сделаем двойную проверку, чтобы убедиться, что наши данные имеют тип float.
le = LabelEncoder()
for col in df.select_dtypes(include='O').columns:
    df[col]=le.fit_transform(df[col])
for col in df.columns:
    df[col]=df[col].astype(float)

Для получения дополнительной информации о кодировании этикеток с помощью Python вы можете просмотреть документацию

"Оглавление"

5. Тренируйте тестовый сплит

  • Теперь, когда наши данные подготовлены, нам придется разделить данные на два набора по причинам, описанным выше.
  • Для этого мы сначала присвоим значения X всему, кроме функции вывода (также известной как все входы).
  • Затем мы присваиваем значения y функции price_bin; наша модификация ценовой характеристики
  • Наконец, мы назначаем X_train и y_train вместе как «соответствующий набор данных» и делаем то же самое для X_test и y_test.
  • Затем мы подгоняем модель с использованием X_train и его выходных данных y_train и проверяем модель, сравнивая прогнозы, основанные на X_test, с фактическими выходными данными y_test.
X = df.drop(['price','price_bin'],axis=1)
y = df.price_bin
X_train, X_test, y_train, y_test = train_test_split(X,y)

Для получения дополнительной информации о train_test_split вы можете просмотреть документацию

"Оглавление"

6. Балансировка данных

  • Хорошо, сделай вдох; мы наконец добрались до балансировки данных

Способ 1

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

  • Что мы видим здесь, так это то, что минимальное значение, которое мы можем сделать, чтобы каждый класс был равен примерно 2000.
  • Я собираюсь использовать 1500
  • Используя make_imbalance из imblearn, мы можем легко удалить строки, чтобы сбалансировать классы (дополнительную информацию об этом процессе можно найти в документации).
  • Я сохраню эти новые значения в X_train_1 и y_train_1, чтобы мы могли увидеть, какой метод работает лучше всего в конце, когда мы запустим все модели.
  • Сбалансировав наши данные, давайте посмотрим на результат, запустив следующий код
y_train_1.value_counts().plot(kind='bar')
plt.title('label balance')
plt.xlabel('label values')
plt.ylabel('amount per label')
plt.show()

Способ 2

  • Для второго метода мы скопируем существующие строки классов меньшинств, используя resample из sklearn.utils
  • Я написал функцию ниже, чтобы помочь вам легко выполнить эту задачу (но ключевой частью функции является импорт resample, и информацию об этой функции можно найти в документации )
  • Однако есть предварительное условие, что вам нужно комбинировать X_train с y_train и только затем повышать дискретизацию классов.

Создание upsample_classes функции

  • Входные данные - это данные, и какова целевая функция этих данных.
  • Выход - сбалансированный набор данных
  • Сначала мы составляем список уникальных меток в данных
  • Затем мы разбиваем строки данных по их меткам на разные наборы данных.
  • Далее мы ищем метку большинства классов.
  • Затем мы снова объединяем классы с помощью pandas.concat (подробнее об этой функции можно найти в документации) и отделяем класс большинства на основе его нового найден ярлык
  • Затем мы удаляем основной класс и повышаем дискретизацию других классов, чтобы они соответствовали длине основного класса.
  • Наконец, мы объединяем класс большинства с другими классами, которые теперь имеют одинаковую длину.
  • Эта функция очень проста из-за того, что есть только два входа
  • Давайте посмотрим, как превратить это в новые наборы X_train и y_train
train = pd.concat([X_train,y_train],axis=1)
train_balanced = (upsample_classes(train,'price_bin'))
X_train_2 = train_balanced.drop(['price_bin'],axis=1)
y_train_2 = train_balanced.price_bin
  • Исход

  • Визуальный
y_train_2.value_counts().plot(kind='bar')
plt.title('label balance')
plt.xlabel('label values')
plt.ylabel('amount per label')
plt.show()

Метод 3 (SMOTE)

  • Хотя фактические расчеты и серверная часть для SMOTE выходят за рамки этого поста (но обсуждаются в другом месте в Интернете), мы будем использовать python и его библиотеки, чтобы усилить технику SMOTE всего с парой строк кода (Doken, S )
  • Напомним, что SMOTE можно рассматривать как способ синтетической генерации новых данных на основе того, что могут подразумевать другие строки данных.
  • Все, что вам нужно для SMOTE, - это две строки кода, и вы можете узнать больше о специфике SMOTE в Python в документации
smote = SMOTE(random_state = 14)
X_train_3, y_train_3 = smote.fit_sample(X_train, y_train)
  • Теперь у вас есть баланс в целевом классе, как мы увидим ниже.
y_train_3.value_counts().plot(kind='bar')
plt.title('label balance')
plt.xlabel('label values')
plt.ylabel('amount per label')
plt.show()

  • Это было неплохо!

"Оглавление"

7. Модели

  • Мы должны начать этот раздел с заявления об отказе от ответственности: набор данных здесь является довольно простым набором данных и содержит несколько функций. Просто потому, что некоторые модели в паре с разными методами балансировки данных могут работать здесь определенным образом, они могут выглядеть по-разному в разных наборах данных и разных сценариях.
  • Начнем с создания экземпляра нашей техники моделирования (мое счастливое число - 14).
  • Мы также будем использовать XGBoost только для целей единообразия (и в любом случае он работает очень хорошо).
  • Чтобы узнать больше о классификаторах XGBoost в Python, вы можете просмотреть документацию
xgbc=XGBClassifier(random_state=14)
  • Чтобы упростить жизнь и не писать все снова и снова, я также создам следующую функцию

Создание classification функции

  • Входные данные поезда, тестовые данные и тип модели
  • Начните с подгонки модели к данным поезда
  • Назначьте переменные прогноза для поезда и набора тестов
  • Из-за несбалансированности классов в тестовых данных отображайте оценку f1 для прогнозов тренировок и тестов с использованием встроенной библиотеки метрик scikit-learn (дополнительную информацию о рейтинге F1 можно найти в документации )
  • Отобразить матрицу неточностей для производительности тестовых данных (дополнительную информацию о матрицах неточностей в Python можно найти в документации)
  • Давайте пробежимся по результатам

Способ 1

Способ 2

Способ 3

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

"Оглавление"

8. Что, если мы уравновесим и тестовые данные?

  • Я в основном задаю этот вопрос, поскольку он поможет укрепить то, что мы видели выше, и вселит уверенность в том, что наши модели очень сильны.
  • Ниже мы начнем с балансировки тестовых данных.
test = pd.concat([X_test,y_test],axis=1)
test_balanced = (upsample_classes(test,'price_bin'))
X_test_b = test_balanced.drop('price_bin',axis=1)
y_test_b = test_balanced.price_bin
  • Ниже приведены новые результаты после балансировки данных.

Способ 1

Способ 2

Способ 3

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

"Оглавление"

9. Заключение

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

"Оглавление"

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

10. Ресурсы

Рейн, С. (2018). Углубленный анализ алмазов. Получено с https://www.kaggle.com/fuzzywizard/diamonds-in-depth-analysis

Тестирование классификации на данных о дисбалансе с передискретизацией. Получено с https://stats.stackexchange.com/questions/60180/testing-classification-on-oversampled-imbalance-data

Брвонли, Дж. (17 января 2020 г.). SMOTE для несбалансированной классификации с помощью Python. Источник https://machinelearningmaster.com/smote-oversampling-for-imbalanced-classification/

Пол, S (2018, 4 октября). Глубокое погружение с несбалансированными данными. Получено с https://www.datacamp.com/community/tutorials/diving-deep-imbalanced-data

Мурали, А (31 мая 2018 г.). Вставить код в средний. Получено с: https://medium.com/@aryamurali/embed-code-in-medium-e95b839cfdda

Махендру, К. (24 июня 2019 г.). Как работать с несбалансированными данными с помощью SMOTE. Получено с https://medium.com/analytics-vidhya/balance-your-data-using-smote-98e4d79fcddb »

Аллиен, М. (10 января 2019 г.). Создание оглавления для средних статей. Получено с https://medium.com/@AllienWorks/creating-table-of-contents-for-medium-articles-5f9087377b82#dc26

Докен, С (2017, 6 ноября). SMOTE объяснил новичкам - технику передискретизации синтетического меньшинства строка за строкой. Получено с https://rikunert.com/SMOTE_explained

Луса, Л. и Благус, Р. (22 марта 2013 г.). Получено с: https://www.datacamp.com/community/tutorials/diving-deep-imbalanced-data

"Оглавление"