Знайте, где подводные камни и как их избежать

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

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

  1. недостаточная выборка класса большинства
  2. чрезмерная выборка классов меньшинств
  3. сочетание недостаточной выборки класса большинства и избыточной выборки класса меньшинства

В этом сообщении блога будет рассмотрен один из видов техники передискретизации, называемый методом SMOTE.

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

Давайте посмотрим на некоторые данные классификации, которые не были сбалансированы с помощью SMOTE. Мы будем использовать набор данных по раку груди из библиотеки sci-kit learn. Давайте импортируем необходимые пакеты, импортируем наши данные и создадим функцию, которая может оценивать наши модели.

import pandas as pd
from sklearn import datasets
from sklearn.model_selection import cross_val_score
from sklearn.metrics import confusion_matrix

def evaluate_model(X_train, y_train, model):
    model.fit(X_train, y_train)
    preds = model.predict(X_test)
    scores = cross_val_score(model, X_train, y_train, cv=3, scoring="accuracy")
    diff = scores.mean() - model.score(X_test, y_test)
    SD = diff / scores.std()
    
    print(f"Training Score:{model.score(X_train, y_train)}")
    print(f"Cross V Score: {scores.mean()} +/- {scores.std()}")
    print(f"Testing Score: {model.score(X_test, y_test)}")
    print(f"Cross & Test Diff: {diff}")
    print(f"Standard Deviations Away: {SD}")
    print(confusion_matrix(y_test, preds))
cancer = datasets.load_breast_cancer()
X = cancer.data
y = cancer.target

Отсюда мы проведем разделение наших данных на обучающий тест и инициализируем нашу базовую модель.

from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression
X_train, X_test, y_train, y_test = train_test_split(X, y, stratify=y, random_state=0)
clf = LogisticRegression(random_state=0, solver='newton-cg', max_iter=1000)

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

Теперь посмотрим, как работает наша базовая модель.

evaluate_model(X_train, y_train, clf)

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

Итак, при работе с синтетическими данными первое правило:

Не добавляйте синтетические данные в данные тестирования!

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

from imblearn.over_sampling import SMOTE
smt = SMOTE(random_state=0)
X_train_SMOTE, y_train_SMOTE = smt.fit_sample(X_train, y_train)

Отсюда мы можем обучить нашу модель на обучающих данных с включенным SMOTE.

evaluate_model(X_train_SMOTE, y_train_SMOTE, clf)

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

Не помещайте синтетические данные в свои данные проверки!

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

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

from imblearn.pipeline import make_pipeline
pipeline = make_pipeline(smt, clf)
evaluate_model(X_train, y_train, pipeline)

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