Index

1、到底什么是不平衡数据

2、处理不平衡数据的理论方法

3、Python里有什么包可以处理不平衡样本

4、Python中具体如何处理失衡样本

印象中很久之前有位朋友说要我写一篇如何处理不平衡数据的文章,整理相关的理论与实践知识(可惜本人太懒了,现在才开始写),于是乎有了今天的文章。失衡样本在我们真实世界中是十分常见的,那么我们在机器学习(ML)中使用这些失衡样本数据会出现什么问题呢?如何处理这些失衡样本呢?以下的内容希望对你有所帮助!

到底什么是不平衡数据

失衡数据发生在分类应用场景中,在分类问题中,类别之间的分布不均匀就是失衡的根本,假设有个二分类问题,target为y,那么y的取值范围为0和1,当其中一方(比如y=1)的占比远小于另一方(y=0)的时候,就是失衡样本了。

那么到底是需要差异多少,才算是失衡呢,根本Google Developer的说法,我们一般可以把失衡分为3个程度:

    轻度:20-40%

    中度:1-20%

    极度:<1%

一般来说,失衡样本在我们构建模型的时候看不出什么问题,而且往往我们还可以得到很高的accuracy,为什么呢?假设我们有一个极度失衡的样本,y=1的占比为1%,那么,我们训练的模型,会偏向于把测试集预测为0,这样子模型整体的预测准确性就会有一个很好看的数字,如果我们只是关注这个指标的话,可能就会被骗了。

 处理不平衡数据的理论方法

在我们开始用Python处理失衡样本之前,我们先来了解一波关于处理失衡样本的一些理论知识,前辈们关于这类问题的解决方案,主要包括以下:

    从数据角度:通过应用一些欠采样or过采样技术来处理失衡样本。欠采样就是对多数类进行抽样,保留少数类的全量,使得两类的数量相当,过采样就是对少数类进行多次重复采样,保留多数类的全量,使得两类的数量相当。但是,这类做法也有弊端,欠采样会导致我们丢失一部分的信息,可能包含了一些重要的信息,过采样则会导致分类器容易过拟合。当然,也可以是两种技术的相互结合。

    从算法角度:算法角度的解决方案就是可以通过对每类的训练实例给予一定权值的调整。比如像在SVM这样子的有参分类器中,可以应用grid search(网格搜索)以及交叉验证(cross validation)来优化C以及gamma值。而对于决策树这类的非参数模型,可以通过调整树叶节点上的概率估计从而实现效果优化。

此外,也有研究员从数据以及算法的结合角度来看待这类问题,提出了两者结合体的AdaOUBoost(adaptive over-sampling and undersampling boost)算法,这个算法的新颖之处在于自适应地对少数类样本进行过采样,然后对多数类样本进行欠采样,以形成不同的分类器,并根据其准确度将这些子分类器组合在一起从而形成强大的分类器,更多的请参考:

    AdaOUBoost:https://dl.acm.org/doi/10.1145/1743384.1743408

Python里有什么包可以处理不平衡样本

这里介绍一个很不错的包,叫 imbalanced-learn,大家可以在电脑上安装一下使用。

    官方文档:https://imbalanced-learn.readthedocs.io/en/stable/index.html

pip install -U imbalanced-learn

使用上面的包,我们就可以实现样本的欠采样、过采样,并且可以利用pipeline的方式来实现两者的结合,十分方便,我们下一节来简单使用一下吧!

 Python中具体如何处理失衡样本

