Разница между взвешенной метрикой точности Keras и Scikit-learn - PullRequest
0 голосов
/ 24 июня 2019

Введение

Привет всем,

Я работаю над дипломной работой и сталкиваюсь с проблемой бинарной классификации с несбалансированным вкладом класса. У меня примерно в 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.

Буду очень признателен за любые ответы!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...