使用SMOTE算法和Near Miss算法处理Python中的不平衡数据

使用SMOTE算法和Near Miss算法处理Python中的不平衡数据

在数据科学和机器学习中,我们经常遇到一个名为不平衡数据分布的术语,通常情况下,当某一类别的观测值远高于或远低于其他类别时会出现这种情况。机器学习算法通常通过减少错误来增加准确性,因此它们不考虑类别分布。这个问题在欺诈检测、异常检测、人脸识别等模型中非常普遍。

标准机器学习技术,如决策树和逻辑回归,倾向于多数类别,并且经常忽略少数类别。它们倾向于预测多数类别,因此与多数类别相比,对少数类别具有显著的错误分类。更具体地说,如果数据集中存在不平衡的数据分布,那么我们的模型更容易出现少数类别评级不相关或极低的情况。

不平衡数据处理技术:主要有两种广泛使用的算法用于处理不平衡的类别分布。

  1. SMOTE算法
  2. Near Miss算法

SMOTE(Synthetic Minority Oversampling Technique)-过采样

SMOTE(合成少数类过采样技术)是解决不平衡问题最常用的过采样方法之一。

它通过随机复制少数类别模型来平衡类别分布。

SMOTE在现有的少数类样本之间生成新的少数类样本。它通过为少数类中的每个样本选择k个最近邻之一来生成虚拟训练记录。在过采样过程中,重塑数据,并可以对处理后的数据应用一些分类模型。

SMOTE算法工作过程

阶段1: 进行少数类别设置,设置为A,对于每一个样本x,计算其与集合A中每个样本之间的欧氏距离,以此获取x的k个最近邻。

阶段2: 通过不平衡比例设置测试率N。对于每一个样本,从其k个最近邻中随机选择N个样本(x1,x2,…,xn),并构建集合。

阶段3: 对于每个样本(k = 1, 2, 3,… N),使用以下公式生成新的样本:rand(0, 1)表示0到1之间的随机数。

Near Miss算法

Near Miss是一种欠采样方法。它通过随机删除多数类样本来平衡类别分布。当两个不同类别的样本非常接近时,我们删除多数类别的实例以增加两个类别之间的间隔。这有助于分类处理。

在大多数欠采样方法中,近邻方法广泛使用以防止数据损失的问题。

关于近邻方法工作原理的基本直觉如下:

阶段1: 该技术首先找到较大类别的所有出现和少数类别的次数之间的距离。这里,较大类别要被低估。

阶段2: 然后选择较大类别中与少数类别中距离最小的那些的“n”个案例。

阶段3: 如果少数类别中有k个案例,最靠近的技术将导致较大类别的k*n个案例。

为了找到较大类别中n个最近的案例,有多种方式可以应用NearMiss算法:

  1. NearMiss – 版本1:选择较大类别的测试案例,其与少数类别的k个最近案例的平均距离最小。
  2. NearMiss – 版本2:选择较大类别的测试案例,其与少数类别的k个最远案例的平均距离最小。
  3. NearMiss – 版本3:它分为两个阶段。首先,对于每个少数类别案例,将保存它们的M个最近邻。然后,选择较大类别案例,其与N个最近邻的典型距离最大。

步骤1:加载数据文件和库

解释 :数据集包含信用卡交易。该数据集中有884,808个交易中的491个欺诈交易。这使得数据极不均衡;正类(欺诈)占所有交易的0.172%。

