還記得上一章我們建立了三層的神經網絡嗎?
有輸入層、中間層和輸出層。
並且我們介紹了激勵函數,放在感知器所構成的神經網路中,把原本線性的運算,變成非線性的。
那個結構非常好,我們可以順利地丟東西,然後讓他跑出東西來,就是使模型完成了"基於固定權重"的一次運算。
然而深度學習的期望和上一章呈現的不一樣,我們才不希望每個任務都要自己去推算權重,像我們設計XOR gate那樣。
我們希望模型自己可以學到權重矩陣應該長甚麼樣!
## 損失函數
損失函數的必要性是毫無疑問的,就像我們走錯路來到錯誤的目的地,我們總是想知道"那我們離目的地有多遠?在哪個方向?"
#### 為何不直接用「準確率」?
在介紹公式之前,我們得先解決一個直覺上的疑問。 既然我們的目標是猜中數字,或是分類正確(比如把貓分成貓),為什麼不直接用「準確率 (Accuracy)」來當作指標就好?
例如 100 題猜對 80 題,分數就是 80 分,不是很直觀嗎?
我們都有過考前刷題的經驗吧?準確率就像是期中考的分數,等測驗結束才得到的一個結果,用來衡量整個學習(訓練)到底如何。
而我們使用的損失函數,卻是要用在刷題的過程中的。
想想如果我們寫數學試題,寫了五千題,卻都沒有對答案和訂正,直接去考期中考會如何?
很難說對吧,也許有用,也可能沒用,和裸考沒有區別。
損失函數就是用紅筆對答案然後訂正,來幫助學習。
### 均方誤差(MSE)
也稱為L2 loss
對於有統計概念的朋友應該都不陌生,實際上這也非常直觀,想像你在地圖上射飛鏢,你想知道你射偏了多少。最直接的方法就是量一下你的落點 $y$ 和靶心 $t$ 的直線距離。為了避免負號抵銷,我們把它平方,還可以放大差異。1/2也是為了導數消除常數用的。
所以其實損失函數都是可以設計的,如果你哪天發現乘以5以後有更好的奇妙效果,那當然也能修改公式甚至發篇論文...
$$E = \frac{1}{2} \sum_{k} (y_k - t_k)^2$$
導數後:
$$\frac{\partial E}{\partial y} = y - t$$
因為有平方,對離群值 (Outliers) 非常敏感。
##### Exercise: 假設終極密碼是5,計算如果猜1到9的話,MSE是多少?你發現了甚麼?
### 平均絕對值誤差(MAE)
也稱為L1 loss
$$E = \sum_{k} |y_k - t_k|$$
公式更加簡單,就是把誤差的絕對值加總起來。
其導數(梯度)為:$$\frac{\partial E}{\partial y} =
\begin{cases}
1, & \text{if } y > t \\
-1, & \text{if } y < t
\end{cases}$$
對離群值不敏感。如果預期數據中有很多雜訊或髒資料就比較好用。
缺點是它的梯度是常數(1 或 -1),這導致在接近目標時,梯度不會變小,也就是地圖不精確了,只告訴你"在附近了",但附近卻涵蓋了一整個鎮!
### 交叉熵誤差(CEE)
MSE/MAE 在回歸問題時很好用,但在深度學習最常見的分類問題中最常用的是:交叉熵。
回想一下上一章的 Softmax,輸出的 $y_k$ 是機率(0 到 1 之間)。這裡通常搭配 One-hot Encoding(獨熱編碼),即正確答案 $t$ 只有在正確的類別為 1,其他為 0。例如:分類 [貓, 狗, 雞],正確答案是狗,則 $t = [0, 1, 0]$。公式如下:
$$E = - \sum_{k} t_k \log (y_k)$$
別被公式嚇到了!因為 $t_k$ 只有一個是 1,其他都是 0,所以公式其實變成:
$$E = - \log (\text{模型預測正確類別的機率})$$
我們來看看自然對數 $\ln(x)$ 的圖形(注意我們只看 $0 < x < 1$ 的範圍,且因為有負號,圖形會上下翻轉):

