如何用 Python 处理不平衡数据集


Posted in Python onJanuary 04, 2021

1. 什么是数据不平衡

所谓的数据不平衡(imbalanced data)是指数据集中各个类别的数量分布不均衡;不平衡数据在现实任务中十分的常见。如

  • 信用卡欺诈数据:99%都是正常的数据, 1%是欺诈数据
  • 贷款逾期数据

不平衡数据一般是由于数据产生的原因导致的,类别少的样本通常是发生的频率低,需要很长的周期进行采集。

在机器学习任务(如分类问题)中,不平衡数据会导致训练的模型预测的结果会偏向于样本数量多的类别,这个时候除了要选择合适的评估指标外,想要提升模型的性能,就要对数据和模型做一些预处理。

处理数据不平衡的主要方法:

  • 欠采样
  • 过采样
  • 综合采样
  • 模型集成

调整类别权重或者样本权重

2. 数据不平衡处理方法

imbalanced-learn库提供了许多不平衡数据处理的方法,本文的例子都以imbalanced-learn库来实现。

pip install -U imbalanced-learn 

https://github.com/scikit-learn-contrib/imbalanced-learn

本文例子的数据来自进行中的比赛山东省第二届数据应用创新创业大赛-日照分赛场-公积金贷款逾期预测

先来看下数据

import pandas as pd
train_data = './data/train.csv'
test_data = './data/test.csv'
train_df = pd.read_csv(train_data)
test_df = pd.read_csv(test_data)

print(train_df.groupby(['label']).size())
# label为是否违约, 1为违约, 0为非违约
#     label
# 0    37243
# 1     2757

如何用 Python 处理不平衡数据集

2.1 欠采样

所谓欠采样,就是将数量多类别(记为majority)的样本进行抽样,使之数量与数量少的类别(minority)的数量相当,以此达到数量的平衡。

如何用 Python 处理不平衡数据集

由于欠采样是丢失了一部分数据,不可避免的使得数量多类别样本的分布发生了变化(方差变大)。好的欠采样策略应该尽可能保持原有数据分布。

欠采样是删除majority的样本,那哪些样本可以删除呢?

  • 一种是overlapping的数据,就是多余的数据
  • 一种是干扰的数据,干扰minority的分布

基于此,有两种思路来欠采样

  • 边界相邻匹配,考虑在近邻空间内删除majority样本,方法如TomekLinks, NearMiss

下面这张图,展示6NN(6个最近邻居)

如何用 Python 处理不平衡数据集

这里重点讲下TomekLinks, TomekLinks方法简单的说:对每一个minority样本找1NN(最近的邻居),如果最近的邻居是majority, 就形成一个tome-links,该方法人为这个majority是干扰的,将它删除。

如何用 Python 处理不平衡数据集

from imblearn.under_sampling import TomekLinks

X_train = train_df.drop(['id', 'type'], axis=1)
y = train_df['label']
tl = TomekLinks()
X_us, y_us = tl.fit_sample(X_train, y)
print(X_us.groupby(['label']).size())
# label
# 0    36069
# 1     2757

从上可知, 有1174个tomek-link被删除,好像删除还不够多,可以测试下是否对分类结果有帮助。需要注意的因为需要计算最近邻,所以样本属性必须数值属性,或者可以转化为数值属性。

  • 聚类

这类方法通过多个聚类,把原始样本划分成多个聚类簇,然后用每个聚类簇的中心来代替这个聚类簇的特性,完成采样的目的。可知,这种采样的样本不是来自原始样本集,而是聚类生成的。

from imblearn.under_sampling import ClusterCentroids 

cc = ClusterCentroids(random_state=42)
X_res, y_res = cc.fit_resample(X_train, y)
X_res.groupby(['label']).size()
# label
# 0    2757
# 1    2757

im-balance提供的欠采样的方法如下:

  • Random majority under-sampling with replacement
  • Extraction of majority-minority Tomek links
  • Under-sampling with Cluster Centroids
  • NearMiss-(1 & 2 & 3)
  • Condensed Nearest Neighbour
  • One-Sided Selection
  • Neighboorhood Cleaning Rule
  • Edited Nearest Neighbours
  • Instance Hardness Threshold
  • Repeated Edited Nearest Neighbours
  • AllKNN

2.2 过采样

所谓过采样,就是将数量少的类别(minority)的样本进行copy,使之数量与数量多的类别(majortity)的数量相当,以此达到数量的平衡。由于复制了多份minoruty样本,过采样会改变minority方差。

如何用 Python 处理不平衡数据集

过采样一种简单的方式是随机copy minority的样本;另外一种是根据现有样本生成人造样本。这里介绍人造样本的经典算法SMOTE(Synthetic Minority Over-sampling Technique)。

SMOTE基于minority样本相似的特征空间构造新的人工样本。步骤如下:

  • 选择一个minority样本,计算其KNN邻居
  • 在K个邻居中,随机选择一个近邻
  • 修改某一个特征,偏移一定的大小:偏移的大小为该minority样本与该近邻差距乘以一个小的随机比率(0, 1), 就此生成新样本

