扫盲-数据不平衡


一、数据不平衡

举个例子,假如说现在又两个样本标签 AB ,而对应的 A 有1000个数据,对应的 B 有100个数据,两个样本标签的数据相差很大,这就产生了数据的不平衡。机器学习算法通常很难从不平衡数据集学习。所以我们需要对于这些不平衡进行处理。

二、如何解决

简单来讲,常规操作是想方设法使 AB 的数量变成一样的,这也是最容易想到的方法,也就是拥有相同的话语权。[2]

1.采样 [1]

采样方法是通过对训练集进行处理使其从不平衡的数据集变成平衡的数据集,在大部分情况下会对最终的结果带来提升。

采样分为上采样(Oversampling)和下采样(Undersampling),上采样是把小众类复制多份,下采样是从大众类中剔除一些样本,或者说只从大众类中选取部分样本。

随机采样最大的优点是简单,但缺点也很明显。上采样后的数据集中会反复出现一些样本,训练出来的模型会有一定的过拟合;而下采样的缺点显而易见,那就是最终的训练集丢失了数据,模型只学到了总体模式的一部分。

1.1随机采样

朴素随机过采样(上采样)[1]

针对不平衡数据, 最简单的一种方法就是生成少数类的样本, 这其中最基本的一种方法就是: 从少数类的样本中进行随机采样来增加新的样本,对应Python库中函数为RandomOverSampler

from imblearn.over_sampling
import RandomOverSampler
ros = RandomOverSampler(random_state=0)
X_resampled, y_resampled = ros.fit_sample(X, y)
朴素随机欠采样(下采样)[1]

与过采样相反,欠采样是从多数类样本中随机选择少量样本,再合并原有少数类样本作为新的训练数据集。

随机欠采样有两种类型分别为有放回和无放回两种,无放回欠采样在对多数类某样本被采样后不会再被重复采样,有放回采样则有可能。

对应Python库中函数为RandomUnderSampler,通过设置RandomUnderSampler中的replacement=True参数, 可以实现自助法(boostrap)抽样。

1.2数据合成 [1]

相对于采样随机的方法进行过采样, 还有两种比较流行的过采样的改进方式:

(1) Synthetic Minority Oversampling Technique(SMOTE)

(2) Adaptive Synthetic (ADASYN)

SMOTE

SMOTE算法的基本思想是对少数类样本进行分析并根据少数类样本人工合成新样本添加到数据集中。

可执行代码如下,可以用AnacondaNotebook可以直接运行(可能会有些需要安装的包,根据提示安装就好了)[3]

import pandas as pd
import matplotlib.pyplot as plt
import numpy as np
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from sklearn.model_selection import GridSearchCV
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import classification_report
from imblearn.over_sampling import SMOTE
 
def load_and_analyse_data():
    data = pd.read_csv('./data/creditcard.csv')
    # ----------------------预处理----------------
 
    # ----------------------标准化Amount列---------
    data['normAmout'] = StandardScaler().fit_transform(data['Amount'].values.reshape(-1, 1))
    data = data.drop(['Time', 'Amount'], axis=1)
    # ----------------------------------------------
 
    X = data.iloc[:, data.columns != 'Class']
    y = data.iloc[:, data.columns == 'Class']
    X_train,X_test,y_train,y_test = train_test_split(X,y,test_size=0.3,random_state=0)
    # ----------------------采样-------------------
    sample_solver = SMOTE(random_state=0)
    X_sample ,y_sample = sample_solver.fit_resample(X_train,y_train)#从原始的训练集采出样本,用来训练模型
    return np.array(X_test),np.array(y_test).reshape(len(y_test)),np.array(X_sample),np.array(y_sample).reshape(len(y_sample))
 
if __name__ == '__main__':
    X_test, y_test, X_sample, y_sample  = load_and_analyse_data()
    X_train,X_dev,y_train,y_dev = train_test_split(X_sample,y_sample,test_size=0.3,random_state=1)
    
    print("X_train:{}  X_dev:{}  X_test:{}".format(len(y_train), len(y_dev), len(y_test)))
    model = LogisticRegression()
    parameters = {'C':[0.001,0.003,0.01,0.03,0.1,0.3,1,3,10]}
    gs = GridSearchCV(model,parameters,verbose=5,cv=5)
    gs.fit(X_train,y_train)#训练模型,训练集为采样后的数据
    print('最佳模型:',gs.best_params_,gs.best_score_)
    print('在采样数据上的性能表现:')
    print(gs.score(X_dev,y_dev))
    y_dev_pre = gs.predict(X_dev)
    print(classification_report(y_dev,y_dev_pre))
    print('在原始数据上的性能表现:')
    print(gs.score(X_test,y_test))
    y_pre = gs.predict(X_test)
    print(classification_report(y_test,y_pre))

目录结构如下(数据集放在data文件夹里面):

目录结构

数据集如下:

数据集文件

运行结果如下:

运行结果

ADASYN [1]

这种改进方法的主要思想是根据数据分布情况为不同的少数类样本生成不同数量的新样本。首先根据最终的平衡程度设定总共需要生成的新少数类样本数量 ,然后为每个少数类样本x计算分布比例。

对应Python库中函数为ADASYN:

from imblearn.over_sampling import ADASYN
X_resampled_adasyn, y_resampled_adasyn = ADASYN().fit_sample(X, y)

参考文献

【1】不平衡数据集的处理

【2】处理数据不平衡

【3】过采样(处理数据不平衡问题)

【4】【机器学习】如何解决数据不平衡问题


文章作者: milu
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 milu !
  目录