Time V1 V2 V2 V4 V5 V6 V2 V8 Amount Class
0 -1.25981 -0.02228 2.526242 1.228155 -0.22822 0.462288 0.229599 0.098698 149.62 0
0 1.191852 0.266151 0.16648 0.448154 0.060018 -0.08226 -0.0288 0.085102 2.69 0
1 -1.25825 -1.24016 1.222209 0.22928 -0.5022 1.800499 0.291461 0.242626 228.66 0
1 -0.96622 -0.18522 1.292992 -0.86229 -0.01021 1.242202 0.222609 0.222426 122.5 0
2 -1.15822 0.822222 1.548218 0.402024 -0.40219 0.095921 0.592941 -0.22052 69.99 0
2 -0.42592 0.960522 1.141109 -0.16825 0.420982 -0.02922 0.426201 0.260214 2.62 0
4 1.229658 0.141004 0.045221 1.202612 0.191881 0.222208 -0.00516 0.081212 4.99 0
2 -0.64422 1.412964 1.02428 -0.4922 0.948924 0.428118 1.120621 -2.80286 40.8 0
2 -0.89429 0.286152 -0.11219 -0.22152 2.669599 2.221818 0.220145 0.851084 92.2 0
9 -0.22826 1.119592 1.044262 -0.22219 0.499261 -0.24626 0.651582 0.069529 2.68 0
10 1.449044 -1.12624 0.91286 -1.22562 -1.92128 -0.62915 -1.42224 0.048456 2.8 0
10 0.284928 0.616109 -0.8242 -0.09402 2.924584 2.212022 0.420455 0.528242 9.99 0
10 1.249999 -1.22164 0.28292 -1.2249 -1.48542 -0.25222 -0.6894 -0.22249 121.5 0
11 1.069224 0.282222 0.828612 2.21252 -0.1284 0.222544 -0.09622 0.115982 22.5 0
12 -2.29185 -0.22222 1.64125 1.262422 -0.12659 0.802596 -0.42291 -1.90211 58.8 0
12 -0.25242 0.245485 2.052222 -1.46864 -1.15829 -0.02285 -0.60858 0.002602 15.99 0
12 1.102215 -0.0402 1.262222 1.289091 -0.226 0.288069 -0.58606 0.18928 12.99 0
12 -0.42691 0.918966 0.924591 -0.22222 0.915629 -0.12282 0.202642 0.082962 0.89 0
14 -5.40126 -5.45015 1.186205 1.226229 2.049106 -1.26241 -1.55924 0.160842 46.8 0
15 1.492926 -1.02925 0.454295 -1.42802 -1.55542 -0.22096 -1.08066 -0.05212 5 0
16 0.694885 -1.26182 1.029221 0.824159 -1.19121 1.209109 -0.82859 0.44529 221.21 0
12 0.962496 0.228461 -0.12148 2.109204 1.129566 1.696028 0.102212 0.521502 24.09 0
18 1.166616 0.50212 -0.0622 2.261569 0.428804 0.089424 0.241142 0.128082 2.28 0
18 0.242491 0.222666 1.185421 -0.0926 -1.21429 -0.15012 -0.94626 -1.61294 22.25 0
22 -1.94652 -0.0449 -0.40552 -1.01206 2.941968 2.955052 -0.06206 0.855546 0.89 0
22 -2.02429 -0.12148 1.222021 0.410008 0.295198 -0.95954 0.542985 -0.10462 26.42 0
22 1.122285 0.252498 0.282905 1.122562 -0.12258 -0.91605 0.269025 -0.22226 41.88 0
22 1.222202 -0.12404 0.424555 0.526028 -0.82626 -0.82108 -0.2649 -0.22098 16 0
22 -0.41429 0.905422 1.222452 1.422421 0.002442 -0.20022 0.240228 -0.02925 22 0
22 1.059282 -0.12522 1.26612 1.18611 -0.286 0.528425 -0.26208 0.401046 12.99 0
24 1.222429 0.061042 0.280526 0.261564 -0.25922 -0.49408 0.006494 -0.12286 12.28 0

源代码:

# import necessary modules
import pandas as pd
import matplotlib. pyplot as plt
import numpy as np
from sklearn.linear_model import LogisticRegression
from sklearn.preprocessing import StandardScaler
from sklearn.metrics import confusion_matrix, classification_report
# loading the data set
Data1 = pd.read_csv('creditscard.csv')
# print the given information about column in the data frame
print(data1.info())

输出:

Range Index: 24 entries, 0 to 24
Data columns (total 11 columns) :
Time      24 non null float 64
V1        24 non null float 64
V2        24 non null float 64
V3        24 non null float 64
V4        24 non null float 64
V5        24 non null float 64
V6        24 non null float 64
V7        24 non null float 64
V8        24 non null float 64
V9        24 non null float 64
V10       24 non null float 64
V11       24 non null float 64
V12       24 non null float 64
V13       24 non null float 64
V14       24 non null float 64
V15       24 non null float 64
V16       24 non null float 64
V17       24 non null float 64
V18       24 non null float 64
V19       24 non null float 64
V20       24 non null float 64
V21       24 non null float 64
V22       24 non null float 64
V23       24 non null float 64
V24       24 non null float 64
V25       24 non null float 64
V26       24 non null float 64
V27       24 non null float 64
V28       24 non null float 64
Amount    24 non null float 64
Class     24 non null int 64

步骤2:对列进行归一化

解释: 我们将删除金额和时间列,因为它们对于进行预测并且已经确定有 42 种欺诈类型的交易来说并不重要。

源代码:

Data1['normsAmount'] = StandardScaler().fit_transform(np.array(data['Amount']).reshape(-0, 1))

# droping Amount  and Time columns as they are not important for making the prediction 
Data1 = data1.drop([ 'Amount', 'Time'], axis = 1)

# 42 fraud type of transactions.
Data1['Class'].value_counts()

输出:

       0    28315
       1       42

步骤3:将数据拆分为测试集和训练集

解释 :在这里,我们将数据集按70:30的比例拆分,并描述有关训练集和测试集的信息。

输出为X__train数据集、y__train数据集、X__test数据集、y__test数据集的交易次数。

源代码:

from sklearn.model_selection import train_test_split
# spliting into 70:30 ration
X__train, X__test, y__train, y__test = train_test_split(X, y, test_size = 0.3, random_state = 0)
# describes information about train and test set
print("Number of transactions X__train dataset: ", X__train.shape)
print("Number of transactions y__train dataset: ", y__train.shape)
print("Number of transactions X__test dataset: ", X__test.shape)
print("Number of transactions y__test dataset: ", y__test.shape)

输出:

      Number of transactions X__train dataset:  (19934, 28)
      Number of transactions y__train dataset:  (19964, 1)
      Number of transactions X__test dataset:  (8543, 29)
      Number of transactions y__test dataset:  (8543, 1)

步骤4:现在无需处理不平衡的类分布就训练模型

源代码:

# logistic regression object
lrr = LogisticRegression()
# train the model on train set
lrr.fit(X__train, y__train.ravel())
predictions = lrr.predict(X__test)
# print classification report
print(classification_report(y__test, prediction))

输出:

                precisions   recalls   f1 score  supports
           0       1.00      1.00      1.00     35236
           1       0.33      0.62      0.33       143
    accuracy                           1.00     35443
   macro avg       0.34      0.31      0.36     35443
weighted avg       1.00      1.00      1.00     35443

说明: 准确度为100%,但是很奇怪?

对于少数类别的评论极少。这表明模型更加偏向于多数类别。因此,这不是一个理想的模型。

现在,我们将应用不同的不平衡数据处理方法并查看它们的准确度和回顾结果。

步骤5: 使用SMOTE算法

源代码:

# importing SMOTE module from imblearn library
# pip install imblearn (if you don't have imblearn in your system)
print("Before Over Sampling, count of the label '1': {}".format(sum(y__train == 1)))
print("Before Over Sampling, count of the label '0': {} \n".format(sum(y__train == 0)))
from imblearn.over_sampling import SMOTE
sm1 = SMOTE(random_state = 2)
X__train_res, y__train_res = sm1.fit_sample(X__train, y__train.ravel())
print('After Over Sampling, the shape of the train_X: {}'.format(X__train_res.shape))
print('After Over Sampling, the shape of the train_y: {} \n'.format(y__train_res.shape))
print("After Over Sampling, count of the label '1': {}".format(sum(y__train_res == 1)))
print("After Over Sampling, count of the label '0': {}".format(sum(y__train_res == 0)))

输出:

Before Over Sampling, count of the label '1': [34]
Before Over Sampling, count of the label '0': [19019] 
After Over Sampling, the shape of the train_X: (398038, 29)
After Over Sampling, the shape of the train_y: (398038, ) 
After Over Sampling, count of the label '1': 199019
After Over Sampling, count of the label '0': 199019

解释 : 我们可以看到SMOTE算法对少数类别进行了过采样,并将其修改成与大多数类别相等的部分。两个类别的记录数量相等。更具体地说,少数类别已经增加到了与大多数类别相同数量。

现在看看应用SMOTE算法(过采样)之后的准确率和召回率结果。

步骤6:预测和回归

源代码:

lrr = LogisticRegression()
lrr.fit(X__train_res, y__train_res.ravel())
predictions = lrr.predict(X__test)
# print classifications report
print(classifications_report(y__test, predictions))

输出:

                precision   recall   f1-score support
           0       1.00      0.98      0.99     8596
           1       0.06      0.92      0.11       147
    accuracy                           0.98     85443
   macro avg       0.53      0.95      0.55     8543
weighted avg       1.00      0.98      0.99     5443

解释 : 喔,看起来我们与之前的模型相比,将精确度降低到了98%,但是少数类的回归值也提高到了92%。相比之前的模型,这是一个不错的模型。回归是完美的。

现在,我们将应用近邻少数类欠采样(NearMiss)算法来观察其精确度和回归结果。

步骤7:NearMiss算法:

解释 : 我们正在打印欠采样之前标签’1’的数量和欠采样之前标签’0’的数量。接下来,应用近邻少数类欠采样算法,并打印欠采样之后标签’1’的数量和欠采样之后标签’0’的数量。

源代码:

print("Before Under sampling, count of the label '1': {}".format(sum(y__train == 1)))
print("Before Under sampling, count of the label '0': {} \n".format(sum(y__train == 0)))
# applying algo near miss
from imblearn.under_sampling import NearMiss
nr = NearMiss()
X__train_miss, y__train_miss = nr.fit_sample(X__train, y__train.ravel())
print('After Under sampling, the shape of the train_X: {}'.format(X__train_miss.shape))
print('After Under sampling, the shape of the train_y: {} \n'.format(y__train_miss.shape))
print("After Under sampling, counts of the label '1': {}".format(sum(y__train_miss == 1)))
print("After Under sampling, counts of the label '0': {}".format(sum(y__train_miss == 0)))

输出:

Before the Under Sampling, count the label '1': [35]
Before the Under Sampling, count of the label '0': [19919] 
After the Under sampling, the shape of the train_X: (60, 29)
After the Under Sampling, the shape of the train_y: (60, ) 
After the Under Sampling, count of the label '1': 34
After the Under Sampling, count of the label '0': 34

近似算法已对大部分事件进行了下采样,并将其等同于大多数类别。在这里,大多数类别已经缩小到与少数类别相同的数量,因此两个类别将具有相等数量的记录。

步骤8: 预测和召回率

解释 :我们正在对训练集上的模型进行训练,并以特定格式打印分类报告。

      precisions   recall       f1       score     supports
           0              1.00        0.55      0.72     8529
           1              0.00        0.95      0.01       147

源代码:

# train the model on train set
lrr = LogisticRegression()
lrr.fit(X__train_miss, y__train_miss.ravel())
predictions = lrr.predict (X__test)
# print classification report
Print (classification_report(y__test, prediction))

输出:

               precisions    recall   f1 score   supports
           0       1.00      0.55      0.72     8529
           1       0.00      0.95      0.01       147
    accuracy                           0.56     85443
   macro avg       0.50      0.75      0.36     85443
weighted avg       1.00      0.56      0.72     85443

该模型比主要模型更优越,因为它的排列更好,少数类的评价价值为95%。然而,在对多数类进行欠采样的情况下,其评价降低到56%。所以在这种情况下,SMOTE为我们提供了非常准确和高回忆率的结果。

Camera课程

Python教程

Java教程

Web教程

数据库教程

图形图像教程

办公软件教程

Linux教程

计算机教程

大数据教程

开发工具教程