smote算法_探索SMOTE算法

smote算法_探索SMOTE算法SMOTE是一种综合采样人工合成数据算法,用于解决数据类别不平衡问题(Imbalancedclassproblem),以Over-sampling少数类和Under-sampling多数类结合的方式来合成数据。本文将以NiteshV.Chawla(2002)的论文为蓝本,阐述SMOTE的核心思想以及实现其朴素算法,在传统分类器(贝叶斯和决策树)上进行对比算法性能并且讨论其算法改进的途径…

大家好,又见面了,我是你们的朋友全栈君。

SMOTE是一种综合采样人工合成数据算法,用于解决数据类别不平衡问题(Imbalanced class problem),以Over-sampling少数类和Under-sampling多数类结合的方式来合成数据。本文将以 Nitesh V. Chawla(2002) 的论文为蓝本,阐述SMOTE的核心思想以及实现其朴素算法,在传统分类器(贝叶斯和决策树)上进行对比算法性能并且讨论其算法改进的途径。

1. 引言

类别不平衡是一种在分类器模型训练过程中常见的问题之一,如通过大量胸透图片来学习判断一个人是否有癌症,又如在网络流日志中学习检测可能是攻击行为的数据模式,这一类的任务中都是正常的类多于异常(诊断属于癌症,属于攻击行为)的类,在类别不平衡数据下训练出来的分类器要非常的小心,即使该分类器拥有很高的精度,因为它很可能会习得大部分的都是正常的,而我们可能需要的是它能够最大程度的识别异常行为,哪怕精度低于前者。

为了解决这一问题,业内已经有以下5种公认的方法去扩充数据集[1],以至于类别均匀:

  1. 随机的增大少数类的样本数量。
  2. 随机的增大特定少数类样本的数量。
  3. 随机的减少多数类样本的数量。
  4. 随机的减少特定多数类样本的数量。
  5. 修改代价函数,使得少数类出错的代价更高。

本文要介绍的SMOTE算法就是一种综合1,3方法的改进方式,它以每个样本点的k个最近邻样本点为依据,随机的选择N个邻近点进行差值乘上一个[0,1]范围的阈值,从而达到合成数据的目的。这种算法的核心是:特征空间上邻近的点其特征都是相似的。它并不是在数据空间上进行采样,而是在特征空间中进行采样,所以它的准确率会高于传统的采样方式。这也是为什么到目前为止SMOTE以及其派生的算法仍然是较为主流的采样技术的原因。

41d943c3d7689f43dbb9d6d274c8728d.png

如上图所示,假设数据点A在特征空间上有4个邻近点,若N为2,则SMOTE会随机选择其中2个邻近点B,C,分别计算A->B, A->C的距离,如图中绿线和红线所示,在绿线或红线上的所有采样点都是合理的,如点A1。为了确保数据点尽可能的多样(不重叠),故乘上一个[0, 1]之间的随机因子。

本文将会在第2章根据SMOTE的核心以及其伪代码实现该算法,并应用在测试数据集上;第3章会使用第三方 imbalanced-learn 库中实现的SMOTE算法进行采样,以验证我们实现的算法的准确性,当然这个库中的算法要优于朴素的SMOTE算法,之后我们会以决策树和高斯贝叶斯分类器为工具,对测试原始数据、应用我们所实现的SMOTE采样后产生的数据以及应用第三方库SMOTE产生的数据三者分别产生的数据集进行性能比较;第4章会讨论朴素SMOTE算法更加鲁棒和表现更好的优化途径;第5章是对本文的总结。

2. 算法分析与实现

下图是在SMOTE论文中提出的伪代码,由两个函数 SMOTE(T, N, K) 和 Populate(N, i, nnarray) 组成。

2cb90d4ad2162f9501541e98b0652c96.png

SMOTE 负责接受要采样的类数据集X,返回一个经过SMOTE采样后的数据集,大小为 (N/100)*T ,函数有三个参数,分别是 T: 需要处理的数据集X的样本数量; N: 采样比例,一般为100, 200, 300等整百数,对应即1倍,2倍,3倍;K: 为采样的最近邻数量,论文中默认为5 。 SMOTE 代码思想非常简单,扫描每一个样本点,计算每一个样本点的K个最近邻,将每一个最近邻样本点的索引记录在 nnarray 中,之后传入 Populate(N, i, nnarray) 中即完成一个样本点的采样。

Populate 则负责根据 nnarray 中的索引去随机生成 N 个与观测样本 i 相似的样本。该函数会计算随机邻近点 nn 与观测样本 i 点的每一个特征之间的差距 dif ,将其差距乘上一个[0, 1]随机因子 gap ,再将 dif*gap 的值加上观测点 i 即完成了一个特征的合成。

在Python中实现如下:

注:为了保证本文中所有代码的可复现性,设置的 random_state 均为 666

