python - Keras 和 Scikit-learn 的加权准确度指标之间的差异
问题描述
介绍
大家好,
我正在写我的毕业论文,我面临着一个班级贡献不平衡的二元分类问题。我的负(“0”)标签是正(“1”)标签的大约 10 倍。出于这个原因,我不仅考虑了观察精度和 ROC-AUC,还考虑了加权/平衡精度和 Precision-Recall-AUC。
我已经在 GitHub ( https://github.com/keras-team/keras/issues/12991 ) 上提出了这个问题,但是这个问题还没有得到解答,所以我认为这里的这个平台可能是更好的地方!
问题描述
在对自定义回调中的验证集进行一些计算时,我或多或少地注意到,加权准确度总是与我使用sklearn.metrics.accuracy_score()的结果不同。
使用 Keras,加权准确度必须在model.compile()中声明,并且是每个 epoch 之后的 logs{} 字典中的一个键(并且还通过 CSVLogger 回调写入日志文件或历史对象)或返回作为model.evaluate()列表中的值,
model.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy'],
weighted_metrics=['accuracy'])
我使用 Sklearn.metrics 函数class_weight.compute_sample_weight()并借助class_weight.compute_class_weight ()根据训练集的类贡献计算 val_sample_weights 向量。
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 分别是正类和负类的类权重。
可以在这里找到一个简单的测试示例:
https://scikit-learn.org/stable/modules/generated/sklearn.metrics.balanced_accuracy_score.html
只是为了完整起见,sklearn.metrics.accuracy_score(..., sample_weight=)返回与sklearn.metrics.balanced_accuracy_score()相同的结果。
系统信息
- GeForce RTX 2080 钛
- Keras 2.2.4
- TensorFlow-GPU 1.13.1
- Sklearn 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 发病糖尿病数据集将从主页机器学习掌握的创建者 Jason Brownlee 的存储库中下载,如上面的链接中所做的那样。但我想它也可以从其他各种网站下载。
所以最后这里是代码:
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 代码。
我真的很感激收到任何答案!
解决方案
我重复了您的确切玩具示例,实际上发现sklearn
并keras
给出了相同的结果。我重复了这个实验 5 次,以确保它不是偶然的,而且每次的结果都是相同的。例如,其中一次运行:
sklearn_accuracy=0.831
sklearn_weighted_accuracy=0.800
keras_evaluate_accuracy=0.831
keras_evaluate_weighted_accuracy=0.800
仅供参考,我正在使用sklearn
和keras
版本:
0.20.3
2.3.1
分别。请参阅此 google colab 示例:https ://colab.research.google.com/drive/1b5pqbp9TXfKiY0ucEIngvz6_Tc4mo_QX
推荐阅读
- java - 如何将 Json Clob 提取到 java
- android - 添加文件或文件夹时,zib4j zipfile 会因 zip 文件大小小于最小值而引发 ZipException
- numpy - 如何组合来自 2 个 numpy 数组的字符串
- python - 使用 Python 解析对 JSON 的请求时出错
- wget - 如何使用 Github Actions 触发下载网站的静态副本并推送到 S3?
- r - 每次在 R 中更新图形时如何计算组件的数量
- android - 如何在 Android 上的 Google Chrome 67.0.3396.87 中提取和(重新)导入书签
- c - 我是否在此函数中创建了内存泄漏?
- adobe - AEM - 网格容器在编辑器/预览中可见,但在发布时不可见
- mongodb - 使用 mongoose 聚合获取多个字段的计数