[ML] 解決樣本類別分佈不均衡的問題

 

轉自:3.4 解決樣本類別分佈不均衡的問題 | 數據常青藤 (組織排版上稍有修改)html

3.4 解決樣本類別分佈不均衡的問題

說明:本文是《Python數據分析與數據化運營》中的「3.4 解決樣本類別分佈不均衡的問題」。算法

 

-----------------------------下面是正文內容--------------------------網絡

 

所謂的不平衡指的是不一樣類別的樣本量異很是大。樣本類別分佈不平衡主要出如今分類相關的建模問題上。樣本類別分佈不均衡從數據規模上能夠分爲大數據分佈不均衡和小數據分佈不均衡兩種。dom

  • 大數據分佈不均衡。這種狀況下總體數據規模大,只是其中的少樣本類的佔比較少。可是從每一個特徵的分佈來看,小樣本也覆蓋了大部分或所有的特徵。例如擁有1000萬條記錄的數據集中,其中佔比50萬條的少數分類樣本便於屬於這種狀況。
  • 小數據分佈不均衡。這種狀況下總體數據規模小,而且佔據少許樣本比例的分類數量也少,這會致使特徵分佈的嚴重不平衡。例如擁有1000條數據樣本的數據集中,其中佔有10條樣本的分類,其特徵不管如何擬合也沒法實現完整特徵值的覆蓋,此時屬於嚴重的數據樣本分佈不均衡。

樣本分佈不均衡將致使樣本量少的分類所包含的特徵過少,並很難從中提取規律;即便獲得分類模型,也容易產生過分依賴於有限的數據樣本而致使過擬合的問題,當模型應用到新的數據上時,模型的準確性和魯棒性將不好。ide

樣本分佈不平衡主要在於不一樣類別間的樣本比例差別,以筆者的工做經驗看,若是不一樣分類間的樣本量差別達到超過10倍就須要引發警覺並考慮處理該問題,超過20倍就要必定要解決該問題。函數

3.4.1 哪些運營場景中容易出現樣本不均衡

在數據化運營過程當中,如下場景會常常產生樣本分佈不均衡的問題:學習

  • 異常檢測場景。大多數企業中的異常個案都是少許的,好比惡意刷單、黃牛訂單、信用卡欺詐、電力竊電、設備故障等,這些數據樣本所佔的比例一般是總體樣本中不多的一部分,以信用卡欺詐爲例,刷實體信用卡的欺詐比例通常都在0.1%之內。
  • 客戶流失場景。大型企業的流失客戶相對於總體客戶一般是少許的,尤爲對於具備壟斷地位的行業巨擘,例如電信、石油、網絡運營商等更是如此。
  • 罕見事件的分析。罕見事件與異常檢測相似,都屬於發生個案較少;但不一樣點在於異常檢測一般都有是預先定義好的規則和邏輯,而且大多數異常事件都對會企業運營形成負面影響,所以針對異常事件的檢測和預防很是重要;但罕見事件則沒法預判,而且也沒有明顯的積極和消極影響傾向。例如因爲某網絡大V無心中轉發了企業的一條趣味廣告致使用戶流量明顯提高便屬於此類。
  • 發生頻率低的事件。這種事件是預期或計劃性事件,可是發生頻率很是低。例如每一年1次的雙11盛會通常都會產生較高的銷售額,但放到整年來看這一天的銷售額佔比極可能只有1%不到,尤爲對於不多參與活動的公司而言,這種狀況更加明顯。這種屬於典型的低頻事件。

3.4.2 解決樣本不均衡的方法

3.4.2.1 經過過抽樣和欠抽樣

抽樣是解決樣本分佈不均衡相對簡單且經常使用的方法,包括過抽樣和欠抽樣兩種。大數據

  • 過抽樣(也叫上採樣、over-sampling)方法經過增長分類中少數類樣本的數量來實現樣本均衡,最直接的方法是簡單複製少數類樣本造成多條記錄,這種方法的缺點是若是樣本特徵少而可能致使過擬合的問題;通過改進的過抽樣方法經過在少數類中加入隨機噪聲、干擾數據或經過必定規則產生新的合成樣本,例如SMOTE算法。
  • 欠抽樣(也叫下采樣、under-sampling)方法經過減小分類中多數類樣本的樣本數量來實現樣本均衡,最直接的方法是隨機地去掉一些多數類樣原本減少多數類的規模,缺點是會丟失多數類樣本中的一些重要信息。

整體上,過抽樣和欠抽樣更適合大數據分佈不均衡的狀況,尤爲是第一種(過抽樣)方法應用更加普遍。spa

3.4.2.2 經過正負樣本的懲罰權重

