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


 

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

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

說明:本文是《Python數據分析與數據化運營》中的“3.4 解決樣本類別分布不均衡的問題”。

 

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

 

所謂的不平衡指的是不同類別的樣本量異非常大。樣本類別分布不平衡主要出現在分類相關的建模問題上。樣本類別分布不均衡從數據規模上可以分為大數據分布不均衡和小數據分布不均衡兩種。

  • 大數據分布不均衡。這種情況下整體數據規模大,只是其中的少樣本類的占比較少。但是從每個特征的分布來看,小樣本也覆蓋了大部分或全部的特征。例如擁有1000萬條記錄的數據集中,其中占比50萬條的少數分類樣本便於屬於這種情況。
  • 小數據分布不均衡。這種情況下整體數據規模小,並且占據少量樣本比例的分類數量也少,這會導致特征分布的嚴重不平衡。例如擁有1000條數據樣本的數據集中,其中占有10條樣本的分類,其特征無論如何擬合也無法實現完整特征值的覆蓋,此時屬於嚴重的數據樣本分布不均衡。

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

樣本分布不平衡主要在於不同類別間的樣本比例差異,以筆者的工作經驗看,如果不同分類間的樣本量差異達到超過10倍就需要引起警覺並考慮處理該問題,超過20倍就要一定要解決該問題。

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

在數據化運營過程中,以下場景會經常產生樣本分布不均衡的問題:

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

3.4.2 解決樣本不均衡的方法

3.4.2.1 通過過抽樣和欠抽樣

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

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

總體上,過抽樣和欠抽樣更適合大數據分布不均衡的情況,尤其是第一種(過抽樣)方法應用更加廣泛。

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

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

使用這種方法時需要對樣本本身做額外處理,只需在算法模型的參數中進行相應設置即可。很多模型和算法中都有基於類別參數的調整設置,以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

https://www.cnblogs.com/laiqun/p/6248784.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


免責聲明!

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



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