總結對比下\(L_1\) 損失函數,\(L_2\) 損失函數以及\(\text{Smooth} L_1\) 損失函數的優缺點。
均方誤差MSE (\(L_2\) Loss)
均方誤差(Mean Square Error,MSE)是模型預測值\(f(x)\) 與真實樣本值\(y\) 之間差值平方的平均值,其公式如下
其中,\(y_i\)和\(f(x_i)\)分別表示第\(i\)個樣本的真實值及其對應的預測值,\(n\)為樣本的個數。
忽略下標\(i\) ,設\(n=1\),以\(f(x) - y\)為橫軸,MSE的值為縱軸,得到函數的圖形如下:
MSE的函數曲線光滑、連續,處處可導,便於使用梯度下降算法,是一種常用的損失函數。 而且,隨着誤差的減小,梯度也在減小,這有利於收斂,即使使用固定的學習速率,也能較快的收斂到最小值。
當\(y\)和\(f(x)\)也就是真實值和預測值的差值大於1時,會放大誤差;而當差值小於1時,則會縮小誤差,這是平方運算決定的。MSE對於較大的誤差(\(>1\))給予較大的懲罰,較小的誤差(\(<1\))給予較小的懲罰。也就是說,對離群點比較敏感,受其影響較大。
如果樣本中存在離群點,MSE會給離群點更高的權重,這就會犧牲其他正常點數據的預測效果,最終降低整體的模型性能。 如下圖:
可見,使用 MSE 損失函數,受離群點的影響較大,雖然樣本中只有 5 個離群點,但是擬合的直線還是比較偏向於離群點。
平均絕對誤差(\(L_1\) Loss)
平均絕對誤差(Mean Absolute Error,MAE) 是指模型預測值\(f(x)\)和真實值\(y\)之間距離的平均值,其公式如下:
忽略下標\(i\) ,設\(n=1\),以\(f(x) - y\)為橫軸,MAE的值為縱軸,得到函數的圖形如下:
MAE曲線連續,但是在\(y-f(x)=0\)處不可導。而且 MAE 大部分情況下梯度都是相等的,這意味着即使對於小的損失值,其梯度也是大的。這不利於函數的收斂和模型的學習。但是,無論對於什么樣的輸入值,都有着穩定的梯度,不會導致梯度爆炸問題,具有較為穩健性的解。
相比於MSE,MAE有個優點就是,對於離群點不那么敏感。因為MAE計算的是誤差\(y-f(x)\)的絕對值,對於任意大小的差值,其懲罰都是固定的。
針對上面帶有離群點的數據,MAE的效果要好於MSE。
顯然,使用 MAE 損失函數,受離群點的影響較小,擬合直線能夠較好地表征正常數據的分布情況。
MSE和MAE的選擇
-
從梯度的求解以及收斂上,MSE是由於MAE的。MSE處處可導,而且梯度值也是動態變化的,能夠快速的收斂;而MAE在0點處不可導,且其梯度保持不變。對於很小的損失值其梯度也很大,在深度學習中,就需要使用變化的學習率,在損失值很小時降低學習率。
-
對離群(異常)值得處理上,MAE要明顯好於MSE。
如果離群點(異常值)需要被檢測出來,則可以選擇MSE作為損失函數;如果離群點只是當做受損的數據處理,則可以選擇MAE作為損失函數。
總之,MAE作為損失函數更穩定,並且對離群值不敏感,但是其導數不連續,求解效率低。另外,在深度學習中,收斂較慢。MSE導數求解速度高,但是其對離群值敏感,不過可以將離群值的導數設為0(導數值大於某個閾值)來避免這種情況。
在某些情況下,上述兩種損失函數都不能滿足需求。例如,若數據中90%的樣本對應的目標值為150,剩下10%在0到30之間。那么使用MAE作為損失函數的模型可能會忽視10%的異常點,而對所有樣本的預測值都為150。這是因為模型會按中位數來預測。而使用MSE的模型則會給出很多介於0到30的預測值,因為模型會向異常點偏移。
這種情況下,MSE和MAE都是不可取的,簡單的辦法是對目標變量進行變換,或者使用別的損失函數,例如:Huber,Log-Cosh以及分位數損失等。
Smooth \(L_1\) Loss
在Faster R-CNN以及SSD中對邊框的回歸使用的損失函數都是Smooth \(L_1\) 作為損失函數,
其中,\(x = f(x_i) - y_i\) 為真實值和預測值的差值。
Smooth \(L_1\) 能從兩個方面限制梯度:
- 當預測框與 ground truth 差別過大時,梯度值不至於過大;
- 當預測框與 ground truth 差別很小時,梯度值足夠小。
對比\(L_1\) Loss 和 \(L_2\) Loss
其中\(x\)為預測框與groud truth之間的差異:
上面損失函數對\(x\)的導數為:
上面導數可以看出:
-
根據公式-4,當\(x\)增大時,\(L_2\)的損失也增大。 這就導致在訓練初期,預測值與 groud truth 差異過於大時,損失函數對預測值的梯度十分大,訓練不穩定。
-
根據公式-5,\(L_1\)對\(x\)的導數為常數,在訓練的后期,預測值與ground truth差異很小時,\(L_1\)的導數的絕對值仍然為1,而 learning rate 如果不變,損失函數將在穩定值附近波動,難以繼續收斂以達到更高精度。
-
根據公式-6,\(\text{Smotth } L_1\)在\(x\)較小時,對\(x\)的梯度也會變小。 而當\(x\)較大時,對\(x\)的梯度的上限為1,也不會太大以至於破壞網絡參數。\(Smooth L_1\)完美的避開了\(L_1\)和\(L_2\)作為損失函數的缺陷。
\(L_1\) Loss ,\(L_2\) Loss以及\(Smooth L_1\) 放在一起的函數曲線對比
從上面可以看出,該函數實際上就是一個分段函數,在[-1,1]之間實際上就是L2損失,這樣解決了L1的不光滑問題,在[-1,1]區間外,實際上就是L1損失,這樣就解決了離群點梯度爆炸的問題
實現 (PyTorch)
def _smooth_l1_loss(input, target, reduction='none'):
# type: (Tensor, Tensor) -> Tensor
t = torch.abs(input - target)
ret = torch.where(t < 1, 0.5 * t ** 2, t - 0.5)
if reduction != 'none':
ret = torch.mean(ret) if reduction == 'mean' else torch.sum(ret)
return ret
也可以添加個參數beta
這樣就可以控制,什么范圍的誤差使用MSE,什么范圍內的誤差使用MAE了。
def smooth_l1_loss(input, target, beta=1. / 9, reduction = 'none'):
"""
very similar to the smooth_l1_loss from pytorch, but with
the extra beta parameter
"""
n = torch.abs(input - target)
cond = n < beta
ret = torch.where(cond, 0.5 * n ** 2 / beta, n - 0.5 * beta)
if reduction != 'none':
ret = torch.mean(ret) if reduction == 'mean' else torch.sum(ret)
return ret
總結
對於大多數CNN網絡,我們一般是使用L2-loss而不是L1-loss,因為L2-loss的收斂速度要比L1-loss要快得多。
對於邊框預測回歸問題,通常也可以選擇平方損失函數(L2損失),但L2范數的缺點是當存在離群點(outliers)的時候,這些點會占loss的主要組成部分。比如說真實值為1,預測10次,有一次預測值為1000,其余次的預測值為1左右,顯然loss值主要由1000決定。所以FastRCNN采用稍微緩和一點絕對損失函數(smooth L1損失),它是隨着誤差線性增長,而不是平方增長。
Smooth L1 和 L1 Loss 函數的區別在於,L1 Loss 在0點處導數不唯一,可能影響收斂。Smooth L1的解決辦法是在 0 點附近使用平方函數使得它更加平滑。
Smooth L1的優點
- 相比於L1損失函數,可以收斂得更快。
- 相比於L2損失函數,對離群點、異常值不敏感,梯度變化相對更小,訓練時不容易跑飛。