經過正負樣本的懲罰權重解決樣本不均衡的問題的思想是在算法實現過程當中,對於分類中不一樣樣本數量的類別分別賦予不一樣的權重(通常思路分類中的小樣本量類別權重高,大樣本量類別權重低),而後進行計算和建模。.net

使用這種方法時須要對樣本自己作額外處理,只需在算法模型的參數中進行相應設置便可。不少模型和算法中都有基於類別參數的調整設置,以scikit-learn中的SVM爲例,經過在class_weight: {dict, 'balanced'}中針對不一樣類別針對不一樣的權重,來手動指定不一樣類別的權重。若是使用其默認的方法balanced,那麼SVM會將權重設置爲與不一樣類別樣本數量呈反比的權重來作自動均衡處理,計算公式爲:n_samples / (n_classes * np.bincount(y))。

若是算法自己支持,這種思路是更加簡單且高效的方法。

3.4.2.3 經過組合/集成方法

組合/集成方法指的是在每次生成訓練集時使用全部分類中的小樣本量,同時從分類中的大樣本量中隨機抽取數據來與小樣本量合併構成訓練集,這樣反覆屢次會獲得不少訓練集和訓練模型。最後在應用時,使用組合方法(例如投票、加權投票等)產生分類預測結果。

例如,在數據集中的正、負例的樣本分別爲100和10000條,比例爲1:100。此時能夠將負例樣本(類別中的大量樣本集)隨機分爲100份(固然也能夠分更多),每份100條數據;而後每次造成訓練集時使用全部的正樣本(100條)和隨機抽取的負樣本(100條)造成新的數據集。如此反覆能夠獲得100個訓練集和對應的訓練模型。

這種解決問題的思路相似於隨機森林。在隨機森林中,雖然每一個小決策樹的分類能力很弱,可是經過大量的「小樹」組合造成的「森林」具備良好的模型預測能力。

若是計算資源充足,而且對於模型的時效性要求不高的話,這種方法比較合適。

3.4.2.4 經過特徵選擇

上述幾種方法都是基於數據行的操做,經過多種途徑來使得不一樣類別的樣本數據行記錄均衡。除此之外,還能夠考慮使用或輔助於基於列的特徵選擇方法。

通常狀況下,樣本不均衡也會致使特徵分佈不均衡,但若是小類別樣本量具備必定的規模,那麼意味着其特徵值的分佈較爲均勻,可經過選擇具備顯著型的特徵配合參與解決樣本不均衡問題,也能在必定程度上提升模型效果。

提示 上述幾種方法的思路都是基於分類問題解決的。實際上,這種從大規模數據中尋找罕見數據的狀況,也可使用非監督式的學習方法,例如使用One-class SVM進行異常檢測。分類是監督式方法,前期是基於帶有標籤(Label)的數據進行分類預測;而採用非監督式方法,則是使用除了標籤之外的其餘特徵進行模型擬合,這樣也能獲得異常數據記錄。因此,要解決異常檢測類的問題,先是考慮總體思路,而後再考慮方法模型。

3.4.3 代碼實操:Python處理樣本不均衡

本示例中,咱們主要使用一個新的專門用於不平衡數據處理的Python包imbalanced-learn,讀者須要先在系統終端的命令行使用pip install imbalanced-learn進行安裝;安裝成功後,在Python或IPython命令行窗口經過使用import imblearn(注意導入的庫名)檢查安裝是否正確,示例代碼包版本爲0.2.1。除此之外,咱們還會使用sklearn的SVM在算法中經過調整類別權重來處理樣本不均衡問題。本示例使用的數據源文件data2.txt位於「附件-chapter3」中,默認工做目錄爲「附件-chapter3」(若是不是,請cd切換到該目錄下,不然會報「IOError: File data2.txt does not exist」)。完整代碼以下:

import pandas as pd
from imblearn.over_sampling import SMOTE # 過抽樣處理庫SMOTE
from imblearn.under_sampling import RandomUnderSampler # 欠抽樣處理庫RandomUnderSampler
from sklearn.svm import SVC #SVM中的分類算法SVC
from imblearn.ensemble import EasyEnsemble # 簡單集成方法EasyEnsemble

# 導入數據文件
df = pd.read_table('data2.txt', sep=' ', names=['col1', 'col2','col3', 'col4', 'col5', 'label']) # 讀取數據文件
x = df.iloc[:, :-1] # 切片,獲得輸入x
y = df.iloc[:, -1] # 切片,獲得標籤y
groupby_data_orgianl = df.groupby('label').count() # 對label作分類彙總
print (groupby_data_orgianl) # 打印輸出原始數據集樣本分類分佈

