玩統計機器學習筆記

神經網路基礎Neural Network Fundamentals

從符號與前向傳播出發,理解激勵函數的角色、交叉熵損失與反向傳播的完整推導,以及權重初始化如何避免梯度消失與爆炸。

第 7 章,共 11 章|初版 2019 年 4 月・2026 年 10 月修訂

本章內容
  1. 7.1符號與網路架構
  2. 7.2激勵函數
  3. 7.3交叉熵損失
  4. 7.4反向傳播
  5. 7.5梯度下降的直覺
  6. 7.6權重初始化
  7. 7.7參考資料

神經網路由許多「線性組合加上非線性轉換」的層堆疊而成。每一層本身都很簡單,但層層疊加之後,就能表達非常複雜的函數。訓練神經網路的流程也只有三個步驟反覆進行:前向傳播(forward propagation)計算預測與損失,反向傳播(backpropagation)計算每個參數的梯度,再用梯度下降更新參數。

這一章整理這個流程中最基礎的元件:符號約定、激勵函數、交叉熵損失、反向傳播的推導,以及常被忽略卻非常關鍵的權重初始化。更進階的最佳化演算法與訓練技巧,分別留到第 8 章與第 9 章。

7.1符號與網路架構Notation & Architecture

符號約定

本書以 aj[l](i)a_j^{[l](i)} 表示第 ii 筆樣本在第 ll 層第 jj 個神經元的激勵值(activation):

符號 意義 範圍
[l][l] 第 ll 層 l=1,…,Ll = 1, \dots, L
(i)(i) 第 ii 筆樣本 i=1,…,Ni = 1, \dots, N
jj 該層的第 jj 個神經元 j=1,…,n[l]j = 1, \dots, n^{[l]}

n[l]n^{[l]} 是第 ll 層的神經元數。輸入層記為第 0 層,a[0]=x\mathbf{a}^{[0]} = \mathbf{x},不計入層數:一個有一層隱藏層加上輸出層的網路,稱為 2 層網路,L=2L = 2。

前向傳播

每一層先做線性組合,得到預激勵值(pre-activation)z[l]\mathbf{z}^{[l]},再經過激勵函數 g[l]g^{[l]} 得到激勵值 a[l]\mathbf{a}^{[l]},作為下一層的輸入:

z[l]=W[l]a[l−1]+b[l]a[l]=g[l](z[l]) \begin{aligned} \mathbf{z}^{[l]} &= W^{[l]}\mathbf{a}^{[l-1]} + \mathbf{b}^{[l]} \\ \mathbf{a}^{[l]} &= g^{[l]}\big(\mathbf{z}^{[l]}\big) \end{aligned}

其中權重矩陣 W[l]W^{[l]} 的形狀是 n[l]×n[l−1]n^{[l]} \times n^{[l-1]},偏權值 b[l]\mathbf{b}^{[l]} 是長度 n[l]n^{[l]} 的向量。寫成單一神經元就是 zj[l]=∑kwjk[l]ak[l−1]+bj[l]z_j^{[l]} = \sum_k w_{jk}^{[l]} a_k^{[l-1]} + b_j^{[l]}。從第 1 層一路算到第 LL 層,最後的 a[L]=y^\mathbf{a}^{[L]} = \hat{\mathbf{y}} 就是網路的預測,再與標籤比較得到損失。

實作時會把 NN 筆樣本排成矩陣的欄,一次算完整批資料,偏權值則以廣播(broadcasting)加到每一欄:

import numpy as np

def forward(X, params, L):
    """X 的形狀為 (n0, N),每一欄是一筆樣本。"""
    A = X
    for l in range(1, L + 1):
        Z = params[f"W{l}"] @ A + params[f"b{l}"]          # (n_l, N)
        A = np.maximum(0, Z) if l < L else 1 / (1 + np.exp(-Z))
    return A                                               # 隱藏層用 ReLU,輸出層用 sigmoid

7.2激勵函數Activation Functions

為什麼需要非線性

激勵函數的目的是提供非線性轉換。如果拿掉激勵函數(或使用線性函數),兩層網路會化簡成

