分类准确率是一个度量指标,它将分类模型的性能总结为正确预测的数量除以总预测数量。
它易于计算和理解,使其成为评估分类器模型最常用的指标。当示例到类别的分布严重倾斜时,这种直觉就会失效。
从业者在平衡数据集上形成的直觉,例如99%代表一个熟练的模型,在不平衡分类预测建模问题上可能是错误的,并且具有危险的误导性。
在本教程中,您将发现分类准确率在不平衡分类问题上的失效。
完成本教程后,您将了解:
- 准确率和错误率是总结分类模型性能的实际标准指标。
- 分类准确率在具有倾斜类分布的分类问题上失效,原因是从业者在具有相等类分布的数据集上形成的直觉。
- 对倾斜类分布导致准确率失效的直觉,附带一个工作示例。
通过我的新书《使用 Python 进行不平衡分类》启动您的项目,其中包括逐步教程和所有示例的Python 源代码文件。
让我们开始吧。
- 2020年1月更新:已针对 scikit-learn v0.22 API 的更改进行更新。

分类准确率对于倾斜的类分布具有误导性
图片由Esqui-Ando con Tònho提供,保留部分权利。
教程概述
本教程分为三个部分;它们是:
- 什么是分类准确率?
- 准确率在不平衡分类中失效
- 不平衡分类中准确率的示例
什么是分类准确率?
分类预测建模涉及在问题域中根据示例预测类标签。
用于评估分类预测模型性能的最常用指标是分类准确率。通常,预测模型的准确率是好的(90%以上),因此,用模型的错误率来总结模型性能也很常见。
准确率及其互补的错误率是分类问题中评估学习系统性能最常用的指标。
— 不平衡分布下的预测建模调查,2015 年。
分类准确率首先使用分类模型对测试数据集中的每个示例进行预测。然后将预测与测试集中这些示例的已知标签进行比较。准确率然后计算为测试集中正确预测的示例比例,除以对测试集进行的所有预测。
- 准确率 = 正确预测 / 总预测
反之,错误率可以计算为测试集中错误预测的总数除以对测试集进行的所有预测。
- 错误率 = 错误预测 / 总预测
准确率和错误率是互补的,这意味着我们总是可以从其中一个计算出另一个。例如:
- 准确率 = 1 – 错误率
- 错误率 = 1 – 准确率
另一种思考准确率的有效方式是根据混淆矩阵。
混淆矩阵是分类模型预测的汇总,按类别组织成表格。表格的每一行表示实际类别,每一列表示预测类别。单元格中的值是针对某一给定类别实际预测的次数。对角线上的单元格表示正确预测,其中预测类别和预期类别一致。
评估分类器性能最直接的方法是基于混淆矩阵分析。……从这样的矩阵中,可以提取出许多广泛使用的指标来衡量学习系统的性能,例如错误率……和准确率……
— A Study Of The Behavior Of Several Methods For Balancing Machine Learning Training Data, 2004。
混淆矩阵不仅提供了对预测模型准确率的更多洞察,还提供了哪些类别被正确预测、哪些被错误预测以及正在发生何种类型错误的信息。
最简单的混淆矩阵适用于二分类问题,包含负类(类别 0)和正类(类别 1)。
在此类混淆矩阵中,表格中的每个单元格都有一个特定且易于理解的名称,总结如下:
1 2 3 |
| 正向预测 | 负向预测 正类别 | 真阳性 (TP) | 假阴性 (FN) 负类别 | 假阳性 (FP) | 真阴性 (TN) |
分类准确率可以从这个混淆矩阵中计算,即表格中正确单元格(真阳性加真阴性)的总和除以表格中所有单元格的总和。
- 准确率 = (TP + TN) / (TP + FN + FP + TN)
类似地,错误率也可以从混淆矩阵中计算,即表格中不正确单元格(假阳性加假阴性)的总和除以表格中所有单元格的总和。
- 错误率 = (FP + FN) / (TP + FN + FP + TN)
现在我们熟悉了分类准确率及其互补的错误率,让我们来了解为什么它们可能不适用于不平衡分类问题。
想要开始学习不平衡分类吗?
立即参加我为期7天的免费电子邮件速成课程(附示例代码)。
点击注册,同时获得该课程的免费PDF电子书版本。
准确率在不平衡分类中失效
分类准确率是评估分类模型最常用的指标。
其广泛使用的原因是它易于计算、易于解释,并且是一个总结模型能力的单一数字。
因此,自然地,它被用于不平衡分类问题,即训练数据集中示例在各个类别中的分布不相等。
这是不平衡分类初学者最常犯的错误。
当类别分布略微倾斜时,准确率仍然是一个有用的指标。当类别分布严重倾斜时,准确率可能成为模型性能不可靠的衡量标准。
这种不可靠性的原因集中在普通机器学习从业者和对分类准确率的直觉上。
通常,分类预测建模在类别分布相等或非常接近相等的小数据集上进行实践。因此,大多数从业者会形成一种直觉,认为高准确率得分(反之,低错误率得分)是好的,而超过90%的值是极好的。
在不平衡分类问题上,达到90%甚至99%的分类准确率可能微不足道。
这意味着在平衡类分布上形成的分类准确率直觉将被应用,并且会是错误的,误导从业者认为模型具有良好甚至出色的性能,而实际上并非如此。
准确率悖论
考虑一个类别不平衡比为1:100的不平衡数据集。
在这个问题中,少数类别(类别1)的每个示例将对应多数类别(类别0)的100个示例。
在这类问题中,多数类别代表“正常”,少数类别代表“异常”,例如故障、诊断或欺诈。对少数类别的好性能将优于对两个类别的好性能。
考虑到用户对少数(正)类别示例的偏好偏差,准确率不适用,因为与多数类别相比,代表性最低但更重要的示例的影响被削弱了。
— 不平衡分布下的预测建模调查,2015 年。
在这个问题上,一个总是预测测试集中所有示例为多数类别(类别0)的模型将具有99%的分类准确率,这反映了测试集中平均预期多数和少数示例的分布。
许多机器学习模型都是围绕平衡类别分布的假设设计的,并且通常会学习简单的规则(显式或隐式),例如总是预测多数类别,导致它们达到99%的准确率,尽管在实践中性能并不比一个不熟练的多数类别分类器更好。
一个初学者会看到一个复杂模型在这种类型的不平衡数据集上达到99%的性能,并认为他们的工作已经完成,而实际上,他们被误导了。
这种情况非常普遍,以至于它有一个名称,被称为“准确率悖论”。
……在不平衡数据集的框架中,准确率不再是一个合适的度量,因为它不区分不同类别中正确分类示例的数量。因此,它可能导致错误的结论……
严格来说,准确率确实报告了一个正确的结果;只是从业者对高准确率分数的直觉是失效点。与其纠正错误的直觉,通常的做法是使用替代指标来总结不平衡分类问题的模型性能。
现在我们熟悉了分类可能具有误导性的想法,让我们看一个具体的例子。
不平衡分类中准确率的示例
尽管已经解释了为什么准确率对不平衡分类来说是一个坏主意,但这仍然是一个抽象的概念。
我们可以通过一个工作示例使准确率的失效具体化,并尝试对抗您可能已经形成的关于平衡类分布的任何准确率直觉,或者更有可能劝阻在不平衡数据集上使用准确率。
首先,我们可以定义一个具有1:100类分布的合成数据集。
make_blobs() scikit-learn函数总是创建具有相等类分布的合成数据集。
然而,我们可以使用这个函数,通过几行额外的代码,创建具有任意类分布的合成分类数据集。类分布可以定义为一个字典,其中键是类值(例如0或1),值是要包含在数据集中的随机生成示例的数量。
下面的函数,名为get_dataset(),将接受一个类分布并返回一个具有该类分布的合成数据集。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
# 创建具有给定类分布的数据集 def get_dataset(proportions): # 确定类的数量 n_classes = len(proportions) # 确定为每个类生成的示例数量 largest = max([v for k,v in proportions.items()]) n_samples = largest * n_classes # 创建数据集 X, y = make_blobs(n_samples=n_samples, centers=n_classes, n_features=2, random_state=1, cluster_std=3) # 收集示例 X_list, y_list = list(), list() for k,v in proportions.items(): row_ix = where(y == k)[0] selected = row_ix[:v] X_list.append(X[selected, :]) y_list.append(y[selected]) return vstack(X_list), hstack(y_list) |
该函数可以接受任意数量的类,尽管我们将它用于简单的二元分类问题。
接下来,我们可以将上一节中用于创建数据集散点图的代码放入一个辅助函数中。下面是plot_dataset()函数,它将绘制数据集并显示一个图例,以指示颜色到类标签的映射。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
# 数据集的散点图,每个类使用不同的颜色 def plot_dataset(X, y): # 为每个类的样本创建散点图 n_classes = len(unique(y)) for class_value in range(n_classes): # 获取具有此类别的样本的行索引 row_ix = where(y == class_value)[0] # 创建这些样本的散点图 pyplot.scatter(X[row_ix, 0], X[row_ix, 1], label=str(class_value)) # 显示图例 pyplot.legend() # 显示图表 pyplot.show() |
最后,我们可以测试这些新函数。
我们将定义一个具有1:100比例的数据集,其中少数类别有1,000个示例,多数类别有10,000个示例,并绘制结果。
完整的示例如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# 定义一个具有1:100类比的不平衡数据集 from numpy import unique from numpy import hstack from numpy import vstack from numpy import where from matplotlib import pyplot from sklearn.datasets import make_blobs # 创建具有给定类分布的数据集 def get_dataset(proportions): # 确定类的数量 n_classes = len(proportions) # 确定为每个类生成的示例数量 largest = max([v for k,v in proportions.items()]) n_samples = largest * n_classes # 创建数据集 X, y = make_blobs(n_samples=n_samples, centers=n_classes, n_features=2, random_state=1, cluster_std=3) # 收集示例 X_list, y_list = list(), list() for k,v in proportions.items(): row_ix = where(y == k)[0] selected = row_ix[:v] X_list.append(X[selected, :]) y_list.append(y[selected]) return vstack(X_list), hstack(y_list) # 数据集的散点图,每个类使用不同的颜色 def plot_dataset(X, y): # 为每个类的样本创建散点图 n_classes = len(unique(y)) for class_value in range(n_classes): # 获取具有此类别的样本的行索引 row_ix = where(y == class_value)[0] # 创建这些样本的散点图 pyplot.scatter(X[row_ix, 0], X[row_ix, 1], label=str(class_value)) # 显示图例 pyplot.legend() # 显示图表 pyplot.show() # 定义类分布1:100 proportions = {0:10000, 1:1000} # 生成数据集 X, y = get_dataset(proportions) # 总结类别分布 major = (len(where(y == 0)[0]) / len(X)) * 100 minor = (len(where(y == 1)[0]) / len(X)) * 100 print('Class 0: %.3f%%, Class 1: %.3f%%' % (major, minor)) # 绘制数据集 plot_dataset(X, y) |
运行示例首先创建数据集并打印类分布。
我们可以看到,数据集中略高于99%的示例属于多数类别,略低于1%的示例属于少数类别。
1 |
类别0:99.010%,类别1:0.990% |
创建了数据集的绘图,我们可以看到每个类别都有更多的示例,并提供了一个有用的图例来指示绘图颜色到类别标签的映射。

