不均衡樣本集的處理
不均衡樣本在分類時會出現問題,本質原因是模型在訓練時優化的目標函數和在測試時使用的評價標准不一致。這種“不一致”可能是由於訓練數據的樣本分布於測試時期望的樣本分布不一致(如訓練集正負樣本比例是1:99,而實際測試時期望的正負樣本比例是1:1);也可能是由於訓練階段不同類別的權重與測試階段不一致(如訓練時認為所有樣本的貢獻是相等的,而測試時假陽性樣本和假陰性樣本有着不同的代價)。
基於上述分析,一般從兩個角度來處理樣本不均衡的問題,分別為基於數據和基於算法。
在Python中對應的處理數據不平衡的庫為(imblearn)。
基於數據的方法
基於數據的方法是對樣本進行重采樣,使原本不均衡的樣本變得均衡。使用\(C_{maj}\)表示樣本數大的類別,\(C_{min}\)表示樣本數小的類別。對應的樣本集分別為\(S_{maj}\)和\(S_{min}\),並且有|\(S_{maj}\)|>>|\(S_{min}\)|。
1. 隨機采樣
隨機采樣是最簡單的一種方法,一般分為過采樣(Over-sampling)和欠采樣(Under-sampling)。
隨機過采樣是從少數樣本集\(S_{min}\)中隨機重復抽取樣本(有放回)以得到更多樣本;隨機欠采樣則相反,從多類樣本集中隨機選取較少的樣本(有放回或無放回)。
在Python庫中函數為RandomOverSampler和RandomUnderSampler。處理過程如下:
from imblearn.over_sampling
from imblearn.under_sampling
import RandomOverSampler
import RandomUnderSampler
ros = RandomOverSampler(random_state=0)
#將replacement參數設置為True,可實現自助法(boostrap)抽樣。
rus = RandomUnderSampler(random_state=0,replacement=True)
X_resampled, y_resampled = ros.fit_sample(X, y)
存在問題:過采樣對少數類樣本進行了多次復制, 擴大了數據規模, 增加了模型訓練的復雜度, 同時也容易造成過擬合; 欠采樣會丟棄一些樣本, 可能會損失部分有用信息, 造成模型只學到了整體模式的一部分。
2. 采樣一些方法生產新樣本,解決過采樣產生的問題
為了解決隨機采樣存在的一些問題,可以在采樣時並不是簡單的復制樣本,而是采用一些方法生產新的樣本。
-
SMOTE算法
SMOTE算法對少數類樣本集\(S_{min}\)中每個樣本x,從它在\(S_{min}\)中的K近鄰中隨機選一個樣本y, 然后在x,y連線上隨機選取一點作為新合成的樣本(根據需要的過采樣倍率重復上述過程若干次),這種合成新樣本的過采樣方法可以降低過擬合風險。
問題:SMOTE算法為每個少數類樣本合成相同數量的新樣本,這可能會增大類間重疊度,並且生成一些不能提供有益信息的無用樣本。下面的兩種算法就是對SMOTE算法的改進。
如下圖所示:
Python庫中SMOTE函數的使用:
from imblearn.over_sampling import SMOTE X_resampled_smote, y_resampled_smote = SMOTE().fit_sample(X, y)
-
Borderline-SMOTE
Borderline-SMOTE只給那些處在分類邊界上的少數類樣本合成新樣本 。
判斷邊界的一個簡單的規則為:K 近鄰中有一半以上多數類樣本的少數類為邊界樣本。直觀地講,只為那些周圍大部分是多數類樣本的少數類樣本生成新樣本。
假設 a 為少數類中的一個樣本,此時少數類的樣本分為三類,如下圖所示:
(i) 噪音樣本 (noise), 該少數類的所有最近鄰樣本都來自於不同於樣本 a 的其他類別:
(ii) 危險樣本 (in danger), 至少一半的最近鄰樣本來自於同一類 (不同於 a 的類別);
(iii) 安全樣本 (safe), 所有的最近鄰樣本都來自於同一個類。
對應的 Python 庫中的實現有三種可以選擇的規則:
SMOTE 函數中的 kind 參數控制了選擇哪種規則:
borderline1:最近鄰中的隨機樣本與該少數類樣本 a 來自於不同的類;
borderline2:最近鄰中的隨機樣本可以是屬於任何一個類的樣本;
svm:使用支持向量機分類器產生支持向量然后再生成新的少數類樣本。
from imblearn.under_sampling import ClusterCentroids cc = ClusterCentroids(random_state=0) X_resampled, y_resampled = cc.fit_sample(X, y)
-
ADASYN
ADASYN改進主要是根據數據分布情況為不同的少數類樣本合成不同個數的新樣本 。
首先根據最終的平衡程度設定總共需要生成的新少數類樣本數量 ,然后為每個少數類樣本 x 計算分布比例。
對應的Python庫中的函數為:
from imblearn.over_sampling import ADASYN X_resampled_adasyn, y_resampled_adasyn = ADASYN().fit_sample(X, y)
-
數據清洗方法
-
Tomek Links
使用數據清理的方法來進一步降低合成樣本帶來的類間重疊,得到更加良定義(well-defined)的類簇。
假設樣本點 \(x_i\) 和 \(x_j\) 屬於不同的類別,d (\(x_i, x_j\)) 表示兩個樣本點之間的距離。稱 (\(x_i\),\(x_j\)) 為一個 Tomek link 對,如果不存在第三個樣本點 \(x_l\) 使得 d (\(x_l,x_i\))<d (\(x_i,x_j\)) 或者 d (\(x_l,x_j\))<d (\(x_i,x_j\)) 成立。也就是說這兩個樣本互為近鄰關系。
容易看出,如果兩個樣本點為 Tomek link 對,則其中某個樣本為噪聲(偏離正常分布太多)或者兩個樣本都在兩類的邊界上。
所以Tomek Links方法的作用有兩個,一個是欠采樣,將 Tomek link 對中屬於多數類的樣本剔除;而是數據清洗,經Tomek link對中的兩個樣本都剔除。
在Python庫中
TomekLinks
函數中的auto
參數控制 Tomek’s links 中的哪些樣本被剔除。默認的ratio='auto'
移除多數類的樣本,當ratio='all'
時,兩個樣本均被移除。 -
EditedNearestNeighbours
EditedNearestNeighbours 這種方法應用最近鄰算法來編輯 (edit) 數據集,找出那些與鄰居不太友好的樣本然后移除。對於每一個要進行下采樣的樣本,那些不滿足一些准則的樣本將會被移除;他們的絕大多數 (kind_sel='mode') 或者全部 (kind_sel='all') 的近鄰樣本都屬於同一個類,這些樣本會被保留在數據集中.
from imblearn.under_sampling import EditedNearestNeighbours enn = EditedNearestNeighbours(random_state=0) X_resampled, y_resampled = enn.fit_sample(X, y)
在此基礎上,延伸出了
RepeatedEditedNearestNeighbours
算法,重復基礎的EditedNearestNeighbours
算法多次。from imblearn.under_sampling import RepeatedEditedNearestNeighbours renn = RepeatedEditedNearestNeighbours(random_state=0) X_resampled, y_resampled = renn.fit_sample(X, y) print sorted(Counter(y_resampled).items())
與
RepeatedEditedNearestNeighbours
算法不同的是,ALLKNN
算法在進行每次迭代的時候,最近鄰的數量都在增加.from imblearn.under_sampling import AllKNN allknn = AllKNN(random_state=0) X_resampled, y_resampled = allknn.fit_sample(X, y)
-
CondensedNearestNeighbour
使用 1 近鄰的方法來進行迭代,來判斷一個樣本是應該保留還是剔除,具體的實現步驟如下:
集合 C: 所有的少數類樣本;
選擇一個多數類樣本 (需要下采樣) 加入集合 C, 其他的這類樣本放入集合 S;
使用集合 S 訓練一個 1-NN 的分類器,對集合 S 中的樣本進行分類;
將集合 S 中錯分的樣本加入集合 C;
重復上述過程,直到沒有樣本再加入到集合 C.from imblearn.under_sampling import CondensedNearestNeighbour cnn = CondensedNearestNeighbour(random_state=0) X_resampled, y_resampled = cnn.fit_sample(X, y)
顯然,
CondensedNearestNeighbour
方法對噪音數據是很敏感的,也容易加入噪音數據到集合 C 中.因此,
OneSidedSelection
函數使用TomekLinks
方法來剔除噪聲數據 (多數類樣本). -
該算法主要關注如何清洗數據而不是篩選 (considering) 他們。因此,該算法將使用
EditedNearestNeighbours
和 3-NN 分類器結果拒絕的樣本之間的並集.from imblearn.under_sampling import NeighbourhoodCleaningRule ncr = NeighbourhoodCleaningRule(random_state=0) X_resampled, y_resampled = ncr.fit_sample(X, y)
-
3. 使用Informed Undersampling來解決欠采樣帶來的數據丟失問題
-
Easy Ensemble算法。
每次從多數類\(S_{maj}\)中上隨機抽取一個子集E(|E|≈|\(S_{min}\)|), 然后用E+\(S_{min}\)訓練一個分類器; 重復上述過程若干次, 得到多個分類器,最終的分類結果是這多個分類器結果的融合。
EasyEnsemble 方法對應 Python 庫中函數為 EasyEnsemble,有兩個很重要的參數: (i) n_subsets 控制的是子集的個數 ;(ii) replacement 決定是有放回還是無放回的隨機采樣。
from imblearn.ensemble import EasyEnsemble ee = EasyEnsemble(random_state=0, n_subsets=10) X_resampled, y_resampled = ee.fit_sample(X, y)
-
Balance Cascade算法
級聯結構, 在每一級中從多數類\(S_{maj}\)中隨機抽取子集E, 用E+\(S_{min}\)訓練該級的分類器; 然后將\(S_{maj}\)中能夠被當前分類器正確判別的樣本剔除掉, 繼續在比之前較小的多數類樣本集中執行下才采樣的操作, 重復若干次得到級聯結構; 最終的輸出結果也是各級分類器結果的融合。
BalanceCascade 方法對應 Python 庫中函數為 BalanceCascade,有三個很重要的參數: (i) estimator 是選擇使用的分類器;(ii) n_max_subset 控制的是子集的個數;(iii) bootstrap 決定是有放回還是無放回的隨機采樣。
from imblearn.ensemble import BalanceCascade from sklearn.linear_model import LogisticRegression bc = BalanceCascade(random_state=0, estimator=LogisticRegression(random_state=0), n_max_subset=4) X_resampled, y_resampled = bc.fit_sample(X, y)
-
NearMiss
利用K近鄰信息挑選具有代表性的多數類樣本。首先計算出每個樣本點之間的距離,通過一定規則來選取保留的多數類樣本點。因此該方法的計算量通常很大。
NearMiss 方法對應 Python 庫中函數為 NearMiss,通過 version 來選擇使用的規則:
NearMiss-1:選擇離 N 個近鄰的負樣本的平均距離最小的正樣本;
NearMiss-2:選擇離 N 個負樣本最遠的平均距離最小的正樣本;
NearMiss-3:是一個兩段式的算法。 首先,對於每一個負樣本, 保留它們的 M 個近鄰樣本;接着,那些到 N 個近鄰樣本平均距離最大的正樣本將被選擇。
from imblearn.under_sampling import NearMiss nm1 = NearMiss(random_state=0, version=1) X_resampled_nm1, y_resampled = nm1.fit_sample(X, y)
-
One-Sided Selection
One-Sided Selection 算法的目的是剔除多數類樣本中噪聲、邊界樣本和多余樣本,其算法流程如下 (S 為原始訓練樣本集合):
初始化集合 C,C應該包括所有的少數類樣本和隨機選擇的一個多數類樣本。
集合 C 訓練一個 1-NN 分類器(即 kNN 中選擇近鄰數為 1),並用這個分類器對 S 中的樣本進行分類,將錯分的多數類樣本並入集合 C。
對集合 C 使用 Tomek links 方法剔除多數類樣本,得到最終的訓練樣本集合 T。
One-Sided Selection 算法中使用 Tomek links 剔除多數類樣本中的噪聲和邊界樣本,未被 1-NN 分類器錯分的樣本則被視為多余樣本,最終得到一個類別分布更為平衡的樣本集合。
from imblearn.under_sampling import OneSidedSelection oss = OneSidedSelection(random_state=0) X_resampled, y_resampled = oss.fit_sample(X, y)
4. 其他一些有用的方法
-
基於聚類的方法
基於聚類的方法,利用數據的類簇信息來指導過采樣/欠采樣操作;
-
數據擴充方法
是一個過采樣技術,對少數類樣本進行一些噪聲擾動或變換(如圖像數據集中對圖像進行裁剪、翻轉、旋轉、光照等)以構造出新樣本;
-
Hard Negative Mining
是一種欠采樣,把比較難分的樣本抽取出來,用於迭代分類器。
基於算法的方法
-
改變模型訓練時的目標函數
當樣本不均衡時,改變模型訓練的目標函數,如使用代價敏感學習中不同類別有不同的權重來矯正這種不平衡性。
-
改變問題類別
當樣本極度不平衡時,可以將問題轉變為單類學習(one-class learning)、異常檢測(anomaly detection)。
如何選擇使用何種處理方法
解決數據不平衡問題的方法有很多,上面只是一些最常用的方法,而最常用的方法也有這么多種,如何根據實際問題選擇合適的方法呢:
在正負樣本都非常之少的情況下,應該采用數據合成的方式;
在負樣本足夠多,正樣本非常之少且比例及其懸殊的情況下,應該考慮一分類方法;
在正負樣本都足夠多且比例不是特別懸殊的情況下,應該考慮采樣或者加權的方法。
采樣和加權在數學上是等價的,但實際應用中效果卻有差別。尤其是采樣了諸如 Random Forest 等分類方法,訓練過程會對訓練集進行隨機采樣。在這種情況下,如果計算資源允許上采樣往往要比加權好一些。
另外,雖然上采樣和下采樣都可以使數據集變得平衡,並且在數據足夠多的情況下等價,但兩者也是有區別的。實際應用中,經驗的做法是如果計算資源足夠且小眾類樣本足夠多的情況下使用上采樣,否則使用下采樣,因為上采樣會增加訓練集的大小進而增加訓練時間,同時小的訓練集非常容易產生過擬合。
對於下采樣,如果計算資源相對較多且有良好的並行環境,應該選擇 Ensemble 方法。
模型在不均衡樣本集上的評價標准
-
對不平衡的數據生成模型應用不當的評估指標是相當危險的,很容易就生成了一個“假”的模型。例如一個模型的正負樣本比例是99:1,若使用精度來衡量模型的好壞,將所有測試樣本分類為 “0” 的模型將具有很好的准確性(99%),但顯然這種模型不會為我們提供任何有價值的信息。
-
可以應用其他評估指標替代精度指標
F1_Score,准確率和召回率的調和平均值;
P-R曲線
G-Mean
ROC曲線和AUC
參考文獻:
Imblearn package study(不平衡數據處理之過采樣、下采樣、綜合采樣)
百面機器學習