Machine Learning Mastery 不平衡数据教程(六)
不平衡数据集的单类分类算法
最后更新于 2020 年 8 月 21 日
离群值或异常值是不符合其余数据的罕见例子。
识别数据中的异常值被称为异常值或异常检测,而机器学习中关注这个问题的一个子领域被称为单类分类。这些是无监督学习算法,试图对“正常”示例进行建模,以便将新示例分类为正常或异常(例如异常值)。
单类分类算法可用于类别分布严重偏斜的二进制分类任务。这些技术可以适用于训练数据集中多数类的输入示例,然后在保持测试数据集上进行评估。
尽管单类分类算法不是为这些类型的问题而设计的,但对于不存在或很少存在少数类的不平衡类别数据集,或者对于没有连贯结构来分离可由监督算法学习的类的数据集,单类分类算法可能是有效的。
在本教程中,您将发现如何对具有严重倾斜类分布的数据集使用单类分类算法。
完成本教程后,您将知道:
- 单类分类是机器学习的一个领域,它为异常值和异常检测提供技术。
- 如何将单类分类算法应用于类别分布严重倾斜的不平衡分类?
- 如何拟合和评估单类分类算法,如 SVM、隔离林、椭圆包络和局部离群因子。
用我的新书Python 不平衡分类启动你的项目,包括分步教程和所有示例的 Python 源代码文件。
我们开始吧。
不平衡分类的单类分类算法 图片由科萨拉·班达拉提供,保留部分权利。
教程概述
本教程分为五个部分;它们是:
- 不平衡数据的单类分类
- 一类支持向量机
- 隔离林
- 最小协方差行列式
- 局部异常因子
不平衡数据的单类分类
异常值既罕见又不寻常。
稀有性表明它们相对于非异常数据(所谓的内联数据)具有较低的频率。不寻常表明它们不适合数据分布。
异常值的存在会导致问题。例如,单个变量可能有一个远离大量示例的异常值,这可能会偏斜汇总统计信息,如平均值和方差。
作为一种数据准备技术,拟合机器学习模型可能需要识别和去除异常值。
识别数据集中异常值的过程一般称为异常检测,其中异常值为“异常”,其余数据为“正常”异常值检测或异常检测是一个具有挑战性的问题,由一系列技术组成。
在机器学习中,解决异常检测问题的一种方法是单类分类。
单类分类,简称 OCC,包括在“正常数据上拟合一个模型,并预测新数据是正常的还是异常的。
单类分类器旨在捕获训练实例的特征,以便能够区分它们和可能出现的异常值。
—第 139 页,从不平衡数据集中学习,2018。
单类分类器适用于仅包含普通类示例的训练数据集。一旦准备好,该模型被用于将新的例子分类为正常或非正常,即异常值或异常值。
单类分类技术可用于二进制(两类)不平衡分类问题,其中负案例(类别 0)被视为“正常”,而正案例(类别 1)被视为异常值或异常。
- 阴性情况:正常或内联。
- 阳性病例:异常或异常值。
鉴于这种方法的性质,单类分类最适合那些正案例在特征空间中没有一致模式或结构的任务,这使得其他分类算法很难学习类边界。相反,将阳性病例视为异常值,它允许单类分类器忽略辨别任务,而是关注偏离正常或预期的情况。
事实证明,当少数民族缺乏任何结构时,这种解决方案特别有用,因为少数民族主要由小的间断或嘈杂的实例组成。
—第 139 页,从不平衡数据集中学习,2018。
如果训练集中的阳性病例数量很少,以至于不值得包含在模型中,例如几十个或更少的例子,也可能是合适的。或者对于在训练模型之前无法收集到正面案例的问题。
明确地说,这种针对不平衡分类的单类分类算法的适应性是不寻常的,但是在某些问题上是有效的。这种方法的缺点是,我们在训练过程中遇到的任何异常值(正例)都不会被单类分类器使用,而是被丢弃。这表明,或许可以并行尝试问题的反向建模(例如,将正案例建模为正常情况)。它还建议单类分类器可以为一组算法提供输入,每个算法以不同的方式使用训练数据集。
人们必须记住,单类分类器的优势是以丢弃所有关于多数类的可用信息为代价的。因此,该解决方案应谨慎使用,可能不适合某些特定应用。
—第 140 页,从不平衡数据集中学习,2018。
Sklearn 库提供了一些常用的单类分类算法,用于异常值或异常检测和变化检测,例如一类 SVM、隔离森林、椭圆包络和局部异常因子。
在接下来的部分中,我们将依次看一看每一个。
在此之前,我们将设计一个二元类别数据集来演示算法。我们将使用make _ classification()sci kit-learn 函数创建 10,000 个示例,少数类中有 10 个示例,多数类中有 9,990 个示例,或者 0.1%对 99.9%,或者大约 1:1000 的类分布。
下面的示例创建并总结了这个数据集。
# Generate and plot a synthetic imbalanced classification dataset
from collections import Counter
from sklearn.datasets import make_classification
from matplotlib import pyplot
from numpy import where
# define dataset
X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0,
n_clusters_per_class=1, weights=[0.999], flip_y=0, random_state=4)
# summarize class distribution
counter = Counter(y)
print(counter)
# scatter plot of examples by class label
for label, _ in counter.items():
row_ix = where(y == label)[0]
pyplot.scatter(X[row_ix, 0], X[row_ix, 1], label=str(label))
pyplot.legend()
pyplot.show()
运行该示例首先总结了类分布,确认不平衡是按预期创建的。
Counter({0: 9990, 1: 10})
接下来,创建散点图,并将示例绘制为由类标签着色的点,显示多数类的大质量(蓝色)和少数类的几个点(橙色)。
正类中的例子如此之少,而正类中的例子又如此之少,这种严重的类不平衡可能为使用单类分类方法奠定了良好的基础。
1 到 1000 类不平衡二分类问题的散点图
一类支持向量机
最初为二进制分类开发的支持向量机或 SVM 算法可用于单类分类。
如果用于不平衡分类,在测试单类版本之前,最好在数据集上评估标准 SVM 和加权 SVM。
当对一个类建模时,该算法捕获多数类的密度,并将密度函数极值上的例子分类为异常值。SVM 的这种修改被称为一级 SVM。
…一种计算二进制函数的算法,该二进制函数应该捕获输入空间中概率密度所在的区域(它的支持),也就是说,一个函数使得大部分数据将位于该函数非零的区域。
——估计高维分布的支持,2001。
Sklearn 库在 OneClassSVM 类中提供了一个一类 SVM 的实现。
与标准 SVM 的主要区别在于,它以无监督的方式进行拟合,并且不像 C 那样提供用于调整余量的正常超参数。取而代之的是,它提供了一个超参数“ nu ”,该参数控制支持向量的灵敏度,并且应该被调整到数据中离群值的近似比率,例如 0.01%。
...
# define outlier detection model
model = OneClassSVM(gamma='scale', nu=0.01)
该模型可以适用于训练数据集中的所有示例,或者只适用于多数类中的那些示例。也许在你的问题上两者都试试。
在这种情况下,我们将尝试只适合训练集中属于多数类的那些示例。
# fit on majority class
trainX = trainX[trainy==0]
model.fit(trainX)
一旦拟合,该模型可用于识别新数据中的异常值。
在模型上调用 predict() 函数时,对于正常例子会输出 a +1,即所谓的内联,对于异常值会输出 a -1。
- 内联预测 : +1
- 异常值预测 : -1
...
# detect outliers in the test set
yhat = model.predict(testX)
如果我们想评估模型作为二进制分类器的表现,我们必须将测试数据集中的标签从多数类和少数类的 0 和 1 分别更改为+1 和-1。
...
# mark inliers 1, outliers -1
testy[testy == 1] = -1
testy[testy == 0] = 1
然后,我们可以将模型的预测与预期目标值进行比较,并计算得分。假设我们有清晰的类标签,我们可以使用像准确率、召回率这样的分数,或者两者的组合,比如 F-measure (F1-score)。
在这种情况下,我们将使用 F-measure 得分,这是准确率和召回率的调和平均值。我们可以使用 f1_score() 函数计算 F-measure,并通过“ pos_label 参数将少数民族类的标签指定为-1。
...
# calculate score
score = f1_score(testy, yhat, pos_label=-1)
print('F1 Score: %.3f' % score)
将这些联系在一起,我们可以在合成数据集上评估单类 SVM 算法。我们将数据集一分为二,用一半以无监督的方式训练模型,另一半评估模型。
下面列出了完整的示例。
# one-class svm for imbalanced binary classification
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.metrics import f1_score
from sklearn.svm import OneClassSVM
# generate dataset
X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0,
n_clusters_per_class=1, weights=[0.999], flip_y=0, random_state=4)
# split into train/test sets
trainX, testX, trainy, testy = train_test_split(X, y, test_size=0.5, random_state=2, stratify=y)
# define outlier detection model
model = OneClassSVM(gamma='scale', nu=0.01)
# fit on majority class
trainX = trainX[trainy==0]
model.fit(trainX)
# detect outliers in the test set
yhat = model.predict(testX)
# mark inliers 1, outliers -1
testy[testy == 1] = -1
testy[testy == 0] = 1
# calculate score
score = f1_score(testy, yhat, pos_label=-1)
print('F1 Score: %.3f' % score)
运行该示例使模型适合训练集中多数类的输入示例。然后使用该模型将测试集中的示例分类为内联和外联。
注:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
在这种情况下,获得了 0.123 的 F1 分数。
F1 Score: 0.123
隔离林
隔离森林,简称 iForest,是一种基于树的异常检测算法。
……隔离森林(iForest),它纯粹基于隔离的概念检测异常,而不采用任何距离或密度测量
——基于隔离的异常检测,2012。
它基于对正常数据进行建模,从而隔离特征空间中数量少且不同的异常。
……我们提出的方法利用了两个异常的数量属性:I)它们是由较少实例组成的少数,以及 ii)它们具有与正常实例非常不同的属性值。
——隔离森林,2008 年。
创建树形结构来隔离异常。结果是孤立的示例在树中具有相对较短的深度,而正常数据则不那么孤立,在树中具有更大的深度。
……可以有效地构建树形结构来隔离每个实例。因为异常容易被隔离,所以它们会被隔离在更靠近树根的地方;而正常点被隔离在树的较深端。
——隔离森林,2008 年。
Sklearn 库在 IsolationForest 类中提供了隔离林的实现。
该模型最重要的超参数可能是设置要创建的树的数量的“n _ estimates”参数和用于帮助定义数据集中异常值数量的“污染”参数。
我们知道污染大约是阳性病例与阴性病例的 0.01%,因此我们可以将“污染”参数设置为 0.01。
...
# define outlier detection model
model = IsolationForest(contamination=0.01, behaviour='new')
该模型可能在排除异常值的例子上训练得最好。在这种情况下,我们只针对多数类的示例,在输入特征上拟合模型。
...
# fit on majority class
trainX = trainX[trainy==0]
model.fit(trainX)
像一类 SVM 一样,该模型将预测标签为+1 的内联体和标签为-1 的外联体,因此,在评估预测之前,必须更改测试集的标签。
将这些联系在一起,完整的示例如下所示。
# isolation forest for imbalanced classification
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.metrics import f1_score
from sklearn.ensemble import IsolationForest
# generate dataset
X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0,
n_clusters_per_class=1, weights=[0.999], flip_y=0, random_state=4)
# split into train/test sets
trainX, testX, trainy, testy = train_test_split(X, y, test_size=0.5, random_state=2, stratify=y)
# define outlier detection model
model = IsolationForest(contamination=0.01, behaviour='new')
# fit on majority class
trainX = trainX[trainy==0]
model.fit(trainX)
# detect outliers in the test set
yhat = model.predict(testX)
# mark inliers 1, outliers -1
testy[testy == 1] = -1
testy[testy == 0] = 1
# calculate score
score = f1_score(testy, yhat, pos_label=-1)
print('F1 Score: %.3f' % score)
运行示例以无监督的方式在训练数据集上拟合隔离森林模型,然后将测试集中的示例分类为内联和外联,并对结果进行评分。
注:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
在这种情况下,获得了 0.154 的 F1 分数。
F1 Score: 0.154
注:污染程度很低,可能会导致多次跑 F1 得分为 0.0。
要提高此数据集上方法的稳定性,请尝试将污染增加到 0.05 甚至 0.1,然后重新运行该示例。
最小协方差行列式
如果输入变量具有高斯分布,则可以使用简单的统计方法来检测异常值。
例如,如果数据集有两个输入变量,并且都是高斯的,那么特征空间形成多维高斯,并且可以使用这种分布的知识来识别远离该分布的值。
这种方法可以通过定义一个覆盖正常数据的超球(椭球)来推广,超出这个形状的数据被认为是异常值。对于多变量数据,这种技术的有效实现被称为最小协方差行列式,简称 MCD。
拥有这样表现良好的数据是不寻常的,但是如果数据集是这种情况,或者您可以使用幂变换来使变量高斯化,那么这种方法可能是合适的。
最小协方差行列式(MCD)方法是一种高度稳健的多元定位和散射估计方法,其快速算法是可用的。[……]它也是一个方便有效的异常值检测工具。
——最小协方差行列式与延拓,2017。
Sklearn 库通过椭圆包络类提供对该方法的访问。
它提供了“污染”参数,该参数定义了在实践中观察到的异常值的预期比率。我们知道这在我们的合成数据集中是 0.01%,所以我们可以相应地设置它。
...
# define outlier detection model
model = EllipticEnvelope(contamination=0.01)
该模型只能拟合来自多数类的输入数据,以便以无监督的方式估计“正常”数据的分布。
...
# fit on majority class
trainX = trainX[trainy==0]
model.fit(trainX)
然后,该模型将用于将新示例分类为正常(+1)或异常值(-1)。
...
# detect outliers in the test set
yhat = model.predict(testX)
将这些联系在一起,下面列出了在我们的合成二进制类别数据集上使用椭圆包络异常值检测模型进行不平衡分类的完整示例。
# elliptic envelope for imbalanced classification
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.metrics import f1_score
from sklearn.covariance import EllipticEnvelope
# generate dataset
X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0,
n_clusters_per_class=1, weights=[0.999], flip_y=0, random_state=4)
# split into train/test sets
trainX, testX, trainy, testy = train_test_split(X, y, test_size=0.5, random_state=2, stratify=y)
# define outlier detection model
model = EllipticEnvelope(contamination=0.01)
# fit on majority class
trainX = trainX[trainy==0]
model.fit(trainX)
# detect outliers in the test set
yhat = model.predict(testX)
# mark inliers 1, outliers -1
testy[testy == 1] = -1
testy[testy == 0] = 1
# calculate score
score = f1_score(testy, yhat, pos_label=-1)
print('F1 Score: %.3f' % score)
运行示例以无监督方式在训练数据集上拟合椭圆包络模型,然后将测试集中的示例分类为内联和外联,并对结果进行评分。
注:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
在这种情况下,获得了 0.157 的 F1 分数。
F1 Score: 0.157
局部异常因子
识别异常值的一个简单方法是定位那些在特征空间中远离其他示例的示例。
这对于低维度(很少特征)的特征空间可以很好地工作,尽管随着特征数量的增加,它会变得不那么可靠,这被称为维度的诅咒。
局部离群因子,简称 LOF,是一种试图利用最近邻概念进行异常值检测的技术。每个例子都被分配了一个分数,根据其局部邻域的大小来衡量孤立的程度或异常值出现的可能性。得分最高的例子更有可能是异常值。
我们为数据集中的每个对象引入一个局部离群值(LOF),指示其离群程度。
——LOF:识别基于密度的局部异常值,2000 年。
Sklearn 库在localhoutlierfactor 类中提供了这种方法的实现。
该模型可以被定义,并且要求指示数据集中异常值的预期百分比,例如在我们的合成数据集中为 0.01%。
...
# define outlier detection model
model = LocalOutlierFactor(contamination=0.01)
模型不合适。相反,通过调用 fit_predict() ,使用“正常数据集作为识别新数据中异常值的基础。
要使用这个模型来识别测试数据集中的异常值,我们必须首先准备训练数据集,使其只包含多数类的输入示例。
...
# get examples for just the majority class
trainX = trainX[trainy==0]
接下来,我们可以将这些示例与测试数据集中的输入示例连接起来。
...
# create one large dataset
composite = vstack((trainX, testX))
然后,我们可以通过调用 fit_predict() 进行预测,并且只检索测试集中示例的那些标签。
...
# make prediction on composite dataset
yhat = model.fit_predict(composite)
# get just the predictions on the test set
yhat yhat[len(trainX):]
为了使事情变得更简单,我们可以将它包装成一个新的函数,其名称为 lof_predict() ,如下所示。
# make a prediction with a lof model
def lof_predict(model, trainX, testX):
# create one large dataset
composite = vstack((trainX, testX))
# make prediction on composite dataset
yhat = model.fit_predict(composite)
# return just the predictions on the test set
return yhat[len(trainX):]
像 Sklearn 中的其他异常值检测算法一样,预测的标签对于正常值为+1,对于离群值为-1。
将这些联系在一起,下面列出了使用 LOF 异常值检测算法进行分类的完整示例。
# local outlier factor for imbalanced classification
from numpy import vstack
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.metrics import f1_score
from sklearn.neighbors import LocalOutlierFactor
# make a prediction with a lof model
def lof_predict(model, trainX, testX):
# create one large dataset
composite = vstack((trainX, testX))
# make prediction on composite dataset
yhat = model.fit_predict(composite)
# return just the predictions on the test set
return yhat[len(trainX):]
# generate dataset
X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0,
n_clusters_per_class=1, weights=[0.999], flip_y=0, random_state=4)
# split into train/test sets
trainX, testX, trainy, testy = train_test_split(X, y, test_size=0.5, random_state=2, stratify=y)
# define outlier detection model
model = LocalOutlierFactor(contamination=0.01)
# get examples for just the majority class
trainX = trainX[trainy==0]
# detect outliers in the test set
yhat = lof_predict(model, trainX, testX)
# mark inliers 1, outliers -1
testy[testy == 1] = -1
testy[testy == 0] = 1
# calculate score
score = f1_score(testy, yhat, pos_label=-1)
print('F1 Score: %.3f' % score)
运行该示例使用本地离群因子模型和训练数据集,以无监督的方式将测试集中的示例分类为内联和离群,然后对结果进行评分。
注:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
在这种情况下,获得了 0.138 的 F1 分数。
F1 Score: 0.138
进一步阅读
如果您想更深入地了解这个主题,本节将提供更多资源。
报纸
- 估计高维分布的支持,2001。
- 隔离森林,2008 年。
- 基于隔离的异常检测,2012。
- 最小协方差行列式估计量的快速算法,2012。
- 最小协方差行列式与延拓,2017。
- LOF:识别基于密度的局部异常值,2000。
书
- 从不平衡数据集中学习,2018。
- 不平衡学习:基础、算法和应用,2013。
蜜蜂
- 新颖性和异常值检测,Sklearn API 。
- 硬化. svm.OneClassSVM API 。
- 硬化。一起。绝缘林 API 。
- 硬化。协方差。椭圆包络 API 。
- 硬化。邻居。局部外显性因子 API 。
文章
摘要
在本教程中,您发现了如何对具有严重倾斜类分布的数据集使用单类分类算法。
具体来说,您了解到:
- 单类分类是机器学习的一个领域,它为异常值和异常检测提供技术。
- 如何将单类分类算法应用于类别分布严重倾斜的不平衡分类?
- 如何拟合和评估 SVM、隔离林、椭圆包络和局部离群因子等单类分类算法。
你有什么问题吗? 在下面的评论中提问,我会尽力回答。
如何计算不平衡分类的准确率、召回率和 F-Measure
最后更新于 2020 年 8 月 2 日
分类准确率是正确预测的总数除以数据集的预测总数。
作为一种表现度量,准确性不适用于不平衡的分类问题。
主要原因是,来自多数类(或多个类)的压倒性数量的示例将压倒少数类中的示例数量,这意味着即使是不熟练的模型也可以达到 90%或 99%的准确性分数,这取决于类不平衡碰巧有多严重。
使用分类准确度的替代方法是使用精确度和召回度量。
在本教程中,您将发现如何计算和发展直觉,以获得不平衡分类的准确率和召回率。
完成本教程后,您将知道:
- 准确率量化了实际属于正类的正类预测的数量。
- Recall 量化了从数据集中所有正面示例中做出的正面类预测的数量。
- F-Measure 提供了一个单一的分数,在一个数字中平衡了对精确度和召回率的关注。
用我的新书Python 不平衡分类启动你的项目,包括分步教程和所有示例的 Python 源代码文件。
我们开始吧。
- 2020 年 1 月更新:关于精确和召回目标的改进语言。修正了准确率和召回寻求最小化的错别字(谢谢评论!).
- 2020 年 2 月更新:修正了召回和 f1 的变量名错别字。
如何计算不平衡分类的准确率、召回率和 F-Measure瓦尔德马尔合并图片,保留部分权利。
教程概述
本教程分为五个部分;它们是:
- 不平衡分类的混淆矩阵
- 不平衡分类的准确率
- 不平衡分类的召回
- 不平衡分类的准确率与召回率
- 不平衡分类的 f-测度
不平衡分类的混淆矩阵
在我们深入精确和回忆之前,回顾一下混淆矩阵是很重要的。
对于不平衡分类问题,多数类通常被称为阴性结果(例如“无变化”或“阴性检测结果”),少数类通常被称为阳性结果(例如“变化”或“阳性检测结果”)。
混淆矩阵不仅能更深入地了解预测模型的表现,还能更深入地了解哪些类被正确预测,哪些被错误预测,以及出现了什么类型的错误。
最简单的混淆矩阵是针对两类分类问题,有负(0 类)和正(1 类)类。
在这种类型的混淆矩阵中,表中的每个单元格都有一个具体且易于理解的名称,总结如下:
| Positive Prediction | Negative Prediction
Positive Class | True Positive (TP) | False Negative (FN)
Negative Class | False Positive (FP) | True Negative (TN)
准确率和召回度量是根据混淆矩阵中的单元格来定义的,特别是像真阳性和假阴性这样的术语。
现在我们已经复习了混淆矩阵,让我们更仔细地看看准确率度量。
不平衡分类的准确率
准确率是一个度量标准,它量化了正确的积极预测的数量。
因此,准确率计算少数民族的准确率。
它的计算方法是正确预测的正例数除以预测的正例总数。
准确率评估被分类为阳性的实例中正确分类实例的比例…
—第 52 页,从不平衡数据集中学习,2018。
二分类的准确率
在具有两个类别的不平衡分类问题中,准确率计算为真阳性数除以真阳性和假阳性总数。
- 准确率=真阳性/(真阳性+假阳性)
结果是一个介于 0.0(无准确率)和 1.0(完全或完美准确率)之间的值。
让我们用一些例子来具体说明这个计算。
考虑一个少数与多数比例为 1:100 的数据集,其中有 100 个少数类示例和 10,000 个多数类示例。
一个模型做出预测,预测 120 个属于少数民族的例子,其中 90 个是正确的,30 个是不正确的。
该模型的准确率计算如下:
- 准确率=真阳性/(真阳性+假阳性)
- 准确率= 90 / (90 + 30)
- 准确率= 90 / 120
- 准确率= 0.75
结果是准确率为 0.75,这是一个合理的值,但并不突出。
你可以看到,准确率仅仅是所有正面预测中正确正面预测的比率,或者少数民族预测的准确率。
考虑同一个数据集,其中一个模型预测了 50 个属于少数民族的例子,其中 45 个是真阳性,5 个是假阳性。我们可以按如下方式计算该模型的准确率:
- 准确率=真阳性/(真阳性+假阳性)
- 准确率= 45 / (45 + 5)
- 准确率= 45 / 50
- 准确率= 0.90
在这种情况下,尽管模型预测属于少数群体的例子少得多,但正确的正面例子的比例要好得多。
这突出表明,尽管精确是有用的,但它并不能说明全部情况。它没有评论有多少真正的正类例子被预测为属于负类,即所谓的假阴性。
多分类准确率
准确率不限于二分类问题。
在具有两个以上类别的不平衡分类问题中,准确率计算为所有类别的真阳性之和除以所有类别的真阳性和假阳性之和。
- 准确率= C 中的和 C 真阳性 _c/C 中的和 C(真阳性 _c +假阳性 _ C)
例如,我们可能有一个不平衡的多类分类问题,其中多数类是负类,但有两个正少数类:类 1 和类 2。准确率可以量化两个正类中正确预测的比率。
考虑一个少数与多数类比率为 1:1:100 的数据集,即每个正类的比率为 1:1,少数类与多数类的比率为 1:100,每个少数类中有 100 个示例,多数类中有 10,000 个示例。
一个模型预测了第一个少数民族阶层的 70 个例子,其中 50 个是正确的,20 个是不正确的。它预测第二类 150 分,正确 99 分,错误 51 分。该模型的准确率可以计算如下:
- 准确率=(真阳性 _1 +真阳性 _2) /((真阳性 _1 +真阳性 _2) +(假阳性 _1 +假阳性 _2))
- 准确率= (50 + 99) / ((50 + 99) + (20 + 51))
- 准确率= 149 / (149 + 71)
- 准确率= 149 / 220
- 准确率= 0.677
我们可以看到,随着少数民族类别数量的增加,准确率度量计算也在增加。
使用 Scikit 计算准确率-学习
可以使用precision _ score()sci kit-learn 功能计算准确率分数。
例如,我们可以使用这个函数来计算上一节中场景的准确率。
首先,有 100 个阳性到 10,000 个阴性例子的情况,一个模型预测 90 个真阳性和 30 个假阳性。下面列出了完整的示例。
# calculates precision for 1:100 dataset with 90 tp and 30 fp
from sklearn.metrics import precision_score
# define actual
act_pos = [1 for _ in range(100)]
act_neg = [0 for _ in range(10000)]
y_true = act_pos + act_neg
# define predictions
pred_pos = [0 for _ in range(10)] + [1 for _ in range(90)]
pred_neg = [1 for _ in range(30)] + [0 for _ in range(9970)]
y_pred = pred_pos + pred_neg
# calculate prediction
precision = precision_score(y_true, y_pred, average='binary')
print('Precision: %.3f' % precision)
运行该示例计算准确率,与我们的手动计算相匹配。
Precision: 0.750
接下来,我们可以使用相同的函数以 1:1:100 计算多类问题的准确率,每个少数类有 100 个示例,多数类有 10,000 个示例。一个模型预测 1 类有 50 个真阳性和 20 个假阳性,2 类有 99 个真阳性和 51 个假阳性。
当使用 precision_score() 函数进行多类分类时,通过“标签参数指定少数类并执行将“平均值参数设置为“微以确保计算如我们预期的那样执行是很重要的。
下面列出了完整的示例。
# calculates precision for 1:1:100 dataset with 50tp,20fp, 99tp,51fp
from sklearn.metrics import precision_score
# define actual
act_pos1 = [1 for _ in range(100)]
act_pos2 = [2 for _ in range(100)]
act_neg = [0 for _ in range(10000)]
y_true = act_pos1 + act_pos2 + act_neg
# define predictions
pred_pos1 = [0 for _ in range(50)] + [1 for _ in range(50)]
pred_pos2 = [0 for _ in range(1)] + [2 for _ in range(99)]
pred_neg = [1 for _ in range(20)] + [2 for _ in range(51)] + [0 for _ in range(9929)]
y_pred = pred_pos1 + pred_pos2 + pred_neg
# calculate prediction
precision = precision_score(y_true, y_pred, labels=[1,2], average='micro')
print('Precision: %.3f' % precision)
同样,运行该示例会计算与我们的手动计算相匹配的多类示例的准确率。
Precision: 0.677
不平衡分类的召回
回忆是一个度量标准,它量化了从所有可能做出的积极预测中做出的正确积极预测的数量。
与只对所有积极预测中正确的积极预测进行评论的精确度不同,回忆提供了错过积极预测的指示。
通过这种方式,回忆提供了积极类覆盖的一些概念。
对于不平衡学习,回忆通常用于衡量少数民族的覆盖范围。
—第 27 页,不平衡学习:基础、算法和应用,2013。
二进制分类的召回
在具有两个类别的不平衡分类问题中,召回率的计算方法是真阳性数除以真阳性和假阴性总数。
- 回忆=真阳性/(真阳性+假阴性)
结果是 0.0(无召回)到 1.0(完全或完美召回)之间的值。
让我们用一些例子来具体说明这个计算。
与上一节一样,考虑一个少数与多数比例为 1:100 的数据集,其中有 100 个少数类示例和 10,000 个多数类示例。
一个模型做出预测,并正确预测了 90 个正类预测,错误预测了 10 个。我们可以这样计算这个模型的召回率:
- 回忆=真阳性/(真阳性+假阴性)
- 回忆= 90 / (90 + 10)
- 回忆= 90 / 100
- 回忆= 0.9
这款车型召回率很高。
多类分类的召回
召回不仅限于二分类问题。
在有两个以上类别的不平衡分类问题中,召回率的计算方法是所有类别的真阳性之和除以所有类别的真阳性和假阴性之和。
- 回忆= C 中的和 C 真实阳性 _c/C 中的和 C(真实阳性 _c +假阴性 _ C)
与上一节一样,考虑少数与多数类比率为 1:1:100 的数据集,即每个正类的比率为 1:1,少数类与多数类的比率为 1:100,每个少数类中有 100 个示例,多数类中有 10,000 个示例。
一个模型预测 1 类有 77 个正确的例子,23 个错误的例子,2 类有 95 个正确的例子,5 个错误的例子。我们可以如下计算该模型的召回率:
- 回忆=(真阳性 _1 +真阳性 _2) /((真阳性 _1 +真阳性 _2) +(假阴性 _1 +假阴性 _2))
- 回忆= (77 + 95) / ((77 + 95) + (23 + 5))
- 回忆= 172 / (172 + 28)
- 召回= 172 / 200
- 回忆= 0.86
用 Scikit 计算召回-学习
可以使用 recall_score() Sklearn 功能计算召回分数。
例如,我们可以使用这个函数来计算上述场景的召回率。
首先,我们可以考虑 1:100 不平衡的情况,分别有 100 个和 10,000 个例子,一个模型预测 90 个真阳性和 10 个假阴性。
下面列出了完整的示例。
# calculates recall for 1:100 dataset with 90 tp and 10 fn
from sklearn.metrics import recall_score
# define actual
act_pos = [1 for _ in range(100)]
act_neg = [0 for _ in range(10000)]
y_true = act_pos + act_neg
# define predictions
pred_pos = [0 for _ in range(10)] + [1 for _ in range(90)]
pred_neg = [0 for _ in range(10000)]
y_pred = pred_pos + pred_neg
# calculate recall
recall = recall_score(y_true, y_pred, average='binary')
print('Recall: %.3f' % recall)
运行该示例,我们可以看到分数与上面的手动计算相匹配。
Recall: 0.900
对于不平衡的多类分类问题,我们也可以使用 recall_score() 。
在这种情况下,数据集具有 1:1:100 的不平衡,每个少数民族类别为 100,多数民族类别为 10,000。一个模型预测 1 类有 77 个真阳性和 23 个假阴性,2 类有 95 个真阳性和 5 个假阴性。
下面列出了完整的示例。
# calculates recall for 1:1:100 dataset with 77tp,23fn and 95tp,5fn
from sklearn.metrics import recall_score
# define actual
act_pos1 = [1 for _ in range(100)]
act_pos2 = [2 for _ in range(100)]
act_neg = [0 for _ in range(10000)]
y_true = act_pos1 + act_pos2 + act_neg
# define predictions
pred_pos1 = [0 for _ in range(23)] + [1 for _ in range(77)]
pred_pos2 = [0 for _ in range(5)] + [2 for _ in range(95)]
pred_neg = [0 for _ in range(10000)]
y_pred = pred_pos1 + pred_pos2 + pred_neg
# calculate recall
recall = recall_score(y_true, y_pred, labels=[1,2], average='micro')
print('Recall: %.3f' % recall)
同样,运行该示例计算与我们的手动计算相匹配的多类示例的召回率。
Recall: 0.860
不平衡分类的准确率与召回率
你可以决定在不平衡的分类问题上使用精确度或召回率。
最大化准确率将最小化假阳性的数量,而最大化召回将最小化假阴性的数量。
- 准确率:当最大限度地减少误报是重点时合适。
- 回忆:适当的时候尽量减少假阴性是重点。
有时候,我们想要积极阶层的优秀预测。我们想要高准确率和高召回率。
这可能很有挑战性,因为召回率的提高往往是以精确度的降低为代价的。
在不平衡的数据集上,目标是在不损害准确率的情况下提高召回率。然而,这些目标往往是相互冲突的,因为为了增加少数民族的人口比例,计划生育的数量也经常增加,导致准确率降低。
—第 55 页,不平衡学习:基础、算法和应用,2013。
然而,我们可以选择一个新的度量标准,将精确度和召回率结合成一个分数,而不是选择一个或另一个。
不平衡分类的 f-测度
分类准确率被广泛使用,因为它是用于总结模型表现的单一度量。
F-Measure 提供了一种方法,可以将准确率和召回率结合到一个能够同时获取这两种属性的度量中。
单独来看,精确和回忆都不能说明全部情况。我们可以有极好的准确率和可怕的回忆,或者,可怕的准确率和极好的回忆。F-measure 提供了一种用一个分数来表达两个问题的方法。
一旦为二进制或多类分类问题计算了准确率和召回率,这两个分数就可以合并到 F-Measure 的计算中。
传统的 F 值计算如下:
- F-Measure = (2 准确率召回)/(准确率+召回)
这是两个分数的调和平均值。这有时被称为 F 分数或 F1 分数,可能是不平衡分类问题中最常用的度量。
……F1-measure 对准确率和召回率进行同等加权,是从不平衡数据中学习时最常使用的变量。
—第 27 页,不平衡学习:基础、算法和应用,2013。
与精确度和召回率一样,较差的 F-Measure 分数为 0.0,最佳或完美的 F-Measure 分数为 1.0
例如,一个完美的准确率和召回分数将产生一个完美的 F-Measure 分数:
- F-Measure = (2 准确率召回)/(准确率+召回)
- f-测量= (2 * 1.0 * 1.0) / (1.0 + 1.0)
- F-Measure = (2 * 1.0) / 2.0
- f-测量= 1.0
让我们用一个实例来具体说明这个计算。
考虑一个少数与多数比例为 1:100 的二元类别数据集,有 100 个少数类示例和 10,000 个多数类示例。
考虑一个模型,该模型预测阳性类有 150 个例子,95 个是正确的(真阳性),这意味着有 5 个被遗漏(假阴性),55 个是不正确的(假阳性)。
我们可以按如下方式计算准确率:
- 准确率=真阳性/(真阳性+假阳性)
- 准确率= 95 / (95 + 55)
- 准确率= 0.633
我们可以按如下方式计算召回:
- 回忆=真阳性/(真阳性+假阴性)
- 回忆= 95 / (95 + 5)
- 回忆= 0.95
这说明该模型准确率差,但召回率优秀。
最后,我们可以如下计算 F-Measure:
- F-Measure = (2 准确率召回)/(准确率+召回)
- f-Measure =(2 * 0.633 * 0.95)/(0.633+0.95)
- F-Measure = (2 * 0.601) / 1.583
- F-Measure = 1.202 / 1.583
- F-Measure = 0.759
我们可以看到,好的召回水平抵消了差的准确率,给出了一个好的或合理的 F-measure 分数。
用科学工具包计算 f 测量-学习
可以使用 f1_score() Sklearn 功能计算 F-measure 分数。
例如,我们使用这个函数来计算上述场景的 F-Measure。
这是 1:100 不平衡的情况,分别有 100 和 10,000 个例子,一个模型预测 95 个真阳性、5 个假阴性和 55 个假阳性。
下面列出了完整的示例。
# calculates f1 for 1:100 dataset with 95tp, 5fn, 55fp
from sklearn.metrics import f1_score
# define actual
act_pos = [1 for _ in range(100)]
act_neg = [0 for _ in range(10000)]
y_true = act_pos + act_neg
# define predictions
pred_pos = [0 for _ in range(5)] + [1 for _ in range(95)]
pred_neg = [1 for _ in range(55)] + [0 for _ in range(9945)]
y_pred = pred_pos + pred_neg
# calculate score
score = f1_score(y_true, y_pred, average='binary')
print('F-Measure: %.3f' % score)
运行该示例计算 F-Measure,匹配我们的手动计算,在一些小的舍入误差内。
F-Measure: 0.760
进一步阅读
如果您想更深入地了解这个主题,本节将提供更多资源。
教程
报纸
- 分类任务绩效测量的系统分析,2009。
书
- 不平衡学习:基础、算法和应用,2013。
- 从不平衡数据集中学习,2018。
应用程序接口
- sklearn . metrics . precision _ score API。
- sklearn . metrics . recall _ score API。
- 硬化. metrics.f1_score API 。
文章
- 混淆矩阵,维基百科。
- 精准与召回,维基百科。
- f1 得分,维基百科。
摘要
在本教程中,您发现了如何为不平衡分类计算和开发精确度和召回率的直觉。
具体来说,您了解到:
- 准确率量化了实际属于正类的正类预测的数量。
- Recall 量化了从数据集中所有正面示例中做出的正面类预测的数量。
- F-Measure 提供了一个单一的分数,在一个数字中平衡了对精确度和召回率的关注。
你有什么问题吗? 在下面的评论中提问,我会尽力回答。
音素不平衡类别数据集的预测模型
最后更新于 2021 年 1 月 5 日
许多二进制分类任务并不是每个类都有相同数量的例子,例如类分布是倾斜的或者不平衡的。
然而,准确性在这两个类别中同样重要。
一个例子是在语音识别中将欧洲语言的元音分类为鼻元音或口元音,其中鼻元音的例子比口元音多得多。分类准确率对于两个类别都很重要,尽管不能直接使用准确率作为度量。此外,在拟合机器学习算法时,可能需要数据采样技术来变换训练数据集,以使其更加平衡。
在本教程中,您将发现如何开发和评估鼻腔和口腔音素的不平衡二分类模型。
完成本教程后,您将知道:
- 如何加载和探索数据集,并为数据准备和模型选择产生想法。
- 如何利用数据过采样技术评估一套机器学习模型并提高其表现?
- 如何拟合最终模型并使用它来预测特定情况下的类标签。
用我的新书Python 不平衡分类启动你的项目,包括分步教程和所有示例的 Python 源代码文件。
我们开始吧。
- 2021 年 1 月更新:更新了 API 文档的链接。
音素不平衡类别数据集的预测模型 图片由埃德·邓恩斯提供,保留部分权利。
教程概述
本教程分为五个部分;它们是:
- 音素数据集
- 浏览数据集
- 模型测试和基线结果
- 评估模型
- 评估机器学习算法
- 评估数据过采样算法
- 对新数据进行预测
音素数据集
在这个项目中,我们将使用一个标准的不平衡机器学习数据集,称为“音素”数据集。
该数据集被归功于名为“T2”的 ESPRIT(欧洲信息技术研究战略计划)项目,并在该项目的进度报告和技术报告中进行了描述。
注重成果的年度报告项目的目标是提高现有分析性语音识别系统(即使用音节、音素和语音特征知识的系统)的鲁棒性,并将其用作具有连接单词和对话能力的语音理解系统的一部分。该系统将针对两种欧洲语言的特定应用进行评估
数据集的目标是区分鼻元音和口元音。
元音被读出并记录到数字文件中。然后从每个声音中自动提取音频特征。
选择了五种不同的属性来表征每个元音:它们是五个一次谐波 AHi 的振幅,用总能量 Ene 归一化(在所有频率上积分):AHi/Ene。每个谐波都有符号:当它对应于频谱的局部最大值时为正,否则为负。
— 音素数据集描述。
这两种声音有两个类别;它们是:
- 0 级:鼻元音(多数级)。
- 第 1 类:口语元音(少数民族类)。
接下来,让我们仔细看看数据。
浏览数据集
音素数据集是一个广泛使用的标准机器学习数据集,用于探索和演示许多专门为不平衡分类设计的技术。
一个例子是流行的 SMOTE 数据过采样技术。
首先,下载数据集,并将其保存在您当前的工作目录中,名称为“音素. csv ”。
查看文件的内容。
文件的前几行应该如下所示:
1.24,0.875,-0.205,-0.078,0.067,0
0.268,1.352,1.035,-0.332,0.217,0
1.567,0.867,1.3,1.041,0.559,0
0.279,0.99,2.555,-0.738,0.0,0
0.307,1.272,2.656,-0.946,-0.467,0
...
我们可以看到,给定的输入变量是数字,鼻腔和口腔的类别标签分别是 0 和 1。
可以使用 read_csv()熊猫函数将数据集加载为数据帧,指定位置和没有标题行的事实。
...
# define the dataset location
filename = 'phoneme.csv'
# load the csv file as a data frame
dataframe = read_csv(filename, header=None)
加载后,我们可以通过打印数据框的形状来总结行数和列数。
...
# summarize the shape of the dataset
print(dataframe.shape)
我们还可以使用 Counter 对象总结每个类中的示例数量。
...
# summarize the class distribution
target = dataframe.values[:,-1]
counter = Counter(target)
for k,v in counter.items():
per = v / len(target) * 100
print('Class=%s, Count=%d, Percentage=%.3f%%' % (k, v, per))
将这些联系在一起,下面列出了加载和汇总数据集的完整示例。
# load and summarize the dataset
from pandas import read_csv
from collections import Counter
# define the dataset location
filename = 'phoneme.csv'
# load the csv file as a data frame
dataframe = read_csv(filename, header=None)
# summarize the shape of the dataset
print(dataframe.shape)
# summarize the class distribution
target = dataframe.values[:,-1]
counter = Counter(target)
for k,v in counter.items():
per = v / len(target) * 100
print('Class=%s, Count=%d, Percentage=%.3f%%' % (k, v, per))
运行该示例首先加载数据集并确认行数和列数,即 5,404 行、5 个输入变量和 1 个目标变量。
然后总结阶级分布,确认适度的阶级不平衡,多数阶级大约 70%(鼻)少数阶级大约 30%(口)。
(5404, 6)
Class=0.0, Count=3818, Percentage=70.651%
Class=1.0, Count=1586, Percentage=29.349%
我们还可以通过为五个数字输入变量创建直方图来查看它们的分布。
下面列出了完整的示例。
# create histograms of numeric input variables
from pandas import read_csv
from matplotlib import pyplot
# define the dataset location
filename = 'phoneme.csv'
# load the csv file as a data frame
df = read_csv(filename, header=None)
# histograms of all variables
df.hist()
pyplot.show()
运行该示例会为数据集中的五个数字输入变量中的每一个创建带有一个直方图子图的图形,以及数字类标签。
我们可以看到这些变量有不同的尺度,尽管大多数似乎有高斯或类似高斯的分布。
根据建模算法的选择,我们期望将分布缩放到相同的范围是有用的,并且可能标准化一些幂变换的使用。
音素数据集变量的直方图
我们还可以为每对输入变量创建散点图,称为散点图矩阵。
这有助于查看是否有任何变量相互关联或朝同一方向变化,例如相互关联。
我们还可以根据类别标签为每个散点图的点着色。在这种情况下,多数类(鼻)将被映射为蓝点,少数类(口)将被映射为红点。
下面列出了完整的示例。
# create pairwise scatter plots of numeric input variables
from pandas import read_csv
from pandas import DataFrame
from pandas.plotting import scatter_matrix
from matplotlib import pyplot
# define the dataset location
filename = 'phoneme.csv'
# load the csv file as a data frame
df = read_csv(filename, header=None)
# define a mapping of class values to colors
color_dict = {0:'blue', 1:'red'}
# map each row to a color based on the class value
colors = [color_dict[x] for x in df.values[:, -1]]
# drop the target variable
inputs = DataFrame(df.values[:, :-1])
# pairwise scatter plots of all numerical variables
scatter_matrix(inputs, diagonal='kde', color=colors)
pyplot.show()
运行该示例会创建一个显示散点图矩阵的图形,其中五个图乘以五个图,将五个数字输入变量相互比较。矩阵的对角线显示了每个变量的密度分布。
每对出现两次,从左上角到右下角的对角线的上方和下方,提供了两种方式来查看相同的变量交互。
我们可以看到,对于两个类别标签,许多变量的分布确实不同,这表明类别之间的一些合理区分是可行的。
音素数据集中数字输入变量的分类散点图矩阵
现在我们已经回顾了数据集,让我们看看开发一个测试工具来评估候选模型。
模型测试和基线结果
我们将使用重复的分层 k 折叠交叉验证来评估候选模型。
k 倍交叉验证程序提供了一个良好的模型表现的总体估计,至少与单个列车测试分割相比,不太乐观。我们将使用 k=10,这意味着每个折叠将包含大约 5404/10 或大约 540 个示例。
分层意味着每个褶皱将包含相同的混合类例子,即大约 70%到 30%的鼻到口元音。重复表示评估过程将执行多次,以帮助避免侥幸结果,并更好地捕捉所选模型的方差。我们将使用三次重复。
这意味着单个模型将被拟合和评估 10 * 3 或 30 次,并且将报告这些运行的平均值和标准偏差。
这可以通过使用repeated stratifiedfold Sklearn 类来实现。
类别标签将被预测,两个类别标签同等重要。因此,我们将选择一个度量来分别量化模型在两个类上的表现。
您可能还记得,灵敏度是阳性类别准确性的衡量标准,特异性是阴性类别准确性的衡量标准。
- 灵敏度=真阳性/(真阳性+假阴性)
- 特异性=真阴性/(真阴性+假阳性)
G 均值寻求这些分数的平衡,即几何均值,其中一个或另一个的不良表现导致低 G 均值分数。
- g-均值= sqrt(灵敏度*特异性)
我们可以使用不平衡学习库提供的几何均值分数()函数来计算模型所做的一组预测的 G 均值。
我们可以定义一个函数来加载数据集,并将列分成输入和输出变量。下面的 load_dataset() 函数实现了这一点。
# load the dataset
def load_dataset(full_path):
# load the dataset as a numpy array
data = read_csv(full_path, header=None)
# retrieve numpy array
data = data.values
# split into input and output elements
X, y = data[:, :-1], data[:, -1]
return X, y
然后,我们可以定义一个函数来评估数据集上的给定模型,并返回每次折叠和重复的 G 均值分数列表。下面的 evaluate_model() 函数实现了这一点,将数据集和模型作为参数,返回分数列表。
# evaluate a model
def evaluate_model(X, y, model):
# define evaluation procedure
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
# define the model evaluation the metric
metric = make_scorer(geometric_mean_score)
# evaluate model
scores = cross_val_score(model, X, y, scoring=metric, cv=cv, n_jobs=-1)
return scores
最后,我们可以使用这个测试工具在数据集上评估一个基线模型。
预测所有情况下的多数类标签(0)或少数类标签(1)的模型将导致 G 均值为零。因此,一个好的默认策略是以 50%的概率随机预测一个或另一个类别标签,目标是 0.5 左右的 G 均值。
这可以通过使用 Sklearn 库中的 DummyClassifier 类并将“策略”参数设置为“制服”来实现。
...
# define the reference model
model = DummyClassifier(strategy='uniform')
一旦模型得到评估,我们就可以直接报告 G 均值分数的均值和标准差。
...
# evaluate the model
scores = evaluate_model(X, y, model)
# summarize performance
print('Mean G-Mean: %.3f (%.3f)' % (mean(scores), std(scores)))
将这些结合起来,下面列出了加载数据集、评估基线模型和报告表现的完整示例。
# test harness and baseline model evaluation
from collections import Counter
from numpy import mean
from numpy import std
from pandas import read_csv
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold
from imblearn.metrics import geometric_mean_score
from sklearn.metrics import make_scorer
from sklearn.dummy import DummyClassifier
# load the dataset
def load_dataset(full_path):
# load the dataset as a numpy array
data = read_csv(full_path, header=None)
# retrieve numpy array
data = data.values
# split into input and output elements
X, y = data[:, :-1], data[:, -1]
return X, y
# evaluate a model
def evaluate_model(X, y, model):
# define evaluation procedure
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
# define the model evaluation the metric
metric = make_scorer(geometric_mean_score)
# evaluate model
scores = cross_val_score(model, X, y, scoring=metric, cv=cv, n_jobs=-1)
return scores
# define the location of the dataset
full_path = 'phoneme.csv'
# load the dataset
X, y = load_dataset(full_path)
# summarize the loaded dataset
print(X.shape, y.shape, Counter(y))
# define the reference model
model = DummyClassifier(strategy='uniform')
# evaluate the model
scores = evaluate_model(X, y, model)
# summarize performance
print('Mean G-Mean: %.3f (%.3f)' % (mean(scores), std(scores)))
运行该示例首先加载和汇总数据集。
我们可以看到加载了正确的行数,并且有五个音频派生的输入变量。
接下来,报告 G 均值分数的平均值。
在这种情况下,我们可以看到基线算法获得的 G 均值约为 0.509,接近理论最大值 0.5。这个分数提供了模特技能的下限;任何平均 G 均值高于约 0.509(或真正高于 0.5)的模型都有技能,而得分低于该值的模型在该数据集上没有技能。
(5404, 5) (5404,) Counter({0.0: 3818, 1.0: 1586})
Mean G-Mean: 0.509 (0.020)
现在我们已经有了测试工具和表现基线,我们可以开始在这个数据集上评估一些模型了。
评估模型
在本节中,我们将使用上一节中开发的测试工具来评估数据集上的一套不同技术。
目标是既演示如何系统地解决问题,又演示为不平衡分类问题设计的一些技术的能力。
报告的表现良好,但没有高度优化(例如,超参数没有调整)。
**你能做得更好吗?**如果你能用同样的测试装具获得更好的 G 均值表现,我很想听听。请在下面的评论中告诉我。
评估机器学习算法
让我们从评估数据集上的混合机器学习模型开始。
在数据集上抽查一套不同的线性和非线性算法可能是一个好主意,以便快速找出哪些算法运行良好,值得进一步关注,哪些算法运行不佳。
我们将在音素数据集上评估以下机器学习模型:
- 逻辑回归
- 支持向量机(SVM)
- 袋装决策树
- 随机森林
- 额外树
我们将主要使用默认的模型超参数,除了集成算法中的树的数量,我们将设置为合理的默认值 1000。
我们将依次定义每个模型,并将它们添加到一个列表中,以便我们可以顺序评估它们。下面的 get_models() 函数定义了用于评估的模型列表,以及用于以后绘制结果的模型简称列表。
# define models to test
def get_models():
models, names = list(), list()
# LR
models.append(LogisticRegression(solver='lbfgs'))
names.append('LR')
# SVM
models.append(SVC(gamma='scale'))
names.append('SVM')
# Bagging
models.append(BaggingClassifier(n_estimators=1000))
names.append('BAG')
# RF
models.append(RandomForestClassifier(n_estimators=1000))
names.append('RF')
# ET
models.append(ExtraTreesClassifier(n_estimators=1000))
names.append('ET')
return models, names
然后,我们可以依次列举模型列表,并对每个模型进行评估,报告平均 G 均值,并存储分数以供以后绘制。
...
# define models
models, names = get_models()
results = list()
# evaluate each model
for i in range(len(models)):
# evaluate the model and store results
scores = evaluate_model(X, y, models[i])
results.append(scores)
# summarize and store
print('>%s %.3f (%.3f)' % (names[i], mean(scores), std(scores)))
在运行结束时,我们可以将每个分数样本绘制成一个方框,并用相同的比例绘制晶须图,这样我们就可以直接比较分布。
...
# plot the results
pyplot.boxplot(results, labels=names, showmeans=True)
pyplot.show()
将所有这些结合在一起,下面列出了在音素数据集上评估一套机器学习算法的完整示例。
# spot check machine learning algorithms on the phoneme dataset
from numpy import mean
from numpy import std
from pandas import read_csv
from matplotlib import pyplot
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold
from imblearn.metrics import geometric_mean_score
from sklearn.metrics import make_scorer
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
from sklearn.ensemble import RandomForestClassifier
from sklearn.ensemble import ExtraTreesClassifier
from sklearn.ensemble import BaggingClassifier
# load the dataset
def load_dataset(full_path):
# load the dataset as a numpy array
data = read_csv(full_path, header=None)
# retrieve numpy array
data = data.values
# split into input and output elements
X, y = data[:, :-1], data[:, -1]
return X, y
# evaluate a model
def evaluate_model(X, y, model):
# define evaluation procedure
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
# define the model evaluation the metric
metric = make_scorer(geometric_mean_score)
# evaluate model
scores = cross_val_score(model, X, y, scoring=metric, cv=cv, n_jobs=-1)
return scores
# define models to test
def get_models():
models, names = list(), list()
# LR
models.append(LogisticRegression(solver='lbfgs'))
names.append('LR')
# SVM
models.append(SVC(gamma='scale'))
names.append('SVM')
# Bagging
models.append(BaggingClassifier(n_estimators=1000))
names.append('BAG')
# RF
models.append(RandomForestClassifier(n_estimators=1000))
names.append('RF')
# ET
models.append(ExtraTreesClassifier(n_estimators=1000))
names.append('ET')
return models, names
# define the location of the dataset
full_path = 'phoneme.csv'
# load the dataset
X, y = load_dataset(full_path)
# define models
models, names = get_models()
results = list()
# evaluate each model
for i in range(len(models)):
# evaluate the model and store results
scores = evaluate_model(X, y, models[i])
results.append(scores)
# summarize and store
print('>%s %.3f (%.3f)' % (names[i], mean(scores), std(scores)))
# plot the results
pyplot.boxplot(results, labels=names, showmeans=True)
pyplot.show()
运行该示例依次评估每个算法,并报告均值和标准差。
注:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
在这种情况下,我们可以看到所有测试的算法都有技巧,实现了高于默认值 0.5 的 G 均值。结果表明,决策树算法的集成在这个数据集上表现更好,可能额外树(et)在 G 均值约为 0.896 时表现最好。
>LR 0.637 (0.023)
>SVM 0.801 (0.022)
>BAG 0.888 (0.017)
>RF 0.892 (0.018)
>ET 0.896 (0.017)
创建一个图形,显示每个算法结果样本的一个方框和须图。方框显示中间 50%的数据,每个方框中间的橙色线显示样本的中值,每个方框中的绿色三角形显示样本的平均值。
我们可以看到,树算法的所有三个集合(BAG、RF 和 ET)都有一个紧密的分布以及紧密对齐的平均值和中值,这可能表明分数的非偏斜和高斯分布,例如稳定。
不平衡音素数据集上机器学习模型的盒须图
现在我们有了第一组好的结果,让我们看看是否可以用数据过采样方法来改进它们。
评估数据过采样算法
数据采样提供了一种在拟合模型之前更好地准备不平衡训练数据集的方法。
最简单的过采样技术是复制少数类中的例子,称为随机过采样。也许最流行的过采样方法是 SMOTE 过采样技术,用于为少数民族创建新的合成示例。
我们将测试五种不同的过采样方法;具体来说:
- 随机过采样
- SMOTE (SMOTE)
- 边缘性雾霾
- SVM smote(SVM mote)
- 阿达辛
每种技术都将使用上一节中表现最好的算法进行测试,特别是额外树。
我们将为每个过采样算法使用默认的超参数,该算法将对少数类进行过采样,以获得与训练数据集中多数类相同数量的示例。
预期每种过采样技术与没有过采样的算法相比,都会带来表现提升,随机过采样提供的提升最小,SMOTE 或其变体提供的提升可能最好。
我们可以更新 get_models() 函数,返回要评估的过采样算法列表;例如:
# define oversampling models to test
def get_models():
models, names = list(), list()
# RandomOverSampler
models.append(RandomOverSampler())
names.append('ROS')
# SMOTE
models.append(SMOTE())
names.append('SMOTE')
# BorderlineSMOTE
models.append(BorderlineSMOTE())
names.append('BLSMOTE')
# SVMSMOTE
models.append(SVMSMOTE())
names.append('SVMSMOTE')
# ADASYN
models.append(ADASYN())
names.append('ADASYN')
return models, names
然后,我们可以枚举每一个,并从知道如何过采样训练数据集的不平衡学习库中创建一个管道。这将确保交叉验证模型评估中的训练数据集被正确采样,而不会出现可能导致对模型表现的乐观评估的数据泄漏。
首先,我们将对输入变量进行归一化,因为大多数过采样技术将使用最近邻算法,使用该技术时,所有变量具有相同的比例非常重要。接下来是给定的过采样算法,然后是适合过采样训练数据集的额外树算法。
...
# define the model
model = ExtraTreesClassifier(n_estimators=1000)
# define the pipeline steps
steps = [('s', MinMaxScaler()), ('o', models[i]), ('m', model)]
# define the pipeline
pipeline = Pipeline(steps=steps)
# evaluate the model and store results
scores = evaluate_model(X, y, pipeline)
将这些联系在一起,下面列出了在音素数据集上使用额外树评估过采样算法的完整示例。
# data oversampling algorithms on the phoneme imbalanced dataset
from numpy import mean
from numpy import std
from pandas import read_csv
from matplotlib import pyplot
from sklearn.preprocessing import MinMaxScaler
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold
from imblearn.metrics import geometric_mean_score
from sklearn.metrics import make_scorer
from sklearn.ensemble import ExtraTreesClassifier
from imblearn.over_sampling import RandomOverSampler
from imblearn.over_sampling import SMOTE
from imblearn.over_sampling import BorderlineSMOTE
from imblearn.over_sampling import SVMSMOTE
from imblearn.over_sampling import ADASYN
from imblearn.pipeline import Pipeline
# load the dataset
def load_dataset(full_path):
# load the dataset as a numpy array
data = read_csv(full_path, header=None)
# retrieve numpy array
data = data.values
# split into input and output elements
X, y = data[:, :-1], data[:, -1]
return X, y
# evaluate a model
def evaluate_model(X, y, model):
# define evaluation procedure
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
# define the model evaluation the metric
metric = make_scorer(geometric_mean_score)
# evaluate model
scores = cross_val_score(model, X, y, scoring=metric, cv=cv, n_jobs=-1)
return scores
# define oversampling models to test
def get_models():
models, names = list(), list()
# RandomOverSampler
models.append(RandomOverSampler())
names.append('ROS')
# SMOTE
models.append(SMOTE())
names.append('SMOTE')
# BorderlineSMOTE
models.append(BorderlineSMOTE())
names.append('BLSMOTE')
# SVMSMOTE
models.append(SVMSMOTE())
names.append('SVMSMOTE')
# ADASYN
models.append(ADASYN())
names.append('ADASYN')
return models, names
# define the location of the dataset
full_path = 'phoneme.csv'
# load the dataset
X, y = load_dataset(full_path)
# define models
models, names = get_models()
results = list()
# evaluate each model
for i in range(len(models)):
# define the model
model = ExtraTreesClassifier(n_estimators=1000)
# define the pipeline steps
steps = [('s', MinMaxScaler()), ('o', models[i]), ('m', model)]
# define the pipeline
pipeline = Pipeline(steps=steps)
# evaluate the model and store results
scores = evaluate_model(X, y, pipeline)
results.append(scores)
# summarize and store
print('>%s %.3f (%.3f)' % (names[i], mean(scores), std(scores)))
# plot the results
pyplot.boxplot(results, labels=names, showmeans=True)
pyplot.show()
运行该示例使用数据集上的额外树模型评估每个过采样方法。
注:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
在这种情况下,正如我们所料,除了随机过采样技术之外,每种过采样技术都在没有任何过采样(0.896)的情况下提升了 ET 算法的表现。
结果表明,改进版本的 SMOTE 和 ADASN 比默认 SMOTE 表现更好,在这种情况下,ADASN 获得了 0.910 的最佳 G 均值分数。
>ROS 0.894 (0.018)
>SMOTE 0.906 (0.015)
>BLSMOTE 0.909 (0.013)
>SVMSMOTE 0.909 (0.014)
>ADASYN 0.910 (0.013)
结果的分布可以与箱线图和须线图进行比较。
我们可以看到这些分布都大致具有相同的紧密分布,并且结果平均值的差异可以用来选择模型。
不平衡音素数据集上数据过采样的额外树模型的盒须图
接下来,让我们看看如何使用最终模型对新数据进行预测。
对新数据进行预测
在本节中,我们将拟合最终模型,并使用它对单行数据进行预测
我们将使用 ADASYN 过采样版本的 Extra Trees 模型作为最终模型,并在拟合模型和进行预测之前对数据进行标准化缩放。使用管道将确保转换始终正确执行。
首先,我们可以将模型定义为管道。
...
# define the model
model = ExtraTreesClassifier(n_estimators=1000)
# define the pipeline steps
steps = [('s', MinMaxScaler()), ('o', ADASYN()), ('m', model)]
# define the pipeline
pipeline = Pipeline(steps=steps)
一旦定义好了,我们就可以在整个训练数据集中使用它。
...
# fit the model
pipeline.fit(X, y)
一旦适合,我们可以通过调用 predict() 函数来使用它对新数据进行预测。这将返回类别标签 0 表示“鼻腔,或 1 表示“口腔”。
例如:
...
# define a row of data
row = [...]
# make prediction
yhat = pipeline.predict([row])
为了证明这一点,我们可以使用拟合模型对一些病例的标签进行一些预测,在这些病例中,我们知道该病例是鼻腔还是口腔。
下面列出了完整的示例。
# fit a model and make predictions for the on the phoneme dataset
from pandas import read_csv
from sklearn.preprocessing import MinMaxScaler
from imblearn.over_sampling import ADASYN
from sklearn.ensemble import ExtraTreesClassifier
from imblearn.pipeline import Pipeline
# load the dataset
def load_dataset(full_path):
# load the dataset as a numpy array
data = read_csv(full_path, header=None)
# retrieve numpy array
data = data.values
# split into input and output elements
X, y = data[:, :-1], data[:, -1]
return X, y
# define the location of the dataset
full_path = 'phoneme.csv'
# load the dataset
X, y = load_dataset(full_path)
# define the model
model = ExtraTreesClassifier(n_estimators=1000)
# define the pipeline steps
steps = [('s', MinMaxScaler()), ('o', ADASYN()), ('m', model)]
# define the pipeline
pipeline = Pipeline(steps=steps)
# fit the model
pipeline.fit(X, y)
# evaluate on some nasal cases (known class 0)
print('Nasal:')
data = [[1.24,0.875,-0.205,-0.078,0.067],
[0.268,1.352,1.035,-0.332,0.217],
[1.567,0.867,1.3,1.041,0.559]]
for row in data:
# make prediction
yhat = pipeline.predict([row])
# get the label
label = yhat[0]
# summarize
print('>Predicted=%d (expected 0)' % (label))
# evaluate on some oral cases (known class 1)
print('Oral:')
data = [[0.125,0.548,0.795,0.836,0.0],
[0.318,0.811,0.818,0.821,0.86],
[0.151,0.642,1.454,1.281,-0.716]]
for row in data:
# make prediction
yhat = pipeline.predict([row])
# get the label
label = yhat[0]
# summarize
print('>Predicted=%d (expected 1)' % (label))
运行该示例首先在整个训练数据集上拟合模型。
然后使用拟合模型来预测从数据集文件中选择的鼻病例的标签。我们可以看到所有的情况都是正确预测的。
然后将一些口腔病例作为模型的输入,并对标签进行预测。正如我们所希望的那样,所有情况下都能预测到正确的标签。
Nasal:
>Predicted=0 (expected 0)
>Predicted=0 (expected 0)
>Predicted=0 (expected 0)
Oral:
>Predicted=1 (expected 1)
>Predicted=1 (expected 1)
>Predicted=1 (expected 1)
进一步阅读
如果您想更深入地了解这个主题,本节将提供更多资源。
报纸
蜜蜂
- sklearn.model_selection。重复的策略应用编程接口。
- 硬化. dummy . dummy class ification API。
- imb learn . metrics . geometry _ mean _ score API。
资料组
摘要
在本教程中,您发现了如何开发和评估鼻腔和口腔音素的不平衡二分类模型。
具体来说,您了解到:
- 如何加载和探索数据集,并为数据准备和模型选择产生想法。
- 如何利用数据过采样技术评估一套机器学习模型并提高其表现?
- 如何拟合最终模型并使用它来预测特定情况下的类标签。
你有什么问题吗? 在下面的评论中提问,我会尽力回答。
如何校准不平衡分类的概率
最后更新于 2020 年 8 月 21 日
许多机器学习模型能够预测类成员的概率或类似概率的分数。
概率为评估和比较模型提供了所需的粒度级别,特别是在不平衡分类问题上,像 ROC 曲线这样的工具用于解释预测,ROC AUC 度量用于比较模型表现,两者都使用概率。
不幸的是,许多模型预测的概率或类似概率的分数没有被校准。这意味着他们在某些情况下可能过于自信,而在其他情况下则信心不足。更糟糕的是,不平衡分类任务中存在的严重倾斜的类分布可能导致预测概率中甚至更多的偏差,因为它们过度倾向于预测多数类。
因此,在评估非线性机器学习模型的表现之前,校准其预测概率通常是一个好主意。此外,在处理不平衡的数据集时,通常校准概率是一种好的做法,即使是像逻辑回归这样的模型,当类标签平衡时,也可以预测校准良好的概率。
在本教程中,您将发现如何校准不平衡分类的预测概率。
完成本教程后,您将知道:
- 对于不平衡的分类问题,需要校准概率来从模型中获得最大收益。
- 如何校准非线性模型(如支持向量机、决策树和 KNN)的预测概率。
- 如何在具有偏斜类分布的数据集上网格搜索不同的概率校准方法?
用我的新书Python 不平衡分类启动你的项目,包括分步教程和所有示例的 Python 源代码文件。
我们开始吧。
如何校准不平衡分类的概率 图片由丹尼斯·贾维斯提供,版权所有。
教程概述
本教程分为五个部分;它们是:
- 未校准概率问题
- 如何校准概率
- 具有校准概率的 SVM
- 具有校准概率的决策树
- KNN 网格搜索概率校准
未校准概率问题
许多机器学习算法可以预测一个概率或一个类似概率的分数来表示类别成员。
例如,逻辑回归可以直接预测类别成员的概率,支持向量机可以预测不是概率但可以解释为概率的分数。
概率可以用来衡量那些需要概率预测的问题的不确定性。在不平衡分类中尤其如此,在不平衡分类中,就评估和选择模型而言,清晰的类标签通常是不够的。预测的概率为更精细的模型评估和选择提供了基础,例如通过使用 ROC 和精确-召回诊断图、ROC AUC 等指标以及阈值移动等技术。
因此,当处理不平衡的分类任务时,使用预测概率的机器学习模型通常是优选的。问题是很少有机器学习模型校准了概率。
……为了被有效地解释为概率,分数应该被校准。
—第 57 页,从不平衡数据集中学习,2018。
校准概率意味着概率反映真实事件的可能性。
如果你考虑到在分类中,我们有正确与否的类标签,而不是概率,这可能会令人困惑。澄清一下,回想一下在二进制分类中,我们预测的是 0 类或 1 类的阴性或阳性病例。如果以 0.8 的概率预测 100 个例子,那么如果概率被校准,80%的例子将具有类别 1,20%将具有类别 0。这里,校准是预测概率与阳性病例发生的一致性。
未校准的概率表明概率分数存在偏差,这意味着概率在某些情况下过于自信或不够自信。
- 校准概率。概率与事件的真实可能性相匹配。
- 未校准概率。概率过于自信和/或不够自信。
这对于没有使用概率框架训练的机器学习模型和具有偏斜分布的训练数据(如不平衡分类任务)来说是常见的。
未校准概率有两个主要原因;它们是:
- 未使用概率框架训练的算法。
- 训练数据中的偏差。
很少有机器学习算法产生校准的概率。这是因为对于预测校准概率的模型,它必须明确地在概率框架下训练,例如最大似然估计。提供校准概率的一些算法示例包括:
- 逻辑回归。
- 线性判别分析。
- 朴素贝叶斯。
- 人工神经网络。
许多算法要么预测一个类似概率的分数,要么预测一个类标签,为了产生一个类似概率的分数,这些算法必须被强制。因此,这些算法通常需要在使用前校准它们的“T0”概率。例子包括:
- 支持向量机。
- 决策树。
- 决策树的集合(装袋、随机森林、梯度提升)。
- k-最近邻居。
训练数据集中的偏差,例如类分布中的偏差,意味着模型将自然地预测多数类的概率高于少数类的平均概率。
问题是,模型可能会过度补偿,把太多的焦点放在多数类上。这甚至适用于通常产生校准概率的模型,如逻辑回归。
…尽管表面上整体校准良好,但在不平衡场景中通过监督学习获得的类概率估计系统地低估了少数类实例的概率。
——不平衡数据的类概率估计不可靠(以及如何修复),2012。
如何校准概率
概率通过重新调整它们的值来校准,以便它们更好地匹配训练数据中观察到的分布。
…我们希望估计的类别概率能够反映样本的真实潜在概率。也就是说,预测的类概率(或类概率值)需要很好地校准。为了更好地校准,概率必须有效地反映感兴趣事件的真实可能性。
—第 249 页,应用预测建模,2013 年。
对训练数据进行概率预测,并将概率分布与预期概率进行比较,并进行调整以提供更好的匹配。这通常涉及分割训练数据集,并使用一部分来训练模型,另一部分作为验证集来缩放概率。
有两种主要的技术来缩放预测概率;它们是普拉特缩放和等张回归。
- 普拉特缩放。转换概率的逻辑回归模型。
- 等渗回归。转换概率的加权最小二乘回归模型
普拉特缩放是一种更简单的方法,它是为了将支持向量机的输出缩放到概率值而开发的。它包括学习逻辑回归模型来执行分数到校准概率的转换。等渗回归是一种更复杂的加权最小二乘回归模型。它需要更多的训练数据,尽管它也更强大、更通用。这里,等渗简单地指原始概率到重新标度值的单调递增映射。
当预测概率中的失真为乙状线形状时,普拉特缩放最有效。等渗回归是一种更强大的校准方法,可以校正任何单调失真。
——用监督学习预测好概率,2005。
Sklearn 库通过校准分类器类提供对用于校准概率的普拉特标度和等渗回归方法的访问。
这是一个模型的包装(像 SVM)。首选的缩放技术通过“方法参数定义,该参数可以是“乙状结肠”(普拉特缩放)或“等张”(等张回归)。
交叉验证用于缩放模型的预测概率,通过“ cv ”参数设置。这意味着模型在训练集上是合适的,在测试集上是校准的,并且这个过程对于 k 倍重复 k 次,其中预测概率在运行中是平均的。
设置“ cv ”参数取决于可用的数据量,尽管可以使用 3 或 5 等值。重要的是,分割是分层的,这在不平衡数据集上使用概率校准时很重要,因为不平衡数据集通常很少有正类的例子。
...
# example of wrapping a model with probability calibration
model = ...
calibrated = CalibratedClassifierCV(model, method='sigmoid', cv=3)
现在我们知道了如何校准概率,让我们来看一些在不平衡类别数据集上校准模型概率的例子。
具有校准概率的 SVM
在本节中,我们将回顾如何在不平衡类别数据集上校准 SVM 模型的概率。
首先,让我们使用make _ classion()函数定义一个数据集。我们将生成 10,000 个示例,其中 99%属于负案例(类别 0),1%属于正案例(类别 1)。
...
# generate dataset
X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0,
n_clusters_per_class=1, weights=[0.99], flip_y=0, random_state=4)
接下来,我们可以定义一个具有默认超参数的 SVM。这意味着模型不会针对数据集进行调整,但会提供一致的比较基础。
...
# define model
model = SVC(gamma='scale')
然后,我们可以使用重复分层 k 倍交叉验证在数据集上评估该模型,重复三次,每次重复 10 倍。
我们将使用 ROC AUC 评估模型,并计算所有重复和折叠的平均得分。中华民国 AUC 将利用 SVM 提供的未经校准的类概率分数。
...
# define evaluation procedure
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
# evaluate model
scores = cross_val_score(model, X, y, scoring='roc_auc', cv=cv, n_jobs=-1)
# summarize performance
print('Mean ROC AUC: %.3f' % mean(scores))
将这些联系在一起,完整的示例如下所示。
# evaluate svm with uncalibrated probabilities for imbalanced classification
from numpy import mean
from sklearn.datasets import make_classification
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.svm import SVC
# generate dataset
X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0,
n_clusters_per_class=1, weights=[0.99], flip_y=0, random_state=4)
# define model
model = SVC(gamma='scale')
# define evaluation procedure
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
# evaluate model
scores = cross_val_score(model, X, y, scoring='roc_auc', cv=cv, n_jobs=-1)
# summarize performance
print('Mean ROC AUC: %.3f' % mean(scores))
运行该示例在不平衡类别数据集上用未校准的概率评估 SVM。
注:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
在这种情况下,我们可以看到 SVM 实现了约 0.804 的 ROC AUC。
Mean ROC AUC: 0.804
接下来,我们可以尝试使用校准分类器类来包装 SVM 模型并预测校准概率。
我们使用分层的 10 倍交叉验证来评估模型;这意味着每个文件夹上有 9000 个示例用于训练,1000 个用于测试。
通过校准分类器和 3 倍,一倍的 9000 个例子将被分成 6000 个用于训练模型,3000 个用于校准概率。这不会留下少数类的许多例子,例如 10 倍交叉验证中的 90/10,然后校准 60/30。
使用校准时,重要的是根据您选择的模型评估方案来计算这些数字,或者调整折叠数量以确保数据集足够大,或者甚至在需要时切换到更简单的训练/测试分割,而不是交叉验证。可能需要实验。
我们将像以前一样定义 SVM 模型,然后用等渗回归定义校准分类器,然后通过重复分层 k 倍交叉验证评估校准模型。
...
# define model
model = SVC(gamma='scale')
# wrap the model
calibrated = CalibratedClassifierCV(model, method='isotonic', cv=3)
因为默认情况下 SVM 概率没有被校准,我们期望校准它们会导致 ROC AUC 的改进,该 AUC 基于它们的概率明确评估模型。
将这些联系在一起,下面列出了用校准概率评估 SVM 的完整例子。
# evaluate svm with calibrated probabilities for imbalanced classification
from numpy import mean
from sklearn.datasets import make_classification
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.calibration import CalibratedClassifierCV
from sklearn.svm import SVC
# generate dataset
X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0,
n_clusters_per_class=1, weights=[0.99], flip_y=0, random_state=4)
# define model
model = SVC(gamma='scale')
# wrap the model
calibrated = CalibratedClassifierCV(model, method='isotonic', cv=3)
# define evaluation procedure
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
# evaluate model
scores = cross_val_score(calibrated, X, y, scoring='roc_auc', cv=cv, n_jobs=-1)
# summarize performance
print('Mean ROC AUC: %.3f' % mean(scores))
运行该示例在不平衡类别数据集上评估具有校准概率的 SVM。
注:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
在这种情况下,我们可以看到 SVM 实现了 ROC AUC 从约 0.804 到约 0.875 的提升。
Mean ROC AUC: 0.875
概率校准可以结合对算法或数据集的其他修改来评估,以解决倾斜的类分布。
例如,SVM 提供了“ class_weight ”参数,该参数可以设置为“ balanced ”来调整保证金,以有利于少数族裔。我们可以将这一变化纳入 SVM 模型,并校准概率,我们可能会看到模型技能的进一步提升;例如:
...
# define model
model = SVC(gamma='scale', class_weight='balanced')
将这些联系在一起,下面列出了具有校准概率的类加权 SVM 的完整示例。
# evaluate weighted svm with calibrated probabilities for imbalanced classification
from numpy import mean
from sklearn.datasets import make_classification
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.calibration import CalibratedClassifierCV
from sklearn.svm import SVC
# generate dataset
X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0,
n_clusters_per_class=1, weights=[0.99], flip_y=0, random_state=4)
# define model
model = SVC(gamma='scale', class_weight='balanced')
# wrap the model
calibrated = CalibratedClassifierCV(model, method='isotonic', cv=3)
# define evaluation procedure
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
# evaluate model
scores = cross_val_score(calibrated, X, y, scoring='roc_auc', cv=cv, n_jobs=-1)
# summarize performance
print('Mean ROC AUC: %.3f' % mean(scores))
运行该示例在不平衡类别数据集上评估具有校准概率的类加权 SVM。
注:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
在这种情况下,我们可以看到,SVM 实现了中华民国 AUC 从约 0.875 到约 0.966 的进一步提升。
Mean ROC AUC: 0.966
具有校准概率的决策树
决策树是另一种不自然产生概率的高效机器学习。
相反,直接预测类别标签,并且可以基于落入为新示例预测的树叶中的训练数据集中的示例的分布来估计类似概率的分数。因此,来自决策树的概率分数应该在被评估和用于选择模型之前被校准。
我们可以使用决策树分类器 Sklearn 类来定义决策树。
该模型可以在我们的合成不平衡类别数据集上用未校准的概率进行评估。
下面列出了完整的示例。
# evaluate decision tree with uncalibrated probabilities for imbalanced classification
from numpy import mean
from sklearn.datasets import make_classification
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.tree import DecisionTreeClassifier
# generate dataset
X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0,
n_clusters_per_class=1, weights=[0.99], flip_y=0, random_state=4)
# define model
model = DecisionTreeClassifier()
# define evaluation procedure
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
# evaluate model
scores = cross_val_score(model, X, y, scoring='roc_auc', cv=cv, n_jobs=-1)
# summarize performance
print('Mean ROC AUC: %.3f' % mean(scores))
运行该示例在不平衡类别数据集上评估具有未校准概率的决策树。
注:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
在这种情况下,我们可以看到决策树实现了大约 0.842 的 ROC AUC。
Mean ROC AUC: 0.842
然后,我们可以使用校准包装器评估相同的模型。
在这种情况下,我们将使用通过将“方法”参数设置为“ sigmoid 而配置的普拉特缩放方法。
...
# wrap the model
calibrated = CalibratedClassifierCV(model, method='sigmoid', cv=3)
下面列出了用校准概率评估不平衡分类决策树的完整示例。
# decision tree with calibrated probabilities for imbalanced classification
from numpy import mean
from sklearn.datasets import make_classification
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.calibration import CalibratedClassifierCV
from sklearn.tree import DecisionTreeClassifier
# generate dataset
X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0,
n_clusters_per_class=1, weights=[0.99], flip_y=0, random_state=4)
# define model
model = DecisionTreeClassifier()
# wrap the model
calibrated = CalibratedClassifierCV(model, method='sigmoid', cv=3)
# define evaluation procedure
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
# evaluate model
scores = cross_val_score(calibrated, X, y, scoring='roc_auc', cv=cv, n_jobs=-1)
# summarize performance
print('Mean ROC AUC: %.3f' % mean(scores))
运行该示例评估不平衡类别数据集上具有校准概率的决策树。
注:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
在这种情况下,我们可以看到决策树实现了 ROC AUC 从大约 0.842 到大约 0.859 的提升。
Mean ROC AUC: 0.859
用 KNN 进行网格搜索概率校准
概率校准对方法和使用方法的方式都很敏感。
因此,最好在模型上测试一套不同的概率校准方法,以便发现最适合数据集的方法。一种方法是将校准方法和交叉验证折叠视为超参数,并对其进行调整。在本节中,我们将研究使用网格搜索来调整这些超参数。
k-最近邻算法,或称 KNN 算法,是另一种非线性机器学习算法,它直接预测类标签,并且必须被修改以产生类似概率的分数。这通常涉及到在邻域中使用类标签的分布。
我们可以使用默认邻域大小为 5 的 KNeighborsClassifier 类在合成不平衡类别数据集上评估具有未校准概率的 KNN。
下面列出了完整的示例。
# evaluate knn with uncalibrated probabilities for imbalanced classification
from numpy import mean
from sklearn.datasets import make_classification
from sklearn.model_selection import cross_val_score
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.neighbors import KNeighborsClassifier
# generate dataset
X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0,
n_clusters_per_class=1, weights=[0.99], flip_y=0, random_state=4)
# define model
model = KNeighborsClassifier()
# define evaluation procedure
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
# evaluate model
scores = cross_val_score(model, X, y, scoring='roc_auc', cv=cv, n_jobs=-1)
# summarize performance
print('Mean ROC AUC: %.3f' % mean(scores))
运行该示例在不平衡类别数据集上用未校准的概率评估 KNN。
注:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
在这种情况下,我们可以看到 KNN 实现了约 0.864 的 ROC AUC。
Mean ROC AUC: 0.864
知道概率依赖于邻域大小并且是未校准的,我们期望一些校准将使用 ROC AUC 提高模型的表现。
我们将使用网格搜索不同的配置,而不是抽查校准分类器类的一个配置。
首先,模型和校准包装器的定义与之前一样。
...
# define model
model = KNeighborsClassifier()
# wrap the model
calibrated = CalibratedClassifierCV(model)
我们将测试“乙状结肠”和“等张”“法”值,以及[2,3,4]中不同的“ cv 值。回想一下“ cv ”控制用于估计校准概率的训练数据集的分割。
我们可以将参数网格定义为一个带有参数名称的字典,我们想要调整校准分类器并提供要尝试的值列表。这将测试 3 * 2 或 6 种不同的组合。
...
# define grid
param_grid = dict(cv=[2,3,4], method=['sigmoid','isotonic'])
然后,我们可以用模型和参数网格定义 GridSearchCV ,并使用我们之前使用的相同的重复分层 k 倍交叉验证来评估每个参数组合。
...
# define evaluation procedure
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
# define grid search
grid = GridSearchCV(estimator=calibrated, param_grid=param_grid, n_jobs=-1, cv=cv, scoring='roc_auc')
# execute the grid search
grid_result = grid.fit(X, y)
评估后,我们将总结 ROC AUC 最高的配置,然后列出所有组合的结果。
# report the best configuration
print("Best: %f using %s" % (grid_result.best_score_, grid_result.best_params_))
# report all configurations
means = grid_result.cv_results_['mean_test_score']
stds = grid_result.cv_results_['std_test_score']
params = grid_result.cv_results_['params']
for mean, stdev, param in zip(means, stds, params):
print("%f (%f) with: %r" % (mean, stdev, param))
将这些联系在一起,下面列出了使用 KNN 模型进行不平衡分类的网格搜索概率校准的完整示例。
# grid search probability calibration with knn for imbalance classification
from numpy import mean
from sklearn.datasets import make_classification
from sklearn.model_selection import GridSearchCV
from sklearn.model_selection import RepeatedStratifiedKFold
from sklearn.neighbors import KNeighborsClassifier
from sklearn.calibration import CalibratedClassifierCV
# generate dataset
X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0,
n_clusters_per_class=1, weights=[0.99], flip_y=0, random_state=4)
# define model
model = KNeighborsClassifier()
# wrap the model
calibrated = CalibratedClassifierCV(model)
# define grid
param_grid = dict(cv=[2,3,4], method=['sigmoid','isotonic'])
# define evaluation procedure
cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1)
# define grid search
grid = GridSearchCV(estimator=calibrated, param_grid=param_grid, n_jobs=-1, cv=cv, scoring='roc_auc')
# execute the grid search
grid_result = grid.fit(X, y)
# report the best configuration
print("Best: %f using %s" % (grid_result.best_score_, grid_result.best_params_))
# report all configurations
means = grid_result.cv_results_['mean_test_score']
stds = grid_result.cv_results_['std_test_score']
params = grid_result.cv_results_['params']
for mean, stdev, param in zip(means, stds, params):
print("%f (%f) with: %r" % (mean, stdev, param))
运行该示例使用不平衡类别数据集上的一组不同类型的校准概率来评估 KNN。
注:考虑到算法或评估程序的随机性,或数值准确率的差异,您的结果可能会有所不同。考虑运行该示例几次,并比较平均结果。
在这种情况下,我们可以看到最佳结果是用 2 的“ cv ”和“方法的“等张值”获得的,平均 ROC AUC 约为 0.895,比没有校准时的 0.864 有所提高。
Best: 0.895120 using {'cv': 2, 'method': 'isotonic'}
0.895084 (0.062358) with: {'cv': 2, 'method': 'sigmoid'}
0.895120 (0.062488) with: {'cv': 2, 'method': 'isotonic'}
0.885221 (0.061373) with: {'cv': 3, 'method': 'sigmoid'}
0.881924 (0.064351) with: {'cv': 3, 'method': 'isotonic'}
0.881865 (0.065708) with: {'cv': 4, 'method': 'sigmoid'}
0.875320 (0.067663) with: {'cv': 4, 'method': 'isotonic'}
这提供了一个模板,您可以使用它来评估自己模型上的不同概率校准配置。
进一步阅读
如果您想更深入地了解这个主题,本节将提供更多资源。
教程
报纸
- 用监督学习预测好概率,2005。
- 不平衡数据的类概率估计不可靠(以及如何修正),2012。
书
- 从不平衡数据集中学习,2018。
- 不平衡学习:基础、算法和应用,2013。
- 应用预测建模,2013。
蜜蜂
- 硬化。校准。校准后的分类 CVI API。
- 硬化. svm.SVC API 。
- 硬化. tree .决策树分类器 API 。
- sklearn . neighborsclassifier API。
- sklearn.model_selection。GridSearchCV API 。
文章
摘要
在本教程中,您发现了如何校准不平衡分类的预测概率。
具体来说,您了解到:
- 对于不平衡的分类问题,需要校准概率来从模型中获得最大收益。
- 如何校准非线性模型(如支持向量机、决策树和 KNN)的预测概率。
- 如何在类分布偏斜的数据集上网格搜索不同的概率校准方法?
你有什么问题吗? 在下面的评论中提问,我会尽力回答。
不平衡分类概率度量的温和介绍
最后更新于 2020 年 1 月 14 日
分类预测建模包括预测示例的类别标签,尽管有些问题需要预测类别成员的概率。
对于这些问题,不需要清晰的类标签,而是需要属于每个类的每个示例的可能性,并在以后进行解释。因此,小的相对概率具有很大的意义,需要专门的度量来量化预测的概率。
在本教程中,您将发现评估不平衡分类的概率预测的指标。
完成本教程后,您将知道:
- 一些分类预测建模问题需要概率预测。
- 对数损失量化了预测和预期概率分布之间的平均差异。
- Brier 评分量化了预测概率和预期概率之间的平均差异。
用我的新书Python 不平衡分类启动你的项目,包括分步教程和所有示例的 Python 源代码文件。
我们开始吧。
不平衡分类概率度量的温和介绍 图片由 a4gpa 提供,保留部分权利。
教程概述
本教程分为三个部分;它们是:
- 概率度量
- 不平衡分类的日志丢失
- 不平衡分类的 Brier 评分
概率度量
分类预测建模包括预测一个例子的类别标签。
在某些问题上,不需要清晰的类标签,而更喜欢类成员的概率。概率总结了属于每个类别标签的示例的可能性(或不确定性)。概率更为微妙,可以由人类操作员或决策系统进行解释。
概率度量是那些专门设计来使用预测概率而不是简单的类标签来量化分类器模型技能的度量。它们通常是提供单个值的分数,该值可用于根据预测概率与预期类别概率的匹配程度来比较不同的模型。
实际上,数据集没有目标概率。相反,它将有类标签。
例如,一个两类(二进制)分类问题的类标签为 0 表示负的情况,1 表示正的情况。当一个例子的类标签为 0 时,那么类标签为 0 和 1 的概率分别为 1 和 0。当一个例子的类标签为 1 时,那么类标签为 0 和 1 的概率分别为 0 和 1。
- Class = 0 的例子 : P(class=0) = 1,P(class=1) = 0
- Class = 1 的例子 : P(class=0) = 0,P(class=1) = 1
我们可以看到这将如何扩展到三个级别或更多;例如:
- Class = 0 的例子 : P(class=0) = 1,P(class=1) = 0,P(class=2) = 0
- Class = 1 的例子 : P(class=0) = 0,P(class=1) = 1,P(class=2) = 0
- Class = 2示例:P(class=0) = 0,P(class=1) = 0,P(class=2) = 1
在二分类问题的情况下,这种表示可以简化为只关注正类。
也就是说,我们只需要一个属于类 1 的例子的概率来表示二进制分类的概率(所谓伯努利分布);例如:
- 【Class = 0 的例子 : P(class=1) = 0
- Class = 1 的例子 : P(class=1) = 1
概率度量将总结类成员的预测分布与已知类概率分布的匹配程度。
这种对预测概率的关注可能意味着模型预测的清晰类标签被忽略。这种关注可能意味着,一个预测概率的模型在根据其清晰的类别标签进行评估时,可能会表现得很糟糕,比如使用准确性或类似的分数。这是因为尽管预测的概率可能显示出技巧,但是在被转换成清晰的类标签之前,它们必须用适当的阈值来解释。
此外,对预测概率的关注可能还要求在使用或评估之前校准由一些非线性模型预测的概率。一些模型将学习校准的概率作为训练过程的一部分(例如逻辑回归),但许多模型不会也需要校准(例如支持向量机、决策树和神经网络)。
通常为每个示例计算给定的概率度量,然后对训练数据集中的所有示例进行平均。
有两种流行的评估预测概率的度量标准;它们是:
- 日志丢失
- 布瑞尔分数
让我们依次仔细看看每一个。
不平衡分类的日志丢失
对数损失或简称对数损失是一个已知的损失函数,用于训练逻辑回归分类算法。
对数损失函数计算二分类模型进行概率预测的负对数似然。最值得注意的是,这是逻辑回归,但是这个函数可以被其他模型使用,例如神经网络,并且被其他名称所知,例如交叉熵。
通常,可以使用每个类别的预期概率和每个类别的预测概率的自然对数来计算对数损失;例如:
- log loss =-(P(class = 0)* log(P(class = 0))+(P(class = 1))* log(P(class = 1))
最佳可能的日志丢失为 0.0,对于逐渐恶化的分数,值为正到无穷大。
如果只是预测正类的概率,那么可以计算一个二分类预测的对数损失函数( yhat )与预期概率( y )的比较如下:
- log loss =-((1–y)* log(1–y hat)+y * log(y hat))
例如,如果预期概率为 1.0,而模型预测为 0.8,则对数损失将为:
- log loss =-((1–y)* log(1–y hat)+y * log(y hat))
- log loss =-((1–1.0)* log(1–0.8)+1.0 * log(0.8))
- LogLoss = -(-0.0 + -0.223)
- LogLoss = 0.223
通过添加额外的项,可以针对多个类别扩大该计算;例如:
- log loss =-(C y _ C * log(yhat _ C)中的和 C)
这种一般化也称为交叉熵,计算两个概率分布不同的位数(如果使用 log base-2)或 nats(如果使用 log base-e)。
具体来说,它建立在信息论中熵的概念之上,并计算一个分布与另一个分布相比,表示或传输一个事件所需的平均位数。
…交叉熵是当我们使用模型 q 时,对来自分布为 p 的源的数据进行编码所需的平均位数…
—第 57 页,机器学习:概率视角,2012。
如果我们考虑一个目标或潜在的概率分布 P 和目标分布的近似 Q ,那么 Q 从 P 的交叉熵就是用 Q 代替 P 来表示一个事件的附加比特数。
目前我们将坚持使用对数损失,因为这是在使用这种计算作为分类器模型的评估指标时最常用的术语。
当计算与测试数据集中的一组预期概率相比的一组预测的对数损失时,计算并报告所有样本的对数损失平均值;例如:
- AverageLogLoss = 1/N * N 中的和 I-((1–y)* log(1–yhat)+y * log(yhat))
训练数据集中一组预测的平均对数损失通常简称为对数损失。
我们可以用一个实例来演示如何计算对数损失。
首先,让我们定义一个合成的二进制类别数据集。我们将使用 make_classification()函数创建 1000 个示例,两个类的分割比例为 99%/1%。下面列出了创建和汇总数据集的完整示例。
# create an imbalanced dataset
from numpy import unique
from sklearn.datasets import make_classification
# generate 2 class dataset
X, y = make_classification(n_samples=1000, n_classes=2, weights=[0.99], flip_y=0, random_state=1)
# summarize dataset
classes = unique(y)
total = len(y)
for c in classes:
n_examples = len(y[y==c])
percent = n_examples / total * 100
print('> Class=%d : %d/%d (%.1f%%)' % (c, n_examples, total, percent))
运行该示例会创建数据集,并报告每个类中示例的分布。
> Class=0 : 990/1000 (99.0%)
> Class=1 : 10/1000 (1.0%)
接下来,我们将开发一种直觉,对概率进行天真的预测。
一个天真的预测策略是预测多数类的确定性,或者 P(类=0) = 1。另一种策略是预测少数民族,或 P(类=1) = 1。
可以使用 log_loss() Sklearn 功能计算 Log loss。它将每个类的概率作为输入,并返回平均日志丢失。具体来说,每个示例必须有一个预测,每个类有一个概率,这意味着一个二进制分类问题示例的预测必须有一个类 0 和类 1 的概率。
因此,为所有示例预测类别 0 的某些概率将按如下方式实现:
...
# no skill prediction 0
probabilities = [[1, 0] for _ in range(len(testy))]
avg_logloss = log_loss(testy, probabilities)
print('P(class0=1): Log Loss=%.3f' % (avg_logloss))
我们可以对 P(类 1)=1 做同样的事情。
这两种策略预计会表现糟糕。
一个更好的简单策略是预测每个例子的类分布。例如,因为我们的数据集对于多数和少数类具有 99%/1%的类分布,所以对于每个示例,该分布可以是“预测的,以给出概率预测的基线。
...
# baseline probabilities
probabilities = [[0.99, 0.01] for _ in range(len(testy))]
avg_logloss = log_loss(testy, probabilities)
print('Baseline: Log Loss=%.3f' % (avg_logloss))
最后,我们还可以通过将测试集的目标值作为预测来计算完美预测概率的对数损失。
...
# perfect probabilities
avg_logloss = log_loss(testy, testy)
print('Perfect: Log Loss=%.3f' % (avg_logloss))
将这些结合在一起,完整的示例如下所示。
# log loss for naive probability predictions.
from numpy import mean
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.metrics import log_loss
# generate 2 class dataset
X, y = make_classification(n_samples=1000, n_classes=2, weights=[0.99], flip_y=0, random_state=1)
# split into train/test sets with same class ratio
trainX, testX, trainy, testy = train_test_split(X, y, test_size=0.5, random_state=2, stratify=y)
# no skill prediction 0
probabilities = [[1, 0] for _ in range(len(testy))]
avg_logloss = log_loss(testy, probabilities)
print('P(class0=1): Log Loss=%.3f' % (avg_logloss))
# no skill prediction 1
probabilities = [[0, 1] for _ in range(len(testy))]
avg_logloss = log_loss(testy, probabilities)
print('P(class1=1): Log Loss=%.3f' % (avg_logloss))
# baseline probabilities
probabilities = [[0.99, 0.01] for _ in range(len(testy))]
avg_logloss = log_loss(testy, probabilities)
print('Baseline: Log Loss=%.3f' % (avg_logloss))
# perfect probabilities
avg_logloss = log_loss(testy, testy)
print('Perfect: Log Loss=%.3f' % (avg_logloss))
运行该示例会报告每个简单策略的日志丢失。
正如预期的那样,预测每个类别标签的确定性会受到较大的日志丢失分数的惩罚,在所有情况下少数类别都是确定的情况下,分数会大得多。
我们可以看到,将数据集中的示例分布预测为基线比其他任何一种简单的度量都会得到更好的分数。该基线代表无技能分类器,低于该策略的日志丢失分数代表具有某种技能的模型。
最后,我们可以看到完美预测概率的对数损失为 0.0,表明实际和预测概率分布之间没有差异。
P(class0=1): Log Loss=0.345
P(class1=1): Log Loss=34.193
Baseline: Log Loss=0.056
Perfect: Log Loss=0.000
现在我们已经熟悉了日志丢失,让我们来看看 Brier 评分。
不平衡分类的 Brier 评分
以格伦·布瑞尔命名的布瑞尔分数计算预测概率和期望值之间的均方误差。
分数总结了概率预测中误差的大小,是为二分类问题设计的。它侧重于评估正类的概率。然而,它可以适用于多类问题。
因此,它是不平衡分类问题的一个合适的概率度量。
概率分数的评估通常通过布瑞尔分数进行。基本思想是计算预测概率分数和真实类别指标之间的均方误差(MSE),其中正类别编码为 1,负类别编码为 0。
—第 57 页,从不平衡数据集中学习,2018。
错误分数总是在 0.0 到 1.0 之间,其中技能完美的模型分数为 0.0。
与预期概率( y )相比,正预测概率( yhat )的 Brier 得分可计算如下:
- briers core = 1/n * I 与 n 之和(yhat _ I–y_i)²)
例如,如果预测的正类别概率为 0.8,预期概率为 1.0,则布瑞尔分数计算如下:
- briers core =(yhat _ I–y_i)²)
- briers core =(0.8–1.0)²
- BrierScore = 0.04
我们可以用一个工作示例来演示如何使用上一节中使用的相同数据集和简单预测模型来计算布瑞尔分数。
Brier 分数可以使用Brier _ score _ loss()Sklearn 功能计算。它只接受正类的概率,并返回一个平均分数。
如前一节所述,我们可以评估预测每个类标签的确定性的简单策略。在这种情况下,由于分数只考虑了正类的概率,这将涉及预测 P(类=1)=0 的 0.0 和 P(类=1)=1 的 1.0。例如:
...
# no skill prediction 0
probabilities = [0.0 for _ in range(len(testy))]
avg_brier = brier_score_loss(testy, probabilities)
print('P(class1=0): Brier Score=%.4f' % (avg_brier))
# no skill prediction 1
probabilities = [1.0 for _ in range(len(testy))]
avg_brier = brier_score_loss(testy, probabilities)
print('P(class1=1): Brier Score=%.4f' % (avg_brier))
我们还可以测试无技能分类器,该分类器预测数据集中正面示例的比率,在本例中为 1%或 0.01。
...
# baseline probabilities
probabilities = [0.01 for _ in range(len(testy))]
avg_brier = brier_score_loss(testy, probabilities)
print('Baseline: Brier Score=%.4f' % (avg_brier))
最后,我们还可以确认完美预测概率的布瑞尔分数。
...
# perfect probabilities
avg_brier = brier_score_loss(testy, testy)
print('Perfect: Brier Score=%.4f' % (avg_brier))
将这些联系在一起,完整的示例如下所示。
# brier score for naive probability predictions.
from numpy import mean
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.metrics import brier_score_loss
# generate 2 class dataset
X, y = make_classification(n_samples=1000, n_classes=2, weights=[0.99], flip_y=0, random_state=1)
# split into train/test sets with same class ratio
trainX, testX, trainy, testy = train_test_split(X, y, test_size=0.5, random_state=2, stratify=y)
# no skill prediction 0
probabilities = [0.0 for _ in range(len(testy))]
avg_brier = brier_score_loss(testy, probabilities)
print('P(class1=0): Brier Score=%.4f' % (avg_brier))
# no skill prediction 1
probabilities = [1.0 for _ in range(len(testy))]
avg_brier = brier_score_loss(testy, probabilities)
print('P(class1=1): Brier Score=%.4f' % (avg_brier))
# baseline probabilities
probabilities = [0.01 for _ in range(len(testy))]
avg_brier = brier_score_loss(testy, probabilities)
print('Baseline: Brier Score=%.4f' % (avg_brier))
# perfect probabilities
avg_brier = brier_score_loss(testy, testy)
print('Perfect: Brier Score=%.4f' % (avg_brier))
运行这个例子,我们可以看到朴素模型和基线无技能分类器的分数。
正如我们可能预期的那样,我们可以看到,为所有示例预测 0.0 会导致较低的分数,因为测试集中所有 0.0 预测和大多数 0 类之间的均方误差会导致较小的值。相反,1.0 预测和大部分 0 类值之间的误差会导致更大的误差分数。
重要的是,我们可以看到默认的无技能分类器比预测所有 0.0 值的结果得分更低。同样,这代表基线分数,低于该分数的模型将展示技能。
P(class1=0): Brier Score=0.0100
P(class1=1): Brier Score=0.9900
Baseline: Brier Score=0.0099
Perfect: Brier Score=0.0000
布瑞尔分数可能会变得非常小,重点将放在远低于小数点的分数上。例如,在上面的例子中,基线分数和完美分数之间的差异在小数点后四位很小。
一种常见的做法是使用参考分数来转换分数,例如无技能分类器。这被称为简单技能分数,计算如下:
- BrierSkillScore = 1–(BrierScore/BrierScore _ ref)
我们可以看到,如果参考分数被评估,它将导致 0.0 的 BSS。这代表着没有技能的预测。低于此值将为负值,表示比没有技能更糟糕。高于 0.0 的值代表熟练的预测,完美的预测值为 1.0。
我们可以通过开发一个函数来计算下面列出的 Brier 技能分数来演示这一点。
# calculate the brier skill score
def brier_skill_score(y, yhat, brier_ref):
# calculate the brier score
bs = brier_score_loss(y, yhat)
# calculate skill score
return 1.0 - (bs / brier_ref)
然后,我们可以为每个天真的预测和完美的预测计算平衡计分卡。
下面列出了完整的示例。
# brier skill score for naive probability predictions.
from numpy import mean
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.metrics import brier_score_loss
# calculate the brier skill score
def brier_skill_score(y, yhat, brier_ref):
# calculate the brier score
bs = brier_score_loss(y, yhat)
# calculate skill score
return 1.0 - (bs / brier_ref)
# generate 2 class dataset
X, y = make_classification(n_samples=1000, n_classes=2, weights=[0.99], flip_y=0, random_state=1)
# split into train/test sets with same class ratio
trainX, testX, trainy, testy = train_test_split(X, y, test_size=0.5, random_state=2, stratify=y)
# calculate reference
probabilities = [0.01 for _ in range(len(testy))]
brier_ref = brier_score_loss(testy, probabilities)
print('Reference: Brier Score=%.4f' % (brier_ref))
# no skill prediction 0
probabilities = [0.0 for _ in range(len(testy))]
bss = brier_skill_score(testy, probabilities, brier_ref)
print('P(class1=0): BSS=%.4f' % (bss))
# no skill prediction 1
probabilities = [1.0 for _ in range(len(testy))]
bss = brier_skill_score(testy, probabilities, brier_ref)
print('P(class1=1): BSS=%.4f' % (bss))
# baseline probabilities
probabilities = [0.01 for _ in range(len(testy))]
bss = brier_skill_score(testy, probabilities, brier_ref)
print('Baseline: BSS=%.4f' % (bss))
# perfect probabilities
bss = brier_skill_score(testy, testy, brier_ref)
print('Perfect: BSS=%.4f' % (bss))
运行该示例首先计算 BSS 计算中使用的参考 Brier 分数。
然后我们可以看到,预测每个职业的确定性分数会导致负的 BSS 分数,这表明他们比没有技能更糟糕。最后,我们可以看到评估参考预测本身的结果为 0.0,表明没有技能,评估真实值作为预测的结果为 1.0 的满分。
因此,布瑞尔技能评分是评估概率预测的最佳实践,广泛用于例行评估概率分类预测的地方,例如天气预报(例如,是否下雨)。
Reference: Brier Score=0.0099
P(class1=0): BSS=-0.0101
P(class1=1): BSS=-99.0000
Baseline: BSS=0.0000
Perfect: BSS=1.0000
进一步阅读
如果您想更深入地了解这个主题,本节将提供更多资源。
教程
书
- 第八章不平衡学习的评估指标,不平衡学习:基础、算法和应用,2013。
- 第三章绩效衡量,从不平衡数据集中学习,2018。
应用程序接口
- sklearn . datasets . make _ classification API。
- sklearn . metrics . log _ loss API。
- sklearn . metrics . brier _ score _ loss API。
文章
摘要
在本教程中,您发现了评估不平衡分类的概率预测的指标。
具体来说,您了解到:
- 一些分类预测建模问题需要概率预测。
- 对数损失量化了预测和预期概率分布之间的平均差异。
- Brier 评分量化了预测概率和预期概率之间的平均差异。
你有什么问题吗? 在下面的评论中提问,我会尽力回答。