序言:
對於小目標圖像分割任務,一副圖畫中往往只有一兩個目標,這樣會加大網絡訓練難度,一般有三種方法解決:
1、選擇合適的loss,對網絡進行合理優化,關注較小的目標。
2、改變網絡結構,使用attention機制。
3、類屬attention機制,即先檢測目標區域,裁剪后再分割訓練。
場景:
現在以U-net網絡為基礎,使用keras進行實現小目標的分割。
Loss函數:
1、Log loss
對於二分類任務,log loss如下:
其中,yi為輸入實例xixi的真實類別, pi為預測輸入實例 xi屬於類別 1 的概率。對所有樣本的對數損失表示對每個樣本的對數損失的平均值。
這個loss函數每一次梯度的回傳對每一個類別具有相同的關注度,所以容易受到類別不平衡的影響。
這種情況參照airbus-ship-detection。這個任務是檢測海面上的船只,整個圖片中大海占幅較大,所以采用一些技巧:使用montage拼接圖片,對只有大海的圖片進行采樣來減少圖片大小。
2、WCE loss(weighted cross-entropy)
帶權重的交叉熵
二分類WCE:
這個loss的缺點時需要人為的調整困難樣本的權重,增加調整難度。
3、Focal loss
能否使網絡主動學習困難樣本呢?
focal loss的提出是在目標檢測領域,為了解決正負樣本比例嚴重失調的問題。
focal函數公式:
對比上面其實就是多了 (1-pi)r 。
loss值隨樣本概率變大而變小。
基本思想是,對於類別極度不平衡的情況下,網絡如果在log loss下會傾向只預測負樣本,並且負樣本的預測概率會非常高,回傳的梯度也很大。
但是如果添加了上述項,則focal 函數會使預測概率大的樣本的loss變小,而預測概率小的樣本的loss變大,從而加強了對正樣本的關注度。
from keras import backend as K ''' Compatible with tensorflow backend ''' def focal_loss(gamma=2., alpha=.25): def focal_loss_fixed(y_true, y_pred): 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)) return focal_loss_fixed
model_prn.compile(optimizer=optimizer, loss=[focal_loss(alpha=.25, gamma=2)])
使用U-net輸入輸出都是一張圖,直接使用會導致loss值很大。而且調參alpha和gamma也麻煩。
4、Dice loss
直觀理解為兩個輪廓的相似程度。
或則表示為:
二分類的dice loss:
def dice_coef(y_true, y_pred, smooth=1): intersection = K.sum(y_true * y_pred, axis=[1,2,3]) union = K.sum(y_true, axis=[1,2,3]) + K.sum(y_pred, axis=[1,2,3]) return K.mean( (2. * intersection + smooth) / (union + smooth), axis=0) def dice_coef_loss(y_true, y_pred): 1 - dice_coef(y_true, y_pred, smooth=1)
使用dice loss有時會不可信,原因是對於sofemax或log loss其梯度簡言之是p-t ,t為目標值,p為預測值。而dice loss 為 2t2 / (p+t)2
如果p,t過小會導致梯度變化劇烈,導致訓練困難。
5、IOU loss
類比dice loss,IOU函數公式:
def IoU(y_true, y_pred, eps=1e-6): if np.max(y_true) == 0.0: return IoU(1-y_true, 1-y_pred) ## empty image; calc IoU of zeros intersection = K.sum(y_true * y_pred, axis=[1,2,3]) union = K.sum(y_true, axis=[1,2,3]) + K.sum(y_pred, axis=[1,2,3]) - intersection return -K.mean( (intersection + eps) / (union + eps), axis=0)
IOU loss的缺點呢同DICE loss是相類似的,訓練曲線可能並不可信,訓練的過程也可能並不穩定,有時不如使用softmax loss等的曲線有直觀性,通常而言softmax loss得到的loss下降曲線較為平滑。
6、Tversky loss
Tversky loss使dice系數和jaccard系數的一種廣義系數。
觀察可得當設置α=β=0.5,此時Tversky系數就是Dice系數。
而當設置α=β=1時,此時Tversky系數就是Jaccard系數。
∣A−B∣則意味着是FP(假陽性),而∣B−A∣則意味着是FN(假陰性);α和β分別控制假陰性和假陽性。通過調整α和β我們可以控制假陽性和假陰性之間的權衡。
def tversky(y_true, y_pred): y_true_pos = K.flatten(y_true) y_pred_pos = K.flatten(y_pred) true_pos = K.sum(y_true_pos * y_pred_pos) false_neg = K.sum(y_true_pos * (1-y_pred_pos)) false_pos = K.sum((1-y_true_pos)*y_pred_pos) alpha = 0.7 return (true_pos + smooth)/(true_pos + alpha*false_neg + (1-alpha)*false_pos + smooth) def tversky_loss(y_true, y_pred): return 1 - tversky(y_true,y_pred) def focal_tversky(y_true,y_pred): pt_1 = tversky(y_true, y_pred) gamma = 0.75 return K.pow((1-pt_1), gamma)
7、敏感性-特異性loss
首先敏感性就是召回率,檢測出確實有病的能力:
特異性,檢測出確實沒病的能力:
綜合
其中左邊為病灶像素的錯誤率即,1−Sensitivity,而不是正確率,所以設置λ 為0.05。其中(rn−pn)2是為了得到平滑的梯度。
8、Generalized dice loss
在使用DICE loss時,對小目標是十分不利的,因為在只有前景和背景的情況下,小目標一旦有部分像素預測錯誤,那么就會導致Dice大幅度的變動,從而導致梯度變化劇烈,訓練不穩定。
當病灶分割有多個區域時,一般針對每一類都會有一個DICE,而Generalized Dice index將多個類別的dice進行整合,使用一個指標對分割結果進行量化。
GDL公式:
其中rln為類別l在第n個像素的標准值(GT),而pln為相應的預測概率值。此處最關鍵的是wl,為每個類別的權重。其中
這樣,GDL就能平衡病灶區域和Dice系數之間的平衡。
def generalized_dice_coeff(y_true, y_pred): Ncl = y_pred.shape[-1] w = K.zeros(shape=(Ncl,)) w = K.sum(y_true, axis=(0,1,2)) w = 1/(w**2+0.000001) # Compute gen dice coef: numerator = y_true*y_pred numerator = w*K.sum(numerator,(0,1,2,3)) numerator = K.sum(numerator) denominator = y_true+y_pred denominator = w*K.sum(denominator,(0,1,2,3)) denominator = K.sum(denominator) gen_dice_coef = 2*numerator/denominator return gen_dice_coef def generalized_dice_loss(y_true, y_pred): return 1 - generalized_dice_coeff(y_true, y_pred)
以上本質上都是根據評測標准設計的loss function,有時候普遍會受到目標太小的影響,導致訓練的不穩定;對比可知,直接使用log loss等的loss曲線一般都是相比較光滑的。
9、BCE + dice loss(BCE : Binary Cross Entropy)
說白了,添加二分類交叉熵損失函數。在數據較為平衡的情況下有改善作用,但是在數據極度不均衡的情況下,交叉熵損失會在幾個訓練之后遠小於Dice 損失,效果會損失。
import keras.backend as K from keras.losses import binary_crossentropy def dice_coef(y_true, y_pred, smooth=1): intersection = K.sum(y_true * y_pred, axis=[1,2,3]) union = K.sum(y_true, axis=[1,2,3]) + K.sum(y_pred, axis=[1,2,3]) return K.mean( (2. * intersection + smooth) / (union + smooth), axis=0) def dice_p_bce(in_gt, in_pred): return 1e-3*binary_crossentropy(in_gt, in_pred) - dice_coef(in_gt, in_pred)