为了更好滴理解,我们引入一个数据集,来自于UCI机器学习存储库的营销活动数据集。(数据集大家可以自己去官网下载:https://archive.ics.uci.edu/ml/machine-learning-databases/00222/  下载bank-additional.zip 或者到公众号后台回复关键字“bank”来获取吧。)

我们在完成imblearn库的安装之后,就可以开始简单的操作了(其余更加复杂的操作可以直接看官方文档),以下我会从4方面来演示如何用Python处理失衡样本,分别是:

1、随机欠采样的实现

 2、使用SMOTE进行过采样

3、欠采样和过采样的结合(使用pipeline)

4、如何获取最佳的采样率?

 那我们开始吧!

    # 导入相关的库(主要就是imblearn库)

    from collections import Counter

    from sklearn.model_selection import train_test_split

    from sklearn.model_selection import cross_val_score

    import pandas as pd

    import numpy as np

    import warnings

    warnings.simplefilter(action='ignore', category=FutureWarning)

    from sklearn.svm import SVC

    from sklearn.metrics import classification_report, roc_auc_score

    from numpy import mean

    # 导入数据

    df = pd.read_csv(r'./data/bank-additional/bank-additional-full.csv', ';') # '';'' 为分隔符

    df.head()

[点击并拖拽以移动]

数据集是葡萄牙银行的某次营销活动的数据,其营销目标就是让客户订阅他们的产品,然后他们通过与客户的电话沟通以及其他渠道获取到的客户信息,组成了这个数据集。

关于字段释义,可以看下面的截图:

[点击并拖拽以移动]

我们可以大致看看数据集是不是失衡样本:

    df['y'].value_counts()/len(df)

    #no     0.887346

    #yes    0.112654

    #Name: y, dtype: float64

可以看出少数类的占比为11.2%,属于中度失衡样本。

    # 只保留数值型变量(简单操作)

    df = df.loc[:,

    ['age', 'duration', 'campaign', 'pdays',

           'previous', 'emp.var.rate', 'cons.price.idx',

           'cons.conf.idx', 'euribor3m', 'nr.employed','y']]

    # target由 yes/no 转为 0/1

    df['y'] = df['y'].apply(lambda x: 1 if x=='yes' else 0)

    df['y'].value_counts()

    #0    36548

    #1     4640

    #Name: y, dtype: int64

 1、随机欠采样的实现

欠采样在imblearn库中也是有方法可以用的,那就是 under_sampling.RandomUnderSampler,我们可以使用把方法引入,然后调用它。可见,原先0的样本有21942,欠采样之后就变成了与1一样的数量了(即2770),实现了50%/50%的类别分布。

    # 1、随机欠采样的实现

    # 导入相关的方法

    from imblearn.under_sampling import RandomUnderSampler

    # 划分因变量和自变量

    X = df.iloc[:,:-1]

    y = df.iloc[:,-1]

    # 划分训练集和测试集

    X_train, X_test, y_train, y_test = train_test_split(X,y,test_size=0.40)

    # 统计当前的类别占比情况

    print("Before undersampling: ", Counter(y_train))

    # 调用方法进行欠采样

    undersample = RandomUnderSampler(sampling_strategy='majority')

    # 获得欠采样后的样本

    X_train_under, y_train_under = undersample.fit_resample(X_train, y_train)

    # 统计欠采样后的类别占比情况

    print("After undersampling: ", Counter(y_train_under))

    # 调用支持向量机算法 SVC

    model=SVC()

    clf = model.fit(X_train, y_train)

    pred = clf.predict(X_test)

    print("ROC AUC score for original data: ", roc_auc_score(y_test, pred))

    clf_under = model.fit(X_train_under, y_train_under)

    pred_under = clf_under.predict(X_test)

    print("ROC AUC score for undersampled data: ", roc_auc_score(y_test, pred_under))

    # Output:

    #Before undersampling:  Counter({0: 21942, 1: 2770})

    #After undersampling:  Counter({0: 2770, 1: 2770})

    #ROC AUC score for original data:  0.603521152028

    #ROC AUC score for undersampled data:  0.829234085179

 2、使用SMOTE进行过采样

过采样技术中,SMOTE被认为是最为流行的数据采样算法之一,它是基于随机过采样算法的一种改良版本,由于随机过采样只是采取了简单复制样本的策略来进行样本的扩增,这样子会导致一个比较直接的问题就是过拟合。因此,SMOTE的基本思想就是对少数类样本进行分析并合成新样本添加到数据集中。

    算法流程如下:

    (1)对于少数类中每一个样本x,以欧氏距离为标准计算它到少数类样本集中所有样本的距离,得到其k近邻。

    (2)根据样本不平衡比例设置一个采样比例以确定采样倍率N,对于每一个少数类样本x,从其k近邻中随机选择若干个样本,假设选择的近邻为xn。

    (3)对于每一个随机选出的近邻xn,分别与原样本按照如下的公式构建新的样本。

[点击并拖拽以移动]

    # 2、使用SMOTE进行过采样

    # 导入相关的方法

    from imblearn.over_sampling import SMOTE

    # 划分因变量和自变量

    X = df.iloc[:,:-1]

    y = df.iloc[:,-1]

    # 划分训练集和测试集

    X_train, X_test, y_train, y_test = train_test_split(X,y,test_size=0.40)

    # 统计当前的类别占比情况

    print("Before oversampling: ", Counter(y_train))

    # 调用方法进行过采样

    SMOTE = SMOTE()

    # 获得过采样后的样本

    X_train_SMOTE, y_train_SMOTE = SMOTE.fit_resample(X_train, y_train)

    # 统计过采样后的类别占比情况

    print("After oversampling: ",Counter(y_train_SMOTE))

    # 调用支持向量机算法 SVC

    model=SVC()

    clf = model.fit(X_train, y_train)

    pred = clf.predict(X_test)

    print("ROC AUC score for original data: ", roc_auc_score(y_test, pred))

    clf_SMOTE= model.fit(X_train_SMOTE, y_train_SMOTE)

    pred_SMOTE = clf_SMOTE.predict(X_test)

    print("ROC AUC score for oversampling data: ", roc_auc_score(y_test, pred_SMOTE))

    # Output:

    #Before oversampling:  Counter({0: 21980, 1: 2732})

    #After oversampling:  Counter({0: 21980, 1: 21980})

    #ROC AUC score for original data:  0.602555700614

    #ROC AUC score for oversampling data:  0.844305732561

 3、欠采样和过采样的结合(使用pipeline)

那如果我们需要同时使用过采样以及欠采样,那该怎么做呢?其实很简单,就是使用 pipeline来实现。

    #  3、欠采样和过采样的结合(使用pipeline)

    # 导入相关的方法

    from imblearn.over_sampling import SMOTE

    from imblearn.under_sampling import RandomUnderSampler

    from imblearn.pipeline import Pipeline

    # 划分因变量和自变量

    X = df.iloc[:,:-1]

    y = df.iloc[:,-1]

    #  定义管道

    model = SVC()

    over = SMOTE(sampling_strategy=0.4)

    under = RandomUnderSampler(sampling_strategy=0.5)

    steps = [('o', over), ('u', under), ('model', model)]

    pipeline = Pipeline(steps=steps)

    # 评估效果

    scores = cross_val_score(pipeline, X, y, scoring='roc_auc', cv=5, n_jobs=-1)

    score = mean(scores)

    print('ROC AUC score for the combined sampling method: %.3f' % score)

    # Output:

    #ROC AUC score for the combined sampling method: 0.937

 4、如何获取最佳的采样率?

在上面的栗子中,我们都是默认经过采样变成50:50,但是这样子的采样比例并非最优选择,因此我们引入一个叫 最佳采样率的概念,然后我们通过设置采样的比例,采样网格搜索的方法去找到这个最优点。

    # 4、如何获取最佳的采样率?

    # 导入相关的方法

    from imblearn.over_sampling import SMOTE

    from imblearn.under_sampling import RandomUnderSampler

    from imblearn.pipeline import Pipeline

    # 划分因变量和自变量

    X = df.iloc[:,:-1]

    y = df.iloc[:,-1]

    # values to evaluate

    over_values = [0.3,0.4,0.5]

    under_values = [0.7,0.6,0.5]

    for o in over_values:

      for u in under_values:

        # define pipeline

        model = SVC()

        over = SMOTE(sampling_strategy=o)

        under = RandomUnderSampler(sampling_strategy=u)

        steps = [('over', over), ('under', under), ('model', model)]

        pipeline = Pipeline(steps=steps)

        # evaluate pipeline

        scores = cross_val_score(pipeline, X, y, scoring='roc_auc', cv=5, n_jobs=-1)

        score = mean(scores)

        print('SMOTE oversampling rate:%.1f, Random undersampling rate:%.1f , Mean ROC AUC: %.3f' % (o, u, score))

        

    # Output:    

    #SMOTE oversampling rate:0.3, Random undersampling rate:0.7 , Mean ROC AUC: 0.938

    #SMOTE oversampling rate:0.3, Random undersampling rate:0.6 , Mean ROC AUC: 0.936

    #SMOTE oversampling rate:0.3, Random undersampling rate:0.5 , Mean ROC AUC: 0.937

    #SMOTE oversampling rate:0.4, Random undersampling rate:0.7 , Mean ROC AUC: 0.938

    #SMOTE oversampling rate:0.4, Random undersampling rate:0.6 , Mean ROC AUC: 0.937

    #SMOTE oversampling rate:0.4, Random undersampling rate:0.5 , Mean ROC AUC: 0.938

    #SMOTE oversampling rate:0.5, Random undersampling rate:0.7 , Mean ROC AUC: 0.939

    #SMOTE oversampling rate:0.5, Random undersampling rate:0.6 , Mean ROC AUC: 0.938

    #SMOTE oversampling rate:0.5, Random undersampling rate:0.5 , Mean ROC AUC: 0.938

从结果日志来看,最优的采样率就是过采样0.5,欠采样0.7。

最后,想和大家说的是没有绝对的套路,只有合适的套路,无论是欠采样还是过采样,只有合适才最重要。还有,欠采样的确会比过采样“省钱”哈(从训练时间上很直观可以感受到)。

本文的文字及图片来源于网络,仅供学习、交流使用,不具有任何商业用途,如有问题请及时联系我们以作处理

想要获取更多Python学习资料可以加QQ:2955637827私聊或加Q群630390733大家一起来学习讨论吧!

如何在Python中处理不平衡数据的更多相关文章

  1. 如何在Python中从零开始实现随机森林

    欢迎大家前往云+社区,获取更多腾讯海量技术实践干货哦~ 决策树可能会受到高度变异的影响,使得结果对所使用的特定测试数据而言变得脆弱. 根据您的测试数据样本构建多个模型(称为套袋)可以减少这种差异,但是 ...

  2. 如何在Python中使用Linux epoll

    如何在Python中使用Linux epoll 内容 介绍 阻塞套接字编程示例 异步套接字和Linux epoll的好处 epoll的异步套接字编程示例 性能考量 源代码 介绍 从2.6版开始,Pyt ...

  3. 面试官问我:如何在 Python 中解析和修改 XML

    摘要:我们经常需要解析用不同语言编写的数据.Python提供了许多库来解析或拆分用其他语言编写的数据.在此 Python XML 解析器教程中,您将学习如何使用 Python 解析 XML. 本文分享 ...

  4. 如何在Python中加速信号处理

    如何在Python中加速信号处理 This post is the eighth installment of the series of articles on the RAPIDS ecosyst ...

  5. python中json格式数据输出实现方式

    python中json格式数据输出实现方式 主要使用json模块,直接导入import json即可. 小例子如下: #coding=UTF-8 import json info={} info[&q ...

  6. 分析Python中解析构建数据知识

    分析Python中解析构建数据知识 Python 可以通过各种库去解析我们常见的数据.其中 csv 文件以纯文本形式存储表格数据,以某字符作为分隔值,通常为逗号:xml 可拓展标记语言,很像超文本标记 ...

  7. 如何在Python中快速画图——使用Jupyter notebook的魔法函数(magic function)matplotlib inline

    如何在Python中快速画图--使用Jupyter notebook的魔法函数(magic function)matplotlib inline 先展示一段相关的代码: #we test the ac ...

  8. Electron-vue实战(三)— 如何在Vuex中管理Mock数据

    Electron-vue实战(三)— 如何在Vuex中管理Mock数据 作者:狐狸家的鱼 本文链接:Vuex管理Mock数据 GitHub:sueRimn 在vuex中管理mock数据 关于vuex的 ...

  9. 如何在Python 中使用UTF-8 编码 && Python 使用 注释,Python ,UTF-8 编码 , Python 注释

    如何在Python 中使用UTF-8 编码 && Python 使用 注释,Python ,UTF-8 编码 , Python  注释 PIP $ pip install beauti ...

随机推荐

  1. 实战教程:如何将自己的Python包发布到PyPI上

    1. PyPi的用途 Python中我们经常会用到第三方的包,默认情况下,用到的第三方工具包基本都是从Pypi.org里面下载. 我们举个栗子: 如果你希望用Python实现一个金融量化分析工具,目前 ...

  2. Centos7安装vscode

    CentOS7 安装vscode                              最近在Linux环境下写几个程序时发现用vim时总出现一点问题,配置了vim也还是不太习惯,因此就安装了vs ...

  3. 《HelloGitHub》第 56 期

    兴趣是最好的老师,HelloGitHub 就是帮你找到兴趣! 简介 分享 GitHub 上有趣.入门级的开源项目. 这是一个面向编程新手.热爱编程.对开源社区感兴趣 人群的月刊,月刊的内容包括:各种编 ...

  4. SpringBoot2整合Redis

    pom.xml <dependency> <groupId>org.springframework.boot</groupId> <artifactId> ...

  5. Java基础教程——包装类

    Java出道之时,自诩为"纯面向对象的语言",意思是之前的所谓"面向对象语言"不纯. 但是,有人指责Java也不纯--8种基本类型并非类类型.为此,Java为他 ...

  6. 11_SurfaceView绘图

    学习SurfaceView绘图API的使用方法,为接下来使用SurfaceView开发游戏做准备. 1 package com.example.surfaceview; 2 3 import andr ...

  7. Windows 10系统运维之OpenSSH

    随着PowerShell和OpenSSH的日渐成熟,在客户终端Windows居多的运维之中,使用Win32-OpenSSH和Powershell来管理一些客户机变成了相当实用的一种解决方案. Open ...

  8. Cys_Control(一) 项目搭建

    一.基础工程搭建 Cys_Controls Cys_Resource(注:一般类库默认不能引入资源文件,故直接创建Custom Control Library) Cys_Demo 删除默认文件夹及类, ...

  9. Mysql-索引分析查询性能

    explain 全文只有一个关键点,那就是explain,explain 显示了MySQL如何使用索引来处理select语句以及连接表.可以帮助选择更好的索引和写出更优化的查询语句.简单讲,它的作用就 ...

  10. 第8.27节 Python中__getattribute__与property的fget、@property装饰器getter关系深入解析

    一. 引言 在<第7.23节 Python使用property函数定义属性简化属性访问的代码实现>和<第7.26节 Python中的@property装饰器定义属性访问方法gette ...