Skip to content

從零理解神經網路(四):反向傳播——誰該為錯誤負責?

本文是「從零理解神經網路」系列的第四篇。上一篇我們學了梯度下降,但還有一個問題:梯度怎麼算?這篇用「傳話遊戲」來解釋深度學習最核心的算法——反向傳播。

問題:梯度怎麼算?

上一篇我們知道,更新權重需要梯度:

w_new = w_old - η × ∂L/∂w

這個 ∂L/∂w 就是「損失對權重 w 的偏導數」,也就是「如果我稍微調整 w,損失會變化多少」。

但神經網路可能有數百萬個權重,難道要一個一個手動算偏導數?

反向傳播(Backpropagation) 就是解決這個問題的算法。它能自動且高效地計算所有權重的梯度。

傳話遊戲比喻

讓我們用一個大家都玩過的遊戲來理解反向傳播。

場景設定

原始訊息:「6」

傳遞過程:
小明 ──→ 小華 ──→ 小美 ──→ 最終輸出

每個人會把聽到的數字乘上自己的「係數」:
- 小明的係數:0.5
- 小華的係數:0.8  
- 小美的係數:0.3

前向傳播(Forward Pass)

訊息從左到右傳遞:

原始:6
  ↓ × 0.5
小明傳出:3
  ↓ × 0.8
小華傳出:2.4
  ↓ × 0.3
小美傳出:0.72(最終輸出)

最終輸出是 0.72,但目標是 6。差太多了!

這就是「前向傳播」——資料從輸入流向輸出。

發現錯誤

目標:6
輸出:0.72
誤差:6 - 0.72 = 5.28(差了這麼多!)

反向追查:誰該負責?

現在要回答一個關鍵問題:這個錯誤,每個人該負多少責任?

這就需要「反向傳播」——從輸出往回追溯。

核心工具:鏈式法則(Chain Rule)

假設損失 L 對最終輸出的梯度是 ∂L/∂output,我們要算 L 對每個係數的梯度。

                  ∂L
                  ──
                ∂小美係數

從輸出往回算:

∂L         ∂L        ∂output
────── = ────── × ──────────
∂小美係數   ∂output   ∂小美係數

         ↑            ↑
      上一層傳來    這一層的貢獻
      的梯度        (小美的輸入值)

具體計算

假設我們用 MSE 損失:L = ½(target - output)²

Step 1:輸出層的梯度

∂L/∂output = -(target - output) = -(6 - 0.72) = -5.28

Step 2:小美的責任(第三層)

∂L/∂w₃ = ∂L/∂output × ∂output/∂w₃
       = -5.28 × 2.4(小美的輸入)
       = -12.67

Step 3:把梯度傳回去

∂L/∂(小美的輸入) = ∂L/∂output × w₃
                 = -5.28 × 0.3
                 = -1.58

Step 4:小華的責任(第二層)

∂L/∂w₂ = ∂L/∂(小美的輸入) × ∂(小美的輸入)/∂w₂
       = -1.58 × 3(小華的輸入)
       = -4.75

Step 5:繼續往回傳...

以此類推,我們可以算出每一層的梯度。

責任分配結果

假設計算完成後:

梯度(責任)責任比例
小明 (w₁)-2.5420%
小華 (w₂)-4.7537%
小美 (w₃)-12.6743%

小美的責任最大!因為她是最後一關,對輸出的影響最直接。

鏈式法則:反向傳播的數學基礎

反向傳播的核心就是微積分的鏈式法則

如果 y = f(g(x)),則 dy/dx = dy/dg × dg/dx

對於多層網路:

L = L(a₃(a₂(a₁(x))))

∂L/∂w₁ = ∂L/∂a₃ × ∂a₃/∂a₂ × ∂a₂/∂a₁ × ∂a₁/∂w₁
         ↑         ↑         ↑         ↑
        第3層     第3層     第2層     第1層
        梯度      對第2層   對第1層   對w₁
                 的導數    的導數    的導數

關鍵洞見:每一層只需要知道「上一層傳來的梯度」和「自己的局部導數」,就能算出「該傳給下一層的梯度」。

這就是為什麼反向傳播如此高效——它是一個局部計算的組合。

完整的訓練流程

現在讓我們把前向傳播和反向傳播組合起來:

┌─────────────────────────────────────────────────────┐
│                    一次訓練迭代                        │
├─────────────────────────────────────────────────────┤
│                                                      │
│  ① 前向傳播(Forward Pass)                           │
│     輸入 ──→ 第1層 ──→ 第2層 ──→ 第3層 ──→ 輸出       │
│                                        ↓             │
│                                     計算損失          │
│                                        ↓             │
│  ② 反向傳播(Backward Pass)                          │
│     ∂L/∂w₁ ←── ∂L/∂w₂ ←── ∂L/∂w₃ ←── ∂L/∂output    │
│                                                      │
│  ③ 更新權重(Gradient Descent)                       │
│     w₁ = w₁ - η × ∂L/∂w₁                            │
│     w₂ = w₂ - η × ∂L/∂w₂                            │
│     w₃ = w₃ - η × ∂L/∂w₃                            │
│                                                      │
└─────────────────────────────────────────────────────┘

                    重複很多次

反向傳播的細節

不同運算的局部導數

每種運算都有對應的局部導數規則:

加法

c = a + b
∂c/∂a = 1, ∂c/∂b = 1
梯度「平均分配」給兩個輸入

乘法

c = a × b
∂c/∂a = b, ∂c/∂b = a
梯度「交叉傳遞」

ReLU

y = max(0, x)
∂y/∂x = 1 if x > 0 else 0
正數時梯度通過,負數時梯度被「關閉」

Tanh

y = tanh(x)
∂y/∂x = 1 - tanh²(x) = 1 - y²

Sigmoid

y = σ(x)
∂y/∂x = σ(x) × (1 - σ(x)) = y × (1 - y)

梯度累加

如果一個節點被多個運算使用,它的梯度要累加

    a ──→ b
     ↘   ↗
       c

如果 a 同時影響 b 和 c,則:
∂L/∂a = ∂L/∂b × ∂b/∂a + ∂L/∂c × ∂c/∂a

這就是為什麼 micrograd 中用 grad += 而不是 grad =

為什麼反向傳播很厲害?

計算效率

假設網路有 N 個權重。

暴力方法

  • 對每個權重,微調一點點,看損失變化多少
  • 需要 N 次前向傳播
  • 時間複雜度 O(N × 前向傳播時間)

反向傳播

  • 一次前向傳播 + 一次反向傳播
  • 反向傳播的時間約等於前向傳播
  • 時間複雜度 O(2 × 前向傳播時間)

對於有數百萬參數的網路,反向傳播快了數百萬倍

模組化

每一層只需要實現:

  1. forward():前向傳播
  2. backward():計算局部梯度並傳遞

這讓我們可以像樂高一樣組合不同的層,自動微分框架會處理所有梯度計算。

常見問題

梯度消失(Vanishing Gradient)

當網路很深時,梯度可能在傳遞過程中越來越小:

假設每層的局部梯度都是 0.25(Sigmoid 的最大值)

10 層後:0.25^10 ≈ 0.000001

梯度幾乎消失,淺層學不動!

解決方案

  • 使用 ReLU(梯度是 0 或 1)
  • 使用殘差連接(ResNet)
  • 使用 BatchNorm

梯度爆炸(Exploding Gradient)

相反地,梯度也可能越來越大:

如果每層的局部梯度都是 4

10 層後:4^10 ≈ 1,000,000

梯度爆炸,參數更新太劇烈!

解決方案

  • 梯度裁剪(Gradient Clipping)
  • 適當的權重初始化
  • 使用 BatchNorm

互動視覺化

我製作了一個互動工具,讓你可以:

  • 觀察三層網路的前向傳播和反向傳播
  • 看每一層的「責任」分配
  • 調整權重和學習率,觀察訓練過程
  • 追蹤詳細的梯度計算

👉 反向傳播互動視覺化

建議你操作時注意:

  1. 反向傳播時,梯度如何從輸出層往回傳
  2. 每層的「責任」(梯度)比例
  3. 更新後,損失如何下降

小結

今天我們學到了:

  1. 反向傳播是高效計算梯度的算法
  2. 傳話遊戲理解:從錯誤往回追,分配責任
  3. 鏈式法則是數學基礎:∂L/∂w = ∂L/∂output × ∂output/∂...× ∂.../∂w
  4. 每層只需要知道「上層梯度」和「局部導數」
  5. 梯度要累加(一個節點可能影響多個輸出)
  6. 反向傳播比暴力方法快數百萬倍
  7. 注意梯度消失梯度爆炸問題

下一篇預告

反向傳播需要追蹤每個運算的依賴關係。這是怎麼做到的?

下一篇我們會介紹計算圖(Computational Graph)——深度學習框架(如 PyTorch)的核心數據結構。我們會實際看看 micrograd 如何用幾十行程式碼實現「自動微分」。


本文是「從零理解神經網路」系列的第四篇。如果你覺得有幫助,歡迎分享給也在學習 ML 的朋友。

MIT Licensed