* 當預測很有信心 ($y \to 1$):$- \log(1) = 0$。損失為 0,代表完全正確」。
* 當預測完全錯誤 ($y \to 0$):$- \log(0) \to \infty$。損失趨近無限大,表示錯得離譜!
但是loss真的越低越好嗎?後續會再說明。
#### 為什麼分類不用 MSE?
雖然硬要用也可以,但 Cross Entropy 搭配 Softmax (上一章提到分類任務常用的輸出層)就像是天造地設的一對!
還記得我們剛剛推導 MSE 的導數是非常簡潔的 $(y-t)$ 嗎?
神奇的是,雖然 Softmax 和 Cross Entropy 各自的公式看起來很複雜,但當它們組合在一起進行反向傳播求導時,那些複雜的運算竟然會互相抵銷!
最後算出來的梯度剛好也是清爽的 $(y-t)$ (預測機率 - 真實標籤)!
多美的巧合。
其餘的損失函數將寫在[DL回歸基本功 ep.2-1附錄 損失函數](/_MMewQkfSI6co_Chr8Shqw)中。
### 小結: mini-batch
回到考試刷題的比喻中,寫一道題然後對答案,再寫下一道題,這效率太差可能三年模擬考都寫不完。而等到所有題都刷完再一次改來訂正,可能題多到記不過來。因此我們選擇寫一頁或一回就改一次,這就是mini-batch的思想。
實務上就是我們每次隨機抓一小把資料(例如 64 筆或 128 筆),算這一小把資料的平均損失,然後更新權重。
這就像是選舉民調,我們不需要問完兩千三百萬人,只需要抽樣一千人,就能大概知道民意方向(梯度方向)。
數學形式上,只是把損失函數除以 Batch Size ($N$):$$E_{avg} = -\frac{1}{N} \sum_{n} \sum_{k} t_{nk} \log y_{nk}$$
這種方法稱為 Mini-batch Stochastic Gradient Descent (Mini-batch SGD)。
至於SGD我們很快就會講到。
## 參數更新
(此處為了方便推導,暫時忽略激勵函數)
既然守密人(Loss Function)已經告訴我們目前的誤差值($E$),現在的問題是:我們該怎麼調整權重($W$),才能讓這個 $E$ 變小?
##### 話說在前頭: 數值微分、真導數和梯度
導數的數學定義如下:$$\frac{df(x)}{dx} = \lim_{h \to 0} \frac{f(x+h) - f(x)}{h}$$$h$ 代表那個無限趨近於 0 的微小時間。
(不贅述高中數學了)
* 數值微分 (Numerical Differentiation)
在電腦程式中,我們無法真的輸入 $\lim_{h \to 0}$,我們只能找一個「很小的數」來代表 $h$。這就叫做數值微分——用數值方法來「近似」求導。
錯誤示範 1:$h$ 太小你可能會想:「既然要趨近 0,那我設 $h = 10^{-50}$ 夠小了吧?」
這卻會導致一個問題:電腦有捨入誤差 (Rounding Error)。在 Python (float32) 中,過小的值會直接被當作 0.0。導致程式出錯。
通常我們會使用 $h = 10^{-4}$ ($0.0001$),這是一個經驗上不錯的數值。
錯誤示範 2:前向差分 (Forward Difference)即使 $h$ 設對了,直接算 $\frac{f(x+h) - f(x)}{h}$ 也有問題。
如圖所示,真正的導數是 $x$ 點的切線,但我們算的是 $x$ 和 $x+h$ 之間的連線。因為 $h$ 不可能真的為 0,這兩條線永遠會有誤差。

正確作法:中心差分 (Central Difference)為了減小誤差,我們改算 $x+h$ 和 $x-h$ 之間的斜率。這種以 $x$ 為中心,計算左右兩邊差分的方法,誤差會比只看一邊來得小。
這就是我們在實作中的數值微分。
但我們現在有更好的方法來更新我們的權重矩陣。
* 真導數 (Analytic Differentiation)
這就是我們微積分課本上學的「公式解」。比如 $y=x^2$,我們透過推導知道 $y' = 2x$。這種透過數學式推導出來的,稱為解析解 (Analytic)。
區分好這兩種的差異方便我們在之後利用這些數學方法:
數值微分包含誤差,計算量大(對每個參數都要做一次加減運算),但容易實作,常用來驗算(Gradient Check)。
解析求導:完全精確(True Derivative),計算速度快。反向傳播 (Backpropagation) 就是屬於這一類。
* 梯度 (Gradient)
當變數只有一個時叫「導數」,當變數有一堆($w_1, w_2, ...$)時,把它們的偏導數集合起來的向量,就叫梯度:$$\nabla E = (\frac{\partial E}{\partial w_1}, \frac{\partial E}{\partial w_2}, ...)$$

