機器學習筆記: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解決非平衡數據問題


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM