绿色健康小清新

耐得住寂寞,守得住繁华

不平衡数据的介绍和处理(Imbalanced Datasets)

不平衡数据(Imbalanced Datasets)

所谓的不平衡数据集指的是数据集各个类别的样本量极不均衡。以二分类问题为例,假设正类的样本数量远大于负类的样本数量,通常情况下通常情况下把多数类样本的比例接近100:1这种情况下的数据称为不平衡数据。不平衡数据的学习即需要在分布不均匀的数据集中学习到有用的信息。

不平衡数据集的处理方法主要分为两个方面:

  1. 从数据的角度出发,主要方法为采样,分为欠采样和过采样以及对应的一些改进方法。

  2. 从算法的角度出发,考虑不同误分类情况代价的差异性对算法进行优化,主要是基于代价敏感学习算法(Cost-Sensitive Learning),代表的算法有adacost;

另外可以将不平衡数据集的问题考虑为一分类(One Class Learning)或者异常检测(Novelty Detection)问题,代表的算法有One-class SVM。

分类的例子

在这个例子中,我们要求进行分类,左边是红色的,右边是蓝色的。这里有两种分类方法A和B;

  • A:采用A分类法,正确率达到了95%
  • B:采用B分类法,正确率达到了90%

A分类法的正确率 > B分类法的正确率,那是不是说A分类法比B分类法好呢?我们可以用G-mean指标来看一下$$G-mean = \sqrt[]{Recall*Specificity}$$

对于A分类法,因为我们分类的左边并没有任何数据,所以此时的Specificity很明显是0,即准确度是0。即使你右边准确度再高,G-mean依然是0。

对不平衡数据的处理

朴素随机过采样(上采样,over-sampling)

针对不平衡数据, 最简单的一种方法就是生成少数类的样本, 这其中最基本的一种方法就是: 从少数类的样本中进行随机采样来增加新的样本。

对应Python库中函数为RandomOverSampler:

1
2
3
4
from imblearn.over_sampling import RandomOverSampler

ros = RandomOverSampler(random_state=0)
X_resampled, y_resampled = ros.fit_sample(X, y)

朴素随机欠采样(下采样,under-sampling)

与过采样相反,欠采样是从多数类样本中随机选择少量样本,再合并原有少数类样本作为新的训练数据集。

随机欠采样有两种类型分别为有放回和无放回两种,无放回欠采样在对多数类某样本被采样后不会再被重复采样,有放回采样则有可能。

对应Python库中函数为RandomUnderSampler,通过设置RandomUnderSampler中的replacement=True参数, 可以实现自助法(boostrap)抽样。

1
2
3
4
from imblearn.under_sampling import RandomUnderSampler

rus=RandomUnderSampler(random_state=0,replacement=True)
x_resample,y_resample=rus.fit_sample(x,y)

随机采样的优缺点

随机采样最大的优点是简单,但缺点也很明显。

  • 上采样后的数据集中会反复出现一些样本,训练出来的模型会有一定的过拟合;
  • 而下采样的缺点显而易见,那就是最终的训练集丢失了数据,模型只学到了总体模式的一部分。

上采样会把小众样本复制多份,一个点会在高维空间中反复出现,这会导致一个问题,那就是运气好就能分对很多点,否则分错很多点。

为了解决这一问题,可以在每次生成新数据点时加入轻微的随机扰动,经验表明这种做法非常有效,但是这一方式会加重过拟合。

过采样的改进:SMOTE与ADASYN

相对于采样随机的方法进行过采样, 还有两种比较流行的过采样的改进方式:

  1. Synthetic Minority Oversampling Technique(SMOTE)

  2. Adaptive Synthetic (ADASYN)


SMOTE

SMOTE算法的基本思想是对少数类样本进行分析并根据少数类样本人工合成新样本添加到数据集中,具体下图所示,算法流程如下:

  1. 对于少数类中每一个样本x,计算该点与少数类中其他样本点的距离,得到最近的k个近邻(即对少数类点进行KNN算法)。
  2. 根据样本不平衡比例设置一个采样比例以确定采样倍率,对于每一个少数类样本x,从其k近邻中随机选择若干个样本,假设选择的近邻为x’。
  3. 对于每一个随机选出的近邻x’,分别与原样本按照如下的公式构建新的样本:

xnew=x+rand(0,1)(xx)x_{new}=x+rand(0,1) ∗ (x^′−x)

但是SMOTE算法缺点也十分明显:一方面是增加了类之间重叠的可能性(由于对每个少数类样本都生成新样本,因此容易发生生成样本重叠(Overlapping)的问题),

另一方面是生成一些没有提供有益信息的样本。

对应Python库中函数为SMOTE:

1
2
3
from imblearn.over_sampling import SMOTE

X_resampled_smote, y_resampled_smote = SMOTE().fit_sample(X, y)

SMOTE的改进:Borderline-SMOTE

Borderline-SMOTE与原始SMOTE不同的地方在于,原始的SMOTE是对所有少数类样本生成新样本。而改进的方法则是先根据规则判断出少数类的边界样本,再对这些样本生成新样本。

判断边界的一个简单的规则为:K近邻中有一半以上多数类样本的少数类为边界样本。直观地讲,只为那些周围大部分是多数类样本的少数类样本生成新样本。