待會我們會看到它如何穿過整個神經網路
### 正向傳播 (Forward Propagation)
這其實就是我們在介紹 Perceptron 和 Neural Network 時做的事情。
從輸入層開始,經過每一層的權重計算 ($W \cdot X + B$),再經過激勵函數 ($Sigmoid/ReLU$),一層層往後傳遞,最後計算出結果 $y$,並與標準答案 $t$ 比較得出 Loss。
這個過程是**計算結果**,也是上一章在做的事。
回憶一下:

$s1=(x_1 \cdot w_{11}+x_2 \cdot w_{21})$
$s2=(x_1 \cdot w_{12}+x_2 \cdot w_{22})$
$y=(s_1 \cdot w_{13}+s_2 \cdot w_{23})$
而y這裡會得到一個答案,再用Loss先看一下這個答案好不好:
(MSE):
$E = \frac{(y-t)^2} {2}$
如果這個E很大,那就說明答案猜得很爛,如果很小就代表模型很厲害。
### 反向傳播 (Backward Propagation)
這就是本章的重頭戲,也是深度學習能訓練百萬參數的秘密武器。
實際上反向傳播就是數值更新的核心,回到準備考試刷題的比喻:
現在是該來對答案,看詳解訂正的時候。我們需要一步步的回來驗算,到底是哪個地方猜錯了,又要怎麼調整。
然而調整權重的方法很多,這裡我們介紹其中一個方法:
#### 梯度下降法
乍聽之下好像很複雜,其實他就表示一件事:
你站在壟罩濃霧的巨大山坡上,怎麼下山最快?
你可能就會想到如果是一顆圓石,那哪裡坡度最大,石頭就會往反方向往下滾,很快就能滾到山腳了。
梯度在這裡就是那個坡度!
數學公式如下:$$W_{new} = W_{old} - \eta \cdot \frac{\partial E}{\partial W}$$
* 極重要的參數 $\eta$ ,稱為學習率 (Learning Rate),附錄會再詳述。
所以我們在這裡用梯度下降法來做一次反向傳播:
承接上個例子:
我們得到$E = \frac{(y-t)^2} {2}$,那我們就用Loss對W求偏導($\frac{\partial E}{\partial W}$),來得到一個權重矩陣的坡度圖,你可以想像成一個等高線圖,而權重矩陣就是那顆石頭。
而且要對W求導那就得讓公式裡的W都現身:
現在展開他:$$ y= (s_1 \cdot w_{13}+s_2 \cdot w_{23})$$
s1,s2代入:
$$ y= ((x_1 \cdot w_{11}+x_2 \cdot w_{21}) \cdot w_{13}+(x_1 \cdot w_{12}+x_2 \cdot w_{22}) \cdot w_{23})$$
$$E = \frac{(y-t)^2} {2}$$
再代入E:
$$E=\frac{(((x_1 \cdot w_{11}+x_2 \cdot w_{21}) \cdot w_{13}+(x_1 \cdot w_{12}+x_2 \cdot w_{22}) \cdot w_{23})-t)^2} {2} $$
現在來對整行求w11的偏微分,但上面一長串的真的非常嚇人。
這邊我們可以應用微分裡的**鏈式法則**
$$\frac{\partial E}{\partial w_{11}}=\frac{\partial E}{\partial y}\frac{\partial y}{\partial w_{11}}$$這裡就可以回到上面比較短的兩個式子來得到答案:
$$\frac{\partial E}{\partial w_{11}}=\frac{\partial E}{\partial y}\frac{\partial y}{\partial w_{11}}=(y-t)(x_1 \cdot w_{13})$$
#### Exercise: 現在我們也能按照一樣的方式求得對$w_{12}、w_{22}、w_{13}、w_{23}、w_{21}$的偏導數。請寫出各是多少。
現在我們要來正式的傳播回去了:

只要把一層層感知器更新回去,$W_{new} = W_{old} - \eta \cdot \frac{\partial E}{\partial W}$,從最近的s1開始:
$w_{new13}=w_{13}-\eta \cdot \frac{\partial E}{\partial w_{13}}$
一直到剛剛算出來的$w_{11}$
$w_{new11}=w_{11}-\eta \cdot (y-t)(x_1 \cdot w_{13})$
而很剛好的$y,x_1,w_{11},w_{13}$通通都可以在神經網路上找到,t則是跟著輸入資料(x)一起交給模型的答案。根據這些**已知**,不用新的空間和變數我們就能夠修改出新的權重了!
此外我們也從結果發現了**反向傳播**名字的由來,原來都是用到後一層的舊權重來修正前一層,如同要改w11要用到w13一樣。
#### Exercise: 還記得我們選擇忽略激勵函數嗎?如果沒有忽略掉,你覺得E對$w_{11}、w_{12}、w_{22}、w_{13}、w_{23}、w_{21}$的偏導數應該改寫成什麼呢?
當然用向量形式也能夠直接算
$$y=\begin{bmatrix}
w_{13} & w_{23}
\end{bmatrix}
\begin{bmatrix}
w_{11} & w_{12} \\
w_{21} & w_{22}
\end{bmatrix}
\begin{bmatrix}
x_1 \\ x_2
\end{bmatrix}
$$
但這裡不贅述,有機會再考慮深挖。
### 最優化算法 (Optimizer)
知道了梯度,也知道了反向傳播,現在我們要探討「怎麼走」如下山最有效率。
#### 隨機梯度下降 SGD (Stochastic Gradient Descent)
這是最基礎的版本、就是剛剛的梯度下降:利用梯度來實現了反向傳播。
$$W \leftarrow W - \eta \frac{\partial L}{\partial W}$$
雖然已經過時,但它是所有優化器的始祖。
它最大的問題在於"各向異性" (Anisotropy),先不管這個高深的詞彙。想像一個山谷地形。
在陡峭的方向(梯度大),SGD 會大步跳躍,甚至來回震盪。在平緩的方向(梯度小),SGD 卻走得非常慢。結果就是呈現「之」字形移動,效率極低,遲遲無法抵達真正的最低點。
#### Momentum
為了解決 SGD 在山谷間震盪的問題,我們引入了物理學中的**動量**概念。
這次我們**細緻的**想像一顆沉重的鐵球從山上滾下來。即使遇到一個小坑洞,因為它有慣性(速度),它會衝過去;即使梯度方向改變了,它也不會馬上掉頭,而是會做一個平滑的轉彎。數學上,我們引入變數 $v$ (速度):$$\begin{aligned}
v &\leftarrow \alpha v - \eta \frac{\partial L}{\partial W} \\
W &\leftarrow W + v
\end{aligned}$$
$\alpha$:動量係數(通常設 0.9),代表摩擦力或空氣阻力,決定了保留多少之前的速度。
$- \eta \frac{\partial L}{\partial W}$:當前的加速度(梯度)。
效果:
* 抑制震盪:在震盪的方向上,正負梯度會互相抵銷,速度變慢。
* 加速收斂:在正確的下坡方向上,速度會不斷疊加,越跑越快。
Momentum 讓優化路徑從「之字形」變成了平滑的曲線,大幅提升了學習效率。
其餘的優化器如 AdaGrad, RMSProp, Adam 將寫在 [DL回歸基本功: ep.2-2附錄 最優化算法](/BtHq2XzKQmqYJImCIAE7AQ)中。
### 小結
至此,我們完成了一個神經網絡訓練的完整流程:
* Forward:資料輸入,經過層層運算,算出預測值與 Loss。
* Backward:利用連鎖律,從 Loss 出發,計算出每一層權重的梯度。
* Update:利用優化器 (Optimizer) 如 Momentum,根據梯度更新權重。
這三個步驟不斷循環,就像人類不斷地練習、對答案、修正大腦神經元連結一樣,最終讓 Loss 降到最低,這就是「學習」的本質。
[下篇: DL回歸基本功 ep.2(下) 數據驅動的學習](https://hackmd.io/4YvO248sQMqOO4kzT-ThEA)