机器学习笔记:imblearn之SMOTE算法处理样本类别不平衡

一、业务背景

日常工作、比赛的分类问题中常遇到类别型的因变量存在严重的偏倚,即类别之间的比例严重失调。

样本量差距过大会导致建模效果偏差。

例如逻辑回归不适合处理类别不平衡问题,会倾向于将样本判定为大多数类别,虽然能达到很高的准确率,但是很低的召回率。

出现样本不均衡场景主要有:

  • 异常检测:恶意刷单、黄牛、欺诈问题(欺诈用户样本可能少于1%);
  • 客户流失:流失用户占比也非常低;
  • 偶发事件:无法预判;
  • 低频事件:频率很大,例如:双11/618等大促活动;

如果数据存在严重的不平衡,预测得出的结论往往也是有偏的,即分类结果会偏向于较多观测的类。

二、处理方法

针对此类问题,有几种处理办法。

1.正负样本惩罚权重

在算法实现过程中,对于分类不同样本数量的类别分别赋予不同的权重,再进行建模计算。

小样本量类别权重高,大样本权重低。

例如,XgBoost 算法提供参数 scale_pos_weight

xgb.XGBClassifier(
   learning_rate =0.1,
   n_estimators=1000,
   eval_metric=['logloss','auc','error'],
   max_depth=5,
   min_child_weight=1,
   gamma=0,
   subsample=0.8,
   colsample_bytree=0.8,
   objective= 'binary:logistic',
   nthread=4,
   scale_pos_weight=883, # 负样本/正样本之比
   seed=42)

2.组合、集成

每次生成训练集时,使用所有分类中的小样本量,而大样本量进行随机抽取,类似于随机森林的做法,进行 Bootstrap 采样。

3.抽样

最简单的上采样方法可以直接将少数类样本复制几份后添加到样本集中,最简单的下采样则可以直接只取一定百分比的多数类样本作为训练集。

  • 欠采样、下采样(under-sampling):删掉多的一类
from imblearn.under_sampling import RandomUnderSampler
  • 过采样、上采样(over-sampling):通过Bootstrap抽样少的一类实现样本均衡
from imblearn.over_sampling import SMOTE

注意:使用 imblearn 时,数据中不能有缺失值,否则会报错!

欠采样容易导致某些隐含信息丢失,过采样中有返回的抽样形成简单复制,容易产生模型过拟合。

三、SMOTE算法

1.目的

合成分类问题中的少数类样本,使得样本达到平衡。

2002Chawla 提出 SMOTE 算法。

2.原理

合成的策略是对每个少数类样本A,从它的最近邻(KNN 欧氏距离)中随机选取一个样本B,然后在A、B之间的连线上随机选取一点作为新合成的少数类样本(近似填充)。

  • 采样最邻近算法,计算出每个少数类样本的K个近邻
  • 从K个近邻中随机挑选N个样本进行随机线性插值
  • 构造新的少数类样本
  • 将新样本与原数据合成,产生新的训练集

四、imblearn包解释

1.安装

# 直接安装
pip install imblearn
pip install --user imblearn

2.参数解释

imblearn.over_sampling.SMOTE(
     radio='auto', # 旧版本
     sampling_strategy="auto", # 新版本 抽样比例
     random_state=None, # 随机种子
     k_neighbors=5, # 近邻个数
     m_neighbors=10, # 随机抽取个数
     out_step=0.5, # 使用kind='svm'
     kind='regular', # 生成样本选项 随机选取少数类的样本 'borderline1'、'borderline2'、'svm'
     svm_estimator=None, # 指定SVM分类器
     n_jobs=-1) # CPU数量 并行

# kind解释
– borderline1: 最近邻中的随机样本b与该少数类样本a来自于不同的类
– borderline2: 随机样本b可以是属于任何一个类的样本
– svm:使用支持向量机分类器产生支持向量然后再生成新的少数类样本

五、实操

1.数据准备

#### 数据集一 ####
# 导入数据
import pandas as pd
df = pd.read_clipboard()
'''
   a  b  c   d   e  label
0  5  3  4   9   7      0
1  3  8  9   4  10      0
2  6  8  8   8   7      1
3  5  7  1   5   4      0
4  5  5  5   8   6      0
5  4  1  2   7   3      0
6  6  6  6  10   2      1
7  6  9  6   7   6      0
8  3  1  5   5   2      0
9  8  4  5   2   6      1
'''