def NaiveSMOTE(X, N=100, K=5):    """    {X}: minority class samples;    {N}: Amount of SMOTE; default 100;    {K} Number of nearest; default 5;    """    # {T}: Number of minority class samples;     T = X.shape[0]    if N < 100:        T = (N/100) * T        N = 100    N = (int)(N/100)        numattrs = X.shape[1]    samples = X[:T]    neigh = NearestNeighbors(n_neighbors=K)    neigh.fit(samples)    Synthetic = np.zeros((T*N, numattrs))    newindex = 0        def Populate(N, i, nns, newindex):        """        Function to generate the synthetic samples.        """        for n in range(N):            nn = np.random.randint(0, K)            for attr in range(numattrs):                dif = samples[nns[nn], attr] - samples[i, attr]                gap = np.random.random()                Synthetic[newindex, attr] = samples[i, attr] + gap*dif            newindex += 1        return newindex        for i in range(T):        nns = neigh.kneighbors([samples[i]], K, return_distance=False)        newindex = Populate(N, i, nns[0], newindex)    return Synthetic

这里没有采用矩阵运算,而是完完全全的按照论文中的方式复现(所以称为NaiveSMOTE),其中最近邻的计算我们使用 scikit-learn 提供的 NearestNeighbors 类完成。

接下来我们使用 scikit-learn 中的 make_classification 来生成测试分类数据集,模拟不平衡类数据,当然有兴趣的读者也可以去寻找论文中所使用的数据集。

from sklearn.datasets import make_classificationX, y = make_classification(n_samples=500,                           n_features=9,                           n_redundant=3,                           weights=(0.1, 0.9),                           n_clusters_per_class=2,                           random_state=666)   # 为了可复现性print(X.shape, y.shape)       # ((500, 9), (500,))# 查看y的各类样本数 print(view_y(y))              # class 0: 50  class 1: 450

原数据集的分布如下图所示,其中红色圆圈为正类即少数类,蓝色×为负类即多数类。

b8bd72092fefe2e3514a65df7750223b.png

将我们实现的 NaiveSMOTE 应用在此测试数据上:

X_over_sampling = NaiveSMOTE(X[y==0], N=800)print(X_over_sampling.shape)         # (400, 9) 新增了400个样本# 将合成数据与原数据集合并new_X = np.r_[X, X_over_sampling]new_y = np.r_[y, np.zeros((X_over_sampling.shape[0]))]print(new_X.shape, new_y.shape)      # ((900, 9), (900,))print(view_y(new_y))                 # class 0: 450 class 1: 450

接下来我们将原数据集与经过 NaiveSMOTE 合成后的数据集进行比对:

095bec586299b9eed37609c98a2ac2d9.png

可以很清晰的看见原来的类增大至一个满意的水平,并且生成的类之间距离都相距不远。

3. 算法性能比对

本章我们将引入第三方库 imbalanced-learn 中提供的 SMOTE 类与依据论文实现的 NaiveSMOTE 进行比较。两者都是基于同一个论文的思想去实现的,只是第三方库中实现的 SMOTE 更为鲁棒,并且能够综合考虑所有的类,是一种完全意义上的 Combination of Over-sampling minority class and Under-sampling majority class 技术。因此我们引入它只为了验证我们所复现的方法的准确性。

from imblearn.over_sampling import SMOTEsm = SMOTE(random_state=666)X_res, y_res = sm.fit_resample(X, y)     # 即完成了合成print(X_res.shape, y_res.shape)          # ((900, 9), (900,))

下图对比 imblearn 的 SMOTE 与我们复现的 NaiveSMOTE 生成的数据集:

a756240f5486e9ec5897a3f7f070d02f.png

能看出 NaiveeSMOTE 合成的数据更加倾向于中部,而第三方的 SMOTE 能够综合考虑全局情况下方区域生成的数据要比 NaiveSMOTE 的多。

接下来我们使用 DecisionTree 和 GaussianNaive 来验证3个数据集(原数据集、NaiveSMOTE合成的数据集和第三方SMOTE合成的数据集)的ROC曲线,具体代码见附录中的 Notebook 文件

原数据的ROC曲线

c57965b0257c27a0ffdd74e912eb078e.png

NaiveSMOTE生成的数据的ROC曲线

34453cefbb821cf741bf1bff872d9e5b.png

第三方SMOTE生成的数据的ROC曲线

a3d82d628628bf5772f7c0cd9205bfe2.png

可以看出 NaiveSMOTE 与 imblearn 的 SMOTE 生成的数据的 AUC 面积均大于原始数据的面积。 imblearn 的 SMOTE 生成的数据在 GaussianNaiveBayes 分类器上的表现要好于 NaiveSMOTE 所生成的数据训练出来的分类器。

4. 算法改进

