在银行要判断一个"新客户是否会违约",通常不违约的人VS违约的人会是99:1
的比例,真正违约的人 其实是非常少的。这种分类状况下,即便模型什么也不做,全把所有人都当成不会违约的人,正确率也能有99%, 这使得模型评估指标变得毫无意义,根本无法达到我们的"要识别出会违约的人"的建模目的。
像这样样本不均衡等例子在生活中随处可见。通常出现在异常检测、客户流失、罕见时间分析、发生低频率事件等场景,具体如垃圾邮件等识别,信用卡征信问题、欺诈交易检测、工厂中不良品检测等。在处理诸如此类的样本不均衡的任务中,使用常规方法并不能达到实际业务需求,正确且尽可能多捕获少数类样本。因为样本不均衡会使得分类模型存在很严重的偏向性。
本文中,介绍了在机器学习中样本不平衡处理策略及常用方法和工具。喜欢本文记得收藏、点赞。
数据集中各个类别的样本数量极不均衡,从数据规模上可分为:
大数据分布不均衡。整体数据规模大,小样本类的占比较少,但小样本也覆盖了大部分或全部特征。
小数据分布不均衡。整体数据规模小,少数样本比例的分类数量也少,导致特征分布严重不均衡。
样本分布不均衡在于不同类别间的样本比例差异,导致很难从样本中提取规律。一般超过10倍就需要引起注意,20倍就一定要处理了。
样本不平衡时,可以增加包含一定比例小类样本数据以扩大数据集,更多的数据往往战胜更好的算法。因为机器学习是使用现有的数据多整个数据的分布进行估计,因此更多的数据往往能够得到更多的分布信息,以及更好分布估计。
但有时在增加小类样本数据的同时,也增加了大类数据,并不能显著解决样本不平衡问题。此时可以通过对大类样本数据进行欠采样,以放弃部分大类数据来解决。
准确度在类别均衡的分类任务中并不能有效地评价分类器模型,造成模型失效,甚至会误导业务,造成较大损失。
最典型的评价指标即混淆矩阵Confusion Matrix
:使用一个表格对分类器所预测的类别与其真实的类别的样本统计,分别为:TP、FN、FP、TN
。包括精确度Precision、召回率Recall、F1得分F1-Score
等。
使用采样sampling策略该减轻数据的不平衡程度。 主要有两种方法
对小类的数据样本进行采样来增加小类的数据样本个数,即过采样over-sampling
对大类的数据样本进行采样来减少该类数据样本的个数,即欠采样under-sampling
采样算法往往很容易实现,并且其运行速度快,并且效果也不错。 在使用采样策略时,可以考虑:
对大类下的样本 (超过1万, 十万甚至更多) 进行欠采样,即删除部分样本
对小类下的样本 (不足1为甚至更少) 进行过采样,即添加部分样本的副本
尝试随机采样与非随机采样两种采样方法
对各类别尝试不同的采样比例
同时使用过采样与欠采样
一种简单的方法,**对该类下的所有样本的每个属性特征的取值空间中随机选取一个值以组成新的样本,即属性值随机采样。**可以使用基于经验对属性值进行随机采样而构造新的人工样本,或使用类似朴素贝叶斯方法假设各属性之间互相独立进行采样,这样便可得到更多的数据,但是无法保证属性之前的线性关系(如果本身是存在的)。
有一个系统的构造人工数据样本的方法SMOTE (Synthetic Minority Over-sampling Technique)
。SMOTE
是一种过采样算法,它构造新的小类样本而不是产生小类中已有的样本的副本,即该算法构造的数据是新样本,原数据集中不存在的。该基于距离度量选择小类别下两个或者更多的相似样本,然后选择其中一个样本,并随机选择一定数量的邻居样本对选择的那个样本的一个属性增加噪声,每次处理一个属性。这样就构造了更多的新生数据。
对待每一个机器学习任务都使用自己喜欢而熟悉的算法,相信很多人都会感同身受。但对待不同任务需要根据任务本身特点选用不同等算法,尤其对样本不均衡等分类任务。应该使用不同的算法对其进行比较,因为不同的算法使用于不同的任务与数据。
决策树往往在类别不均衡数据上表现不错。它使用基于类变量的划分规则去创建分类树,因此可以强制地将不同类别的样本分开。
你可以使用相同的分类算法,但是使用一个不同的角度,比如你的分类任务是识别那些小类,那么可以对分类器的小类样本数据增加权值,降低大类样本的权值,从而使得分类器将重点集中在小类样本身上。一个具体做法就是,在训练分类器时,若分类器将小类样本分错时额外增加分类器一个小类样本分错代价,这个额外的代价可以使得分类器更加"关心"小类样本。如penalized-SVM
和penalized-LDA
算法。
我们可以从不同于分类的角度去解决数据不均衡性问题,我们可以把那些小类的样本作为异常点outliers
,因此该问题便转化为异常点检测anomaly detection
与变化趋势检测问题change detection
。
异常点检测即是对那些罕见事件进行识别。如通过机器的部件的振动识别机器故障,又如通过系统调用序列识别恶意程序。这些事件相对于正常情况是很少见的。
变化趋势检测类似于异常点检测,不同在于其通过检测不寻常的变化趋势来识别。如通过观察用户模式或银行交易来检测用户行为的不寻常改变。
将小类样本作为异常点这种思维的转变,可以帮助考虑新的方法去分离或分类样本。这两种方法从不同的角度去思考,让你尝试新的方法去解决问题。
仔细对你的问题进行分析与挖掘,是否可以将你的问题划分成多个更小的问题,而这些小问题更容易解决。
又称上采样(over-sampling)通过增加分类中少数类样本的数量来实现样本不均衡。比较流行的算法有
The Synthetic Minority OverSampling Technique (SMOTE) 算法。
SMOTE: 对于少数类样本a, 随机选择一个最近邻的样本b, 然后从a与b的连线上随机选取一个点c作为新的少数类样本。
语法:
imblearn.over_sampling.SMOTE(
sampling_strategy='auto',
random_state=None,
k_neighbors=5,
n_jobs=1)
举例:
from collections import Counter
from sklearn.datasets import make_classification
from imblearn.over_sampling import SMOTE
# doctest: +NORMALIZE_WHITESPACE
X, y = make_classification(
n_classes=2,
class_sep=2,
weights=[0.1, 0.9],
n_informative=3,
n_redundant=1, flip_y=0,
n_features=20,
n_clusters_per_class=1,
n_samples=1000,
random_state=10)
print('Original dataset shape %s'
% Counter(y))
Original dataset shape Counter
({1: 900, 0: 100})
smote = SMOTE(random_state=42)
X_res, y_res = smote.fit_resample(X, y)
print('Resampled dataset shape %s'
% Counter(y_res))
Resampled dataset shape Counter
({0: 900, 1: 900})
Step1: 对于少数类样本中的某个样本x,找到其K近邻的样本(当然这里的KNN是同类别的KNN)。
Step2: 随机挑选一个K近邻样本 y。
Step3: 生成新样本为λ, λ 是[0,1] 上随机数。
SMOTE 变体 borderlineSMOTE、SVMSMOTE
相对于基本的SMOTE算法, 关注的是所有的少数类样本, 这些情况可能会导致产生次优的决策函数, 因此SMOTE就产生了一些变体: 这些方法关注在最优化决策函数边界的一些少数类样本, 然后在最近邻类的相反方向生成样本。
这两种类型的SMOTE使用的是危险样本来生成新的样本数据。
borderlineSMOTE(kind='borderline-1')
最近邻中的随机样本b与该少数类样本a来自于不同的类。
borderlineSMOTE(kind='borderline-2')
随机样本b可以是属于任何一个类的样本。
SVMSMOTE()
使用支持向量机分类器产生支持向量然后再生成新的少数类样本。
噪音样本noise, 该少数类的所有最近邻样本都来自于不同于样本a的其他类别。
危险样本in danger, 至少一半的最近邻样本来自于同一类(不同于a的类别)。
安全样本safe, 所有的最近邻样本都来自于同一个类。
语法:
imblearn.over_sampling.BorderlineSMOTE(sampling_strategy='auto',
random_state=None,
k_neighbors=5,
n_jobs=1, m_neighbors=10,
kind='borderline-1')
imblearn.over_sampling.SVMSMOTE(
sampling_strategy='auto',
random_state=None,
k_neighbors=5, n_jobs=1,
m_neighbors=10,
svm_estimator=None,
out_step=0.5)
Adaptive Synthetic (ADASYN)自适应合成上采样
ADASYN: 关注的是在那些基于K最近邻分类器被错误分类的原始样本附近生成新的少数类样本。
语法:
imblearn.over_sampling.ADASYN(
sampling_strategy='auto',
random_state=None,
n_neighbors=5, n_jobs=1,
ratio=None)
举例:
from collections import Counter
from sklearn.datasets import make_classification
from imblearn.over_sampling import ADASYN
# doctest: +NORMALIZE_WHITESPACE
X, y = make_classification(
n_classes=2,
class_sep=2,
weights=[0.1, 0.9],
n_informative=3,
n_redundant=1,
flip_y=0,
n_features=20,
n_clusters_per_class=1,
n_samples=1000,
random_state=10)
print('Original dataset shape %s'
% Counter(y))
Original dataset shape Counter
({1: 900, 0: 100})
ada = ADASYN(random_state=42)
X_res, y_res = ada.fit_resample(X, y)
print('Resampled dataset shape %s'
% Counter(y_res))
Resampled dataset shape Counter
({0: 904, 1: 900})
RandomOverSampler随机采样增加新样本
例:跟之前类似,此处及后面均简化列出。
from imblearn.over_sampling import RandomOverSampler
ros = RandomOverSampler(andom_state=0)
X_resampled, y_resampled = ros.fit_resample(X, y)
随机对欠表达样本进行采样,该算法允许对heterogeneous data
(异构数据)进行采样(例如含有一些字符串)。通过对原少数样本的重复取样进行上采样。
又称下采样(under-sampling
)通过减少分类中多数类样本的数量来实现样本不均衡。
RandomUnderSampler直接随机选取删除法
RandomUnderSampler函数是一种快速并十分简单的方式来平衡各个类别的数据----随机选取数据的子集。
语法:
from imblearn.under_sampling import RandomUnderSample
model_undersample = RandomUnderSample()
x_undersample_resampled, y_undersample_resampled = model_undersample.fit_sample(X,y)
注意1: 当设置replacement=True
时, 采用bootstrap
。
注意2: 允许非结构化数据,例如数据中含字符串。
注意3: 默认参数的时候, 采用的是不重复采样。
NearMiss 基于NN启发式算法
NearMiss函数则添加了一些启发式heuristic
的规则来选择样本, 通过设定version
参数来实现三种启发式的规则。
from imblearn.under_sampling import NearMiss
nm1 = NearMiss(version=1)
X_resampled_num1, y_resampled = nm1.fit_resample(X, y)
version=1:
选取正例样本中与N个最近邻负样本平均距离最小的样本。
version=2:
选取正例样本中与N个最远邻负样本平均距离最小的样本。
version=3:
2-steps 算法----首先对于每个负类样本, 保留它们的M近邻正样本。然后那些到N个近邻样本平均距离最大的正样本将被选择。
参考资料
[1] Jason Brownlee的回答: https://machinelearningmastery.com/tactics-to-combat-imbalanced-classes-in-your-machine-learning-dataset/
[2] SMOTE: https://imbalanced-learn.readthedocs.io/en/stable/generated/imblearn.over_sampling.SMOTE.html
[3] BorderlineSMOTE: https://imbalanced-learn.readthedocs.io/en/stable/generated/imblearn.over_sampling.BorderlineSMOTE.html
[4] SVMSMOTE: https://imbalanced-learn.readthedocs.io/en/stable/generated/imblearn.over_sampling.SVMSMOTE.html
[5] over_sampling: https://imbalanced-learn.readthedocs.io/en/stable/generated/imblearn.over_sampling.ADASYN.html
[6] RandomOverSampler: https://imbalanced-learn.readthedocs.io/en/stable/generated/imblearn.over_sampling.RandomOverSampler.html
[7] NearMiss: https://imbalanced-learn.readthedocs.io/en/stable/generated/imblearn.under_sampling.NearMiss.html
[8] ibmlearn官网: https://imbalanced-learn.readthedocs.io/en/stable/api.html