JOVANA
Explore Library Glossary Getting Started Three Levels Fields How it works Mission
Join the mission
All guides

堆疊神經元:層、深度與多層感知器

把許多神經元接成層,看著一排扁平的像素彎成曲面決策區——多層感知器,以及深度為何讓它驚人地無所不能。

從一個神經元到一整層

上一篇我們造出了最小的學習機器:單一個人工神經元。它接收一個輸入向量 x,用自己專屬的權重 w 與偏置 b 算出加權總和 z = w·x + b,再把這個總和送進激活函數壓縮一下。一個神經元只能對影像問一個近似是非題——「這一個模式吻合嗎?」要辨認像手寫數字這麼豐富的東西,一個問題遠遠不夠。

於是我們做最理所當然的事:把神經元複製很多份。每一份都讀同一個輸入向量 x——同樣那些像素——但各自帶著自己的權重與偏置,因此各自在搜尋不同的模式。別把它想成一個評審團,而是想成一整排並列的評審團:一團打「圓不圓」的分、一團打「中央是不是暗的」、一團打「有沒有直筆畫」。把一群神經元接成全部同時盯著同一個輸入,你就造出了一個全連接層——叫「全連接」是因為每個輸入都餵給每一個神經元。

一個全連接層:單一個輸入向量扇出到數個神經元,每個神經元算出自己的加權總和與激活。

多層感知器示意圖:左側的輸入節點各自連到下一欄的每一個神經元,呈現輸入向量與一整層神經元之間的全連接。

\mathbf{h} = g(W\mathbf{x} + \mathbf{b})

把這條式子讀成:「對每個神經元同時做一遍上一篇的加權總和,然後壓縮。」這裡 x 是長度為 n_in 的輸入向量(輸入的個數——小例子裡是 2 個像素特徵)。W 是一個有 n_out 列、n_in 行的矩陣;它的第 j 列正好就是上一篇神經元 j 的權重向量 w。乘積 W x 是長度為 n_out 的一串加權總和——每一列做一個內積。b 是長度為 n_out 的偏置向量,替每個神經元加上各自的位移。g 是激活函數,對每個元素獨立作用。結果 h 長度為 n_out,每個神經元一個數。具體地說,2 個輸入、3 個神經元時:x 長度 2,W 是 3×2,W x 得到 3 個總和,加 b 仍是 3 個,h 長度 3。完全沒有新東西——這就是 z = w·x + b 疊三次,矩陣只是替我們把三個內積記好帳。若 x = [1, 2],三列權重為 [0.5, −0.5]、[1, 0]、[−1, 2],偏置為 [0, 1, −3],則原始總和 W x 是 −0.5、1.0、3.0;加上偏置得到 z = [−0.5, 2.0, 0.0];再用 ReLU 壓縮(留下正數、把負數歸零)得到 h = [0, 2.0, 0]——第 2 個神經元發火,其他保持沉默。

import numpy as np

x = np.array([1.0, 2.0])           # input vector, length n_in = 2

# Each ROW of W is one neuron's weights; n_out = 3 neurons
W = np.array([[ 0.5, -0.5],        # neuron 1 looks for this pattern
              [ 1.0,  0.0],        # neuron 2
              [-1.0,  2.0]])       # neuron 3   ->  W has shape (3, 2)
b = np.array([0.0, 1.0, -3.0])     # one bias per neuron, length 3

z = W @ x + b                      # 3 weighted sums at once -> [-0.5, 2.0, 0.0]
h = np.maximum(0.0, z)             # ReLU, elementwise       -> [ 0.0, 2.0, 0.0]
print(z, h)
整層只用一次矩陣–向量乘法:形狀 (3×2)·(2) + (3) = (3)。要更多神經元,只要替 W 多加幾列。

堆疊多層:多層感知器

把這些層疊起來,你就得到一個多層感知器(MLP):若干個全連接層排成一列接起來,前一層的輸出向量成為下一層的輸入向量。第 1 層把像素變成一串「模式分數」;第 2 層把這些分數當成自己的輸入,產生新的一串;如此繼續。某一層既不知道、也不在乎它的輸入是來自另一層還是來自原始像素——對它而言,輸入向量就是輸入向量。

三個你會用一輩子的名字。輸入層就是送進去的原始資料——對我們來說是攤平成一排的像素(它本身不做運算,它就是那張圖)。隱藏層是中間那些層,我們從不把它們的輸出直接當成答案來讀;它們存放網路學到的中間特徵。輸出層是最後一層,我們把它的數字解讀成答案——也就是各類別的分數。「隱藏」只是指「既不是輸入、也不是最終輸出」——埋在中間而已。

層層相疊構成 MLP:輸入層、一個或多個隱藏層,然後是輸出層——每一層的輸出餵給下一層。

多層感知器畫成一欄欄節點:輸入節點、兩欄隱藏節點、一欄輸出節點,每一欄的每個節點都連到下一欄的每個節點。

