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