1. 程式人生 > >在深度學習中處理不均衡資料集

在深度學習中處理不均衡資料集

在深度學習中處理不均衡資料集

不是所有的資料都是完美的。實際上,如果你拿到一個真實的完全均衡的資料集的話,那你真的是走運了。大部分的時候,你的資料都會有某種程度上的不均衡,也就是說你的資料集中每個類別的數量會不一樣。

我們為什麼想要資料是均衡的?

在我們開始花時間做深度學習專案之前,非常重要的一點是需要理解為什麼我們要做這個事情,確保我們的投入是值得的。當我們真正關心的是少數的類別的時候,類別均衡技術就是真正的必須的了。

 

比如說,我們想預測基於當前的市場情況,房子的屬性,自己的預算,是否應該買房子。在這種情況下,如果我們買了,那麼這是個正確的決定是非常重要的,因為這個是很大的一筆投資。同時,如果你的模型說不要買,而事實上需要買的話,這也沒什麼大不了的。你錯過了這個,總是有其他的房子可以買的。但是如果買錯了的話,那就是個大事了。

 

在上面的情況中,我們當然需要我們的少數“買”的類別要特別的準確,而“不買”的類別則無關緊要。但是在實際情況中,由於買的情況比不買的情況要少得多,我們的模型預測會偏向“不買”的類別,而“買”的類別的準確率則可能會很差。這就需要資料均衡了,我們可以讓“買”類別的權重變大,來讓“買”類別的預測更加準確。

 

但是如果我們對少數類別不關心怎麼辦呢?例如,我要做一個影象的分類,而你的類別的分佈看起來是這樣的:

 

 

第一眼看上去似乎資料均衡是有好處的。但是也許我們對那些少數的類別並不關心。也許我們的目的就是得到最高的準確率。在這種情況下,做資料均衡並沒什麼意義,因為大多數的準確率來自於包含了大量樣本的類別中。第二,交叉熵的損失即使是在不均衡的資料的時候,也是趨向於得到最高的準確率。總的來說,我們的少數類別並沒有對準確率有多少貢獻,所以說,資料均衡並不需要。

 

綜上所述,當我們遇到需要均衡資料的時候,有兩個方法可以幫助我們辦到:

 

權值均衡

權值均衡是在訓練樣本的時候,在計算loss的時候,通過權值來均衡資料的分佈。正常情況下,每個類別在損失函式中的權值是1.0。但是有時候,當某些類別特別重要的時候,我們需要給該類別的訓練樣本更大權值。參考我們的買房的例子,由於“買”的類別的準確率非常的重要,訓練樣本中的這個類別應該對損失函式有更大的影響。

 

可以直接給對應的類別的樣本的loss乘上一個因子來設定權值。在Keras中,我們可以這樣:

import keras
class_weight = {"buy": 0.75,
               "don't buy": 0.25}
model.fit(X_train, Y_train, epochs=10, batch_size=32, class_weight=class_weight)

我們建立了一個字典,其中,“買”類別為75%,表示了佔據了75%的loss,因為比“不買”的類別更加的重要,“不買”的類別設定成了25%。當然,這兩個數字可以修改,直到找到最佳的設定為止。我們可以使用這種方法來均衡不同的類別,當類別之間的樣本數量差別很大的時候。我們可以使用權值均衡的方式來使我們的所有的類別對loss的貢獻是相同的,而不用取費力的收集少數類別的樣本了。

 

另一個可以用來做訓練樣本的權值均衡的是Focal loss。如下所示,主要思想是這樣:在資料集中,很自然的有些樣本是很容易分類的,而有些是比較難分類的。在訓練過程中,這些容易分類的樣本的準確率可以達到99%,而那些難分類的樣本的準確率則很差。問題就在於,那些容易分類的樣本仍然在貢獻著loss,那我們為什麼要給所有的樣本同樣的權值?

 

 

這正是Focal loss要解決的問題。focal loss減小了正確分類的樣本的權值,而不是給所有的樣本同樣的權值。這和給與訓練樣本更多的難分類樣本時一樣的效果。在實際中,當我們有資料不均衡的情況時,我們的多數的類別很快的會訓練的很好,分類準確率很高,因為我們有更多的資料。但是,為了確保我們在少數類別上也能有很好的準確率,我們使用focal loss,給與少數類別的樣本更高的權值。focal loss使用Keras是很容易實現的:

import keras
from keras import backend as K
import tensorflow as tf

# Define our custom loss function
def focal_loss(y_true, y_pred):
   gamma = 2.0, alpha = 0.25
   pt_1 = tf.where(tf.equal(y_true, 1), y_pred, tf.ones_like(y_pred))
   pt_0 = tf.where(tf.equal(y_true, 0), y_pred, tf.zeros_like(y_pred))
   return -K.sum(alpha * K.pow(1. - pt_1, gamma) * K.log(pt_1))-K.sum((1-alpha) *K.pow( pt_0, gamma) * K.log(1. - pt_0))

# Compile our model
adam = Adam(lr=0.0001)
model.compile(loss=[focal_loss], metrics=["accuracy"], optimizer=adam)

 

過取樣和欠取樣

選擇合適的類別的權重有時候比較複雜。做一個簡單的頻率倒數可能有時候效果也不好。Focal loss有點用,但是仍然會對所有的正確分類的樣本都做權值的下降。另外一個數據均衡的方法就是直接的取樣。下面的圖給出了一個大概的說明.

 

 

在影象的兩邊,藍色的類別比橘黃色的類別的樣本多得多。這種情況下,我們在預處理時,有兩種選擇。

 

欠取樣意思是從多數的類別中只採樣其中的一部分的樣本,選擇和少數類別同樣多的樣本。這種取樣保持了該類別原來的資料分佈。這很容易,我們只需要少用點樣本就可以讓資料變得均衡。

 

過取樣的意思是我們複製少數類別中的樣本,使得數量和多數樣本一樣多。複製操作需要保持少數樣本的原有的資料分佈。我們不需要獲取更多的資料就可以讓資料集變得均衡。取樣的方法是一個很好的類別均衡的方法,如果你發現類別權值很難做而且效果不好的時候,可以試試!