這裡就是支撐整個深度學習的核心想法。每一層只能用下一層交給它的東西來搭建。所以讀原始像素的第一個隱藏層只能學到簡單的東西——這裡有道亮邊、那裡有塊暗斑。下一層讀這些「邊與斑」的分數,能把它們組合成更豐富的東西——一個角、一段弧、一個圈。再上一層能把圈與筆畫拼成「9 的上半」或「貓的耳朵」。深度建立起一個特徵的階層:複雜的模式被表達成下層較簡單模式的組合。沒有任何單一神經元被告知該找什麼——有用的中間特徵是從訓練中浮現出來的(第 4 篇)。

兩個旋鈕描述一個 MLP 的形狀。深度是層的數目——資料在穿過網路時被重新組合幾次。寬度是一層裡神經元的數目——這一層一次能搜尋多少種不同的模式。兩者都是工程師在訓練前自己定的設計選擇,不是網路學出來的值;我們把這類旋鈕叫做超參數。更寬的層能在同一層級辨認更多模式;更深的網路能搭出更抽象的模式。把它們挑好,一半靠工藝、一半靠實驗。

前向傳播:資料一層一層往前流

前向傳播(又叫一次前向傳遞)就只是把一個輸入依序推過每一層、產生最終輸出這件事。你餵進影像,第 1 層轉換它,第 2 層再轉換那個結果,一路到輸出層交給你分數。關鍵在於:權重與偏置固定時,這個過程是完全決定性的——同一張影像永遠給出同一個輸出。前向傳遞當中不發生學習;學習是另一件事——調整權重——我們在第 4 篇處理。現在權重凍結,我們只看資料流動。

  1. 從輸入開始:攤平的像素向量成為 h⁰ = x。
  2. 第 1 層:算 z = W¹h⁰ + b¹,再套上激活函數得到 h¹。
  3. 第 2 層:把 h¹ 餵進去,用同樣方式算出 h²。
  4. 一層一層重複——每一層的輸出都成為下一層的輸入。
  5. 最後一層:它的輸出就是預測——各類別的分數。
\mathbf{h}^{(l)} = g^{(l)}\!\left(W^{(l)}\mathbf{h}^{(l-1)} + \mathbf{b}^{(l)}\right), \qquad \mathbf{h}^{(0)} = \mathbf{x}

別被上標嚇到——括號裡的 (l) 只是表示「屬於第 l 層」。所以 W⁽ˡ⁾ 是第 l 層的權重矩陣,b⁽ˡ⁾ 是它的偏置,g⁽ˡ⁾ 是它的激活函數,h⁽ˡ⁾ 是它輸出的向量。這條式子讀作:要得到第 l 層的輸出,就把前一層的輸出 h⁽ˡ⁻¹⁾ 拿來,過一遍這一層自己的「加權總和加偏置」,再壓縮。它就是第 1 節那條單層規則 h = g(Wx + b),只是輸入現在叫 h⁽ˡ⁻¹⁾(下面那一層)而不是 x。起始條件 h⁽⁰⁾ = x 不過是說「第 0 層就是輸入本身」。對 l = 1, 2, … 一直套到最後一層,最終的 h 就是你的預測。

來看一個值穿過一個迷你 2→2→1 網路。餵入 x = [2, −1]。第 1 層的權重列是 [1, 1] 和 [1, −1],偏置是 [0, −4]:原始總和是 2+(−1)=1 和 2−(−1)=3,加上偏置後 z¹ = [1, −1]。ReLU 留下 1,但把 −1 夾到 0,得到 h¹ = [1, 0]——第二個隱藏神經元對這個輸入是關著的。第 2 層(輸出)權重是 [2, 5]、偏置是 1:它的分數是 2·1 + 5·0 + 1 = 3。所以整段旅程是 [2, −1] → [1, −1] → [1, 0] → 3。注意激活函數確實改變了答案:若沒有 ReLU 的夾擠,輸出會是 2·1 + 5·(−1) + 1 = −2,而不是 3。

import numpy as np

def relu(z):
    return np.maximum(0.0, z)

x  = np.array([2.0, -1.0])             # h^(0): the input

# ---- Layer 1 (hidden) ----
W1 = np.array([[1.0,  1.0],
               [1.0, -1.0]])           # shape (2, 2)
b1 = np.array([0.0, -4.0])
h1 = relu(W1 @ x + b1)                  # z=[1,-1] -> relu -> [1., 0.]

# ---- Layer 2 (output) ----
W2 = np.array([[2.0, 5.0]])            # shape (1, 2)
b2 = np.array([1.0])
out = W2 @ h1 + b2                      # 2*1 + 5*0 + 1 = 3.0  (a raw score)

print(h1, out)                         # [1. 0.] [3.]
用程式碼寫前向傳播:每一層都重複使用第 1 節那條單行式,前一層的輸出餵給下一層。這支同樣的程式會在第 4 篇回來。

選擇壓縮方式:ReLU 與 sigmoid