# 使用SMOTE方法進行過抽樣處理
model_smote = SMOTE() # 創建SMOTE模型對象
x_smote_resampled, y_smote_resampled = model_smote.fit_sample(x,y) # 輸入數據並做過抽樣處理
x_smote_resampled = pd.DataFrame(x_smote_resampled, columns=['col1','col2', 'col3', 'col4', 'col5']) # 將數據轉換爲數據框並命名列名
y_smote_resampled = pd.DataFrame(y_smote_resampled,columns=['label']) # 將數據轉換爲數據框並命名列名
smote_resampled = pd.concat([x_smote_resampled, y_smote_resampled],axis=1) # 按列合併數據框
groupby_data_smote = smote_resampled.groupby('label').count() # 對label作分類彙總
print (groupby_data_smote) # 打印輸出通過SMOTE處理後的數據集樣本分類分佈

# 使用RandomUnderSampler方法進行欠抽樣處理
model_RandomUnderSampler = RandomUnderSampler() # 創建RandomUnderSampler模型對象
x_RandomUnderSampler_resampled, y_RandomUnderSampler_resampled =model_RandomUnderSampler.fit_sample(x,y) # 輸入數據並做欠抽樣處理
x_RandomUnderSampler_resampled =pd.DataFrame(x_RandomUnderSampler_resampled,columns=['col1','col2','col3','col4','col5'])
# 將數據轉換爲數據框並命名列名
y_RandomUnderSampler_resampled =pd.DataFrame(y_RandomUnderSampler_resampled,columns=['label']) # 將數據轉換爲數據框並命名列名
RandomUnderSampler_resampled =pd.concat([x_RandomUnderSampler_resampled, y_RandomUnderSampler_resampled], axis= 1) # 按列合併數據框
groupby_data_RandomUnderSampler =RandomUnderSampler_resampled.groupby('label').count() # 對label作分類彙總
print (groupby_data_RandomUnderSampler) # 打印輸出通過RandomUnderSampler處理後的數據集樣本分類分佈

# 使用SVM的權重調節處理不均衡樣本
model_svm = SVC(class_weight='balanced') # 建立SVC模型對象並指定類別權重
model_svm.fit(x, y) # 輸入x和y並訓練模型

# 使用集成方法EasyEnsemble處理不均衡樣本
model_EasyEnsemble = EasyEnsemble() # 創建EasyEnsemble模型對象
x_EasyEnsemble_resampled, y_EasyEnsemble_resampled =
model_EasyEnsemble.fit_sample(x, y) # 輸入數據並應用集成方法處理
print (x_EasyEnsemble_resampled.shape) # 打印輸出集成方法處理後的x樣本集概況
print (y_EasyEnsemble_resampled.shape) # 打印輸出集成方法處理後的y標籤集概況

# 抽取其中一份數據作審查
index_num = 1 # 設置抽樣樣本集索引
x_EasyEnsemble_resampled_t =pd.DataFrame(x_EasyEnsemble_resampled[index_num],columns=['col1','col2','col3','col4','col5'])
# 將數據轉換爲數據框並命名列名
y_EasyEnsemble_resampled_t =pd.DataFrame(y_EasyEnsemble_resampled[index_num],columns=['label']) # 將數據轉換爲數據框並命名列名
EasyEnsemble_resampled = pd.concat([x_EasyEnsemble_resampled_t,
y_EasyEnsemble_resampled_t], axis = 1) # 按列合併數據框
groupby_data_EasyEnsemble =EasyEnsemble_resampled.groupby('label').count() # 對label作分類彙總
print (groupby_data_EasyEnsemble) # 打印輸出通過EasyEnsemble處理後的數據集樣本分類分佈
View Code

 

示例代碼以空行分爲6部分。

 

第一部分導入庫。本示例中用到了第三方庫imbalanced-learn實現主要的樣本不均衡處理,而pandas的引入主要用於解釋和說明不一樣處理方法獲得的結果集樣本的分佈狀況,sklearn.svm中的SVC主要用於說明SVM如何在算法中自動調整分類權重。

 

第二部分導入數據文件。該過程當中使用pandas的read_table讀取本地文件,爲了更好的區別不一樣的列,經過names指定列名;對數據框作切片分割獲得輸入的x和目標變量y;經過pandas的groupby()方法按照label類作分類彙總,彙總方式是使用count()函數計數。輸入原始數據集樣本分類分佈以下:

col1 col2 col3 col4 col5 label 
0.0 942 942 942 942 942
1.0 58 58 58 58 58

 輸出結果顯示了原始數據集中,正樣本(label爲1)的數量僅有58個,佔總樣本量的5.8%,屬於嚴重不均衡分佈。

 

