Learning from Imbalanced Classes

Posted by Qing on March 3, 2019

最近参加了一次阿里算法岗的笔试,其有一个项目:目标变量极端不平衡(Imbalance)分布下的分类预测,这种极端分布在现实生活中其实很常见,比如欺诈、疾病诊断,在这种情况下,如何得到一个不错的分类器?

一般刚开始接触机器学习的人,接触到的数据都是非常干净,非常平衡的数据,比如以下的二分类数据:

分类算法的目标一般是学习一种分类方法,能将上面两种数据分开,通用的分类算法即可实现此目标,比如Logisitic Regression, SVM, Random Forest, Adaboost, GBDT, KNN,不同的算法学习到到分类边界不一样:

但是当你在现实中碰到此类问题时,最先注意到的通常是数据非常不干净,目标变量分布非常不平衡,比如下面这样:

其中最主要的问题是分类变量极端不平衡:红色的点相对于蓝色的点非常少

研究表明,不平衡到数据为少数分类占比在10%到20%,现实中到极端分布占比可能更少。

  • 每年约有2%的信用卡账户受到欺诈1。 (大多数欺诈检测域严重失衡。)
  • 通常对没有该病症的大量人群进行疾病的医学筛查,以检测其中的少数人(例如,美国的HIV患病率为~0.4%)。
  • 磁盘驱动器故障每年约为1%。
  • 在线广告的转化率估计在10-3到10-6之间。
  • 工厂生产缺陷率通常约为0.1%。

这些领域中许多都是不平衡的,机器学习分类器用于对大量负面(无趣)案例进行分类,以找到少量积极的案例。

遇到这些问题时,使用标准算法解决问题肯定会遇到困难。 传统算法通常偏向大多数类,因为它们的损失函数(Loss Function)试图优化比如错误率之类的数量,而不考虑原始数据的分布。

在最坏的情况下,占少数的分类被视为多数类的异常值而被忽略。学习算法简单地生成一个看似准确度很高的简单分类器 - 将每个示例分类为多数类

这可能看起来像病态行为,实际上,如果目标是最大限度地提高简单准确度(或者等同于最小化错误率),那么这种行为是一个完全可以接受的解决方案。但是,如果我们假设稀有类示例对于分类更为重要,那么必须更加小心并且更加复杂地解决这个问题

处理不平衡的数据

怎样学习不平衡数据在机器学习的许多学术会议以及研讨会上是一个特别的主题,研究者尝试了多种方法,结果却各不相同,答案也很少。数据科学家第一次面临这个问题时该怎么办呢?对于一般问题哪种学习算法最好的原因相同,但是这里没有明确的答案,因为答案取决于数据。

下面是有一些通常有用的方法。

  • 什么都不做。有时候很幸运,不需要做什么,就可以使用所谓的自然(或分层)分布进行训练。
  • 在数据层面上,以某种方法平衡训练集:
    • 过度采样少数分类。
    • 对多数分类进行抽样。
    • 合成新的少数分类。
    • 抛弃一定少数分类,转而采用异常检测框架。
  • 在算法层面上:
    • 调整样本权重(调整错误分类的损失)。
    • 调整决策阈值。
    • 修改现有算法以对稀有类更敏感。
  • 构建一个全新的算法,从而在不平衡数据上表现良好

模型评估中的注意事项

