在文本分类问题当中,数据集打完标签后,类别数量不均衡,这时候的测试集与训练集应该如何制作?

在使用搜狗实验室的新闻数据集时,打完标签后的各类新闻数量如下

可以看到各个类别的新闻数量十分不均衡,在制作训练集和测试集时,应该怎么做?如果采用一刀切的方式只从每个类别当中抽取2000条数据制作,会觉得浪费了数据。那么遇到这种情况时,应该如何抽取数据来制作数据集和测试集呢?


多分类任务中类别不均衡是非常常见的一个问题,但是差别多少才算分布不均匀呢?

这个没有一个确定的衡量标准。根据我个人的经验的话,不同类别数量差异超过一个数量级,我才会认为样本类别分布不均匀,需要特别关注和调整。比如题主的截图,最大的类别数是最小类别的三个数量集,差距非常大,触犯了样本类别分布不均的情况。

针对样本分布不均匀,可以从以下几个方面来多加关注和尝试:

  1. sample数据:

当样本类别分布不均匀的时候,什么都不管还是直接将数据shuffle,随机切分训练/验证/测试集,显然是不太合理的。常用的有简单欠采样和过采样的方法。

欠采样:如题主,就这桶的最短板「一刀切」,每个类别sample最小值239,这样就保证了数据分布绝对均匀,但是造成了严重的数据浪费。

过采样:以最高板为标准,重复拼接短板(重复抽样类别量小的数据)。这样数据重复使用,肯定不会造成浪费了吧。但是问题来了,对数量少的类别(如总共只有239个样本),强行将每个样本重复10^3遍,模型训练一个epoch的时候,对这类数据重复学习了10^3次,并且数据量本身就非常小,只有239个,极大可能造成在这些类别上过拟合

两者之间更智能的方法:

简单讲一下工作中亲测有效的方法,也是在multi-task learning中常用的sample方式。使用multinomial distribution,对不均衡的数据分布做平滑。

pi为原始数据分布中第i类数据出现的概率,qi为平滑处理后抽取第i类数据的概率。通过这个分布平滑抽样,抽样结果不会改变原始的各类别数据量大小的序关系,但是对类别数量过大的类数据量会相对减少,对类别数量过小的类数据量会相对增加。减少过拟合的可能性,也没有过度浪费数据。

参见论文:https://arxiv.org/pdf/1901.07291.pdf

2. 加权的损失函数

对于样本数量少的类别,可以考虑加大对该类别误判的惩罚,实质上时增加这类样本的权重,修正模型因为数量少而关注少的问题。

3. 评估指标

不要一股脑的只看所有类别整合的评估指标如ACC(当类别分布不均匀的时候会有极度的欺骗性)。

不妨细致的对比验证集和测试集的混淆矩阵,计算各类别的precision和recall,通过结果发现问题,指导你后续应该怎么优化。

采样方法、加权损失,个人认为还是治标不治本。在使用神经网路等需要大量数据支撑的模型演算法时,该增加数据量还是得跟上,不管通过人工标注,还是使用其他一些数据增强的方法。


类别不平衡学习大致有三种策略:一,欠采样,针对数量过多的样例。二,过采样,针对数量较少的样例。三,阈值移动,就是对阈值进行调整。直接基于原始数据训练,进行预测时,用样例的真实观测几率来修正阈值。


机器学习任务的目标,是找出使得 [公式] 成立的函数 [公式] 。大部分机器学习演算法都有一个(隐含)假设,即训练集和测试集(验证集)遵循相同的统计分布(独立同分布)。这既意味著训练集和测试集的输入样本( [公式] )满足同样的分布,又意味著二者的的输出样本( [公式] )满足同样的分布。以分类问题为例,如果我们已经获得了一个标注好的数据集,那么只要随机采样,获得的两个没有交集的样本,就可以作为理想的训练集和测试集,因为这两个样本是独立同分布的。