这部分我们从 NaiveSMOTE 的三个方面进行优化讨论:

  1. 处理速度。 NaiveSMOTE 中有许多处都可以改成使用矩阵运算的方式,这样会提高数据处理的速度。并且 Populate 函数也显得非常冗余,可以用矩阵运算将其改写。
  2. 全局合理性。 全局合理性包括两个方面:合成数据比率的合理性和合成数据在全局的合理性。合成数据比率的合理性:在 NaiveSMOTE 中可以知道样本的数量有 N 合成比率来控制,只能合成其整数倍,本文中使用的数据集恰好是 1:9 ,只要合成原始数据的8倍即可是两类都到达一个相对数量同等的水平,但是在现实数据集中大部分都不具备成倍的数量关系,因此可以考虑更换一个更好的生成比率,使得每个类均能处于相对数量近似的水平,避免出现合成后的原少数类变多数类的情况。合成数据在全局的合理性:回想在 NaiveSMOTE 与 imblearn SMOTE 各自合成的数据对比中可以发现, NaiveSMOTE 更加容易使得合成的数据聚集在某一样本点附近,而 imblearn SMOTE 所合成的数据更为稀疏且分布均匀,更加接近原始数据的概率分布。其原因在于 NaiveSMOTE 在进行合成时只考虑原始的数据样本,没不考虑合成后的数据样本会如何影响全局数据。可以考虑在每次合成数据后将其加入数据集,在处理过程中将合成数据也加入考虑范围。
  3. 鲁棒性。 不难发现 NaiveSMOTE 仅能够处理数值型的数据并且其距离计算公式很有可能产生误解。在现实中有许多非数值型的数据,如 性别 , 职业 等等。当然可以将其全部重新编码成可以应用数值处理的数据,如将性别进行 OneHot 编码,但是此时的距离计算公式就会出现误解,可以考虑更换为欧氏距离、曼哈顿距离或者马氏距离等。

Note:在对性别进行 OneHot 编码时情况如下:

男性: 0 1女性: 1 0

如果按照 NaiveSMOTE 原始的距离计算公式,很有可能会将其理解为男性和女性的差距为1,因此产生误解。

5. 结论

本文对三种数据进行对比,经过 NaiveSMOTE 和 imblearn SMOTE 合成后的数据在传统分类器上的表现均好于原始数据(即不做任何修改),且 imblearn SMOTE 在鲁棒性上要高于 NaiveSMOTE 。讨论 NaiveSMOTE 的不足与其可能的优化方向。建议在实际应用中优先考虑鲁棒性更高的 imlearn SMOTE 而不是自己造轮子, imblearn SMOTE 的实现更加符合主流标准。但不能因此就忽略了 NaiveSMOTE 的意义,任何的优化有必要要基于原有的基础。理解 NaiveSMOTE 才能去更好的使用和优化它。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/148992.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 计算机病毒的分类

    计算机病毒的分类病毒与木马病毒:指编制或在计算机程序中插入的破坏计算机功能或破坏数据,影响计算机使用并且能够自我复制的一组计算机指令或程序代码。木马:是一种后门程序,被黑客用作控制远程计算机的工具。木马与病毒不同的是,木马不会自我繁殖,并不会刻意地感染其他文件,它的作用就是为黑客打开远程计算机的门户,从而可以让黑客来远程控制计算机,使黑客获取有用的信息。病毒是自动破坏目标计算机,而木马需要人为的去操控…

  • Unity3d场景快速烘焙【2020】

    Unity3d场景快速烘焙【2020】很多刚刚接触Unity3d的童鞋花了大量的时间自学,可总是把握不好Unity3d的烘焙,刚从一个坑里爬出来,又陷入另一个新的坑,每次烘焙一个场景少则几个小时,多则几十个小时,机器总是处于假死机状态,半天看不到结果,好不容易烘焙完了,黑斑、撕裂、硬边、漏光或漏阴影等缺陷遍布,惨不忍睹,整体效果暗无层次,或者苍白无力,灯光该亮的亮不起来,该暗的暗不下去,更谈不上有什么意境,痛苦的折磨,近乎失去了信心,一个团队从建模到程序,都没什么问题,可一到烘焙这一关,就堵得心塞,怎么也搞不出好的视觉效果,作品没法及时向用户交

  • winzip15.0许可证

    winzip15.0许可证

    2021年12月30日
  • sprintf、fprintf和printf这三个函数有什么区别?

    sprintf、fprintf和printf这三个函数有什么区别?

  • Eclipse安装Activiti教程

    Eclipse安装Activiti教程方式一:在线安装(坑,一般都安装不成功),可以直接看方式二1.点击eclipse上方工具栏的Help,选择InstallNewSoftware2、弹出如下窗口,然后填写插件名称和安装地址Name:ActivitiBPMN2.0designerLocation:http://activiti.org/designer/update/然后便是不停的next和finish了,组图如下点击Next点击Next点击Next点击Finish3、安

  • 电容是根据什么分类_电容的分类与识别图片

    电容是根据什么分类_电容的分类与识别图片一、瓷介电容器(CC)1.结构用陶瓷材料作介质,在陶瓷表面涂覆一层金属(银)薄膜,再经高温烧结后作为电极而成。瓷介电容器又分1类电介质(NPO、CCG));2类电介质(X7R、2X1)和3类电介质(Y5V、2F4)瓷介电容器。2.特点1类瓷介电容器具有温度系数小、稳定性高、损耗低、耐压高等优点。最大容量不超过1000pF,常用的有CC1、CC2、CC18A、CC11、CCG等系…

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号