第 1 篇我們認識了兩種壓縮神經元總和的方式,並答應做一次正式比較。就在這裡。激活函數的工作是把筆直的加權總和彎成有非線性的東西——少了它,正如第 5 節會證明的,整個深層堆疊會塌成什麼都不是。兩個經典選擇:sigmoid 像調光旋鈕,把任何輸入平滑地滑到 0 與 1 之間的某個值;ReLU 像單向閥,擋掉所有負的、讓正的原封不動直接通過。

\operatorname{ReLU}(z) = \max(0,\,z) \qquad\quad \sigma(z) = \frac{1}{1 + e^{-z}}

兩個都讀一遍。ReLU(z) = max(0, z),當 z 為正時輸出 z,z 為負時輸出 0——在原點有個硬折角。sigmoid σ(z) = 1/(1 + e^(−z)) 把任意實數 z 擠進 (0, 1):很大的正 z → 接近 1,很大的負 z → 接近 0,z = 0 → 正好 0.5(因為 e⁰ = 1,所以是 1/2)。等網路開始學習時,最要緊的數字是每個函數的斜率——它的導數,也就是輸入微微移動時輸出變多快。ReLU 的斜率在每個 z > 0 是平平的 1、在每個 z < 0 是 0:正的這一側信號毫不衰減地通過。sigmoid 的斜率在 z = 0 附近最陡(約 0.25),對大的 |z| 則縮向 0——在兩端尾巴上曲線變平。把「斜率」記牢,因為第 4 篇裡網路學習的方式,正是把信號往回傳、乘上的就是這些斜率。

ReLU:負輸入時平貼在零,正輸入時是一條斜率為 1 的直線——一個在原點有硬折角的單向閥。

ReLU 函數的圖:x 為負時沿著零水平延伸,x 為正時以 45 度直線上升。

常見激活函數並排比較:注意 sigmoid 在兩端變平(飽和),而 ReLU 在正側保持固定的斜率 1。

數個激活函數疊在一起的圖,凸顯 S 形 sigmoid 在兩極變平,對比折線狀的 ReLU。

這就是為什麼現代的隱藏層幾乎總是用 ReLU。它便宜到不行(一個 max 而已),而且正側斜率是 1,讓學習信號一層又一層流過去而不縮水——而 sigmoid 會飽和:餵它一個稍大的輸入,它接近 0 的斜率就把信號掐住,這個問題會隨深度累積成第 4 篇要遇到的梯度消失。不過 ReLU 也不是完美:一個對所有樣本輸入都保持負值的神經元會永遠輸出 0、斜率也是 0,於是沒有任何學習信號到得了它——它卡死了。這個 dying ReLU(神經元死亡) 的陷阱是真實存在的,但通常只影響一小部分神經元(而且像「leaky ReLU」這樣的變體可以補救)。sigmoid 仍有它的舞台——在二元分類器的輸出層,那裡我們確實想要一個介於 0 與 1 的數來當機率讀(第 3 篇)。一句話總結:裡面用 ReLU,會飽和的壓縮留給輸出。

非線性為何重要:通用近似定理

我們一再強調激活函數不可或缺。該證明了。假設我們偷懶把激活函數拿掉,於是每一層都是純線性映射——就只是乘一個矩陣、加一個偏置。那麼把兩個這樣的層疊起來,到底算出什麼?

W_2\,(W_1\mathbf{x} + \mathbf{b}_1) + \mathbf{b}_2 = (W_2 W_1)\,\mathbf{x} + (W_2\mathbf{b}_1 + \mathbf{b}_2) = W'\mathbf{x} + \mathbf{b}'

把代數走一遍。第一層把 x 變成 W₁x + b₁。第二層把它乘上 W₂ 再加 b₂,得到 W₂(W₁x + b₁) + b₂。把 W₂ 乘進去、重新分組:含 x 的項收成 (W₂W₁)x,常數項收成 (W₂b₁ + b₂)。但 W₂W₁ 不過是某個矩陣——叫它 W'——而 W₂b₁ + b₂ 不過是某個向量 b'。所以這個兩層堆疊等於 W'x + b':一個單一的線性層。同樣的把戲能把任意多個沒有激活函數的層摺成一個。直覺上,兩個平的映射複合起來還是平的映射——把一張平的紙拉伸一次、再拉伸一次,它還是平的。在層與層之間不楔進一個非線性的 g,深度買到的東西字面上是零。

有了非線性激活函數,MLP 能切出彎曲的決策區域來分開糾纏的類別——這是單一線性層(一條直分界)永遠做不到的。

二維散點圖:兩個交纏的類別之間有一條彎曲邊界穿梭其中,對比一條無法分開它們的直線。

把非線性激活函數放回層與層之間,一切都變了。通用近似定理把這份回報講得精確:一個至少有一個隱藏層、並帶非線性激活函數的 MLP,可以把幾乎任何連續函數近似到你要的任意精度——只要這個隱藏層獲准擁有足夠多的神經元。「任何連續函數」包含那個極其彎曲、把一格格原始像素值對映到「貓」還是「狗」的函數。於是原則上,一疊樸素的神經元,表達力已足以成為一套視覺系統:定理說,對的權重是存在的。