具有1比100类分布的二元分类数据集的散点图
接下来,我们可以拟合一个总是预测多数类别的朴素分类器模型。
我们可以使用scikit-learn中的DummyClassifier,并使用“most_frequent”策略,它将始终预测训练数据集中出现最多的类标签。
1 2 3 |
... # 定义模型 model = DummyClassifier(strategy='most_frequent') |
然后我们可以使用重复的k折分层交叉验证在训练数据集上评估这个模型。重要的是我们使用分层交叉验证,以确保数据集的每个分割都具有与训练数据集相同的类别分布。这可以通过使用RepeatedStratifiedKFold 类来实现。
下面的evaluate_model()函数实现了这一点,并返回模型每次评估的分数列表。
1 2 3 4 5 6 7 8 |
# 使用重复k折交叉验证评估模型 def evaluate_model(X, y, metric): # 定义模型 model = DummyClassifier(strategy='most_frequent') # 使用重复分层k折交叉验证评估模型 cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1) scores = cross_val_score(model, X, y, scoring=metric, cv=cv, n_jobs=-1) return scores |
然后我们可以评估模型并计算每次评估分数的平均值。
我们期望朴素分类器能达到约99%的分类准确率,我们知道这一点是因为这是训练数据集中多数类别的分布。
1 2 3 4 5 |
... # 评估模型 scores = evaluate_model(X, y, 'accuracy') # 报告分数 print('Accuracy: %.3f%%' % (mean(scores) * 100)) |
综合起来,在具有1:100类分布的合成数据集上评估朴素分类器的完整示例如下所示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
# 在1:100不平衡数据集上评估多数类分类器 from numpy import mean from numpy import hstack from numpy import vstack from numpy import where from sklearn.datasets import make_blobs from sklearn.dummy import DummyClassifier from sklearn.model_selection import cross_val_score from sklearn.model_selection import RepeatedStratifiedKFold # 创建具有给定类分布的数据集 def get_dataset(proportions): # 确定类的数量 n_classes = len(proportions) # 确定为每个类生成的示例数量 largest = max([v for k,v in proportions.items()]) n_samples = largest * n_classes # 创建数据集 X, y = make_blobs(n_samples=n_samples, centers=n_classes, n_features=2, random_state=1, cluster_std=3) # 收集示例 X_list, y_list = list(), list() for k,v in proportions.items(): row_ix = where(y == k)[0] selected = row_ix[:v] X_list.append(X[selected, :]) y_list.append(y[selected]) return vstack(X_list), hstack(y_list) # 使用重复k折交叉验证评估模型 def evaluate_model(X, y, metric): # 定义模型 model = DummyClassifier(strategy='most_frequent') # 使用重复分层k折交叉验证评估模型 cv = RepeatedStratifiedKFold(n_splits=10, n_repeats=3, random_state=1) scores = cross_val_score(model, X, y, scoring=metric, cv=cv, n_jobs=-1) 返回 分数 # 定义类分布1:100 proportions = {0:10000, 1:1000} # 生成数据集 X, y = get_dataset(proportions) # 总结类别分布 major = (len(where(y == 0)[0]) / len(X)) * 100 minor = (len(where(y == 1)[0]) / len(X)) * 100 print('Class 0: %.3f%%, Class 1: %.3f%%' % (major, minor)) # 评估模型 scores = evaluate_model(X, y, 'accuracy') # 报告分数 print('Accuracy: %.3f%%' % (mean(scores) * 100)) |
运行示例首先再次报告训练数据集的类别分布。
然后评估模型并报告平均准确率。我们可以看到,正如预期的那样,朴素分类器的性能与类别分布完全匹配。
通常,达到99%的分类准确率是值得庆祝的。然而,正如我们所见,由于类别分布不平衡,99%实际上是该数据集可接受的最低准确率,也是更复杂模型必须在此基础上改进的起点。
1 2 |
类别0:99.010%,类别1:0.990% 准确率:99.010% |
进一步阅读
如果您想深入了解,本节提供了更多关于该主题的资源。
教程
论文
- 不平衡分布下预测建模的调查, 2015.
- 《不平衡分类问题集成方法综述:Bagging-、Boosting-和混合方法》, 2011.
书籍
- 不平衡学习:基础、算法与应用 (Imbalanced Learning: Foundations, Algorithms, and Applications), 2013.
- 从不平衡数据集中学习 (Learning from Imbalanced Data Sets), 2018.
API
- sklearn.datasets.make_blobs API.
- sklearn.dummy.DummyClassifier API.
- sklearn.model_selection.RepeatedStratifiedKFold API.
文章
总结
在本教程中,您发现了分类准确率在不平衡分类问题上的失效。
具体来说,你学到了:
- 准确率和错误率是总结分类模型性能的实际标准指标。
- 分类准确率在具有倾斜类分布的分类问题上失效,原因是从业者在具有相等类分布的数据集上形成的直觉。
- 对倾斜类分布导致准确率失效的直觉,附带一个工作示例。
你有什么问题吗?
在下面的评论中提出你的问题,我会尽力回答。
对问题解释得很好。请求一个“解决方案”:当训练集在预测变量上存在显著不平衡时,应该怎么做(例如,欺诈检测)……
有很多方法可以尝试,例如:
- 数据采样
- 定制算法
- 成本敏感算法
- 单类别算法
- 阈值移动
- 概率校准
– ……
我很快会提供一个关于该主题的框架。
你好 Jason,
新年快乐!
在之前关于如何对机器学习和深度学习算法进行分类的帖子中,您提到如果我使用Scikit-Learn,那就是机器学习;如果我使用Keras,那就是深度学习。
那么这样说对吗:
使用Scikit-Learn解决分类问题(例如IRIS)是机器学习?
使用Keras解决分类问题(例如IRIS)是深度学习?
谢谢,
Marco
当然可以。
感谢您这篇精彩的帖子,Jason。
在“不平衡分类中准确率的示例”部分下的第三段代码中,您提到使用1:100的类别比率,“少数类别有1,000个示例,多数类别有10,000个示例”。然而,这实际上是1:10的类别比率。结果打印输出然后不正确,绘图也不正确……
谢谢,已修复!
抱歉,这可能听起来很愚蠢。使用虚拟分类器的目的是什么?
我是否可以这样说,我们也可以通过使用虚拟分类器来获得与原始结果相同的结果?因此,原始结果的准确性是有缺陷的?
谢谢
很好的问题!
虚拟分类器为给定数据集建立性能基线。
https://machinelearning.org.cn/how-to-develop-and-evaluate-naive-classifier-strategies-using-probability/
如果一个模型的性能无法优于虚拟分类器(一个朴素模型),那么它在这个数据集上就没有“技能”。
感谢Jason的回复!不胜感激。总之,我真的很喜欢您的页面和内容。感谢您的付出!
谢谢你的支持!
对于多标签不平衡问题,哪些指标效果最好?
二元分类任务的相同指标也可以用于相同的目的。
嗨,Jason,
这是一篇很棒的帖子!我想知道我们以何种比例来判断数据集是否不平衡?因为您使用的是1:100的极端情况。那么40:60或30:70呢?我们也应该使用其他指标吗?谢谢!
谢谢。
是的,很有可能。这个框架将帮助您选择一个指标。
https://machinelearning.org.cn/tour-of-evaluation-metrics-for-imbalanced-classification/
感谢您的回答,Jason!那么这是否意味着“准确率”这个指标用途非常有限,因为在现实世界中,我们拥有精确的50%:50%对照数据集的情况非常罕见。对吗?
是的!
谢谢Jason。非常棒的帖子。
谢谢!
通常,分类预测建模在类别分布相等或非常接近“相等”的小数据集上进行实践。有错别字吗?
怎么会呢?
嗨,Jason,
感谢您的帖子!我有一个问题……我有一个不平衡的数据集,我所做的是通过对训练数据集中的少数类别进行过采样来解决类别不平衡问题,然后用过采样后的训练数据集来训练我的数据……我的问题是:我是否也应该处理测试数据集中的类别不平衡问题?还是只在测试数据集上运行模型即可?
我感觉我不应该更改测试数据集中的任何内容,但想知道不平衡的测试数据集是否也会影响准确率……
一些背景信息:我正在使用简单的逻辑回归
再次感谢
不,你绝不能改变测试数据集的平衡性。
当文本说“我们可以看到数据集中略高于90%的示例属于多数类别,略低于1%的示例属于少数类别”时,作者是不是想说“略高于99%”?
是的,谢谢。已修正。
你好Jason,感谢您的精彩帖子,我认为比例应该是
# 定义类分布1:100
proportions = {0:10000, 1:100}
谢谢
谢谢。
增加类别数量会影响分类准确率吗?例如,将数据分类为三类,其中两类与特定领域相关,一类与另一领域相关,已知研究领域讨论的是与一个领域相关的两类。
是的,肯定会。你应该会看到准确率下降。获得更多选项意味着随机猜测正确的可能性会降低。
如果我有一个包含大约150个标签的多类别数据集,每个标签的样本数量各不相同。总样本数大约为10000个。有些标签可能有400个样本,有些甚至不到10个样本。毫无疑问,使用单个分类器(使用不同的分类器)或投票分类器获得的准确率会超过99%。根据您的文章,在这种每个标签样本数量不同的情况下,所获得的准确率不是一个合适的衡量标准。您在这种情况下建议使用什么方法或衡量标准?其次,在这种情况下,我如何获得单个标签的准确率?如果您能提供带代码的回复,我将不胜感激。
你好 Vijay……以下内容可能对您有帮助
https://machinelearning.org.cn/model-prediction-versus-interpretation-in-machine-learning/