机器学习中非平衡数据处理
總第97篇
這一篇主要說一下機器學習中非平衡數據的處理方式以及用python如何實現.
在前面的一篇推文中我們提到過,非平衡數據會影響最后的評判效果,嚴重的會帶來過擬合的效果,即模型總是把樣本劃分到樣本量較多的那一種。為了讓模型的評判更準確,我們需要對非平衡數據進行一定的處理,主要有以下幾種方式:
欠采樣
過采樣
人工合成
調權重
在開始介紹不同的處理方式之前,我們先引入一組非平衡數據。
#導入一些相關庫 from sklearn.model_selection import train_test_split from sklearn.linear_model import LogisticRegression from sklearn.metrics import classification_report from sklearn.metrics import roc_curve, auc from sklearn.preprocessing import scale #導入數據 df=pd.read_excel(r"C:\Users\zhangjunhong\Desktop\Unbanlanced-data.xlsx").fillna(0)看一下正負樣本的具體數據量情況。
x=df.iloc[:,1:-1] y=df["label"] print(y.value_counts()) print("-------------------------") print(y.value_counts(normalize=True))該數據量的正負樣本比例接近7:3,我們看一下不做任何處理的情況下,模型的預測效果如何。
#將模型進行封裝,方便調用 def get_result_data(x,y):x_=scale(x,with_mean=True,with_std=True)x_train,x_test,y_train,y_test=train_test_split(x,y,test_size=0.4,random_state=0)model=LogisticRegression()clf=model.fit(x_train,y_train)print("LR模型測試成績:{:.2f}".format(clf.score(x_test,y_test)))y_pred=clf.predict(x_test)target_names = ['class 0', 'class 1']print(classification_report(y_test, y_pred, target_names=target_names))y_pred1=clf.decision_function(x_test)fpr,tpr,threshold=roc_curve(y_test,y_pred1)rocauc=auc(fpr,tpr)#計算AUCprint("ROC分數:{:.2f}".format(rocauc))if __name__=="__main__":get_result_data(x,y)模型的準確率是0.75,ROC分數也就是AUC值為0.76,看著還不錯,但是class1的召回率要明顯高于class0的召回率,這是因為原樣本量中,class1的量要明顯高于class0的原因。
欠采樣
下采樣(under-sampling),是對非平衡數據中樣本數較多的那一類進行采樣,采樣使其約等于樣本量較少那一類的樣本量。
df1=df[df["label"]==1]#正樣本部分 df0=df[df["label"]==0]#負樣本部分#對正樣本按0.5的比例進行下采樣 df2=df1.sample(frac=0.5)#將下采樣后的正樣本與負樣本進行組合 df_new=pd.concat([df0,df2])x=df_new.iloc[:,1:-1] y=df_new["label"]#下采樣以后正負樣本量情況 print(y.value_counts()) print("-------------------------") print(y.value_counts(normalize=True))對模型進行下采樣以后,正負樣本的樣本量基本接近1:1,符合我們目的,接下來看看下采樣后的模型表現。
if __name__=="__main__":get_result_data(x,y)模型的準確率略有下降,但是ROC分數沒發生什么變化,class0和class1的召回率也接近相等。
過采樣
過采樣(over-sampling),是對非平衡數據中樣本數較少的那一類進行采樣,常規的做法就是將其復制幾遍來達到正負樣本平衡,因為是同樣的數據復制多份,很容易發生過擬合,一般比較少用。具體的實現方式就比較簡單啦,這里不羅列。
人工合成
人工合成就是人為地去合成一些樣本量較少的數據,來達到正負樣本平衡,人工合成數據能夠很好地避免過采樣帶來的模型過擬合。比較常用的方法就是SMOTE。
SMOTE的算法原理如下:
根據正負樣本比例,確認采樣的比例,即要合成樣本的數量(k值)
對于少數樣本中的每個x,利用KNN算法,選取k個待采樣的值x_n
然后對x_n進行如下運算得到對應的x_new:x_new=x+rand(1)*|x-x_n|
(rand(1)表示生成0-1之間的一個隨機數)
關于SMOTE算法的實現也由現成的庫,我們直接pip安裝就可以使用。
from collections import Counter from imblearn.over_sampling import SMOTE print('Original dataset shape {}'.format(Counter(y))) sm = SMOTE(random_state=42) X_res, y_res = sm.fit_sample(x, y) print('Resampled dataset shape {}'.format(Counter(y_res)))原本正負樣本絕對量分別為12193:5617,人工合成部分樣本量以后,正負樣本的絕對量變為了12193:12193,完全平衡。
人工合成以后模型預測效果
if __name__=="__main__":get_result_data(X_res, y_res)模型的準確率和ROC分數較欠采樣都有略微的上漲,其中class0的召回上漲,class1略降。
調權重
調權重就是調整模型中正負樣本的在模型表現中的表決權重,以此來平衡樣本絕對量的不平衡。比如正負樣本絕對量的比值為1:10,為了抵消這種量級上的不平衡,我們在模型中可以給與模型正負樣本10:1的表決權重,也就是10個正樣本的表決相當于1個負樣本的表決。
這個過程我們也可以直接設置模型參數class_weight="balanced"進行實現。
x_=scale(x,with_mean=True,with_std=True) x_train,x_test,y_train,y_test=train_test_split(x,y,test_size=0.4,random_state=0)model=LogisticRegression(class_weight="balanced") clf=model.fit(x_train,y_train) print("LR模型測試成績:{:.2f}".format(clf.score(x_test,y_test))) y_pred=clf.predict(x_test)target_names = ['class 0', 'class 1'] print(classification_report(y_test, y_pred, target_names=target_names))y_pred1=clf.decision_function(x_test) fpr,tpr,threshold=roc_curve(y_test,y_pred1) rocauc=auc(fpr,tpr)#計算AUC print("ROC分數:{:.2f}".format(rocauc))調權重的結果和人工合成數據的結果接近一致。
最后
通過上面幾種方法的模型結果可以看出:
用任意一種方式處理或者不處理,ROC基本是一致的,這也驗證了我們在前面的推文中說到的,ROC是和樣本是否平衡沒關系的。
如果不做任何處理,模型的準確率會高,但是會發生嚴重的過擬合。
在做處理的這幾種方式中,欠采樣的效果要差于其他三種。
綜合來看,直接在模型參數中調權重是效果最好,也是最快捷的一種方式,不用事先去做什么處理。
本文最后的結論是針對本次數據得出的結論,不代表在任何數據上效果都是如此,可能會限于數據本身的原因,結果會有所不同,本文重點講述非平衡數據不同的處理方式以及實現方式。
如果對本文的一些指標不是很清楚,你可以看:機器學習模型效果評估
總結
以上是生活随笔為你收集整理的机器学习中非平衡数据处理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 快手赞了又取消别人知道吗(快手短视频Ap
- 下一篇: 阴阳师秘闻几级开启