为什么不平衡分类如此困难?
由于类别分布严重偏差,不平衡分类作为预测建模任务主要具有挑战性。
这就是传统机器学习模型和假设平衡类别分布的评估指标表现不佳的原因。
然而,分类数据集的其他属性不仅对预测建模具有挑战性,而且还会增加或加剧建模不平衡数据集时的难度。
在本教程中,您将发现加剧不平衡分类挑战的数据特征。
完成本教程后,您将了解:
- 由于类别分布严重偏斜和错误分类成本不平等,不平衡分类尤其困难。
- 数据集大小、标签噪声和数据分布等特性加剧了分类不平衡的难度。
- 如何直观地了解不同数据集属性对建模难度产生的复合影响。
教程概述
本教程分为四个部分:
- 为何不平衡分类如此困难
- 数据集规模的复合效应
- 标签噪声的复合效应
- 数据分布的复合效应
为何不平衡分类如此困难
不平衡分类是由具有偏斜类别分布的数据集定义的。
这通常以二元(两类)分类任务为例,其中大多数示例属于类 0,只有少数示例属于类 1。分布的严重程度范围可能为 1:2、1:10、1:100 甚至 1:1000。
由于类别分布不平衡,大多数机器学习算法都会表现不佳,需要进行修改以避免在所有情况下都简单地预测多数类别。此外,分类等指标失去了意义,需要使用替代方法来评估不平衡示例的预测,例如ROC 曲线下面积。
这是分类不平衡的基本挑战。
- 倾斜类别分布
额外的复杂程度来自于示例所来自的问题领域。
多数类别通常表示领域中的正常情况,而少数类别则表示异常情况,例如故障、欺诈、异常值、异常、疾病状态等。因此,对误分类错误的解释可能因类别而异。
例如,将来自多数类别的样本错误地归类为来自少数类别的样本(称为假阳性)通常是不希望的,但是与将来自少数类别的样本归类为属于多数类别(称为假阴性)相比,其严重性较低。
这被称为错误分类错误的成本敏感性,也是不平衡分类的第二个基本挑战。
- 错误分类错误的成本不平等
在描述不平衡分类的难度时,通常会参考这两个方面,即偏斜的类别分布和成本敏感性。
然而,分类问题还有其他特点,当与这些特性相结合时,其效果会加剧。这些是分类预测模型的一般特征,它们会放大不平衡分类任务的难度。
普遍认为类别不平衡是分类的一个复杂因素。然而,一些研究也认为,不平衡率并不是导致从不平衡数据中学习时性能下降的唯一原因。
此类特征有很多,但最常见的可能包括以下三个:
- 数据集大小。
- 标签噪音。
- 数据分布。
不仅要承认这些特性,还要特别培养对其影响的直觉,这一点很重要。这将使您能够在自己的预测建模项目中选择和开发解决这些问题的技术。
了解这些数据的内在特征及其与类别不平衡的关系,对于应用现有技术和开发新技术处理不平衡数据至关重要。
数据集规模的复合效应
数据集大小只是指从域中收集的用于拟合和评估预测模型的示例数量。
通常,数据越多越好,因为它可以提供更多的领域覆盖,但也许会达到收益递减的程度。
具体来说,更多的数据可以更好地表示特征空间中的特征组合和方差以及它们到类标签的映射。由此,模型可以更好地学习和概括类边界,以区分未来的新示例。
如果多数类与少数类的示例比例是固定的,那么我们预计,随着数据集规模的扩大,少数类中的示例会增多。
如果我们能够收集更多的例子那就好了。
这通常是一个问题,因为数据很难收集或收集成本高昂,而且我们收集和使用的数据通常比我们希望的少得多。因此,这会极大地影响我们从少数类中获得足够大或具有代表性的样本的能力。
分类中经常出现的一个问题是训练实例数量太少。这个问题通常被称为数据稀缺或缺乏数据,与“密度不足”或“信息不足”有关。
例如,对于具有平衡类别分布的中等分类任务,我们可能只需要数千或数万个示例即可开发、评估和选择模型。
具有 10,000 个示例的平衡二元分类将有 5,000 个每个类别的示例。具有 1:100 分布且示例数量相同的不平衡数据集将只有 100 个少数类别的示例。
因此,数据集的大小会极大地影响不平衡分类任务,而通常被认为很大的数据集在处理不平衡分类问题时实际上可能不够大。
如果没有足够大的训练集,分类器可能无法概括数据的特征。此外,分类器还可能过度拟合训练数据,导致在样本外测试实例中表现不佳。
为了提供帮助,让我们通过一个实际的例子来具体说明这一点。
我们可以使用make_classification() scikit-learn 函数创建一个给定大小的数据集,其中少数类与多数类的示例比例约为 1:100(1% 到 99%)。
...
# create the dataset
X, y = make_classification(n_samples=1000, n_features=2, n_redundant=0,
n_clusters_per_class=1, weights=[0.99], flip_y=0, random_state=1)
然后,我们可以创建数据集的散点图,并用分隔颜色为每个类的点着色,以了解示例的空间关系。
...
# 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()
然后可以使用不同大小的数据集重复此过程,以直观地展示类别不平衡的影响。我们将比较包含 100、1,000、10,000 和 100,000 个示例的数据集。
完整的示例如下。
# vary the dataset size for a 1:100 imbalanced dataset
from collections import Counter
from sklearn.datasets import make_classification
from matplotlib import pyplot
from numpy import where
# dataset sizes
sizes = [100, 1000, 10000, 100000]
# create and plot a dataset with each size
for i in range(len(sizes)):
# determine the dataset size
n = sizes[i]
# create the dataset
X, y = make_classification(n_samples=n, n_features=2, n_redundant=0,
n_clusters_per_class=1, weights=[0.99], flip_y=0, random_state=1)
# summarize class distribution
counter = Counter(y)
print('Size=%d, Ratio=%s' % (n, counter))
# define subplot
pyplot.subplot(2, 2, 1+i)
pyplot.title('n=%d' % n)
pyplot.xticks([])
pyplot.yticks([])
# 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()
# show the figure
pyplot.show()
运行该示例将创建并绘制使用四种不同大小的具有 1:100 类分布的相同数据集。
首先,显示每个数据集大小的类别分布。我们可以看到,对于包含 100 个示例的小数据集,我们只能得到一个少数类示例,正如我们预期的那样。即使数据集中有 100,000 个示例,我们也只能得到少数类中的 1,000 个示例。
Size=100, Ratio=Counter({0: 99, 1: 1})
Size=1000, Ratio=Counter({0: 990, 1: 10})
Size=10000, Ratio=Counter({0: 9900, 1: 100})
Size=100000, Ratio=Counter({0: 99000, 1: 1000})
为每个不同大小的数据集创建散点图。
我们可以看到,直到样本量非常大时,类别分布的底层结构才会变得明显。
这些图突显了数据集大小在分类不平衡中发挥的关键作用。很难想象一个模型在给出 990 个多数类样本和 10 个少数类样本后,如何能够在绘制 100,000 个样本后描绘的同一问题上取得良好效果。
标签噪声的复合效应
标签噪声是指属于一个类但被分配给另一个类的例子。
这可能会使大多数机器学习算法难以确定特征空间中的类边界,并且这种难度通常会随着标签中噪声的百分比而增加。
文献中区分了两种类型的噪声:特征(或属性)噪声和类噪声。在 ML 中,通常认为类噪声比属性噪声更有害 […] 类噪声以某种方式影响观察到的类值(例如,以某种方式将少数类实例的标签翻转为多数类标签)。
其原因往往在于问题领域本身,比如对类边界的模糊观察,甚至数据收集中的错误,这些错误都可能影响特征空间中任何地方的观察。
对于不平衡的分类,噪声标签的影响更为显著。
鉴于正类中的例子非常少,一些噪音的丢失会减少有关少数类的可用信息量。
此外,将多数类别的例子错误地标记为属于少数类别,会导致由于缺乏观察而本来就稀疏的少数类别脱节或分裂。
我们可以想象,如果类别边界上存在模糊的示例,我们可以识别并删除或纠正它们。标记为少数类的示例位于特征空间中多数类的高密度区域中,也可能很容易识别并删除或纠正。
当两个类的观测值在特征空间中都很稀疏时,这个问题通常会变得特别困难,尤其是对于不平衡的分类。在这些情况下,未经修改的机器学习算法将以牺牲少数类为代价来定义有利于多数类的类边界。
错误标记的少数类实例将增加感知不平衡率,并在少数类的类区域内引入错误标记的噪声实例。另一方面,错误标记的多数类实例可能会导致学习算法或不平衡处理方法关注输入空间的错误区域。
我们可以举一个例子来体现这一挑战。
我们可以保持数据集大小不变,类比例为 1:100,并改变标签噪声量。这可以通过将make_classification()函数的“ flip_y ”参数设置为常量来实现,该参数是每个类中用于更改或翻转标签的示例数量的百分比。
我们将探索将其从 0%、1%、5% 和 7% 进行变化。
完整的示例如下。
# vary the label noise for a 1:100 imbalanced dataset
from collections import Counter
from sklearn.datasets import make_classification
from matplotlib import pyplot
from numpy import where
# label noise ratios
noise = [0, 0.01, 0.05, 0.07]
# create and plot a dataset with different label noise
for i in range(len(noise)):
# determine the label noise
n = noise[i]
# create the dataset
X, y = make_classification(n_samples=1000, n_features=2, n_redundant=0,
n_clusters_per_class=1, weights=[0.99], flip_y=n, random_state=1)
# summarize class distribution
counter = Counter(y)
print('Noise=%d%%, Ratio=%s' % (int(n*100), counter))
# define subplot
pyplot.subplot(2, 2, 1+i)
pyplot.title('noise=%d%%' % int(n*100))
pyplot.xticks([])
pyplot.yticks([])
# 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()
# show the figure
pyplot.show()
运行该示例将使用四种不同数量的标签噪声创建并绘制具有 1:100 类分布的相同数据集。
首先,打印每个数据集的类别分布,其中包含不同数量的标签噪声。我们可以看到,正如我们所料,随着噪声的增加,少数类中的示例数量会增加,其中大多数都是错误标记的。
我们可能预计,少数类中的这些额外的 30 个示例带有 7% 的标签噪声,对于试图在特征空间中定义清晰的类边界的模型来说,这将造成很大的损害。
Noise=0%, Ratio=Counter({0: 990, 1: 10})
Noise=1%, Ratio=Counter({0: 983, 1: 17})
Noise=5%, Ratio=Counter({0: 963, 1: 37})
Noise=7%, Ratio=Counter({0: 959, 1: 41})
为每个具有不同标签噪声的数据集创建散点图。
在这个特定案例中,我们没有看到很多类别边界上的混淆示例。相反,我们可以看到,随着标签噪声的增加,少数类(蓝色区域中的橙色点)中的示例数量增加,这代表了在建模之前应该从数据集中识别和删除的误报。
具有不同标签噪声的不平衡分类数据集的散点图
数据分布的复合效应
另一个重要的考虑因素是特征空间中示例的分布。
如果我们从空间角度考虑特征空间,我们可能希望一个类中的所有示例都位于空间的一部分,而另一个类中的所有示例则出现在空间的另一部分。
如果是这样,我们就有良好的类别可分性,机器学习模型可以绘制清晰的类别边界并获得良好的分类性能。这适用于类别分布平衡或不平衡的数据集。
这种情况很少见,更有可能的是每个类都有多个“概念”,从而导致特征空间中出现多个不同的示例组或聚类。
…通常,一个类别下的“概念”会被分成几个子概念,分布在输入空间中。
这些组正式称为“分离项”,来自基于规则的系统的定义,即涵盖由子概念组成的一组案例的规则。小分离项是指与训练数据集中的少数示例相关或“涵盖”的分离项。
从示例中学习的系统通常无法为每个概念创建纯粹的连接定义。相反,它们会创建一个由多个析取项组成的定义,其中每个析取项都是原始概念的子概念的连接定义。
这种分组使类可分性变得困难,需要隐式或显式地识别每个组或聚类并将其纳入类边界的定义中。
对于不平衡的数据集,如果少数类在特征空间中有多个概念或聚类,则这是一个特殊的问题。这是因为此类中的示例密度已经很稀疏,很难用如此少的示例辨别出单独的分组。它可能看起来像一个巨大的稀疏分组。
这种缺乏同质性在基于分而治之策略的算法中尤其成问题,其中子概念导致小分离项的产生。
— 第 255 页,从不平衡数据集中学习,2018 年。
例如,我们可能会考虑描述患者是健康(多数类)还是患病(少数类)的数据。数据可能包含许多不同类型的疾病,并且可能有类似疾病的组,但如果病例太少,那么类内的任何分组或概念可能都不明显,可能看起来像是与健康病例混合在一起的分散集。
为了具体说明这一点,我们可以看一个例子。
我们可以使用数据集中的聚类数量作为“概念”的代理,并将每个类别有一个示例聚类的数据集与每个类别有两个聚类的第二个数据集进行比较。
这可以通过改变用于创建数据集的make_classification()函数的“ n_clusters_per_class ”参数来实现。
我们预计,在不平衡的数据集中,例如 1:100 的类别分布,聚类数量的增加对于多数类别来说是明显的,但对于少数类别来说却并非如此。
完整的示例如下。
# vary the number of clusters for a 1:100 imbalanced dataset
from collections import Counter
from sklearn.datasets import make_classification
from matplotlib import pyplot
from numpy import where
# number of clusters
clusters = [1, 2]
# create and plot a dataset with different numbers of clusters
for i in range(len(clusters)):
c = clusters[i]
# define dataset
X, y = make_classification(n_samples=10000, n_features=2, n_redundant=0,
n_clusters_per_class=c, weights=[0.99], flip_y=0, random_state=1)
counter = Counter(y)
# define subplot
pyplot.subplot(1, 2, 1+i)
pyplot.title('Clusters=%d' % c)
pyplot.xticks([])
pyplot.yticks([])
# 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()
# show the figure
pyplot.show()
运行该示例将使用两个不同数量的聚类创建并绘制具有 1:100 类分布的相同数据集。
在第一个散点图(左)中,我们可以看到每个类别都有一个聚类。多数类别(蓝色)显然有一个聚类,而少数类别(橙色)的结构不太明显。在第二个散点图(右)中,我们可以再次清楚地看到多数类别有两个聚类,少数类别(橙色)的结构也很分散,并且样本来自两个聚类并不明显。
这凸显了数据集的大小与其揭示少数类样本的密度或分布之间的关系。样本如此之少,机器学习模型的泛化即使不是非常困难,也是具有挑战性的。
具有不同聚类数的不平衡分类数据集的散点图
关注微信公众号,获取源代码,了解更多内容。