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

Вопрос в том, правильно ли мы его применяем?

Цель этой статьи - показать способ использования методов балансировки при перекрестной проверке без принудительной балансировки складок теста CV; таким образом, чтобы получить более реалистичные результаты оценки резюме.

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

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

Однако встроенные функции, такие как cross_validate
sickit-learn, по умолчанию не решают проблему несбалансированности классов.

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

Давайте рассмотрим этот ход мыслей на простом примере на Python.
Я использую здесь набор данных Kaggle Обнаружение мошенничества с кредитными картами,
следуя приведенным ниже инструкциям.

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

Внимание! Соответствующий код находится в конце статьи.

Раздел 1. Балансировка запрещена

Здесь мы наносим точные результаты при полном отсутствии балансировки:

Average Train Precision among C-V folds: 76.8 %
Average Test Precision among C-V folds: 71.98 %
Single Test set precision: 75.86 %
Single Test set Low Class(No-Fraud) Precision: 99.9 %
Single Test set High Class(Fraud) Precision: 75.9 %
  • Точные измерения складок CV кажутся весьма нестабильными.
  • Некоторые показатели точности превышают соответствующую точность в наборе поездов.
  • Возникают большие различия между точностью набора тестов CV-складок.
  • Средняя точность CV не сильно отличается от неизвестного набора данных о мошенничестве, но разница все же важна.
  • Таким образом, мы рассмотрим балансировку для нашего набора данных.

Раздел 2: Балансировка вне C-V (занижение выборки)

Здесь мы наносим на график результаты точности балансировки с недостаточной выборкой только подмножества поездов перед применением к нему CV:

Average Train Precision among C-V folds: 99.81 %
Average Test Precision among C-V folds: 95.24 %
Single Test set precision: 3.38 %
Single Test set Low Class(No-Fraud) Precision: 100.0 %
Single Test set High Class(Fraud) Precision: 3.40 %
  • Как и раньше:
    - Результаты CV кажутся в основном высокими, но нестабильными.
    - Некоторые метрики точности превышают их соответствующую точность на наборе поездов.
    - Между точностью тестового набора тестового набора наблюдаются большие различия. Резюме складывается.
  • Кроме того, любая положительная интерпретация некоторых из вышеупомянутых высоких чисел, вероятно, неверна, поскольку «неизвестный» несбалансированный тест дает плохую точность.
  • Вышеупомянутое различие исходит из того факта, что:
    - тестовый набор каждой складки сбалансирован,
    - модель каждой складки соответствует данным сбалансированной последовательности складок.
  • Таким образом, модель хорошо работает в CV, поскольку она обучается и тестируется на аналогичных распределениях классов (сбалансировано).
  • Однако «неизвестный» набор данных о мошенничестве не будет сбалансирован, поэтому мы видим очень низкую точность на тестовом наборе.

Раздел 3: Балансировка внутри C-V (недостаточная выборка)

Здесь мы наносим на график точные результаты балансировки с недостаточной выборкой,
только набор поездов каждой CV-складки перед подгонкой модели на нее и
прогнозированием на тестовой выборке CV-складки:

Average Train Precision among C-V folds: 99.21 %
Average Test Precision among C-V folds: 4.2 %
Single Test set precision: 3.38 %
Single Test set Low Class(No-Fraud) Precision: 100.0 %
Single Test set High Class(Fraud) Precision: 3.40 %
  • Мы видим стабильные результаты точности среди складок резюме, но недостаточно эффективные.
  • Мы видим точность неизвестных данных очень близкую к средней точности кратных CV.
  • Что мы здесь сделали, так это то, что мы заставили модель каждой свертки распознавать существование мошеннических транзакций и пытаться предсказать на реалистичной несбалансированной версии неизвестных тестовых (сверток) данных.
  • Тем не менее точность очень плохая, в отличие от ~ 76% несбалансированной версии.
  • Означает ли это, что мы отказываемся от балансировки?
  • Следующим шагом может быть изучение различных соотношений балансировки.
  • Но сначала давайте рассмотрим избыточную выборку, а не недостаточную выборку, потому что у нас слишком мало мошеннических транзакций.

Раздел 4: Балансировка вне C-V с передискретизацией

Здесь мы наносим на график точные результаты балансировки с передискретизацией,
только подмножество поезда перед применением к нему CV:

Average Train Precision among C-V folds: 98.51 %
Average Test Precision among C-V folds: 98.52 %
Single Test set precision: 12.61 %
Single Test set Low Class(No-Fraud) Precision: 100.0 %
Single Test set High Class(Fraud) Precision: 12.6 %
  • Результаты более стабильны, чем в Разделе 2, благодаря избыточной выборке;
    Недостаточно выборки осталось слишком мало экземпляров.
  • И снова баллы CV не отражают действительность неизвестного набора тестов.

Раздел 5: Балансировка внутри C-V (передискретизация)

Здесь мы наносим на график точные результаты балансировки с передискретизацией,
только набор поездов каждой CV-складки перед подгонкой модели на нее и выполнением прогнозов на тестовой выборке CV-складки:

Average Train Precision among C-V folds: 98.38 %
Average Test Precision among C-V folds: 8.7 %
Single Test set precision: 12.61 %
Single Test set Low Class(No-Fraud) Precision: 100.0 %
Single Test set High Class(Fraud) Precision: 12.6 %
  • Благодаря передискретизации оценка точности повышается по сравнению с разделом 3.
  • Тем не менее, мы получаем в каждой оценке кратности CV результаты, достаточно близкие к тем, которые модель будет производить на неизвестном тестовом наборе данных.

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

Наконец, я бы не рекомендовал строгую адаптацию вышеизложенного,
но чтобы вы держали от вышесказанного предложение и пищу для размышлений!

Спасибо, что прочитали это!
Меня интересуют любые комментарии или предложения.

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

Импорт и разделение данных:

import pandas as pd
import numpy as np
from sklearn import datasets
from sklearn.model_selection import cross_validate
from sklearn.metrics import accuracy_score, precision_score
from sklearn.linear_model import LogisticRegression
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split,StratifiedKFold
from imblearn.over_sampling import SMOTE
from imblearn.under_sampling import RandomUnderSampler

df = pd.read_csv('creditcard.csv').sample(50000, random_state=0)
train, test = train_test_split(df, test_size=0.3, random_state=0, shuffle=True)
y_train = np.array(train["Class"])
y_test = np.array(test["Class"])
del train["Class"]
del test["Class"]
train = train.reset_index(drop=True)
test = test.reset_index(drop=True)

Количество экземпляров в классе на данный момент:

Train Set Class 0:  34941
Train Set Class 1:  59

Test Set Class 0:  14967
Test Set Class 1:  33

Теперь нам понадобятся некоторые вспомогательные функции.

Один для построения оценок точности:

def plotit(tr,te):
    fig, ax = plt.subplots()
    r = range(1,11)
    ax.scatter(r, tr,label='cv train scores')
    ax.plot(r, tr, 'C3', lw=1)
    ax.scatter(r, te,label='cv test scores')
    ax.plot(r, te, 'C2', lw=1)
    ax.set_xlabel('CV Folds')
    ax.set_ylabel('Precision')
    ax.legend()
    plt.show()

Один для точности значения класса:

def class_precisions(yt,yp):
    df = pd.DataFrame(np.transpose([yp,yt]),
    columns=('test_actual','test_preds'))
    mask1 = (df['test_actual']==0)
    mask2 = (df['test_actual']==df['test_preds'])
    low_CORRECT = df[mask1 & mask2].count()[0]
    low_NOFRAUD = df[mask1].count()[0]
    print("Single Test set Low Class(No-Fraud) Precision:", 
    np.round(low_CORRECT/low_NOFRAUD,3)*100,"%")
    high_class_df = pd.DataFrame(np.transpose([yp,yt]),
    columns=('test_actual','test_preds'))
    mask1 = (df['test_actual']==1)
    mask2 = (df['test_actual']==df['test_preds'])
    high_CORRECT = df[mask1 & mask2].count()[0]
    high_FRAUD = df[mask1].count()[0]
    print("Single Test set High Class(Fraud) Precision:", 
    np.round(high_CORRECT/high_FRAUD,3)*100,"%")

И тот, который собирает весь поток:

def compare(x_train,y,x_test,yt,custom=False,method='over'):
    if custom:
        tr,te = custom_balancing_cv(x_train, y,    
                cv=10,method=method)
    else:
        results = cross_validate( LogisticRegression(              
                  random_state=0, solver='liblinear',   
                  max_iter=10000), x_train, y, cv=10,
                  return_train_score=True,scoring=['precision'])
        tr = results['train_precision']
        te = results['test_precision']
    plotit(tr,te)
    clf = LogisticRegression(random_state=0,solver='liblinear',                     
                             max_iter=10000)
    # If we customly balance train set of folds,
    # we need to compare with a balanced single prediction
    if custom:
        if method=='under':
            s = RandomUnderSampler(random_state=0)
        else:
            s = SMOTE(random_state=0)
        x_train, y = s.fit_sample(x_train, y)
    clf.fit(x_train,y)
    y_pred = clf.predict(x_test)
    print("Average Train Precision among C-V            
          folds:",np.round(np.average(tr)*100,2),"%")
    print("Average Test Precision among C-V 
          folds:",np.round(np.average(te)*100,2),"%")
    print("Single Test set   precision:", 
          np.round(precision_score(yt, y_pred)*100,2),"%")
    class_precisions(yt,y_pred)

Раздел 1: балансировка отсутствует

compare(train, y_train,test,y_test)

Раздел 2: балансировка вне C-V (недостаточная выборка)

rus = RandomUnderSampler(random_state=0)
X_res, y_res = rus.fit_sample(train, y_train)
compare(X_res, y_res,test,y_test)

Раздел 3: балансировка внутри C-V (недостаточная выборка)

compare(train, y_train,test,y_test,custom=True, method='under')

Раздел 4: балансировка вне C-V (передискретизация)

sm = SMOTE(random_state=0)
X_res, y_res = sm.fit_sample(train, y_train)
compare(X_res, y_res,test,y_test)

Раздел 5: балансировка внутри C-V (передискретизация)

compare(train, y_train,test,y_test,custom=True, method='over')

Еще раз спасибо за прочтение, BB!