如何用 Python 處理不平衡數據集
1. 什麼是數據不平衡
所謂的數據不平衡(imbalanced data)是指數據集中各個類別的數量分佈不均衡;不平衡數據在現實任務中十分的常見。如
- 信用卡欺詐數據:99%都是正常的數據, 1%是欺詐數據
- 貸款逾期數據
不平衡數據一般是由於數據產生的原因導致的,類別少的樣本通常是發生的頻率低,需要很長的周期進行采集。
在機器學習任務(如分類問題)中,不平衡數據會導致訓練的模型預測的結果會偏向於樣本數量多的類別,這個時候除瞭要選擇合適的評估指標外,想要提升模型的性能,就要對數據和模型做一些預處理。
處理數據不平衡的主要方法:
- 欠采樣
- 過采樣
- 綜合采樣
- 模型集成
調整類別權重或者樣本權重
2. 數據不平衡處理方法
imbalanced-learn庫提供瞭許多不平衡數據處理的方法,本文的例子都以imbalanced-learn庫來實現。
pip install -U imbalanced-learn
https://github.com/scikit-learn-contrib/imbalanced-learn
本文例子的數據來自進行中的比賽山東省第二屆數據應用創新創業大賽-日照分賽場-公積金貸款逾期預測
先來看下數據
import pandas as pd train_data = './data/train.csv' test_data = './data/test.csv' train_df = pd.read_csv(train_data) test_df = pd.read_csv(test_data) print(train_df.groupby(['label']).size()) # label為是否違約, 1為違約, 0為非違約 # label # 0 37243 # 1 2757
2.1 欠采樣
所謂欠采樣,就是將數量多類別(記為majority)的樣本進行抽樣,使之數量與數量少的類別(minority)的數量相當,以此達到數量的平衡。
由於欠采樣是丟失瞭一部分數據,不可避免的使得數量多類別樣本的分佈發生瞭變化(方差變大)。好的欠采樣策略應該盡可能保持原有數據分佈。
欠采樣是刪除majority的樣本,那哪些樣本可以刪除呢?
- 一種是overlapping的數據,就是多餘的數據
- 一種是幹擾的數據,幹擾minority的分佈
基於此,有兩種思路來欠采樣
- 邊界相鄰匹配,考慮在近鄰空間內刪除majority樣本,方法如TomekLinks, NearMiss
下面這張圖,展示6NN(6個最近鄰居)
這裡重點講下TomekLinks, TomekLinks方法簡單的說:對每一個minority樣本找1NN(最近的鄰居),如果最近的鄰居是majority, 就形成一個tome-links,該方法人為這個majority是幹擾的,將它刪除。
from imblearn.under_sampling import TomekLinks X_train = train_df.drop(['id', 'type'], axis=1) y = train_df['label'] tl = TomekLinks() X_us, y_us = tl.fit_sample(X_train, y) print(X_us.groupby(['label']).size()) # label # 0 36069 # 1 2757
從上可知, 有1174個tomek-link被刪除,好像刪除還不夠多,可以測試下是否對分類結果有幫助。需要註意的因為需要計算最近鄰,所以樣本屬性必須數值屬性,或者可以轉化為數值屬性。
- 聚類
這類方法通過多個聚類,把原始樣本劃分成多個聚類簇,然後用每個聚類簇的中心來代替這個聚類簇的特性,完成采樣的目的。可知,這種采樣的樣本不是來自原始樣本集,而是聚類生成的。
from imblearn.under_sampling import ClusterCentroids cc = ClusterCentroids(random_state=42) X_res, y_res = cc.fit_resample(X_train, y) X_res.groupby(['label']).size() # label # 0 2757 # 1 2757
im-balance提供的欠采樣的方法如下:
- Random majority under-sampling with replacement
- Extraction of majority-minority Tomek links
- Under-sampling with Cluster Centroids
- NearMiss-(1 & 2 & 3)
- Condensed Nearest Neighbour
- One-Sided Selection
- Neighboorhood Cleaning Rule
- Edited Nearest Neighbours
- Instance Hardness Threshold
- Repeated Edited Nearest Neighbours
- AllKNN
2.2 過采樣
所謂過采樣,就是將數量少的類別(minority)的樣本進行copy,使之數量與數量多的類別(majortity)的數量相當,以此達到數量的平衡。由於復制瞭多份minoruty樣本,過采樣會改變minority方差。
過采樣一種簡單的方式是隨機copy minority的樣本;另外一種是根據現有樣本生成人造樣本。這裡介紹人造樣本的經典算法SMOTE(Synthetic Minority Over-sampling Technique)。
SMOTE基於minority樣本相似的特征空間構造新的人工樣本。步驟如下:
- 選擇一個minority樣本,計算其KNN鄰居
- 在K個鄰居中,隨機選擇一個近鄰
- 修改某一個特征,偏移一定的大小:偏移的大小為該minority樣本與該近鄰差距乘以一個小的隨機比率(0, 1), 就此生成新樣本
from imblearn.over_sampling import SMOTE smote = SMOTE(k_neighbors=5, random_state=42) X_res, y_res = smote.fit_resample(X_train, y) X_res.groupby(['label']).size() # label # 0 37243 # 1 37243
對於SMOTE方法,對每一個minority都會構造新樣本。但是並不總是這樣的,考慮下面A,B,C三個點。從數據分佈來看,C點很可能是一個異常點(Noise),B點是正常分佈的點(SAFE),而A點分佈在邊界位置(DANGER);
直觀上,對於C點我們不應該去構造新樣本,對B點,構造新樣本不會豐富minority類別的分佈。隻有A點,如果構造新樣本能夠使得A點從(DANGER)到(SAFE),加強minority類別的分類邊界。這個就是Borderline-SMOTE
from imblearn.over_sampling import BorderlineSMOTE bsmote = BorderlineSMOTE(k_neighbors=5, random_state=42) X_res, y_res = bsmote.fit_resample(X_train, y) X_res.groupby(['label']).size() # label # 0 37243 # 1 37243
ADASYN方法從保持樣本分佈的角度來確定生成數據,生成數據的方式和SMOTE是一樣的,不同在於每個minortiy樣本生成樣本的數量不同。
- 先確定要生成樣本的數量 beta為[0, 1]
- 對每個每個minortiy樣本,確定有它生成樣本的比例。先找出K最近鄰,計算K最近鄰中屬於majority的樣本比例(即分子),Z是歸一化因子,保證所有的minortiry的比例和為1,可以認為是所有分子的和。
- 計算每個minortiy生成新樣本的數量
- 按照SMOTE方式生成樣本
from imblearn.over_sampling import ADASYN adasyn = ADASYN(n_neighbors=5, random_state=42) X_res, y_res = adasyn.fit_resample(X_train, y) X_res.groupby(['label']).size() # label # 0 37243 # 1 36690
im-balance提供的過采樣的方法如下(包括SMOTE算法的變種):
- Random minority over-sampling with replacement
- SMOTE – Synthetic Minority Over-sampling Technique
- SMOTENC – SMOTE for Nominal Continuous
- bSMOTE(1 & 2) – Borderline SMOTE of types 1 and 2
- SVM SMOTE – Support Vectors SMOTE
- ADASYN – Adaptive synthetic sampling approach for imbalanced learning
- KMeans-SMOTE
- ROSE – Random OverSampling Examples
2.3 綜合采樣
過采樣是針對minority樣本,欠采樣是針對majority樣本;而綜合采樣是既對minority樣本,又對majority樣本,同時進行操作的方法。主要有SMOTE+Tomek-links和SMOTE+Edited Nearest Neighbours。
綜合采樣的方法,是先進行過采樣,在進行欠采樣。
from imblearn.combine import SMOTETomek smote_tomek = SMOTETomek(random_state=0) X_res, y_res = smote_tomek.fit_sample(X_train, y) X_res.groupby(['label']).size() # label # 0 36260 # 1 36260
2.4 模型集成
這裡的模型集成主要體現在數據上,即用眾多平衡的數據集(majortiry的樣本進行欠采樣加上minority樣本)訓練多個模型,然後進行集成。imblearn.ensemble提供幾種常見的模型集成算法,如BalancedRandomForestClassifier
from imblearn.ensemble import BalancedRandomForestClassifier from sklearn.datasets import make_classification X, y = make_classification(n_samples=1000, n_classes=3, n_informative=4, weights=[0.2, 0.3, 0.5], random_state=0) clf = BalancedRandomForestClassifier(max_depth=2, random_state=0) clf.fit(X, y) print(clf.feature_importances_) print(clf.predict([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]))
im-balance提供的模型集成的方法如下
- Easy Ensemble classifier
- Balanced Random Forest
- Balanced Bagging
- RUSBoost
2.5 調整類別權重或者樣本權重
對於很多用梯度下降方法來學習(使得某個損失Loss最小)的機器學習的方法,可以通過調整類別權重或樣本權重的方式,來一定程度上平衡不平衡數據。如gbdt模型lightgbm 中 class_weight
import lightgbm as lgb clf = lgb.LGBMRegressor(num_leaves=31, min_child_samples= np.random.randint(20,25), max_depth=25, learning_rate=0.1, class_weight={0:1, 1:10}, n_estimators=500, n_jobs=30)
3. 總結
本文分享瞭常見的幾種處理不平衡數據集的方法,並且提供imbalanced-learn的簡單例子。總結如下:
- 欠采樣: 減少majoritry樣本
- 過采樣:增加minority樣本
- 綜合采樣:先過采樣,在欠采樣
- 模型集成:制造平衡數據(majoritry樣本欠采樣+minority樣本),多次不同的欠采樣,訓練不同的模型,然後融合
- 不管是欠采樣和過采樣,都一定程度的改變瞭原始數據的分佈,可能造成模型過擬合。需要去嘗試哪種方法,符合實際的數據分佈。當然不一定有效果,去勇敢嘗試吧 just do it!
4. 參考資料
- Learning from Imbalanced Data
- Two Modifications of CNN(Tomek links,CNN乍一看還以為卷積神經網絡,其實是condensed nearest-neighbor)
- imbalanced-learn API:https://imbalanced-learn.org/stable/
以上就是如何用 Python 處理不平衡數據集的詳細內容,更多關於Python 處理不平衡數據集的資料請關註WalkonNet其它相關文章!
推薦閱讀:
- Python實現機器學習算法的分類
- Python實現8種常用抽樣方法
- 使用Python和scikit-learn創建混淆矩陣的示例詳解
- Python機器學習之底層實現KNN
- Python機器學習庫scikit-learn入門開發示例