1. 程式人生 > >資料預處理:樣本非平衡處理

資料預處理:樣本非平衡處理




轉載:https://zhuanlan.zhihu.com/p/37311047

非平衡資料會影響最後的評判效果,嚴重的會帶來過擬合的效果,即模型總是把樣本劃分到樣本量較多的那一種。為了讓模型的評判更準確,我們需要對非平衡資料進行一定的處理,主要有以下幾種方式:

  • 欠取樣
  • 過取樣
  • 人工合成
  • 調權重

在開始介紹不同的處理方式之前,我們先引入一組非平衡資料。


  
  1. #匯入一些相關庫
  2. from
    sklearn.model_selection import train_test_split
  3. from sklearn.linear_model import LogisticRegression
  4. from sklearn.metrics import classification_report
  5. from
    sklearn.metrics import roc_curve, auc
  6. from sklearn.preprocessing import scale
  7. #匯入資料
  8. df=pd.read_excel( r"C:\Users\zhangjunhong\Desktop\Unbanlanced-data.xlsx"
    ).fillna( 0)

看一下正負樣本的具體資料量情況。


  
  1. x=df.iloc[:, 1: -1]
  2. y=df[ "label"]
  3. print(y.value_counts())
  4. print( "-------------------------")
  5. print(y.value_counts(normalize= True))

該資料量的正負樣本比例接近7:3,我們看一下不做任何處理的情況下,模型的預測效果如何。


  
  1. #將模型進行封裝,方便呼叫
  2. def get_result_data(x,y):
  3. x_=scale(x,with_mean= True,with_std= True)
  4. x_train,x_test,y_train,y_test=train_test_split(x,y,test_size= 0.4,random_state= 0)
  5. model=LogisticRegression()
  6. clf=model.fit(x_train,y_train)
  7. print( "LR模型測試成績:{:.2f}".format(clf.score(x_test,y_test)))
  8. y_pred=clf.predict(x_test)
  9. target_names = [ 'class 0', 'class 1']
  10. print(classification_report(y_test, y_pred, target_names=target_names))
  11. y_pred1=clf.decision_function(x_test)
  12. fpr,tpr,threshold=roc_curve(y_test,y_pred1)
  13. rocauc=auc(fpr,tpr) #計算AUC
  14. print( "ROC分數:{:.2f}".format(rocauc))
  15. if __name__== "__main__":
  16. get_result_data(x,y)

模型的準確率是0.75,ROC分數也就是AUC值為0.76,看著還不錯,但是class1的召回率要明顯高於class0的召回率,這是因為原樣本量中,class1的量要明顯高於class0的原因。

欠取樣

下采樣(under-sampling),是對非平衡資料中樣本數較多的那一類進行取樣,取樣使其約等於樣本量較少那一類的樣本量。


  
  1. df1=df[df[ "label"]== 1] #正樣本部分
  2. df0=df[df[ "label"]== 0] #負樣本部分
  3. #對正樣本按0.5的比例進行下采樣
  4. df2=df1.sample(frac= 0.5)
  5. #將下采樣後的正樣本與負樣本進行組合
  6. df_new=pd.concat([df0,df2])
  7. x=df_new.iloc[:, 1: -1]
  8. y=df_new[ "label"]
  9. #下采樣以後正負樣本量情況
  10. print(y.value_counts())
  11. print( "-------------------------")
  12. print(y.value_counts(normalize= True))

對模型進行下采樣以後,正負樣本的樣本量基本接近1:1,符合我們目的,接下來看看下采樣後的模型表現。


  
  1. if __name__== "__main__":
  2. get_result_data(x,y)

模型的準確率略有下降,但是ROC分數沒發生什麼變化,class0和class1的召回率也接近相等。

過取樣

過取樣(over-sampling),是對非平衡資料中樣本數較少的那一類進行取樣,常規的做法就是將其複製幾遍來達到正負樣本平衡,因為是同樣的資料複製多份,很容易發生過擬合,一般比較少用。具體的實現方式就比較簡單啦,這裡不羅列。

人工合成

人工合成就是人為地去合成一些樣本量較少的資料,來達到正負樣本平衡,人工合成數據能夠很好地避免過取樣帶來的模型過擬合。比較常用的方法就是SMOTE。

SMOTE的演算法原理如下:

  1. 根據正負樣本比例,確認取樣的比例,即要合成樣本的數量(k值)
  2. 對於少數樣本中的每個x,利用KNN演算法,選取k個待取樣的值x_n
  3. 然後對x_n進行如下運算得到對應的x_new:x_new=x+rand(1)*|x-x_n|

(rand(1)表示生成0-1之間的一個隨機數)

關於SMOTE演算法的實現也由現成的庫,我們直接pip安裝就可以使用。


  
  1. from collections import Counter
  2. from imblearn.over_sampling import SMOTE
  3. print( 'Original dataset shape {}'.format(Counter(y)))
  4. sm = SMOTE(random_state= 42)
  5. X_res, y_res = sm.fit_sample(x, y)
  6. print( 'Resampled dataset shape {}'.format(Counter(y_res)))

原本正負樣本絕對量分別為12193:5617,人工合成部分樣本量以後,正負樣本的絕對量變為了12193:12193,完全平衡。

人工合成以後模型預測效果


  
  1. if __name__== "__main__":
  2. get_result_data(X_res, y_res)

模型的準確率和ROC分數較欠取樣都有略微的上漲,其中class0的召回上漲,class1略降。

調權重

調權重就是調整模型中正負樣本的在模型表現中的表決權重,以此來平衡樣本絕對量的不平衡。比如正負樣本絕對量的比值為1:10,為了抵消這種量級上的不平衡,我們在模型中可以給與模型正負樣本10:1的表決權重,也就是10個正樣本的表決相當於1個負樣本的表決。

這個過程我們也可以直接設定模型引數class_weight="balanced"進行實現。


  
  1. x_=scale(x,with_mean= True,with_std= True)
  2. x_train,x_test,y_train,y_test=train_test_split(x,y,test_size= 0.4,random_state= 0)
  3. model=LogisticRegression(class_weight= "balanced")
  4. clf=model.fit(x_train,y_train)
  5. print( "LR模型測試成績:{:.2f}".format(clf.score(x_test,y_test)))
  6. y_pred=clf.predict(x_test)
  7. target_names = [ 'class 0', 'class 1']
  8. print(classification_report(y_test, y_pred, target_names=target_names))
  9. y_pred1=clf.decision_function(x_test)
  10. fpr,tpr,threshold=roc_curve(y_test,y_pred1)
  11. rocauc=auc(fpr,tpr) #計算AUC
  12. print( "ROC分數:{:.2f}".format(rocauc))

調權重的結果和人工合成數據的結果接近一致。

最後

通過上面幾種方法的模型結果可以看出:

  • 用任意一種方式處理或者不處理,ROC基本是一致的,這也驗證了我們在前面的推文中說到的,ROC是和樣本是否平衡沒關係的。
  • 如果不做任何處理,模型的準確率會高,但是會發生嚴重的過擬合。
  • 在做處理的這幾種方式中,欠取樣的效果要差於其他三種。
  • 綜合來看,直接在模型引數中調權重是效果最好,也是最快捷的一種方式,不用事先去做什麼處理。

本文最後的結論是針對本次資料得出的結論,不代表在任何資料上效果都是如此,可能會限於資料本身的原因,結果會有所不同,本文重點講述非平衡資料不同的處理方式以及實現方式




轉載:https://zhuanlan.zhihu.com/p/37311047