W[2](W[1]x+b[1])+b[2]=(W[2]W[1])x+(W[2]b[1]+b[2]) W^{[2]}\big(W^{[1]}\mathbf{x} + \mathbf{b}^{[1]}\big) + \mathbf{b}^{[2]} = \big(W^{[2]}W^{[1]}\big)\mathbf{x} + \big(W^{[2]}\mathbf{b}^{[1]} + \mathbf{b}^{[2]}\big)

仍然只是一個線性函數。無論疊多少層,結果都和單層線性模型一樣,深度完全沒有意義。加入非線性激勵函數,網路才能逼近複雜的非線性關係。

常見的激勵函數

函數 定義 值域 導數 g′(z)g'(z)
sigmoid σ(z)=11+e−z\sigma(z) = \tfrac{1}{1 + e^{-z}} (0,1)(0, 1) σ(z)(1−σ(z))\sigma(z)\big(1 - \sigma(z)\big)
tanh tanh⁡(z)=ez−e−zez+e−z\tanh(z) = \tfrac{e^{z} - e^{-z}}{e^{z} + e^{-z}} (−1,1)(-1, 1) 1−tanh⁡2(z)1 - \tanh^2(z)
ReLU max⁡(0,z)\max(0, z) [0,∞)[0, \infty) z>0z > 0 時為 1,z<0z < 0 時為 0
Leaky ReLU max⁡(αz,z)\max(\alpha z, z) (−∞,∞)(-\infty, \infty) z>0z > 0 時為 1,z<0z < 0 時為 α\alpha

ReLU 在 z=0z = 0 不可微,實作上通常直接取導數為 0。Leaky ReLU 的 α\alpha 是一個小的正數(例如 0.01),必須小於 1,max⁡(αz,z)\max(\alpha z, z) 的寫法才成立。

飽和與梯度消失

sigmoid 與 tanh 都是 S 形曲線,兩端趨於平坦。sigmoid 的導數最大只有 0.25(在 z=0z = 0),tanh 的導數最大為 1(同樣在 z=0z = 0),但只要 ∣z∣\lvert z \rvert 稍大,導數就迅速趨近 0:例如 tanh⁡′(3)=1−tanh⁡2(3)≈0.0099\tanh'(3) = 1 - \tanh^2(3) \approx 0.0099,σ′(5)≈0.0066\sigma'(5) \approx 0.0066。這種現象稱為飽和(saturation)。落在飽和區的神經元幾乎得不到梯度,參數更新得極慢;反向傳播時又要把各層的導數連乘,多層之後梯度會越乘越小,形成梯度消失(vanishing gradient)。

在兩者之間,tanh 的輸出以 0 為中心(事實上 tanh⁡(z)=2σ(2z)−1\tanh(z) = 2\sigma(2z) - 1),下一層的輸入比較平衡,作為隱藏層通常比 sigmoid 好;但它一樣會飽和。

ReLU 與其變體

ReLU 在 z>0z > 0 時導數恆為 1,正半邊不會飽和,計算又非常便宜,因此是隱藏層的預設選擇。它的缺點是 z<0z < 0 時梯度為 0:若某個神經元的輸入長期為負(例如一次過大的參數更新之後),它就再也不會更新,稱為「死亡 ReLU」(dying ReLU)。Leaky ReLU 在負半邊保留一個小斜率 α\alpha,讓梯度仍能流動;ELU、GELU(Transformer 中常用)等變體也出於類似的考量。

輸出層的選擇

輸出層的激勵函數由任務決定,而且要與損失函數搭配:

任務 輸出層激勵函數 搭配的損失
二元分類 sigmoid 二元交叉熵
多類別分類(單一標籤) softmax 多類別交叉熵
多標籤分類 每個輸出各自用 sigmoid 二元交叉熵
迴歸 線性(恆等函數) 平方誤差
非負的迴歸目標 ReLU 或 softplus 依任務而定

7.3交叉熵損失Cross-Entropy Loss

二元交叉熵

