Введение
Привет всем,
Я работаю над дипломной работой и сталкиваюсь с проблемой бинарной классификации с несбалансированным вкладом класса. У меня примерно в 10 раз больше отрицательных ("0") ярлыков как положительных ("1") ярлыков. По этой причине я рассмотрел не только точность наблюдения и ROC-AUC, но также взвешенную / сбалансированную точность и Precision-Recall-AUC.
Я уже задавал вопрос на GitHub (https://github.com/keras-team/keras/issues/12991), но проблема еще не решена, поэтому я подумал, что эта платформа может быть лучшим местом!
Описание проблемы
Во время некоторых расчетов проверки, установленной в пользовательском обратном вызове, я заметил, более или менее по совпадению, что взвешенная точность всегда отличается от моих результатов с использованием sklearn.metrics.accuracy_score () .
Используя Keras, взвешенная точность должна быть объявлена в model.compile () и является ключом в словаре logs {} после каждой эпохи (а также записывается в файл журнала обратным вызовом CSVLogger или объекту истории) или возвращается в виде значения в списке model.evaluate () ,
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'],
weighted_metrics=['accuracy'])
Я вычисляю вектор val_sample_weights на основе вклада класса обучающего набора с помощью функции Sklearn.metrics class_weight.compute_sample_weight () и с помощью class_weight.compute_class_weight () .
cls_weights = class_weight.compute_class_weight('balanced', np.unique(y_train._values),
y_train._values)
cls_weight_dict = {0: cls_weights[0], 1: cls_weights[1]}
val_sample_weights = class_weight.compute_sample_weight(cls_weight_dict, y_test._values)
В model.fit () Я передаю этот вектор вместе с данными проверки и sklearn.metrics.accuracy_score () Я передаю его в имя параметра sample_weight для сравнения результатов на той же основе.
model_output = model.fit(x_train, y_train, epochs=500, batch_size=32, verbose=1,
validation_data=(x_test, y_test, val_sample_weights))
Кроме того, я вывел уравнение того, как Scitkit-learn вычисляет взвешенную точность из нескольких простых примеров, и кажется, что оно вычисляется по следующему уравнению (что мне кажется вполне разумным):
Уравнение Латекса
TP, TN, FP и FN - значения, указанные в матрице путаницы, а w_p и w_n - веса классов положительного и отрицательного классов соответственно.
Простой пример проверки можно найти здесь:
* ** 1053 одна тысяча пятьдесят два * -learn.org / стабильный / модули / полученные / sklearn.metrics.balanced_accuracy_score.html
Просто для полноты sklearn.metrics.accuracy_score (..., sample_weight =) возвращает тот же результат, что и sklearn.metrics.balanced_accuracy_score () .
Информация о системе
- GeForce RTX 2080 Ti
- Керас 2.2.4
- Tensorflow-GPU 1.13.1
- Склеарн 0.19.2
- Python 3.6.8
- CUDA Версия 10.0.130
Пример кода
Я искал простой пример, чтобы облегчить воспроизведение проблемы, даже если дисбаланс классов здесь слабее (1: 2, а не 1:10). Он основан на вводном руководстве по Keras, которое можно найти здесь:
https://towardsdatascience.com/k-as-in-keras-simple-classification-model-a9d2d23d5b5a
Набор данных Pima Indianas будет загружен, как и сделано по ссылке выше, из хранилища Джейсона Браунли, создателя домашней страницы Machine Learning Mastery. Но я думаю, что это также может быть загружено с различных других сайтов.
Итак, вот код:
from keras.layers import Dense, Dropout
from keras.models import Sequential
from keras.regularizers import l2
import pandas as pd
import numpy as np
from sklearn.utils import class_weight
from sklearn.metrics import accuracy_score
from sklearn.model_selection import train_test_split
file = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/' \
'pima-indians-diabetes.data.csv'
# Load csv data from file to data using pandas
data = pd.read_csv(file, names=['pregnancies', 'glucose', 'diastolic', 'triceps', 'insulin',
'bmi', 'dpf', 'age', 'diabetes'])
# Process data
data.head()
x = data.drop(columns=['diabetes'])
y = data['diabetes']
# Split into train and test
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.1, random_state=0)
# define a sequential model
model = Sequential()
# 1st hidden layer
model.add(Dense(100, activation='relu', input_dim=8, kernel_regularizer=l2(0.01)))
model.add(Dropout(0.3))
# 2nd hidden layer
model.add(Dense(100, activation='relu', kernel_regularizer=l2(0.01)))
model.add(Dropout(0.3))
# Output layer
model.add(Dense(1, activation='sigmoid'))
# Compilation with weighted metrics
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'],
weighted_metrics=['accuracy'])
# Calculate validation _sample_weights_ based on the class distribution of train labels and
# apply it to test labels using Sklearn
cls_weights = class_weight.compute_class_weight('balanced', np.unique(y_train._values),
y_train._values)
cls_weight_dict = {0: cls_weights[0], 1: cls_weights[1]}
val_sample_weights = class_weight.compute_sample_weight(cls_weight_dict, y_test._values)
# Train model
model_output = model.fit(x_train, y_train, epochs=500, batch_size=32, verbose=1,
validation_data=(x_test, y_test, val_sample_weights))
# Predict model
y_pred = model.predict(x_test, batch_size=32, verbose=1)
# Classify predictions based on threshold at 0.5
y_pred_binary = (y_pred > 0.5) * 1
# Sklearn metrics
sklearn_accuracy = accuracy_score(y_test, y_pred_binary)
sklearn_weighted_accuracy = accuracy_score(y_test, y_pred_binary,
sample_weight=val_sample_weights)
# metric_list has 3 entries: [0] val_loss weighted by val_sample_weights, [1] val_accuracy
# [2] val_weighted_accuracy
metric_list = model.evaluate(x_test, y_test, batch_size=32, verbose=1,
sample_weight=val_sample_weights)
print('sklearn_accuracy=%.3f' %sklearn_accuracy)
print('sklearn_weighted_accuracy=%.3f' %sklearn_weighted_accuracy)
print('keras_evaluate_accuracy=%.3f' %metric_list[1])
print('keras_evaluate_weighted_accuracy=%.3f' %metric_list[2])
Итоги и резюме
Например, я получаю:
sklearn_accuracy=0.792
sklearn_weighted_accuracy=0.718
keras_evaluate_accuracy=0.792
keras_evaluate_weighted_accuracy=0.712
Значение «невзвешенной» точности одинаково как для Sklearn, так и для Keras. Разница не очень большая, но она увеличивается по мере того, как набор данных становится более несбалансированным. Например, для моей задачи она всегда отличается примерно на 5% друг от друга!
Может быть, я что-то упускаю, и это должно быть так, но в любом случае сбивает с толку то, что Keras и Sklearn предоставляют разные значения, особенно думая о всей вещи class_weights и sample_weights как о теме, которую трудно понять. К сожалению, я не слишком глубоко в Keras, чтобы самостоятельно искать в коде Keras.
Буду очень признателен за любые ответы!