假设a为少数类中的一个样本,此时少数类的样本分为三类,如下图所示:

  1. 噪音样本(noise), 该少数类的所有最近邻样本都来自于不同于样本a的其他类别:
  2. 危险样本(in danger), 至少一半的最近邻样本来自于同一类(不同于a的类别);
  3. 安全样本(safe), 所有的最近邻样本都来自于同一个类。

对应的Python库中的实现有三种可以选择的规则:

SMOTE函数中的kind参数控制了选择哪种规则:

  1. borderline1:最近邻中的随机样本与该少数类样本a来自于不同的类;

  2. borderline2:最近邻中的随机样本可以是属于任何一个类的样本;

  3. svm:使用支持向量机分类器产生支持向量然后再生成新的少数类样本。

具体实现如下:

1
2
3
4
from imblearn.under_sampling import ClusterCentroids

cc = ClusterCentroids(random_state=0)
X_resampled, y_resampled = cc.fit_sample(X, y)

ADASYN

这种改进方法的主要思想是根据数据分布情况为不同的少数类样本生成不同数量的新样本。首先根据最终的平衡程度设定总共需要生成的新少数类样本数量 ,然后为每个少数类样本x计算分布比例。

对应Python库中函数为ADASYN:

1
2
3
from imblearn.over_sampling import ADASYN

X_resampled_adasyn, y_resampled_adasyn = ADASYN().fit_sample(X, y)

基于聚类的过采样方法

以二分类为例,该方法是首先分别对正负例进行聚类,在聚类之后进行再进行上述的过采样方法。例如:有一个二分类数据集,其正负类比例为:1000:50。首先通过kmeans算法对正负类分别聚类,

得到正类:600,300,100;负类:30,20。然后使用过采样的方法分别对所有类别进行过采样得到正类:600,600,600;对于负类的上采样个数为:(600+600+600)/2 = 900,即负类为:900,900。

最终得到的数据集为正类1800,负类1800。基于聚类的过采样方法的优点是不仅可以解决类间不平衡问题,而且还能解决类内部不平衡问题。

欠采样的改进:EasyEnsemble、BalanceCascade与NearMiss

随机欠采样的问题主要是信息丢失,为了解决信息丢失的问题提出了以下几种改进的方式:

EasyEnsemble,利用模型融合的方法(Ensemble)

多次过采样(放回采样,这样产生的训练集才相互独立)产生多个不同的训练集,进而训练多个不同的分类器,通过组合多个分类器的结果得到最终的结果。简单的最佳实践是建立n个模型,每个模型使用少数类的所有样本和多数类的n个不同样本。假设二分类数据集的正负类比例为50000:1000,最后要得到10个模型,那么将保留负类的1000个样本,并随机采样得到10000个正类样本。
然后,将10000个样本成10份,每一份与负类样本组合得到新的子训练集,训练10个不同的模型。

EasyEnsemble方法对应Python库中函数为EasyEnsemble,有两个很重要的参数:

  1. n_subsets控制的是子集的个数
  2. replacement决定是有放回还是无放回的随机采样
1
2
3
4
from imblearn.ensemble import EasyEnsemble

ee = EasyEnsemble(random_state=0, n_subsets=10)
X_resampled, y_resampled = ee.fit_sample(X, y)

BalanceCascade,利用增量训练的思想(Boosting)

先通过一次下采样产生训练集,训练一个分类器,对于那些分类正确的多数类样本不放回,然后对这个更小的多数类样本下采样产生训练集,训练第二个分类器,以此类推,最终组合所有分类器的结果得到最终结果。

BalanceCascade方法对应Python库中函数为BalanceCascade,有三个很重要的参数:

  1. estimator是选择使用的分类器
  2. n_max_subset控制的是子集的个数
  3. bootstrap决定是有放回还是无放回的随机采样
1
2
3
4
5
6
7
from imblearn.ensemble import BalanceCascade
from sklearn.linear_model import LogisticRegression

bc = BalanceCascade(random_state=0,
           estimator=LogisticRegression(random_state=0),
           n_max_subset=4)
X_resampled, y_resampled = bc.fit_sample(X, y)

NearMiss,利用KNN试图挑选那些最具代表性的多数类样本

首先计算出每个样本点之间的距离,通过一定规则来选取保留的多数类样本点。因此该方法的计算量通常很大。

NearMiss方法对应Python库中函数为NearMiss,通过version来选择使用的规则:

  1. NearMiss-1:选择离N个近邻的负样本的平均距离最小的正样本;

  2. NearMiss-2:选择离N个负样本最远的平均距离最小的正样本;

  3. NearMiss-3:是一个两段式的算法。 首先,对于每一个负样本, 保留它们的M个近邻样本;接着, 那些到N个近邻样本平均距离最大的正样本将被选择。

1
2
3
4
from imblearn.under_sampling import NearMiss 

nm1 = NearMiss(random_state=0, version=1)
X_resampled_nm1, y_resampled = nm1.fit_sample(X, y)

不平衡问题其他的处理方式

除了上述提到的过采样与欠采样的方法之外,还可以将多种方法进行组合使用。

另外还可以通过为不同的样本点赋予不同的权重的方式来处理不平衡问题(与改进损失函数的方式有所类似)。

在算法层面除了对算法本身的改进之外,还需要关注模型的评价指标,来确认使用的方法是否有效。

个人实践发现,组合方法的结果会比单一某种方法会好很多,例如SMOTEENN和SMOTETomek。

-------------本文结束感谢您的阅读-------------
六经蕴籍胸中久,一剑十年磨在手

欢迎关注我的其它发布渠道