原文:Class Imbalance, Outliers, and Distribution Shift · Introduction to Data-Centric AI
这节课是关于真实世界中机器学习的数据中出现的三大问题:类别不均衡,离群点以及分布改变。
类别不均衡:
现实世界的分类问题中常常会出现的情况就是某些类别的数据比其他类别的数据要多很多。例如:
--COVID感染:在所有的病人中,只有10%可能感染COVID
--欺诈检测:在所有的信用卡交易中,欺诈交易可能只占0.2%
--制造瑕疵的分类:对于不同的制造瑕疵类型占比也不同
--自动驾驶汽车的目标检测:不同类型的目标有不同的占比(汽车 vs 货车 vs 行人)
评价标准
如果要把数据切割成训练集和测试集,确保你使用的是分层数据切割。(分层数据切割参考:3.1. Cross-validation: evaluating estimator performance — scikit-learn 1.3.1 documentation 又比如sklearn的train_test_split方法也提供了分层切割数据的参数stratify)
这样可以确保你的训练集和数据集的分布相差不大。
对于类别不均衡的数据来说,准确率(accuracy)这个评价标准是没有什么意义的。比如,在信用卡欺诈检测中,一个总是预测“非欺诈交易”的分类器有99.8%的准确率。
没有一个通用的方法去选择评价标准:要根据问题决定评价标准。比如,信用卡欺诈检测中,F-beta这个评价标准比较适合。F-beta评价标准是对精确率(precision)和召回率(recall)的加权平均。这个权重是权衡两种代价决定的,这两种代价是没有成功地阻止欺诈交易的代价以及错误地阻止真正的交易的代价。
这里补充一个介绍混淆矩阵,准确率,召回率,精确率的文章:https://www.cnblogs.com/wuliytTaotao/p/9285227.html
文章中提到在β=1时,此时Fβ认为精确率和召回率一样重要;当β>1时,Fβ认为召回率更重要;当0<β<1时,Fβ认为精确率更重要。
在非均衡的数据上训练模型
一旦选择好评价标准后,你就可以用标准的方式去训练模型。如果训练出来的模型在真实分布的数据上表现很好(即在选出的评价标准下,模型在一个符合真实分布的测试数据集上得分很高),那你就可以收工了。
否则,你可以使用一些技巧去提高模型的表现。这些技巧针对的是模型在处理少数的类别数据上的表现的提高。
样本权重 很多模型可以为每个样本设置不同的权重。这种技巧注重的是优化损失的加权平均,给某些样本(样本也可以称为数据点)更大的权重。而不是注重优化目标函数,这种方式认为损失的加权平均中每个样本的权重是一样的。尽管这种技巧很简单,概念也好理解,但是实际应用中通常并非那么好用。对于使用小批量数据(mini-batches)训练的分类器来说,使用样本权重导致不同的小批量数据之间有效的学习率(learning rate)的不同,这样会使得学习不稳定。
上采样 还是关于样本权重,为了增加少数类别的样本权重,你可以简单地直接复制少数类别的样本,甚至可以复制多次少数类别样本放到数据集中。这样可以使得数据集更加均衡。这种方法在简单模型(比如最小二乘法回归,过采样方法跟样本权重的方法差不多)和复杂模型(比如使用小批量数据梯度下降法训练的神经网络)的效果大不一样,但是这种方法比样本权重方法更加有效。注意这种方法的效果不稳定,而且可能会造成过拟合。
下采样 另一种使得数据均衡的方法是减少多数类别的样本。丢弃数据看起来反直觉,但是这个方法在实际中效果很好。但是在某些情况下效果却很差,比如在数据极度不均衡的情况下丢弃大量数据。
SMOTE(Synthetic Minority Oversampling TEchnique)上采样的技巧是简单的复制少数类别的样本,而SMOTE则是通过对少数类别样本的组合和打乱(combing and perturbing) 的方式创建新的样本。SMOTE方法适合在特定的数据类型上使用,比如特征空间的插值是有意义的数据类型。而在某些数据类型上不适用,比如对一幅狗的图片和另一幅狗的图片的像素值做平均并不会产生一幅新的狗的图片。
均衡训练中的小批量数据集 对于使用小批量数据训练的模型,比如神经网络,当为每个小批量数据组装随机子集时(即从数据集中随机选择样本形成一个小批量数据集),要以更高的概率把少数类别的数据包括进来,这样小批量数据集就均衡了。这种方法类似上采样,而且不丢弃数据。
以上的技巧可以组合使用。比如把SMOTE和下采样方法一起使用会比单独使用下采样更好。
一些参考:(可以作为扩展阅读)
做非均衡数据学习的python包:imbalanced-learn documentation — Version 0.11.0
SMOTE基础:https://towardsdatascience.com/smote-fdce2f605729?gi=696e938a1dd5
从非均衡数据学习:https://dl.acm.org/doi/10.1145/1273496.1273614
非均衡数据的评价标准:Tour of Evaluation Metrics for Imbalanced Classification - MachineLearningMastery.com
离群点
离群点就是指跟其他样本非常不同的样本。造成离群点的原因可能是测量错误(比如传感器坏了),数据收集质量不好(比如表格里面有空缺值)等等。
数据集中有离群点会造成在训练、预测以及对模型使用一些统计技巧时出现问题。离群点对于模型的训练没有好处,某些机器学习模型(比如vanilla SVM)在训练过程中对离群点非常敏感。如果给定的输入数据含有离群点,会造成模型在部署应用的时候输出不合理的结果(这也是一种分布改变的形式)。如果数据集含有离群点,那么数据分析技巧可能会给出很差的结果。
如果发现了离群点,该如何处理呢?这要看情况。比如,在训练集中发现离群点,你不能简单的丢弃:因为很可能这些不是离群点,而是罕见的事件。这时你可能需要一个领域专家去决定这些样本是离群点还是罕见的事件。
问题设置
两种有点意思的任务:
离群点检测 这个任务不会把一个清洁好的数据集(清洁好的数据即全是in-distribution分布内的样本组成的数据集)给你。而是给定一个没有标签的数据集,目标是检测出数据集中的离群点。这个任务通常是在机器学习中清洁训练数据时出现。
异常检测 这个任务的数据集是一个没有标签的全是分布内的样本组成的数据集。给定一个新的分布外的样本,目标是判断这个新的样本是不是跟数据集的分布一样。这个任务会在比如预测阶段的时候,判断一个样本是不是从跟训练集一样分布的数据集中出来的。
离群点检测
有很多研究离群点检测的技巧和文章,这里给出一些精选的技巧。
Tukey的围栏(Tukey's fences)这个简单的技巧适用于标量实数。给定Q1和Q3分别表示第一四分位数和第三四分位数(即Q1是下四分位数,Q3是上四分为数)。任何落在范围外的样本即是离群点。k=1.5的设置是由John Tukey提出。
Z-score 对于一维或低维数据,Z-score的计算
其中是所有数据的均值,是所有数据的标准差。可以把Z-score看作是一个数值偏离了均值多少个标准差。离群点即的那些点,通常。可应用于单个特征。
隔离森林(Isolation Forest)这个技巧与决策树有关。简单来说,这种方法建立了一棵随机决策树,然后给每个样本评分,评分的依据是需要多少个节点(node,或者说分割点)去隔离样本。算法通过不断随机地选择一个特征以及分割值去分割数据集(或者说数据集的子集),直到这个子集只有一个样本。这个技巧的想法就是离群点需要更少的分割点就被隔离了(即需要更少的分割点就只剩下一个样本它自己的那些样本是离群点)。
KNN距离 分布内的(In-distribution)数据跟它的邻居更加相似。可以使用一个数据点离最近邻k个点的平均距离(或者选一个合适的距离标准,比如cosin距离)作为评分。对于高维数据,比如图片,可以使用训练模型中的嵌入特征(https://arxiv.org/abs/2207.03061)方法,然后在这个嵌入空间上使用KNN。
基于重建的方法 自编码器(Autoencoder)经过训练,可以压缩高维数据为低维数据的表现形式,之后还可以重建原始数据(重建应该是低维又变回高维的意思)。如果自编码器学到了数据的分布,那么它就能够对一个分布内的数据编码,以及之后能够解码以使得这个数据接近原始的输入数据。然而,对于分布外的数据(out-of-distribution),重建会很困难。所以你可以使用重建后的损失作为评分标准来识别离群点。
大部分离群点检测方法都是给样本评分,然后使用阈值去评判这个样本是不是离群。离群点检测方法的好坏可以用ROC曲线去评价,如果需要一个数值去评价离群点检测方法可以使用AUROC。
一些参考:(可以作为扩展阅读)
离群检测的python包:pyod 1.1.0 documentation
使用自编码器做离群点检测:https://towardsdatascience.com/outlier-detection-with-autoencoders-6c7ac3e2aa90
scikit-learn的离群检测:2.7. Novelty and Outlier Detection — scikit-learn 1.3.1 documentation
补充一个,scikit-leran的离群检测例子:https://scikit-learn.org/stable/auto_examples/miscellaneous/plot_anomaly_comparison.html
分布改变
当训练集和测试集的输入与输出的联合分布不同时,我们就遇到了分布改变这个大问题,即
分布改变的种类
协变量改变/数据改变
协变量改变即在训练集和测试集之间发生了改变,但是没有发生改变。换句话说,输入的分布发生了改变(训练集和测试集输入的分布不同),但是输入和输出的关系没有发生改变。
协变量改变的例子:
--自动驾驶汽车在三藩市阳关明媚的街道训练,而在波士顿下雪的街道部署应用。
--语音识别模型使用英语母语者的语音材料训练,然后部署应用到所有说英语的人。
--糖尿病预测模型使用波士顿医院的数据,然后部署应用到印度。
观念改变(concept shift)
观念改变即在训练集和测试集之间发生了改变,但是没有发生改变。也就是说,输入的分布在两种数据集中没有改变,但是输入和输出的关系发生了改变。这是其中一种最难检测以及纠正的分布改变。
举出一些现实中观念改变的例子需要技巧,因为这种例子需要输入的分布或者说完全没改变。我们看一些现实中例子:
--基于公司的基本面数据预测股票价格:使用1975年的数据训练,部署应用在2023年。公司的基本面数据包括了像每股收益这样的统计数据。这些数据()自身当然随着时间改变,这些数据和价值之间的关系也会发生改变。P/E比率(市盈率,股票价格比上每股收益,即每股市价除以每股盈余)随着时间变化发生巨大改变。S&P的P/E值在1975年是8.30,而在2023年则达到了20。这就是观念改变,即改变了:人们认为一个公司的价值提升了(超过两倍),即使每股盈余是一样的。
--根据浏览行为给用户推荐商品:使用疫情前的数据训练,应用部署在2020年三月。浏览行为()没有发生很大改变(即大部分人浏览着相同的网站,看着相同的youtube,无论在疫情前后),而浏览行为和购买行为之间的关系发生了改变(比如,某个人在youtube上看很多旅游视频,疫情前他会买机票,订酒店,疫情间他可能就买自然纪录片)
先验概率改变/标签改变
先验概率改变出现在的变化会影响的问题中,即。就是在训练集和测试集之间发生了改变,但是没有。可以看作协变量改变的相反版本。
举个例子,垃圾邮件分类中常用的模型朴素贝叶斯。如果模型在均衡的数据(50%垃圾邮件,50%非垃圾邮件的数据)上训练,那么当它在有90%垃圾邮件的真实世界应用时就属于先验概率改变的情况。
另一个例子,给定一些症状,使用一个训练好的分类器去给出诊断。随着时代的不同,疾病的相对流行程度也不同。先验概率改变在这种情况下就是很好的假设,因为疾病引起症状(疾病-->症状)。
分布改变的检测以及处理
一些方法:
--监督模型的表现。监督准确率,精确率,统计描述,或者其他评价标准。如果这些指标随着时间变化,很可能是分布改变导致。
--监督数据。比较训练集和测试集的统计特性可以发现数据改变(协变量改变)。
总的来说,分布改变的问题可以通过处理数据或者重新训练模型来解决。在某些情况下,最好的办法是收集更好的训练集。
如果在训练的时候就可以获得没有标签的测试集,一个解决数据改变(协变量改变)问题的方法是为每个训练样本赋予一个权重以对特征的分布赋权,使得这些权重形成的分布与测试集的特征分布类似。尽管测试集标签未知,标签改变(先验概率改变)问题也可以用类似这样设置的方法解决。然而,观念改变问题用这种方式无法解决,因为我们不知道测试集的标签。
一些参考:
--机器学习中数据集改变:Dataset Shift in Machine Learning | Books Gateway | MIT Press
--环境和分布改变:4.7. Environment and Distribution Shift — Dive into Deep Learning 1.0.3 documentation
实验
原文给了代码链接https://github.com/dcai-course/dcai-lab/blob/master/outliers/Lab%20-%20Outliers.ipynb
这个实验注重异常检测。给定一个由很多狗图片组成的清洁好的训练集,以及一个含有异常值(非狗图片)的测试集。你的任务就是比较不同的离群检测方法。可以使用本节课的方法,也可以使用扩展阅读的离群检测的方法,或者自己在网上找一些方法。