二元分類時,令 ti∈{0,1}t_i \in \{0, 1\} 為目標(target),代表第 ii 筆是否為正例;yi∈(0,1)y_i \in (0, 1) 為模型預測 ti=1t_i = 1 的機率(sigmoid 的輸出)。損失為

J=−∑i=1N[ tilog⁡yi+(1−ti)log⁡(1−yi)] J = -\sum_{i=1}^{N}\Big[\,t_i \log y_i + (1 - t_i)\log(1 - y_i)\Big]

每一項中只有一半會作用:ti=1t_i = 1 時損失是 −log⁡yi-\log y_i,ti=0t_i = 0 時是 −log⁡(1−yi)-\log(1 - y_i)。模型越有把握地答錯(例如 ti=1t_i = 1 卻預測 yi≈0y_i \approx 0),損失就越趨近無限大。

這個形式不是憑空設計的。若把每筆標籤視為伯努利分布,概似是 ∏iyiti(1−yi)1−ti\prod_i y_i^{t_i}(1 - y_i)^{1 - t_i},取負對數正好得到 JJ。因此最小化交叉熵就是最大概似估計,也等於最小化真實分布與預測分布之間的 KL 散度(見第 6 章)。

多類別交叉熵

KK 個類別時,輸出層以 softmax 把 logit aka_k 轉成機率,目標 ti\mathbf{t}^{i} 以 one-hot 向量表示:

yk=eak∑l=1Keal,J=−∑i=1N∑k=1Ktkilog⁡yki y_k = \frac{e^{a_k}}{\sum_{l=1}^{K} e^{a_l}}, \qquad J = -\sum_{i=1}^{N}\sum_{k=1}^{K} t_k^{i}\log y_k^{i}

因為 ti\mathbf{t}^{i} 只有正確類別 cic_i 那一格是 1,損失其實就是 −∑ilog⁡ycii-\sum_i \log y_{c_i}^{i}:只看模型給正確答案多少機率。二元交叉熵是 K=2K = 2 的特例。實務上常除以 NN 取平均,不改變最小值的位置,但讓學習率不受批次大小影響。

為什麼不用平方誤差

分類問題若改用 sigmoid 加平方誤差,對 logit 的梯度會多出一個 σ′(a)\sigma'(a) 因子。當模型錯得很離譜、輸出落在飽和區時,σ′(a)≈0\sigma'(a) \approx 0,梯度反而消失,學不動。下一節會看到,交叉熵對 logit 的梯度正好是 y−ty - t,誤差越大梯度越大,沒有這個問題。

實作時,不要先算 softmax 再取對數,而是直接由 logit 計算,並先減去最大值避免 eae^{a} 溢位(log-sum-exp 技巧)。主流框架的交叉熵函數(例如 PyTorch 的 nn.CrossEntropyLoss)也都直接接收 logit:

def softmax_cross_entropy(logits, t):
    """logits、t 的形狀皆為 (N, K),t 為 one-hot。回傳平均損失。"""
    a = logits - logits.max(axis=1, keepdims=True)          # 避免 exp 溢位
    log_y = a - np.log(np.exp(a).sum(axis=1, keepdims=True))
    return -(t * log_y).sum(axis=1).mean()

7.4反向傳播Backpropagation

反向傳播就是有系統地套用連鎖律(chain rule):從輸出層開始,一層一層往回計算損失對每個中間量的偏導數,並重複利用已經算好的結果。這讓計算所有參數梯度的成本,與一次前向傳播大致同一個數量級。

設定

以一個單隱藏層、softmax 輸出的分類網路為例:

輸入層 權重 隱藏層 權重 輸出層
DD 個特徵 x1,…,xDx_1, \dots, x_D wdmw_{dm} MM 個單元 z1,…,zMz_1, \dots, z_M vmkv_{mk} KK 個類別 y1,…,yKy_1, \dots, y_K
sm=∑d=1Dwdm xd,zm=g(sm)ak=∑m=1Mvmk zm,yk=eak∑l=1Keal \begin{aligned} s_m &= \sum_{d=1}^{D} w_{dm}\,x_d, \qquad z_m = g(s_m) \\ a_k &= \sum_{m=1}^{M} v_{mk}\,z_m, \qquad y_k = \frac{e^{a_k}}{\sum_{l=1}^{K} e^{a_l}} \end{aligned}

