深度學習基礎-損失函數詳解

語言: CN / TW / HK

本文總結分類和迴歸任務的常用損失函數,比如重點解析了交叉熵損失函數的由來,並給出詳細計算公式和、案例分析、代碼,同時也描述了 MAEMSE 損失函數,給出了詳細的計算公式、曲線圖及優缺點。

一,損失函數概述

大多數深度學習算法都會涉及某種形式的優化,所謂優化指的是改變 $x$ 以最小化或最大化某個函數 $f(x)$ 的任務,我們通常以最小化 $f(x)$ 指代大多數最優化問題。

在機器學習中,損失函數是代價函數的一部分,而代價函數是目標函數的一種類型。 - 損失函數loss function): 用於定義單個訓練樣本預測值與真實值之間的誤差 - 代價函數cost function): 用於定義單個批次/整個訓練集樣本預測值與真實值之間的累計誤差。 - 目標函數objective function): 泛指任意可以被優化的函數。

損失函數定義:損失函數是深度學習模型訓練過程中關鍵的一個組成部分,其通過前言的內容,我們知道深度學習算法優化的第一步首先是確定目標函數形式。

損失函數大致可分為兩種:迴歸損失(針對連續型變量)和分類損失(針對離散型變量)。

常用的減少損失函數的優化算法是“梯度下降法”(Gradient Descent)。

二,交叉熵函數-分類損失

交叉熵損失(Cross-Entropy Loss) 又稱為對數似然損失(Log-likelihood Loss)、對數損失,二分類時還可稱之為邏輯斯諦迴歸損失(Logistic Loss)。

2.1,交叉熵(Cross-Entropy)的由來

交叉熵損失的由來參考文檔 AI-EDU: 交叉熵損失函數

1,信息量

信息論中,信息量的表示方式:

《深度學習》(花書)中稱為自信息(self-information) 。 在本文中,我們總是用 $\text{log}$ 來表示自然對數,其底數為 $e$。

$$ I(x_j) = -\log (p(x_j)) $$

  • $x_j$:表示一個事件
  • $p(x_j)$:表示事件 $x_j$ 發生的概率
  • $I(x_j)$:信息量,$x_j$ 越不可能發生時,它一旦發生後的信息量就越大

2,熵

信息量只處理單個的輸出。我們可以用熵(也稱香農熵 Shannon entropy)來對整個概率分佈中的不確定性總量進行量化:

$$ H(p) = - \sum_j^n p(x_j) \log (p(x_j)) $$

則上面的問題的熵是:

$$ \begin{aligned} H(p)&=-[p(x_1) \ln p(x_1) + p(x_2) \ln p(x_2) + p(x_3) \ln p(x_3)] \\ &=0.7 \times 0.36 + 0.2 \times 1.61 + 0.1 \times 2.30 \\ &=0.804 \end{aligned} $$

3,相對熵(KL散度)

相對熵又稱 KL 散度,如果對於同一個隨機變量 $x$ 有兩個單獨的概率分佈 $P(x)$ 和 $Q(x)$,則可以使用 KL 散度(Kullback-Leibler (KL) divergence)來衡量這兩個分佈的差異,這個相當於信息論範疇的均方差。

KL散度的計算公式:

$$ D_{KL}(p||q)=\sum_{j=1}^m p(x_j) \log {p(x_j) \over q(x_j)} $$

$m$ 為事件的所有可能性(分類任務中對應類別數目)。$D$ 的值越小,表示 $q$ 分佈和 $p$ 分佈越接近

4,交叉熵

把上述交叉熵公式變形:

$$ \begin{aligned} D_{KL}(p||q)&=\sum_{j=1}^m p(x_j) \log {p(x_j)} - \sum_{j=1}^m p(x_j) \log q(x_j) \\ &=- H(p(x)) + H(p,q) \end{aligned} $$

等式的前一部分恰巧就是 $p$ 的熵,等式的後一部分,就是交叉熵(機器學習中 $p$ 表示真實分佈(目標分佈),$q$ 表示預測分佈):

$$ H(p,q) =- \sum_{j=1}^m p(x_j) \log q(x_j) $$

在機器學習中,我們需要評估標籤值 $y$ 和預測值 $a$ 之間的差距熵(即兩個概率分佈之間的相似性),使用 KL 散度 $D_{KL}(y||a)$ 即可,但因為樣本標籤值的分佈通常是固定的,即 $H(a)$ 不變。因此,為了計算方便,在優化過程中,只需要關注交叉熵就可以了。所以,在機器學習中一般直接用交叉熵做損失函數來評估模型