在讨论如何利用不平衡数据训练分类器之前,首先需要讨论如何正确评估分类器。这一点怎么强调都不过分。只有做到正确评估一个模型,才能取得比较好的效果

  1. 不要使用准确度(或错误率)来评估分类器!它有个重要问题:准确性使用0.5的阈值来决定给分类,当类别不平衡的时候,通常是错误的。分类的准确性基于错误的简单计数,在极端不平衡的情况下(如正类占比0.1%,负类占比99.9%),模型仅需将所有的样本预测为负类,模型的准确度就会达到99.9%,但是这样的模型在实际应用中是没有任何意义的。在这种情况下,我们应该使用ROC曲线,PR(precision-recall)曲线 或者提升图(Lift Plot)来准确评估模型的性能。ROC 至于哪种评估方法更好,一般来看我们在极端类别不平衡中更在意“少数的类别”,因此ROC不像precision-recall curve那样更具有吸引力。在这种情况下,Precision-recall curve不失为一种好的评估标准。还有一种做法是,仅分析ROC曲线左边的一小部分,从这个角度看和precision-recall curve有很高的相似性。
  2. 不要直接分类器中获得直接分类(标签)(分类器通常采用0.5作为判别阈值,在极端不平衡分类中,概率估计通常都低于0.5,直接用模型产生的分类会获得全为同一分类的结果)。相反,通过probapredict_proba获得概率估计
  3. 获得概率估计后,不要盲目地用0.50决策阈值来分类。需要查看ROC曲线并找出最佳的决定阈值
  4. 无论做什么样训练,都需要在原始样本分布(sklearn.cross_validation.StratifiedKFold.)上做测试

图像要比数字更有说服力,但是如果需要使用单一的数字作为评判标准,下面的统计量会比准确度要好得多:

  • ROC曲线下面积(AUC)是一个很好的一般统计量。它等于随机正例将在随机负例之上排名的概率。
  • F1分数是精度和召回的调和平均值。

过采样和欠采样

Oversampling and undersampling。 过采样随机复制少数情况用来增加样本数。欠采样随机降采样大多数类,减少其样本数。一些数据科学家认为过采样是优越的,因为它会产生更多数据,而欠采样会丢弃数据。但是,复制数据会导致重复数据,这让变量看起来比它们具有更低的方差。相反,欠采样让自变量看起来比原来具有更高的方差。至于采用过采样还是欠采样好,需要具体情况具体分析。

大多数机器学习包中包括有采样方法,比如scikit-learn.cross_validation中有基础的采样设置。这里着重介绍Python库imbalanced-learn,它提供了常用的重新采样技术,而且与scikit-learn兼容,其API接口非常简单方便。常用的降采样方法(随机欠采样, Tomek’s links),过采样方法(随机过采样,SMOTE)等等在这个库里面都有实现。

调整分类权重

Scikit-learn中的许多分类器都有办法调整类的“重要性”,它们采用可设置为高于1的可选class_weight参数。下面这个例子直接来自scikit-learn文档,显示了将少数分类的权重增加10倍的效果。实线黑色线显示分隔边框(两个类均等),而少数(红色)类的class_weight参数后的虚线更改为10。

可以看到少数类别的重要性增加(因为它的错误被认为比其他类别的错误更昂贵)并且调整分离超平面以减少损失。

改变分类样本的权重通过提高少数分类的损失,减少多数分类的损失,简介影响学习的方向,在Scikit-learn中设置可选参数 class_weight="balanced"可以自动调节样本的权重。

异常侦测

在更极端的情况下,可以考虑将分类问题考虑成异常检测(anomaly detection)问题。在异常检测问题中,我们假设有一个或一组“正常”的数据点分布,而任何与该分布足够偏离的东西都是异常值。将分类问题置于异常检测的框架下以后,我们将主要类别视为点的“正常”分布,将少数类别视为异常。有许多用于异常检测的算法,例如聚类(clustering)方法,单类SVM(One-class SVM)和孤立森林(Isolation Forests)。

FAQ

  • 不均衡数据,采样是在划分数据集(train/valid/test)之前做合适,还是在划分之后呢?

    采样应该在划分数据集之后。做离线测试有个基本原则,测试(验证)数据集一定要尽可能地接近真实环境的数据分布。 因此,test集和validation集应该不做采样,因为test和validation是代表了真实世界的不均衡分布。采样是为了更好的建模,所以应该在train上采样。因为除了训练集(这里包括训练集全集和为了划分验证集之后的本地训练集)之外,其他都不应该做采样。

总结

不平衡样本在真实数据中是一个痛点,针对这一问题,我们可以在数据层面(过采样/欠采样)和算法层面(调节权重/改进算法)进行优化,但是具体到项目层面,针对什么样的数据,需要采用什么样的模型方法,还需要做进一步的探索。

参考文档

Learning from Imbalanced Classes - TOM FAWCETT

如何处理数据中的「类别不平衡」?