然而,在现实生活中,类别不平衡问题广泛存在。许多针对类别不平衡问题提出的解决方案主要关注这种情形:训练集的类别严重偏离平衡(不平衡比 [公式] ),但测试集(验证集)的类别是比较平衡的( [公式] )。解决方案主要有:

  1. 过采样(随机采样、SMOTE、ADASYN等)
  2. 降采样
  3. 过采样与降采样结合(SMOTEENN、SMOTETomek)等。

在这些思想或者演算法的基础上,有许多人提出了各种自带采样器的集成学习方法,比如包含采样的随机森林(Random Forest)、提升学习(Boosting)方法等。采样可以说是处理类别不平衡问题的第一种思路。关于这部分,python包imbalanced-learn做了很好的总结,可以直接使用它的模块,或者参考模块的写法。

第二种思路是修改权重(re-weighting)。简言之,如果在训练时降低多数类样本对损失函数的贡献,使其总贡献与少数类样本对损失函数的总贡献均衡,那么学习器对多数类的偏重就会减轻。比如,sklearn中的随机森林等演算法,可以手动设置"class_weight"参数,一般来说将class_weight设置成与类别比成反比即可,多类的情况同理,具体可以参考文档。

有的答主提到了深度学习中的「focal loss」损失函数,它和GHM等损失函数也都是通过修改权重来起作用的。与简单的「class weight」不同,「focal loss」和GHM将类别不平衡问题的难点归结为分类「硬度」(hardness)问题,即造成困难的主要是少数难分类的样本,而非大多数易分类的样本。因此,如果将某些难分样本的损失函数权重提高,将大多数易分类样本的权重降低,那么就能提升整体的学习效果。

以上两种思路是数据和演算法层面的考量。第三点也有答主指出来了,就是使用比accuracy、F1-score等更合适的评价标准。Accuracy、F1-score并未考虑类别比例,在类别不平衡情况下没有太高的参考价值。我个人倾向于使用Matthews correlation coefficient (MCC) ,因为它在数学上考虑了类别比例。下面这篇文章对MCC的重要性有比较详细的检验和介绍:【Chicco, D., Jurman, G. The advantages of the Matthews correlation coefficient (MCC) over F1 score and accuracy in binary classification evaluation.BMC Genomics21,6 (2020). https://doi.org/10.1186/s12864-019-6413-7】

https://bmcgenomics.biomedcentral.com/articles/10.1186/s12864-019-6413-7?

bmcgenomics.biomedcentral.com

当然,类别不平衡问题是一个深坑,很多出色的演算法遇到它时也风光不再。不过有时候,你可能会发现类别不平衡并未对你的任务带来什么困难(考虑「硬度」,或者特征空间中的类别重叠程度)。我觉得目前还没有通用的/好用的针对类别不平衡问题的方法,但是现有的演算法,结合你自己对于数据的理解,多多少少可以减轻类别不平衡带来的影响。(有空再填)


这是我的步骤:

如果数据集不平衡,改用F1分数作为评判标准,然后启用演算法的惩罚机制,例如sklearn里的各个机器学习演算法都有一个参数叫class weight,把它设置成balanced,样本数量少的种类权重就会变大,以这样一个方式训练所有模型。

然后还没完。

根据你的case,有些种类的样本数量很少,只有几百,所以可以用过采样演算法比如smote,这属于人为地制造一些数据直到达到样本平衡。此时再训练一遍所有模型。

比较前后2次的训练结果来得出最终模型,根据我的以往项目经验,第一次选出的最优模型在使用过采样演算法之后依然是最优的。

我个人其实很怀疑这些人为制造数据的演算法,所以在我这里我只是当个验证而已。

另外模型的精确度,F1分数之类的只有在选择模型上有参考价值,一旦选出,实际效果其实很难判断,最后选择出的模型也只能表明,这个模型可能最接近现实。


说句实话吧,不平衡数据咋搞都很难。但实战经验告诉我,不平衡数据根本不用处理。如果不死心,可以试试把多数类做欠采样,搞成和少数类差不多的样本量,然后做个融合建模。如有兴趣,可以看看这个。

https://github.com/yzkang/My-Data-Competition-Experience?

github.com


推荐阅读:
相关文章