$$ loss = \sum_{j = 1}^{m}y_{j}\text{log}(a_{j}) $$

上式是單個樣本的情況,$m$ 並不是樣本個數,而是分類個數。所以,對於批量樣本的交叉熵損失計算公式(很重要!)是:

$$ J = -\frac{1}{n}\sum_{i=1}^n \sum_{j=1}^{m} y_{ij} \log a_{ij} $$

其中,$n$ 是樣本數,$m$ 是分類數。

公式參考文章-AI-EDU: 交叉熵損失函數,但是將樣本數改為 $n$,類別數改為 $m$。

有一類特殊問題,就是事件只有兩種情況發生的可能,比如“是狗”和“不是狗”,稱為 $0/1$ 分類或二分類。對於這類問題,由於 $m=2,y_1=1-y_2,a_1=1-a_2$,所以二分類問題的單個樣本的交叉熵可以簡化為:

$$ loss =-[y \log a + (1-y) \log (1-a)] $$

二分類對於批量樣本的交叉熵計算公式是:

$$ J= -\frac{1}{n} \sum_{i=1}^n [y_i \log a_i + (1-y_i) \log (1-a_i)] $$

為什麼交叉熵的代價函數是求均值而不是求和? Cross entropy loss is defined as the “expectation” of the probability distribution of a random variable 𝑋, and that’s why we use mean instead of sum. 參見這裏

2.1.1,熵、相對熵以及交叉熵總結

交叉熵 $H(p, q)$ 也記作 $CE(p, q)$、$H(P, Q)$,其另一種表達公式(公式表達形式雖然不一樣,但是意義相同): $$H(P, Q) = -\mathbb{E}_{\textrm{x}\sim p}log(q(x))$$

交叉熵函數常用於邏輯迴歸(logistic regression),也就是分類(classification)。

根據信息論中熵的性質,將熵、相對熵(KL 散度)以及交叉熵的公式放到一起總結如下:

$$ \begin{aligned} H(p) &= -\sum_{j}p(x_j) \log p(x_j) \ D_{KL}(p \parallel q) &= \sum_{j}p(x_j)\log \frac{p(x_j)}{q(x_j)} = \sum_j (p(x_j)\log p(x_j) - p(x_j) \log q(x_j)) \ H(p,q) &= -\sum_j p(x_j)\log q(x_j) \ \end{aligned} $$

2.2,二分類問題的交叉熵

把二分類的交叉熵公式 4 分解開兩種情況: - 當 $y=1$ 時,即標籤值是 $1$ ,是個正例,加號後面的項為: $loss = -\log(a)$ - 當 $y=0$ 時,即標籤值是 $0$,是個反例,加號前面的項為 $0$: $loss = -\log (1-a)$

橫座標是預測輸出,縱座標是損失函數值。$y=1$ 意味着當前樣本標籤值是1,當預測輸出越接近1時,損失函數值越小,訓練結果越準確。當預測輸出越接近0時,損失函數值越大,訓練結果越糟糕。此時,損失函數值如下圖所示。

二分類交叉熵損失函數圖

2.3,多分類問題的交叉熵

當標籤值不是非0即1的情況時,就是多分類了。

假設希望根據圖片動物的輪廓、顏色等特徵,來預測動物的類別,有三種可預測類別:貓、狗、豬。假設我們訓練了兩個分類模型,其預測結果如下:

模型1:

|預測值|標籤值|是否正確| |-----|-----|-------| |0.3 0.3 0.4|0 0 1(豬)|正確| |0.3 0.4 0.4|0 1 0(狗)|正確| |0.1 0.2 0.7|1 0 0(貓)|錯誤|

每行表示不同樣本的預測情況,公共 3 個樣本。可以看出,模型 1 對於樣本 1 和樣本 2 以非常微弱的優勢判斷正確,對於樣本 3 的判斷則徹底錯誤。

模型2:

|預測值|標籤值|是否正確| |-----|-----|-------| |0.1 0.2 0.7|0 0 1(豬)|正確| |0.1 0.7 0.2|0 1 0(狗)|正確| |0.3 0.4 0.4|1 0 0(貓)|錯誤|

可以看出,模型 2 對於樣本 1 和樣本 2 判斷非常準確(預測概率值更趨近於 1),對於樣本 3 雖然判斷錯誤,但是相對來説沒有錯得太離譜(預測概率值遠小於 1)。