為了簡潔,偏權值省略不寫(可以視為一個恆等於 1 的額外輸入 x0=z0=1x_0 = z_0 = 1)。

步驟一:softmax 的導數

對 yj=eaj/∑lealy_j = e^{a_j} / \sum_l e^{a_l} 用商的微分法則,分 j=kj = k 與 j≠kj \ne k 兩種情況:

∂yj∂ak={yk(1−yk),j=k− yj yk,j≠k  =  yj (δjk−yk) \frac{\partial y_j}{\partial a_k} = \begin{cases} y_k(1 - y_k), & j = k \\ -\,y_j\,y_k, & j \ne k \end{cases} \;=\; y_j\,(\delta_{jk} - y_k)

其中 δjk\delta_{jk} 是 Kronecker delta:j=kj = k 時為 1,否則為 0。

步驟二:損失對 logit 的導數

先看單一樣本(省略上標 ii),J=−∑jtjlog⁡yjJ = -\sum_j t_j \log y_j。改變 aka_k 會影響所有的 yjy_j,所以要對 jj 加總:

∂J∂ak=−∑j=1Ktj 1yj ∂yj∂ak=−∑j=1Ktj (δjk−yk)=−(tk−yk∑j=1Ktj)=yk−tk \begin{aligned} \frac{\partial J}{\partial a_k} &= -\sum_{j=1}^{K} t_j\,\frac{1}{y_j}\,\frac{\partial y_j}{\partial a_k} \\ &= -\sum_{j=1}^{K} t_j\,(\delta_{jk} - y_k) \\ &= -\Big(t_k - y_k\sum_{j=1}^{K} t_j\Big) \\ &= y_k - t_k \end{aligned}

第二行中,1yj\tfrac{1}{y_j} 與 softmax 導數裡的 yjy_j 互相抵銷;第三行中,∑jtjδjk=tk\sum_j t_j \delta_{jk} = t_k;最後一行用到 ∑jtj=1\sum_j t_j = 1,因為目標本身是一個機率分布(one-hot 向量只有一格為 1)。

步驟三:損失對輸出層權重的導數

vmkv_{mk} 只透過 aka_k 影響損失,且 ∂ak/∂vmk=zm\partial a_k / \partial v_{mk} = z_m。把所有樣本的貢獻加總:

∂J∂vmk=∑i=1N∂J∂aki ∂aki∂vmk=∑i=1N(yki−tki) zmi \frac{\partial J}{\partial v_{mk}} = \sum_{i=1}^{N}\frac{\partial J}{\partial a_k^{i}}\,\frac{\partial a_k^{i}}{\partial v_{mk}} = \sum_{i=1}^{N}\big(y_k^{i} - t_k^{i}\big)\,z_m^{i}

讀法很直觀:權重的梯度 = 輸出端的誤差訊號 × 輸入端的激勵值。寫成矩陣,若 ZZ 是 N×MN \times M 的隱藏層輸出、YY 與 TT 是 N×KN \times K 的預測與目標,則梯度為 Z⊤(Y−T)Z^\top (Y - T)。

步驟四:傳回隱藏層

令輸出層的誤差訊號 eki=yki−tkie_k^{i} = y_k^{i} - t_k^{i}。隱藏單元 zmz_m 透過所有的 aka_k 影響損失,因此要把各輸出的誤差沿權重 vmkv_{mk} 加權傳回,再乘上自己的激勵函數導數:

emi≡∂J∂smi=g′(smi)∑k=1Kvmk eki∂J∂wdm=∑i=1Nemi xdi \begin{aligned} e_m^{i} \equiv \frac{\partial J}{\partial s_m^{i}} &= g'(s_m^{i})\sum_{k=1}^{K} v_{mk}\,e_k^{i} \\ \frac{\partial J}{\partial w_{dm}} &= \sum_{i=1}^{N} e_m^{i}\,x_d^{i} \end{aligned}

同樣的模式可以一路往回套用到任意深度。以 [l][l] 符號、單一樣本表示,記 dz[l]=∂J/∂z[l]d\mathbf{z}^{[l]} = \partial J / \partial \mathbf{z}^{[l]}:

dz[L]=a[L]−tdz[l]=(W[l+1])⊤dz[l+1]⊙g′[l](z[l])∂J∂W[l]=dz[l] (a[l−1])⊤,∂J∂b[l]=dz[l] \begin{aligned} d\mathbf{z}^{[L]} &= \mathbf{a}^{[L]} - \mathbf{t} \\ d\mathbf{z}^{[l]} &= \big(W^{[l+1]}\big)^\top d\mathbf{z}^{[l+1]} \odot g'^{[l]}\big(\mathbf{z}^{[l]}\big) \\ \frac{\partial J}{\partial W^{[l]}} &= d\mathbf{z}^{[l]}\,\big(\mathbf{a}^{[l-1]}\big)^\top, \qquad \frac{\partial J}{\partial \mathbf{b}^{[l]}} = d\mathbf{z}^{[l]} \end{aligned}

其中 ⊙\odot 是逐元素相乘。第二行也說明了梯度消失與爆炸的來源:每往回一層,就要乘上一次權重矩陣與激勵函數導數。

7.5梯度下降的直覺Gradient Descent Intuition

梯度指向函數值上升最快的方向。要讓損失變小,就沿著梯度的反方向走一小步:

θ←θ−η ∇θJ(θ) \theta \leftarrow \theta - \eta\,\nabla_\theta J(\theta)

其中 η\eta 是學習率(learning rate)。可以把凸的成本函數想像成一個碗,全域最小值在碗底;不論從哪裡出發,一路往下坡走,最後都會到達碗底,這就是梯度下降(gradient descent)。反過來,若目標是最大化某個函數(例如對數概似),就沿梯度方向往上走,稱為梯度上升(gradient ascent):θ←θ+η ∇θℓ(θ)\theta \leftarrow \theta + \eta\,\nabla_\theta \ell(\theta),這與對 −ℓ-\ell 做梯度下降完全相同。

神經網路的損失函數並不是凸的,存在許多局部最小值、鞍點與平坦區,梯度下降不保證找到全域最小值;但在高維的參數空間中,實務上通常仍能找到表現很好的解。學習率太大會來回震盪甚至發散,太小則收斂緩慢。小批次梯度下降、動量、Adam 等改良方法見第 8 章。

7.6權重初始化Weight Initialization

為什麼不能全部設為 0

如果同一層的權重全部相同(例如全為 0),該層每個神經元的輸出都一樣,反向傳播得到的梯度也一樣,更新之後依然相同。無論訓練多久,整層都只相當於一個神經元。因此權重必須隨機初始化,以打破對稱性(symmetry breaking);偏權值則可以設為 0。

梯度爆炸與消失

隨機初始化的尺度也很重要。暫時忽略激勵函數與偏權值,深層網路的輸出近似於 W[L]⋯W[2]W[1]xW^{[L]} \cdots W^{[2]} W^{[1]}\mathbf{x}。若每一層的權重都讓訊號放大 1.5 倍,50 層之後就放大了 1.550≈6.4×1081.5^{50} \approx 6.4 \times 10^{8} 倍;若每層縮小為 0.5 倍,則變成 0.550≈8.9×10−160.5^{50} \approx 8.9 \times 10^{-16}。反向傳播的梯度同樣是權重的連乘,所以權重只要略大於或略小於 1,影響就會隨深度呈指數成長,造成梯度爆炸(exploding gradient)或梯度消失。

讓變異數維持穩定

好的初始化讓每一層輸出的變異數大致維持不變。考慮 z=∑k=1nwkakz = \sum_{k=1}^{n} w_k a_k,其中 n=n[l−1]n = n^{[l-1]}。假設權重與輸入互相獨立、權重平均為 0,則

Var⁡(z[l])=n[l−1]Var⁡(w) E[(a[l−1])2] \operatorname{Var}\big(z^{[l]}\big) = n^{[l-1]}\operatorname{Var}(w)\,\mathbb{E}\Big[\big(a^{[l-1]}\big)^2\Big]
  • 對 tanh 這類以 0 為中心、在原點附近近似線性的激勵函數,E[a2]≈Var⁡(z[l−1])\mathbb{E}[a^2] \approx \operatorname{Var}(z^{[l-1]}),要讓變異數不變,就需要 Var⁡(w)=1/n[l−1]\operatorname{Var}(w) = 1/n^{[l-1]}。
  • ReLU 把一半的輸入歸零,E[a2]=12Var⁡(z[l−1])\mathbb{E}[a^2] = \tfrac{1}{2}\operatorname{Var}(z^{[l-1]}),因此需要兩倍的變異數 Var⁡(w)=2/n[l−1]\operatorname{Var}(w) = 2/n^{[l-1]},這就是 He 初始化。

Glorot 與 Bengio 進一步考慮反向傳播時梯度的變異數(需要 Var⁡(w)=1/n[l]\operatorname{Var}(w) = 1/n^{[l]}),取兩者折衷得到 2/(n[l−1]+n[l])2/(n^{[l-1]} + n^{[l]})。

方法 搭配的激勵函數 Var⁡(w)\operatorname{Var}(w) 標準常態樣本乘上
He 初始化 ReLU 及其變體 2/n[l−1]2/n^{[l-1]} 2/n[l−1]\sqrt{2/n^{[l-1]}}
LeCun(Xavier 只考慮前向的版本) tanh 1/n[l−1]1/n^{[l-1]} 1/n[l−1]\sqrt{1/n^{[l-1]}}
Xavier/Glorot tanh、sigmoid 2/(n[l−1]+n[l])2/(n^{[l-1]} + n^{[l]}) 2/(n[l−1]+n[l])\sqrt{2/(n^{[l-1]} + n^{[l]})}

只考慮前向的 1/n[l−1]1/n^{[l-1]} 版本也常稱為 LeCun 初始化。實作上就是從標準常態分布抽樣,再乘上縮放係數:

# 第 l 層的權重,形狀為 (n_l, n_prev)
W = np.random.randn(n_l, n_prev) * np.sqrt(2.0 / n_prev)   # He,搭配 ReLU
W = np.random.randn(n_l, n_prev) * np.sqrt(1.0 / n_prev)   # LeCun(常稱 Xavier),搭配 tanh
b = np.zeros((n_l, 1))

適當的初始化能緩解梯度消失與爆炸,但無法完全消除。其他手段還包括批次正規化(batch normalization,見第 8 章)、殘差連接(residual connection,見第 10 章)與梯度裁剪(gradient clipping,見第 11 章)。

7.7參考資料References

  • Goodfellow, Bengio & Courville, Deep Learning, MIT Press, 2016,第 6 章 Deep Feedforward Networks、第 8 章 Optimization for Training Deep Models。
  • Bishop, C. M., Pattern Recognition and Machine Learning, Springer, 2006,第 5 章 Neural Networks。
  • Rumelhart, D. E., Hinton, G. E. & Williams, R. J., "Learning representations by back-propagating errors", Nature, 1986。
  • Glorot, X. & Bengio, Y., "Understanding the difficulty of training deep feedforward neural networks", AISTATS, 2010。
  • He, K., Zhang, X., Ren, S. & Sun, J., "Delving deep into rectifiers: Surpassing human-level performance on ImageNet classification", ICCV, 2015。
  • Nair, V. & Hinton, G. E., "Rectified linear units improve restricted Boltzmann machines", ICML, 2010。
  • Stanford CS231n, Convolutional Neural Networks for Visual Recognition 課程筆記。
  • deeplearning.ai, Neural Networks and Deep Learning 與 Improving Deep Neural Networks(Coursera 深度學習專項課程第一、二門課)。