---
# System prepended metadata

title: 'DL回歸基本功: ep.2(上) 數據驅動的學習'

---

還記得上一章我們建立了三層的神經網絡嗎?
有輸入層、中間層和輸出層。
並且我們介紹了激勵函數，放在感知器所構成的神經網路中，把原本線性的運算，變成非線性的。
那個結構非常好，我們可以順利地丟東西，然後讓他跑出東西來，就是使模型完成了"基於固定權重"的一次運算。
然而深度學習的期望和上一章呈現的不一樣，我們才不希望每個任務都要自己去推算權重，像我們設計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$ 的範圍，且因為有負號，圖形會上下翻轉）：

![image](https://hackmd.io/_uploads/H1_b6IObbx.png)


* 當預測很有信心 ($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，這兩條線永遠會有誤差。
![image](https://hackmd.io/_uploads/HykF_LmWbe.png)

正確作法：中心差分 (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}, ...)$$

![image](https://hackmd.io/_uploads/rkfU6tOZWx.png)


待會我們會看到它如何穿過整個神經網路


### 正向傳播 (Forward Propagation)
這其實就是我們在介紹 Perceptron 和 Neural Network 時做的事情。
從輸入層開始，經過每一層的權重計算 ($W \cdot X + B$)，再經過激勵函數 ($Sigmoid/ReLU$)，一層層往後傳遞，最後計算出結果 $y$，並與標準答案 $t$ 比較得出 Loss。
這個過程是**計算結果**，也是上一章在做的事。
回憶一下:
![image](https://hackmd.io/_uploads/BJ1koOdW-l.png)

$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}$的偏導數。請寫出各是多少。
現在我們要來正式的傳播回去了:
![image](https://hackmd.io/_uploads/r1YmTKObbl.png)



只要把一層層感知器更新回去，$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)