# 数据分布
df.groupby('label').count()
'''
       a  b  c  d  e
label               
0      7  7  7  7  7
1      3  3  3  3  3
'''

# 切片
x, y = df.iloc[:, :-1], df.iloc[:, -1]
# 同上
x, y = df.loc[:, df.columns != 'label'], df.loc[:, df.columns == 'label']

另外一种数据集准备方法。

# 生成类别不平衡数据
from sklearn.datasets import make_classification
# 0和1比例为9:1
X, y = make_classification(n_classes=2,
                           class_sep=2,
                           weights=[0.9,0.1],
                           n_informative=3,
                           n_redundant=1,
                           flip_y=0,
                           n_features=20,
                           n_clusters_per_class=1,
                           n_samples=1000,
                           random_state=10)
# 数据分布
from collections import Counter
Counter(y) # Counter({0: 900, 1: 100})

2.SMOTE过采样

imblearn 中过采样接口提供了随机过采样 RandomOverSamplerSMOTEADASYN 三种方式,调用方式基本一致。

SMOTE 只适合处理连续性变量特征,不适合离散型特征。

# 导包
from imblearn.over_sampling import SMOTE
# 建模
smote_model = SMOTE(k_neighbors=2, random_state=42)
# fit
x_smote, y_smote = smote_model.fit_resample(x, y)
# 组合
df_smote = pd.concat([x_smote, y_smote], axis=1)
# 数据分布
df_smote.groupby('label').count()
'''
       a  b  c  d  e
label               
0      7  7  7  7  7
1      7  7  7  7  7
'''

fit_resamplefit_sample 因版本不同,修改方法名。

SMOTE 算法默认生成1:1的数据,如果想生成其他比例,可通过 ratio 参数设置。

# SMOTE
from imblearn.over_sampling import SMOTE
# 转换类型
X = X.astype('float64')
# SMOTE
smo = SMOTE(random_state=42)
X_smo, y_smo = smo.fit_resample(X, y)
# 查看分布
Counter(y_smo) # Counter({0: 900, 1: 900})

# 设置比例
# 旧版本
# smo = SMOTE(ratio={1:300}, random_state=42)

# 新版本
smo = SMOTE(sampling_strategy=1/3, random_state=42)
X_smo, y_smo = smo.fit_resample(X, y)
Counter(y_smo) # Counter({0: 900, 1: 300})

新版本通过 sampling_strategy 参数设置。否则会报错。

TypeError: __init__() got an unexpected keyword argument 'ratio'

3.RandomUnderSampler欠采样

# 导包
from imblearn.under_sampling import RandomUnderSampler
# 建模
under_model = RandomUnderSampler()
# fit
x_under, y_under = under_model.fit_resample(x, y)
# 合并
df_under = pd.concat([x_under, y_under], axis=1)
# 数据分布
df_under.groupby('label').count()
'''
       a  b  c  d  e
label               
0      3  3  3  3  3
1      3  3  3  3  3
'''

六、其他问题

ValueError: Unknown label type: ‘continuous’

标签类型必须是整型 int

ValueError: Expected n_neighbors <= n_samples, but n_samples = 1, n_neighbors = 6

数据量过少,导致无法求近邻样本。

可通过设置 k_neighbors 参数值,修改低一点数值。

参考链接:数据预处理 | python 第三方库 imblearn 处理样本分布不均衡问题

参考链接:类别不平衡问题之SMOTE算法(Python imblearn极简实现)

参考链接:TypeError: init() got an unexpected keyword argument ‘ratio‘

参考链接:SMOTE过采样技术原理与实现

参考链接:python调用imblearn中SMOTE踩坑

参考链接:[scikit-learn-contrib

参考链接:from imblearn.over_sampling import SMOTE 参数介绍

参考链接:浅谈SMOTE算法 如何利用Python解决非平衡数据问题

posted @ 2022-03-07 16:47  Hider1214  阅读(8301)  评论(0编辑  收藏  举报