第三部分使用SMOTE方法進行過抽樣處理。該過程當中首先創建SMOTE模型對象,並直接應用fit_sample對數據進行過抽樣處理,若是要得到有關smote的具體參數信息,可先使用fit(x,y)方法得到模型信息,並獲得模型不一樣參數和屬性;從fit_sample方法分別獲得對x和y過抽樣處理後的數據集,將兩份數據集轉換爲數據框而後合併爲一個總體數據框;最後經過pandas提供的groupby()方法按照label類作分類彙總,彙總方式是使用count()函數計數。通過SMOTE處理後的數據集樣本分類分佈以下:

col1 col2 col3 col4 col5 label 
0.0 942 942 942 942 942
1.0 942 942 942 942 942

 經過對比第二部分代碼段的原始數據集返回結果發現,該結果中的正樣本(label爲1)的數量增長,並與負樣本數量相同,均爲942條,數據分類樣本獲得平衡。

 

第四部分使用RandomUnderSampler方法進行欠抽樣處理。該過程與第三部分步驟徹底相同,在此略過各模塊介紹,用途都已在代碼備註中註明。通過RandomUnderSampler處理後的數據集樣本分類分佈以下:

col1 col2 col3 col4 col5 label 
0.0 58 58 58 58 58
1.0 58 58 58 58 58

 經過對比第二部分代碼段的原始數據集返回的結果,該結果中的負樣本(label爲0)的數量減小,並跟正樣本相同,均爲58條,樣本獲得平衡。

 

第五部分使用SVM的權重調節處理不均衡樣本。該過程主要經過配置SVC中的class_weight參數和值的設置來處理樣本權重,該參數可設置爲字典、None或字符串balanced三種模式:

  • 字典:經過手動指定的不一樣類別的權重,例如{1:10,0:1}
  • None:表明類別的權重相同
  • balanced:表明算法將自動調整與輸入數據中的類頻率成反比的權重,具體公式爲n_samples /(n_classes * np.bincount(y)),程序示例中使用了該方法

通過設置後,算法自動處理樣本分類權重,無需用戶作其餘處理。要對新的數據集作預測,只須要調用model_svm模型對象的predict方法便可。

 

第六部分使用集成方法EasyEnsemble處理不均衡樣本。該方法的主要過程與其餘imblearn方法過程相似,不一樣點在於集成方法返回的數據爲三維數據,即將數據在原來的基礎上新增了一個維度——「份數」,集成方法返回的數據x和y的形狀爲(10, 116, 5)和(10, 116)。爲了更詳細的查看其中每一份數據,抽取其中一份數據作審查,獲得的每份數據返回結果以下:

col1 col2 col3 col4 col5 label 
0.0 58 58 58 58 58
1.0 58 58 58 58 58

經過對比第二部分代碼段的原始數據集返回的結果,該結果中的負樣本(label爲0)的數量減小,並跟正樣本相同,均爲58條,樣本集獲得平衡。隨後的應用中,能夠經過循環讀取每一份數據訓練模型並獲得結果,而後將10(x處理後返回的結果,經過形狀名年齡返回的元組中的第一個數值,x_EasyEnsemble_resampled.shape[0])份數據的結果經過必定方法作彙總。

 

上述過程當中,主要須要考慮的關鍵點是:

  • 如何針對不一樣的具體場景選擇最合適的樣本均衡解決方案,選擇過程當中既要考慮到每一個類別樣本的分佈狀況以及總樣本狀況,又要考慮後續數據建模算法的適應性,以及整個數據模型計算的數據時效性。

代碼實操小結:本小節示例中,主要用了幾個知識點:

  • 經過pandas的read_table方法讀取文本數據文件,並指定列名
  • 對數據框作切片處理
  • 經過pandas提供的groupby()方法配合count()作分類彙總
  • 使用imblearn.over_sampling中的SMOTE作過抽樣處理
  • 使用imblearn.under_sampling中的RandomUnderSampler作欠抽樣處理
  • 使用imblearn.ensemble中的EasyEnsemble作集成處理
  • 使用sklearn.svm 中的SVC自動調整算法對不一樣類別的權重設置

提示 第三方庫imblearn提供了很是多的樣本不均衡處理方法,限於篇幅沒法作一一介紹,建議讀者自行安裝並學習和了解不一樣的用法。

 

其它參考:

How to Handle Imbalanced Classes in Machine Learning  (譯文https://yq.aliyun.com/articles/226016

http://www.javashuo.com/article/p-fpwqhxde-p.html

https://blog.csdn.net/login_sonata/article/details/54290402

http://www.cnblogs.com/lyr2015/p/8711120.html

https://blog.csdn.net/weixin_42243942/article/details/80480313

相關文章
相關標籤/搜索