如何用 Python 处理不平衡数据集

from imblearn.over_sampling import SMOTE
smote = SMOTE(k_neighbors=5, random_state=42)
X_res, y_res = smote.fit_resample(X_train, y)
X_res.groupby(['label']).size()
# label
# 0    37243
# 1    37243

对于SMOTE方法,对每一个minority都会构造新样本。但是并不总是这样的,考虑下面A,B,C三个点。从数据分布来看,C点很可能是一个异常点(Noise),B点是正常分布的点(SAFE),而A点分布在边界位置(DANGER);

直观上,对于C点我们不应该去构造新样本,对B点,构造新样本不会丰富minority类别的分布。只有A点,如果构造新样本能够使得A点从(DANGER)到(SAFE),加强minority类别的分类边界。这个就是Borderline-SMOTE

如何用 Python 处理不平衡数据集

from imblearn.over_sampling import BorderlineSMOTE
bsmote = BorderlineSMOTE(k_neighbors=5, random_state=42)
X_res, y_res = bsmote.fit_resample(X_train, y)
X_res.groupby(['label']).size()
# label
# 0    37243
# 1    37243

ADASYN方法从保持样本分布的角度来确定生成数据,生成数据的方式和SMOTE是一样的,不同在于每个minortiy样本生成样本的数量不同。

  • 先确定要生成样本的数量 beta为[0, 1]

如何用 Python 处理不平衡数据集

  • 对每个每个minortiy样本,确定有它生成样本的比例。先找出K最近邻,计算K最近邻中属于majority的样本比例(即分子),Z是归一化因子,保证所有的minortiry的比例和为1,可以认为是所有分子的和。

如何用 Python 处理不平衡数据集

  • 计算每个minortiy生成新样本的数量

如何用 Python 处理不平衡数据集

  • 按照SMOTE方式生成样本
from imblearn.over_sampling import ADASYN 
adasyn = ADASYN(n_neighbors=5, random_state=42)
X_res, y_res = adasyn.fit_resample(X_train, y)
X_res.groupby(['label']).size()

# label
# 0    37243
# 1    36690

im-balance提供的过采样的方法如下(包括SMOTE算法的变种):

  • Random minority over-sampling with replacement
  • SMOTE - Synthetic Minority Over-sampling Technique
  • SMOTENC - SMOTE for Nominal Continuous
  • bSMOTE(1 & 2) - Borderline SMOTE of types 1 and 2
  • SVM SMOTE - Support Vectors SMOTE
  • ADASYN - Adaptive synthetic sampling approach for imbalanced learning
  • KMeans-SMOTE
  • ROSE - Random OverSampling Examples

2.3 综合采样

过采样是针对minority样本,欠采样是针对majority样本;而综合采样是既对minority样本,又对majority样本,同时进行操作的方法。主要有SMOTE+Tomek-links和SMOTE+Edited Nearest Neighbours。

综合采样的方法,是先进行过采样,在进行欠采样。

from imblearn.combine import SMOTETomek

smote_tomek = SMOTETomek(random_state=0)
X_res, y_res = smote_tomek.fit_sample(X_train, y)
X_res.groupby(['label']).size()
# label
# 0    36260
# 1    36260

2.4 模型集成

这里的模型集成主要体现在数据上,即用众多平衡的数据集(majortiry的样本进行欠采样加上minority样本)训练多个模型,然后进行集成。imblearn.ensemble提供几种常见的模型集成算法,如BalancedRandomForestClassifier

from imblearn.ensemble import BalancedRandomForestClassifier
from sklearn.datasets import make_classification

X, y = make_classification(n_samples=1000, n_classes=3,
                           n_informative=4, weights=[0.2, 0.3, 0.5],
                           random_state=0)
clf = BalancedRandomForestClassifier(max_depth=2, random_state=0)
clf.fit(X, y)  

print(clf.feature_importances_)  
print(clf.predict([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
                    0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]))

im-balance提供的模型集成的方法如下

  • Easy Ensemble classifier
  • Balanced Random Forest
  • Balanced Bagging
  • RUSBoost

2.5 调整类别权重或者样本权重

对于很多用梯度下降方法来学习(使得某个损失Loss最小)的机器学习的方法,可以通过调整类别权重或样本权重的方式,来一定程度上平衡不平衡数据。如gbdt模型lightgbm 中 class_weight

import lightgbm as lgb
clf = lgb.LGBMRegressor(num_leaves=31, 
                        min_child_samples= np.random.randint(20,25),
                        max_depth=25,
                        learning_rate=0.1, 
                        class_weight={0:1, 1:10},
                        n_estimators=500, 
                        n_jobs=30)

3. 总结

