不平衡分类(二)-过采样(SMOTE)【Synthetic Minority Over-Sampling Technique ,“人工少数类过采样法“】

不平衡分类(二)-过采样(SMOTE)【Synthetic Minority Over-Sampling Technique ,“人工少数类过采样法“】,第1张

SMOTE的全称是Synthetic Minority Over-Sampling Technique 即“人工少数类过采样法”,非直接对少数类进行重采样,而是设计算法来人工合成一些新的少数样本。

一、SMOTE原理 1、SMOTE步骤__1.选一个正样本

红色圈覆盖

2、SMOTE步骤__2.找到该正样本的K个近邻(假设K = 3)

3、SMOTE步骤__3.随机从K个近邻中选出一个样本

绿色的

4、SMOTE步骤__4.在正样本和随机选出的这个近邻之间的连线上,随机找一点。这个点就是人工合成的新正样本了

二、调包实现
imblearn.over_sampling.SMOTE(

sampling_strategy = ‘auto’,

random_state = None, ## 随机器设定

k_neighbors = 5, ## 用相近的 5 个样本(中的一个)生成正样本

m_neighbors = 10, ## 当使用 kind={'borderline1', 'borderline2', 'svm'}

out_step =0.5, ## 当使用kind = 'svm'

kind = 'regular', ## 随机选取少数类的样本

– borderline1: 最近邻中的随机样本b与该少数类样本a来自于不同的类

– borderline2: 随机样本b可以是属于任何一个类的样本;

– svm:使用支持向量机分类器产生支持向量然后再生成新的少数类样本

svm_estimator = SVC(), ## svm 分类器的选取

n_jobs = 1, ## 使用的例程数,为-1时使用全部CPU

ratio=None )
from imblearn.over_sampling import SMOTE
 
sm = SMOTE(random_state = 42, n_jobs = -1)
x, y = sm.fit_sample(x_val, y_val)

仅用正样本的K近邻生成新正样本是正是SMOTE方法,考虑到(SMOTE的最终目的是分清正负样本的边界),所以需要对样本生成进行优化

利用Python的第三方包imbalanced_learn实现SMOTE算法:

@author:Dylan;
@desc:imbalanced_learn
2018/5/21
"""
#-*- encoding:utf-8 -*-
import matplotlib.pyplot as plt
from sklearn.datasets import make_classification
from sklearn.decomposition import PCA
from imblearn.over_sampling import  SMOTE

print(__doc__)

def plot_resampling(ax,X,y,title):
    c0=ax.scatter(X[y==0,0],X[y==0,1],label="Class #0",alpha=0.5)
    c1=ax.scatter(X[y==1,0],X[y==1,1],label="Class #1",alpha=0.5)
    ax.set_title(title)
    ax.spines['top'].set_visible(False)
    ax.spines['right'].set_visible(False)
    ax.get_xaxis().tick_bottom()
    ax.get_yaxis().tick_left()
    ax.spines['left'].set_position(('outward',10))
    ax.spines['bottom'].set_position(('outward',10))
    ax.set_xlim([-6,8])
    ax.set_ylim([-6,6])

    return c0,c1

if __name__=='__main__':
    X,y=make_classification(n_classes=2,class_sep=2,weights=[0.3,0.7],
                            n_informative=3,n_redundant=1,flip_y=0,
                            n_features=20,n_clusters_per_class=1,n_samples=80,random_state=10)
    ##使用PCA降维到两维,方便进行可视化
    pca=PCA(n_components=2)
    X_vis=pca.fit_transform(X)

    ###运用SMOTE算法
    kind=['regular','borderline1','borderline2','svm']
    sm=[SMOTE(kind=k) for k in kind]
    X_resampled=[]
    y_resampled=[]
    X_res_vis=[]
    for method in sm:
        X_res,y_res=method.fit_sample(X,y)
        X_resampled.append(X_res)
        y_resampled.append(y_res)
        X_res_vis.append(pca.fit_transform(X_res))

    f,((ax1,ax2),(ax3,ax4),(ax5,ax6))=plt.subplots(3,2)

    ##展示结果
    ax2.axis('off')
    ax_res=[ax3,ax4,ax5,ax6]

    c0,c1=plot_resampling(ax1,X_vis,y,'original_set')

    for i in range(len(kind)):
        plot_resampling(ax_res[i],X_res_vis[i],y_resampled[i],'smote{}'.format(kind[i]))

    ax2.legend((c0,c1),('Class #0','Class #1'),loc='center',ncol=1,labelspacing=0.)
    plt.tight_layout()
    plt.show()

这段代码中,使用了sklearn简单是生成了一个不平衡的样本,使用了imblearn.over_sampling的SMOTE算法进行了过采样处理.生成结果如下图所示:

上图中.图1是原始数据的分布,图3-6分别是采样 ‘regular’,‘borderline1’,‘borderline2’,'svm’这四种类型处理方法的结果.个人倾向于使用svm.

使用SMOTE算法可以解决随机复制样本带来的问题,但是也可能会存在重叠的问题,因此基于smote算法提出了Borderline算法.

三、SMOTE优化

仅用正样本的K近邻生成新正样本是正是SMOTE方法,考虑到(SMOTE的最终目的是分清正负样本的边界),所以需要对样本生成进行优化。

Borderline-SMOTE算法

  • 该算法对SMOTE算法中的少数类的最近邻加以限制,在Borderline-SMOTE中,若少数类样本的每个样本xi求k近邻,记作Si−knn,且Si−knn属于整个样本集合
  • S而不再是少数类样本,若满足 k/2<|si−knn∩smax|
  • 将DANGER当作SMOTE种子样本的输入生成新样本.特别地,当上述条件取右边界,即k近邻中全部样本都是多数类时此样本不会被选择为种样本生成新样本,

此情况下的样本为噪音。

1、borderline1 方法简述

Dgr = [] # 危险集

for i in 正样本:

      1) 计算点 i 在训练集 D 上的 m 个最近邻。

         x =  i 的最近邻中属于负样本的数量

      2) 如果 x  = m,则 p 是一个噪声

          next

      3) 如果 0 ≤ x ≤ m/2, 则说明p很安全

          next

      4) 如果 m/2 ≤ x ≤ m, 那么点p就很危险了,我们需要在这个点附近生成一些新的少数类点

         Dgr.append(x)

最后,对于每个在危险集(Dgr)中的点,使用SMOTE算法生成新的样本

2、borderline2 方法简述

前面1-4步骤均同 borderline1 方法

在最后进行SMOTE的时候:

采用了 比例分配 生成新样本

for i in Dgr:

    1) 正样本 K 个近邻

    2) 负样本 K 个近邻

    3) 正样本 K 个近邻选取 alpha 比例的样本点

     和 i 作随机的线性插值 ==>> 新正样本点

    4) 负样本K个近邻选取 (1 - alpha) 比例的样本点

     和 i 作随机的线性插值 ==>> 新正样本点
四、SMOTE算法应用 1、人工实现SMOTE算法
#! /user/bin/python 3
# -*- coding: utf-8 -*-
# author: Scc_hy
# 2018-11-17
# SMOTE
from sklearn.neighbors import NearestNeighbors
import numpy as np
import pandas as pd
import copy
from sklearn.datasets import load_iris
from sklearn.ensemble import RandomForestClassifier


class TWO_SMOTE():
    """
    不平二分类人工插值法采样
    """

    def __init__(self,
                 K_neighbors=5,
                 N_need=200,
                 random_state=42):
        self.K_neighbors = K_neighbors
        self.N_need = N_need
        self.random_state = 42

    def get_param_describe(self):
        print(
            "算法参数: \n" +
            'K_neighbors: 和正样本相近的随机样本数' + "\n" +
            "N_need: 需要增加的正样本数 (N_need // 100 * a)" + "\n" +
            "random_state: 随机器设定" + "\n"
                                    "\nover_sample 参数:\n" +
            "x_data: 需要进行过采样的全部数据集(非文本DataFrame)" + "\n" +
            "y_label: 类别标签(非文本DataFrame.Series)" + "\n"
        )

    def div_data(self, x_data, y_label):
        """
        将数据依据类分开
        """
        tp = set(y_label)
        tp_less = [a for a in tp if sum(y_label == a) < sum(y_label != a)][0]
        data_less = x_data.iloc[y_label == tp_less, :]
        data_more = x_data.iloc[y_label != tp_less, :]
        tp.remove(tp_less)
        return data_less, data_more, tp_less, list(tp)[0]

    def get_SMOTE_sample(self, x_data, y_label):
        """
        获取需要抽样的正样本
        """
        sample = []
        data_less, data_more, tp_less, tp_more = self.div_data(x_data, y_label)
        n_integ = self.N_need // 100
        data_add = copy.deepcopy(data_less)
        if n_integ == 0:
            print('WARNING: PLEASE RE-ENTER N_need')
        else:
            for i in range(n_integ - 1):
                data_out = data_less.append(data_add)

        data_out.reset_index(inplace=True, drop=True)
        return data_out, tp_less

    def over_sample(self, x_data, y_label):
        """
        SMOTE算法简单实现
        """
        sample, tp_less = self.get_SMOTE_sample(x_data, y_label)
        knn = NearestNeighbors(n_neighbors=self.K_neighbors, n_jobs=-1).fit(sample)
        n_atters = x_data.shape[1]
        label_out = copy.deepcopy(y_label)
        new = pd.DataFrame(columns=x_data.columns)
        for i in range(len(sample)):  # 1. 选择一个正样本
            # 2.选择少数类中最近的K个样本
            k_sample_index = knn.kneighbors(np.array(sample.iloc[i, :]).reshape(1, -1),
                                            n_neighbors=self.K_neighbors + 1,
                                            return_distance=False)

            # 计算插值样本
            # 3.随机选取K中的一个样本
            np.random.seed(self.random_state)
            choice_all = k_sample_index.flatten()
            choosed = np.random.choice(choice_all[choice_all != 0])

            # 4. 在正样本和随机样本之间选出一个点
            diff = sample.iloc[choosed,] - sample.iloc[i,]
            gap = np.random.rand(1, n_atters)
            new.loc[i] = [x for x in sample.iloc[i,] + gap.flatten() * diff]
            label_out = np.r_[label_out, tp_less]

        new_sample = pd.concat([x_data, new])
        new_sample.reset_index(inplace=True, drop=True)
        return new_sample, label_out


if __name__ == '__main__':
    iris = load_iris()
    irisdf = pd.DataFrame(data=iris.data, columns=iris.feature_names)
    y_label = iris.target
    # 生成不平二分类数据
    iris_1 = irisdf.iloc[y_label == 1,]
    iris_2 = irisdf.iloc[y_label == 2,]
    iris_2imb = pd.concat([iris_1, iris_2.iloc[:10, :]])
    label_2imb = np.r_[y_label[y_label == 1], y_label[y_label == 2][:10]]
    iris_2imb.reset_index(inplace=True, drop=True)

    print("iris_2imb = \n", iris_2imb)
    print("label_2imb = \n", label_2imb)
    print("-" * 200)

    smt = TWO_SMOTE()
    x_new, y_new = smt.over_sample(iris_2imb, label_2imb)
    print("过采样之后:x_new = \n", x_new)
    print("过采样之后:y_new = \n", y_new)
    print("-" * 200)

打印结果:

iris_2imb = 
     sepal length (cm)  sepal width (cm)  petal length (cm)  petal width (cm)
0                 7.0               3.2                4.7               1.4
1                 6.4               3.2                4.5               1.5
2                 6.9               3.1                4.9               1.5
3                 5.5               2.3                4.0               1.3
4                 6.5               2.8                4.6               1.5
5                 5.7               2.8                4.5               1.3
6                 6.3               3.3                4.7               1.6
7                 4.9               2.4                3.3               1.0
8                 6.6               2.9                4.6               1.3
9                 5.2               2.7                3.9               1.4
10                5.0               2.0                3.5               1.0
11                5.9               3.0                4.2               1.5
12                6.0               2.2                4.0               1.0
13                6.1               2.9                4.7               1.4
14                5.6               2.9                3.6               1.3
15                6.7               3.1                4.4               1.4
16                5.6               3.0                4.5               1.5
17                5.8               2.7                4.1               1.0
18                6.2               2.2                4.5               1.5
19                5.6               2.5                3.9               1.1
20                5.9               3.2                4.8               1.8
21                6.1               2.8                4.0               1.3
22                6.3               2.5                4.9               1.5
23                6.1               2.8                4.7               1.2
24                6.4               2.9                4.3               1.3
25                6.6               3.0                4.4               1.4
26                6.8               2.8                4.8               1.4
27                6.7               3.0                5.0               1.7
28                6.0               2.9                4.5               1.5
29                5.7               2.6                3.5               1.0
30                5.5               2.4                3.8               1.1
31                5.5               2.4                3.7               1.0
32                5.8               2.7                3.9               1.2
33                6.0               2.7                5.1               1.6
34                5.4               3.0                4.5               1.5
35                6.0               3.4                4.5               1.6
36                6.7               3.1                4.7               1.5
37                6.3               2.3                4.4               1.3
38                5.6               3.0                4.1               1.3
39                5.5               2.5                4.0               1.3
40                5.5               2.6                4.4               1.2
41                6.1               3.0                4.6               1.4
42                5.8               2.6                4.0               1.2
43                5.0               2.3                3.3               1.0
44                5.6               2.7                4.2               1.3
45                5.7               3.0                4.2               1.2
46                5.7               2.9                4.2               1.3
47                6.2               2.9                4.3               1.3
48                5.1               2.5                3.0               1.1
49                5.7               2.8                4.1               1.3
50                6.3               3.3                6.0               2.5
51                5.8               2.7                5.1               1.9
52                7.1               3.0                5.9               2.1
53                6.3               2.9                5.6               1.8
54                6.5               3.0                5.8               2.2
55                7.6               3.0                6.6               2.1
56                4.9               2.5                4.5               1.7
57                7.3               2.9                6.3               1.8
58                6.7               2.5                5.8               1.8
59                7.2               3.6                6.1               2.5
label_2imb = 
 [1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2]
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
过采样之后:x_new = 
     sepal length (cm)  sepal width (cm)  petal length (cm)  petal width (cm)
0            7.000000          3.200000           4.700000          1.400000
1            6.400000          3.200000           4.500000          1.500000
2            6.900000          3.100000           4.900000          1.500000
3            5.500000          2.300000           4.000000          1.300000
4            6.500000          2.800000           4.600000          1.500000
..                ...               ...                ...               ...
75           7.314786          2.926801           6.420402          2.053194
76           5.755643          2.646399           4.859195          1.731204
77           7.585214          2.973199           6.479598          1.846806
78           6.319714          2.792798           5.680268          1.800000
79           7.104929          3.160804           5.980268          2.437593

[80 rows x 4 columns]
过采样之后:y_new = 
 [1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1
 1 1 1 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2 2
 2 2 2 2 2 2]
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Process finished with exit code 0

以上就是SMOTE的简单实现,尚未有考虑到仅有 0 1变量




参考资料:
SMOTE(Synthetic Minority Over-Sampling Technique ,即“人工少数类过采样法“)----Python调包简单实现
Py之imblearn:imblearn/imbalanced-learn库的简介、安装、使用方法之详细攻略
不平衡分类学习方法 --Imbalaced_learn
不平衡学习的方法 Learning from Imbalanced Data

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/727506.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-26
下一篇 2022-04-26

发表评论

登录后才能评论

评论列表(0条)

保存