机器学习入门系列(2)–如何构建一个完整的机器学习项目,第四篇!
该系列的前三篇文章:
- 机器学习入门系列(2)–如何构建一个完整的机器学习项目(一)
- 机器学习数据集的获取和测试集的构建方法
- 特征工程之数据预处理(上)
上篇文章介绍了如何处理缺失值和图片数据扩充的问题,这篇文章会介绍另外两种情况,处理异常值和类别不平衡的问题。
3.1.3 处理异常值
异常值分析是检验数据是否有录入错误以及含有不合常理的数据。忽视异常值的存在是十分危险的,不加剔除地把异常值包括进数据的计算分析过程中,对结果会产生不良影响。
异常值是指样本中的个别值,其数值明显偏离其余的观测值。异常值也称为离群点,异常值分析也称为离群点分析。
异常值检测
- 简单统计:比如利用
pandas
库的describe()
方法观察数据的统计性描述,或者简单使用散点图也能观察到异常值的存在,如下图所示:
- 3∂原则: 这个原则有个条件:数据需要服从正态分布。在 3∂ 原则下,异常值如超过 3 倍标准差,那么可以将其视为异常值。正负3∂ 的概率是 99.7%,那么距离平均值 3∂ 之外的值出现的概率为
P(|x-u| > 3∂) <= 0.003
,属于极个别的小概率事件。如果数据不服从正态分布,也可以用远离平均值的多少倍标准差来描述。如下图所示:
- 箱型图
这种方法是利用箱型图的四分位距(IQR)对异常值进行检测,也叫Tukey‘s test。箱型图的定义如下:
四分位距(IQR)就是上四分位与下四分位的差值。而我们通过IQR的1.5倍为标准,规定:超过上四分位+1.5倍IQR距离,或者下四分位-1.5倍IQR距离的点为异常值。下面是Python中的代码实现,主要使用了numpy
的percentile
方法。
Percentile = np.percentile(df['length'],[0,25,50,75,100])
IQR = Percentile[3] - Percentile[1]
UpLimit = Percentile[3]+ageIQR*1.5
DownLimit = Percentile[1]-ageIQR*1.5
也可以使用seaborn
的可视化方法boxplot
来实现:
f,ax=plt.subplots(figsize=(10,8))
sns.boxplot(y='length',data=df,ax=ax)
plt.show()
如下图所示:
上面三种方法是比较简单的异常值检测方法,接下来是一些较复杂的异常值检测方法,因此这里简单介绍下这些方法的基本概念。
- 基于模型预测
顾名思义,该方法会构建一个概率分布模型,并计算对象符合该模型的概率,将低概率的对象视为异常点。
如果模型是簇的组合,则异常点是不在任何簇的对象;如果模型是回归,异常点是远离预测值的对象(就是第一个方法的图示例子)。
优缺点:
- 有坚实的统计学理论基础,当存在充分的数据和所用的检验类型的知识时,这些检验可能非常有效;
- 对于多元数据,可用的选择少一些,并且对于高维数据,这些检测可能性很差。
- 基于近邻度的离群点检测
一个对象的离群点得分由到它的 k-最近邻(KNN)的距离给定。
这里需要注意 k 值的取值会影响离群点得分,如果 k 太小,则少量的邻近离群点可能会导致较低的离群点得分;如果 k 太大,则点数少于 k 的簇中所有的对象可能都成了离群点。为了增强鲁棒性,可以采用 k 个最近邻的平均距离。
优缺点:
- 简单;
- 基于邻近度的方法需要
O(m2)
时间,大数据集不适用; - k 值的取值导致该方法对参数的选择也是敏感的;
- 不能处理具有不同密度区域的数据集,因为它使用全局阈值,不能考虑这种密度的变化。
- 基于密度的离群点检测
一种常用的定义密度的方法是,定义密度为到k个最近邻的平均距离的倒数。如果该距离小,则密度高,反之亦然。
另一种密度定义是使用 DBSCAN 聚类算法使用的密度定义,即一个对象周围的密度等于该对象指定距离 d 内对象的个数。
优缺点:
- 给出了对象是离群点的定量度量,并且即使数据具有不同的区域也能够很好的处理;
- 与基于距离的方法一样,这些方法必然具有
O(m2)
的时间复杂度。对于低维数据使用特定的数据结构可以达到O(mlogm)
; - 参数选择是困难的。虽然
LOF
算法通过观察不同的 k 值,然后取得最大离群点得分来处理该问题,但是,仍然需要选择这些值的上下界。
- 基于聚类的离群点检测
一个对象是基于聚类的离群点,如果该对象不强属于任何簇,那么该对象属于离群点。
离群点对初始聚类的影响:如果通过聚类检测离群点,则由于离群点影响聚类,存在一个问题:结构是否有效。这也是 k-means
算法的缺点,对离群点敏感。
为了处理该问题,可以使用如下方法:对象聚类,删除离群点,对象再次聚类(这个不能保证产生最优结果)。
优缺点:
- 基于线性和接近线性复杂度(k均值)的聚类技术来发现离群点可能是高度有效的;
- 簇的定义通常是离群点的补集,因此可能同时发现簇和离群点;
- 产生的离群点集和它们的得分可能非常依赖所用的簇的个数和数据中离群点的存在性;
- 聚类算法产生的簇的质量对该算法产生的离群点的质量影响非常大。
- 专门的离群点检测
除了以上提及的方法,还有两个专门用于检测异常点的方法比较常用:One Class SVM
和Isolation Forest
,
异常值处理
- 删除含有异常值的记录:直接将含有异常值的记录删除;
- 视为缺失值:将异常值视为缺失值,利用缺失值处理的方法进行处理;
- 平均值修正:可用前后两个观测值的平均值修正该异常值;
- 不处理:直接在具有异常值的数据集上进行数据挖掘;
将含有异常值的记录直接删除的方法简单易行,但缺点也很明显,在观测值很少的情况下,这种删除会造成样本量不足,可能会改变变量的原有分布,从而造成分析结果的不准确。视为缺失值处理的好处是可以利用现有变量的信息,对异常值(缺失值)进行填补。
在很多情况下,要先分析异常值出现的可能原因,在判断异常值是否应该舍弃,如果是正确的数据,可以直接在具有异常值的数据集上进行挖掘建模。
3.1.4 处理类别不平衡问题
什么是类别不平衡呢?它是指分类任务中存在某个或者某些类别的样本数量远多于其他类别的样本数量的情况。
比如,一个十分类问题,总共有 10000 个样本,但是类别 1 到 4 分别包含 2000 个样本,剩余 6 个类别的样本数量加起来刚刚 2000 个,即这六个类别各自包含的样本平均数量大约是 333 个,相比前四个类别是相差了 6 倍左右的数量。这种情况就是类别不平衡了。
那么如何解决类别不平衡问题呢?
这里介绍八大解决办法。
- 扩充数据集
首先应该考虑数据集的扩充,在刚刚图片数据集扩充一节介绍了多种数据扩充的办法,而且数据越多,给模型提供的信息也越大,更有利于训练出一个性能更好的模型。
如果在增加小类样本数量的同时,又增加了大类样本数据,可以考虑放弃部分大类数据(通过对其进行欠采样方法)。
- 尝试其他评价指标
一般分类任务最常使用的评价指标就是准确度了,但它在类别不平衡的分类任务中并不能反映实际情况,原因就是即便分类器将所有类别都分为大类,准确度也不会差,因为大类包含的数量远远多于小类的数量,所以这个评价指标会偏向于大类类别的数据。
其他可以推荐的评价指标有以下几种
- 混淆矩阵:实际上这个也是在分类任务会采用的一个指标,可以查看分类器对每个类别预测的情况,其对角线数值表示预测正确的数量;
- 精确度(Precision):表示实际预测正确的结果占所有被预测正确的结果的比例,P=TP / (TP+FP)
- 召回率(Recall):表示实际预测正确的结果占所有真正正确的结果的比例,R = TP / (TP+FN)
- F1 得分(F1 Score):精确度和召回率的加权平均,F1=2PR / (P+R)
- Kappa (Cohen kappa)
- ROC 曲线(ROC Curves):常被用于评价一个二值分类器的优劣,而且对于正负样本分布变化的时候,ROC 曲线可以保持不变,即不受类别不平衡的影响。
其中 TP、FP、TN、FN 分别表示正确预测的正类、错误预测的正类、预测正确的负类以及错误预测的负类。图例如下:
- 对数据集进行重采样
可以使用一些策略该减轻数据的不平衡程度。该策略便是采样(sampling),主要有两种采样方法来降低数据的不平衡性。
- 对小类的数据样本进行采样来增加小类的数据样本个数,即过采样(over-sampling ,采样的个数大于该类样本的个数)。
- 对大类的数据样本进行采样来减少该类数据样本的个数,即欠采样(under-sampling,采样的次数少于该类样本的个素)。
采样算法往往很容易实现,并且其运行速度快,并且效果也不错。 一些经验法则:
- 考虑对大类下的样本(超过 1 万、十万甚至更多)进行欠采样,即删除部分样本;
- 考虑对小类下的样本(不足 1万甚至更少)进行过采样,即添加部分样本的副本;
- 考虑尝试随机采样与非随机采样两种采样方法;
- 考虑对各类别尝试不同的采样比例,比一定是 1:1,有时候 1:1 反而不好,因为与现实情况相差甚远;
- 考虑同时使用过采样与欠采样。
- 尝试人工生成数据样本
一种简单的人工样本数据产生的方法便是,对该类下的所有样本每个属性特征的取值空间中随机选取一个组成新的样本,即属性值随机采样。
你可以使用基于经验对属性值进行随机采样而构造新的人工样本,或者使用类似朴素贝叶斯方法假设各属性之间互相独立进行采样,这样便可得到更多的数据,但是无法保证属性之前的线性关系(如果本身是存在的)。
有一个系统的构造人工数据样本的方法 SMOTE(Synthetic Minority Over-sampling Technique)。SMOTE 是一种过采样算法,它构造新的小类样本而不是产生小类中已有的样本的副本,即该算法构造的数据是新样本,原数据集中不存在的。
它基于距离度量选择小类别下两个或者更多的相似样本,然后选择其中一个样本,并随机选择一定数量的邻居样本,然后对选择的那个样本的一个属性增加噪声,每次处理一个属性。这样就构造了更多的新生数据。
python 实现的 SMOTE 算法代码地址如下,它提供了多种不同实现版本,以及多个重采样算法。
https://github.com/scikit-learn-contrib/imbalanced-learn
- 尝试不同分类算法
强烈建议不要对待每一个分类都使用自己喜欢而熟悉的分类算法。应该使用不同的算法对其进行比较,因为不同的算法适用于不同的任务与数据。
决策树往往在类别不均衡数据上表现不错。它使用基于类变量的划分规则去创建分类树,因此可以强制地将不同类别的样本分开。目前流行的决策树算法有:C4.5、C5.0、CART和Random Forest等。
- 尝试对模型进行惩罚
你可以使用相同的分类算法,但使用一个不同的角度,比如你的分类任务是识别那些小类,那么可以对分类器的小类样本数据增加权值,降低大类样本的权值(这种方法其实是产生了新的数据分布,即产生了新的数据集),从而使得分类器将重点集中在小类样本身上。
一个具体做法就是,在训练分类器时,若分类器将小类样本分错时额外增加分类器一个小类样本分错代价,这个额外的代价可以使得分类器更加“关心”小类样本。如 penalized-SVM 和 penalized-LDA 算法。
如果你锁定一个具体的算法时,并且无法通过使用重采样来解决不均衡性问题而得到较差的分类结果。这样你便可以使用惩罚模型来解决不平衡性问题。但是,设置惩罚矩阵是一个复杂的事,因此你需要根据你的任务尝试不同的惩罚矩阵,并选取一个较好的惩罚矩阵。
- 尝试一个新的角度理解问题
从一个新的角度来理解问题,比如我们可以将小类的样本作为异常点,那么问题就变成异常点检测与变化趋势检测问题。
- 异常点检测:即是对那些罕见事件进行识别。如通过机器的部件的振动识别机器故障,又如通过系统调用序列识别恶意程序。这些事件相对于正常情况是很少见的。
- 变化趋势检测:类似于异常点检测,不同在于其通过检测不寻常的变化趋势来识别。如通过观察用户模式或银行交易来检测用户行为的不寻常改变。
将小类样本作为异常点这种思维的转变,可以帮助考虑新的方法去分离或分类样本。这两种方法从不同的角度去思考,让你尝试新的方法去解决问题。
- 尝试创新
仔细对问题进行分析和挖掘,是否可以将问题划分为多个更小的问题,可以尝试如下方法:
- 将你的大类压缩成小类;
- 使用 One Class 分类器(将小类作为异常点);
- 使用集成方式,训练多个分类器,然后联合这些分类器进行分类;
对于类别不平衡问题,还是需要具体问题具体分析,如果有先验知识可以快速挑选合适的方法来解决,否则最好就是逐一测试每一种方法,然后挑选最好的算法。最重要的还是多做项目,多积累经验,这样遇到一个新的问题,也可以快速找到合适的解决方法。
小结
本篇文章介绍了如何检测和处理缺失值,以及解决类别不平衡的问题,结合上一篇文章,基本就是常见的数据预处理内容。
参考:
- 《百面机器学习》第一章 特征工程
- https://gofisher.github.io/2018/06/22/数据预处理/
- https://gofisher.github.io/2018/06/20/数据探索/
- https://www.zhihu.com/question/47716840
- http://www.huaxiaozhuan.com/统计学习/chapters/8_feature_selection.html
欢迎关注我的微信公众号–机器学习与计算机视觉,或者扫描下方的二维码,大家一起交流,学习和进步!
往期精彩推荐
机器学习系列
- 机器学习入门系列(1)–机器学习概览
- 机器学习入门系列(2)–如何构建一个完整的机器学习项目(一)
- 机器学习数据集的获取和测试集的构建方法
- 特征工程之数据预处理(上)
数学学习笔记
- 程序员的数学笔记1–进制转换
- 程序员的数学笔记2–余数
- 程序员的数学笔记3–迭代法
Github项目 & 资源教程推荐
- [Github 项目推荐] 一个更好阅读和查找论文的网站
- [资源分享] TensorFlow 官方中文版教程来了
- 必读的AI和深度学习博客
- [教程]一份简单易懂的 TensorFlow 教程
- [资源]推荐一些Python书籍和教程,入门和进阶的都有!