本文分享了常见的几种处理不平衡数据集的方法,并且提供imbalanced-learn的简单例子。总结如下:

  • 欠采样: 减少majoritry样本
  • 过采样:增加minority样本
  • 综合采样:先过采样,在欠采样
  • 模型集成:制造平衡数据(majoritry样本欠采样+minority样本),多次不同的欠采样,训练不同的模型,然后融合
  • 不管是欠采样和过采样,都一定程度的改变了原始数据的分布,可能造成模型过拟合。需要去尝试哪种方法,符合实际的数据分布。当然不一定有效果,去勇敢尝试吧 just do it!

4. 参考资料

  • Learning from Imbalanced Data
  • Two Modifications of CNN(Tomek links,CNN乍一看还以为卷积神经网络,其实是condensed nearest-neighbor)
  • imbalanced-learn API:https://imbalanced-learn.org/stable/

以上就是如何用 Python 处理不平衡数据集的详细内容,更多关于Python 处理不平衡数据集的资料请关注三水点靠木其它相关文章!

Python 相关文章推荐
Python爬虫DOTA排行榜爬取实例(分享)
Jun 13 Python
python 按照固定长度分割字符串的方法小结
Apr 30 Python
tensorflow使用神经网络实现mnist分类
Sep 08 Python
对python中的高效迭代器函数详解
Oct 18 Python
Python基于opencv实现的简单画板功能示例
Mar 04 Python
详解python selenium 爬取网易云音乐歌单名
Mar 28 Python
python中对数据进行各种排序的方法
Jul 02 Python
python 3.7.4 安装 opencv的教程
Oct 10 Python
PyTorch 解决Dataset和Dataloader遇到的问题
Jan 08 Python
Python 爬取必应壁纸的实例讲解
Feb 24 Python
Python编写冷笑话生成器
Apr 20 Python
如何Python使用re模块实现okenizer
Apr 30 Python
Python创建简单的神经网络实例讲解
Jan 04 #Python
python实现跨年表白神器--你值得拥有
Jan 04 #Python
Python列表元素删除和remove()方法详解
Jan 04 #Python
python3列表删除大量重复元素remove()方法的问题详解
Jan 04 #Python
关于python中remove的一些坑小结
Jan 04 #Python
python中remove函数的踩坑记录
Jan 04 #Python
python 日志模块logging的使用场景及示例
Jan 04 #Python
You might like
如何分别全角和半角以避免乱码
2006/10/09 PHP
ThinkPHP视图查询详解
2014/06/30 PHP
php和editplus正则表达式去除空白行
2015/04/17 PHP
适用于初学者的简易PHP文件上传类
2015/10/29 PHP
PHP保存session到memcache服务器的方法
2016/01/19 PHP
PHPExcel中文帮助手册|PHPExcel使用方法(分享)
2017/06/09 PHP
php使用curl获取header检测开启GZip压缩的方法
2018/08/15 PHP
对YUI扩展的Gird组件 Part-2
2007/03/10 Javascript
jquery EasyUI的formatter格式化函数代码
2011/01/12 Javascript
JavaScript 变量作用域分析
2011/07/04 Javascript
javascript编码的几个方法详细介绍
2013/01/06 Javascript
探讨js字符串数组拼接的性能问题
2014/10/11 Javascript
JavaScript中的pow()方法使用详解
2015/06/15 Javascript
js实现Select列表内容自动滚动效果代码
2015/08/20 Javascript
基于JavaScript实现动态创建表格和增加表格行数
2015/12/20 Javascript
前端微信支付js代码
2016/07/25 Javascript
JS Select下拉框(支持输入模糊查询)
2017/02/04 Javascript
详解webpack提取第三方库的正确姿势
2017/12/22 Javascript
vue element动态渲染、移除表单并添加验证的实现
2019/01/16 Javascript
vue+高德地图写地图选址组件的方法
2019/05/18 Javascript
JavaScript监听键盘事件代码实现
2020/06/03 Javascript
jquery简易手风琴插件的封装
2020/10/13 jQuery
[04:03]2014DOTA2西雅图国际邀请赛 LGD战队巡礼
2014/07/07 DOTA
浅析Python3爬虫登录模拟
2018/02/07 Python
python一键去抖音视频水印工具
2018/09/14 Python
python实现俄罗斯方块游戏(改进版)
2020/03/13 Python
Django QuerySet查询集原理及代码实例
2020/06/13 Python
基于python获取本地时间并转换时间戳和日期格式
2020/10/27 Python
Python实现简单的2048小游戏
2021/03/01 Python
HTML5 canvas基本绘图之绘制线段
2016/06/27 HTML / CSS
39美元购买一副眼镜或太阳镜:39DollarGlasses.com
2018/06/17 全球购物
C++面试题目
2013/06/25 面试题
公司财务工作总结的自我评价
2013/11/23 职场文书
英语四级考试作弊检讨书
2014/09/29 职场文书
2015应届毕业生求职信范文
2015/03/20 职场文书
家长会开场白和结束语
2015/05/29 职场文书