Кто не сталкивался с необходимостью применять метод перекрестной проверки, в то время как набор данных несбалансирован в отношении количества экземпляров на значение целевого класса.
Вопрос в том, правильно ли мы его применяем?
Цель этой статьи - показать способ использования методов балансировки при перекрестной проверке без принудительной балансировки складок теста 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!