結合多分類的交叉熵損失函數公式可得,模型 1 的交叉熵為:

$$ \begin{aligned} \text{sample}\ 1\ \text{loss} = -(0\times log(0.3) + 0\times log(0.3) + 1\times log(0.4) = 0.91 \ \text{sample}\ 1\ \text{loss} = -(0\times log(0.3) + 1\times log(0.4) + 0\times log(0.4) = 0.91 \ \text{sample}\ 1\ \text{loss} = -(1\times log(0.1) + 0\times log(0.2) + 0\times log(0.7) = 2.30 \end{aligned} $$

對所有樣本的 loss 求平均:

$$ L = \frac{0.91 + 0.91 + 2.3}{3} = 1.37 $$

模型 2 的交叉熵為:

$$ \begin{aligned} \text{sample}\ 1\ \text{loss} = -(0\times log(0.1) + 0\times log(0.2) + 1\times log(0.7) = 0.35 \ \text{sample}\ 1\ \text{loss} = -(0\times log(0.1) + 1\times log(0.7) + 0\times log(0.2) = 0.35 \ \text{sample}\ 1\ \text{loss} = -(1\times log(0.3) + 0\times log(0.4) + 0\times log(0.4) = 1.20 \end{aligned} $$

對所有樣本的 loss 求平均:

$$ L = \frac{0.35 + 0.35 + 1.2}{3} = 0.63 $$

可以看到,0.63 比 1.37 的損失值小很多,這説明預測值越接近真實標籤值,即交叉熵損失函數可以較好的捕捉到模型 1 和模型 2 預測效果的差異。交叉熵損失函數值越小,反向傳播的力度越小

多分類問題計算交叉熵的實例來源於知乎文章-損失函數|交叉熵損失函數

2.4,PyTorch 中的 Cross Entropy

PyTorch 中常用的交叉熵損失函數為 torch.nn.CrossEntropyLoss

python class torch.nn.CrossEntropyLoss(weight=None, size_average=None, ignore_index=-100, reduce=None, reduction='elementwise_mean')

1,函數功能:

將輸入經過 softmax 激活函數之後,再計算其與 target 的交叉熵損失。即該方法將 nn.LogSoftmax()nn.NLLLoss()進行了結合。嚴格意義上的交叉熵損失函數應該是 nn.NLLLoss()

2,參數解釋:

  • weight(Tensor)- 為每個類別的 loss 設置權值,常用於類別不均衡問題。weight 必須是 float 類型的 tensor,其長度要於類別 C 一致,即每一個類別都要設置有 weight。
  • size_average(bool)- 當 reduce=True 時有效。為 True 時,返回的 loss 為平均值;為 False 時,返回的各樣本的 loss 之和。
  • reduce(bool)- 返回值是否為標量,默認為 True。
  • ignore_index(int)- 忽略某一類別,不計算其 loss,其 loss 會為 0,並且,在採用 size_average 時,不會計算那一類的 loss,除的時候的分母也不會統計那一類的樣本。

2.4.1,Softmax 多分類函數

注意: Softmax 用作模型最後一層的函數通常和交叉熵作損失函數配套搭配使用,應用於多分類任務。

對於二分類問題,我們使用 Logistic 函數計算樣本的概率值,從而把樣本分成了正負兩類。對於多分類問題,則使用 Softmax 作為模型最後一層的激活函數來將多分類的輸出值轉換為範圍在 [0, 1] 和為 1 的概率分佈

Softmax 從字面上來説,可以分成 soft 和 max 兩個部分。max 故名思議就是最大值的意思。Softmax 的核心在於 soft,而 soft 有軟的含義,與之相對的是 hard 硬,即 herdmax。下面分佈演示將模型輸出值取 max 值引入 Softmax 的對比情況。

取max值(hardmax)

假設模型輸出結果 $z$ 值是 $[3,1,-3]$,如果取 max 操作會變成 $[1, 0, 0]$,這符合我們的分類需要,即三者相加為1,並且認為該樣本屬於第一類。但是有兩個不足:

  1. 分類結果是 $[1,0,0]$,只保留非 0 即 1 的信息,即非黑即白,沒有各元素之間相差多少的信息,可以理解是“Hard Max”;
  2. max 操作本身不可導,無法用在反向傳播中。

引入Softmax

Softmax 加了個"soft"來模擬 max 的行為,但同時又保留了相對大小的信息。

$$ a_j = \text{Softmax}(z_j) = \frac{e^{z_j}}{\sum\limits_{i=1}^m e^{z_i}}=\frac{e^{z_j}}{e^{z_1}+e^{z_2}+\dots+e^{z_m}} $$

上式中:

  • $z_j$ 是對第 $j$ 項的分類原始值,即矩陣運算的結果
  • $z_i$ 是參與分類計算的每個類別的原始值
  • $m$ 是總分類數
  • $a_j$ 是對第 $j$ 項的計算結果

和 hardmax 相比,Softmax 的含義就在於不再唯一的確定某一個最大值,而是為每個輸出分類的結果都賦予一個概率值(置信度),表示屬於每個類別的可能性。

下圖可以形象地説明 Softmax 的計算過程。

Softmax工作過程

當輸入的數據 $[z_1,z_2,z_3]$ 是 $[3, 1, -3]$ 時,按照圖示過程進行計算,可以得出輸出的概率分佈是 $[0.879,0.119,0.002]$。對比 max 運算和 Softmax 的不同,如下表所示。

|輸入原始值|MAX計算|Softmax計算| |--------|-------|----------| |$[3, 1, -3]$|$[1, 0, 0]$|$[0.879, 0.119, 0.002]$|

可以看出 Softmax 運算結果兩個特點:

  1. 三個類別的概率相加為 1
  2. 每個類別的概率都大於 0

下面我再給出 hardmax 和 softmax 計算的代碼實現。

```python

example of the argmax of a list of numbers

from numpy import argmax from numpy import exp

define data

data = [3, 1, -3]

def hardmax(data): """# calculate the argmax of the list""" result = argmax(data) return result

def softmax(vector): """# calculate the softmax of a vector""" e = exp(vector) return e / e.sum()

hardmax_result = hardmax(data)

運行該示例返回列表索引值“0”,該值指向包含列表“3”中最大值的數組索引 [1]。

print(hardmax(data)) # 0

convert list of numbers to a list of probabilities

softmax_result = softmax(data) print(softmax_result) # report the probabilities print(sum(softmax_result)) # report the sum of the probabilitie ```

運行以上代碼後,輸出結果如下:

0

[0.87887824 0.11894324 0.00217852]

1.0

很明顯程序的輸出結果和我們手動計算的結果是一樣的。

Pytorch 中的 Softmax 函數定義如下:

python def softmax(x): return torch.exp(x)/torch.sum(torch.exp(x), dim=1).view(-1,1)

dim=1 用於 torch.sum() 對所有列的每一行求和,.view(-1,1) 用於防止廣播。

2.5,為什麼不能使用均方差做為分類問題的損失函數?

迴歸問題通常用均方差損失函數,可以保證損失函數是個凸函數,即可以得到最優解。而分類問題如果用均方差的話,損失函數的表現不是凸函數,就很難得到最優解。而交叉熵函數可以保證區間內單調。

分類問題的最後一層網絡,需要分類函數,Sigmoid 或者 Softmax,如果再接均方差函數的話,其求導結果複雜,運算量比較大。用交叉熵函數的話,可以得到比較簡單的計算結果,一個簡單的減法就可以得到反向誤差。

三,迴歸損失

與分類問題不同,迴歸問題解決的是對具體數值的預測。解決迴歸問題的神經網絡一般只有只有一個輸出節點,這個節點的輸出值就是預測值。

迴歸問題的一個基本概念是殘差或稱為預測誤差,用於衡量模型預測值與真實標記的靠近程度。假設迴歸問題中對應於第 $i$ 個輸入特徵 $x_i$ 的標籤為 $y^i = (y_1,y_2,...,y_M)^{\top}$,$M$ 為標記向量總維度,則 $l_{t}^{i}$ 即表示樣本 $i$ 上神經網絡的迴歸預測值 ($y^i$) 與其樣本標籤值在第 $t$ 維的預測誤差(亦稱殘差):

$$ l_{t}^{i} = y_{t}^{i} - \hat{y}_{t}^{i} $$

常用的兩種損失函數為 $\text{MAE}$(也叫 L1 損失) 和 $\text{MSE}$ 損失函數(也叫 L2 損失)。

3.1,MAE 損失

平均絕對誤差(Mean Absolute Error,MAE)是用於迴歸模型的最簡單但最強大的損失函數之一。

因為存在離羣值(與其餘數據差異很大的值),所以迴歸問題可能具有本質上不是嚴格高斯分佈的變量。 在這種情況下,平均絕對誤差將是一個理想的選擇,因為它沒有考慮異常值的方向(不切實際的高正值或負值)。

顧名思義,MAE 是目標值和預測值之差的絕對值之和。$n$ 是數據集中數據點的總數,其公式如下: $$ \text{MAE loss} = \frac{1}{n}\sum_{i=1}^{N}\sum_{t=1}^{M} |y_{t}^{i} - \hat{y}_{t}^{i}| $$

3.2,MSE 損失

均方誤差(Mean Square Error, MSE)幾乎是每個數據科學家在迴歸損失函數方面的偏好,這是因為大多數變量都可以建模為高斯分佈

均方誤差計算方法是求預測值與真實值之間距離的平方和。預測值和真實值越接近,兩者的均方差就越小。公式如下:

$$ \text{MSE loss} = \frac{1}{n}\sum_{i=1}^{N}\sum_{t=1}^{M} (y_{t}^{i} - \hat{y}_{t}^{i})^2 $$

3.3,Huber 損失

MAE 和 MSE 損失之間的比較產生以下結果:

  1. MAE 損失比 MSE 損失更穩健。仔細查看公式,可以觀察到如果預測值和實際值之間的差異很大,與 MAE 相比,MSE 損失會放大效果。 由於 MSE 會屈服於異常值,因此 MAE 損失函數是更穩健的損失函數。

  2. MAE 損失不如 MSE 損失穩定。由於 MAE 損失處理的是距離差異,因此一個小的水平變化都可能導致迴歸線波動很大。在多次迭代中發生的影響將導致迭代之間的斜率發生顯著變化。總結就是,MSE 可以確保迴歸線輕微移動以對數據點進行小幅調整。

  3. MAE 損失更新的梯度始終相同。即使對於很小的損失值,梯度也很大。這樣不利於模型的學習。為了解決這個缺陷,我們可以使用變化的學習率,在損失接近最小值時降低學習率。
  4. MSE 損失的梯度隨損失增大而增大,而損失趨於0時則會減小。其使用固定的學習率也可以有效收斂。

Huber Loss 結合了 MAE 的穩健性和 MSE 的穩定性,本質上是 MAE 和 MSE 損失中最好的。對於大誤差,它是線性的,對於小誤差,它本質上是二次的

Huber Loss 的特徵在於參數 $\delta$。當 $|y − \hat{y}|$ 小於一個事先指定的值 $\delta $ 時,變為平方損失,大於 $\delta $ 時,則變成類似於絕對值損失,因此其是比較robust 的損失函數。其定義如下:

$$ \text{Huber loss} = \left \lbrace \begin{matrix} \frac12[y_{t}^{i} - \hat{y}{t}^{i}]^2 & |y{t}^{i} - \hat{y}{t}^{i}| \leq \delta \ \delta|y{t}^{i} - \hat{y}{t}^{i}| - \frac12\delta^2 & |y{t}^{i} - \hat{y}_{t}^{i})| > \delta \end{matrix}\right. $$

三種迴歸損失函數的曲線圖比較如下:

loss_for_regression

代碼來源 Loss Function Plot.ipynb

三種迴歸損失函數的其他形式定義如下:

three_regression_loss

3.4,代碼實現

下面是三種迴歸損失函數的 python 代碼實現,以及對應的 sklearn 庫的內置函數。

```python

true: Array of true target variable

pred: Array of predictions

def mse(true, pred): return np.sum((true - pred)**2)

def mae(true, pred): return np.sum(np.abs(true - pred))

def huber(true, pred, delta): loss = np.where(np.abs(true-pred) < delta , 0.5((true-pred)2),deltanp.abs(true - pred) - 0.5(delta*2))

return np.sum(loss)

also available in sklearn

from sklearn.metrics import mean_squared_error from sklearn.metrics import mean_absolute_error ```

參考資料

  1. 《動手學深度學習-22.11. Information Theory》
  2. 損失函數|交叉熵損失函數
  3. AI-EDU: 交叉熵損失函數
  4. 常見迴歸和分類損失函數比較
  5. 《PyTorch_tutorial_0.0.5_餘霆嵩》
  6. http://pytorch.org/docs/stable/generated/torch.nn.CrossEntropyLoss.html
  7. 一文詳解Softmax函數
  8. AI-EDU: 多分類函數