第 3 章:神經網絡如何學習
在第二章中,我們詳細了解了神經網絡的靜態結構:由神經元組成的層,以及連接它們的權重和偏置。現在,我們將進入整個教程最核心的部分:神經網絡是如何從數據中"學習"的?
這個學習過程是一個動態的、不斷調整自身參數以求更佳預測的過程。我們將通過四個關鍵概念來揭示這個秘密:
- 前向傳播 (Forward Propagation):數據如何通過網絡產生一個預測?
- 損失函數 (Loss Function):如何量化這個預測的"好壞"?
- 梯度下降 (Gradient Descent):如何根據"好壞"程度,找到參數優化的方向?
- 反向傳播 (Backpropagation):如何高效地在整個網絡中執行這個優化?
讓我們從第一步開始。
3.1 前向傳播:從輸入到輸出
前向傳播,顧名思義,是信息在神經網絡中 從前向后 傳遞的過程。它描述了當給定一個輸入樣本時,網絡是如何一步步進行計算,并最終在輸出層得到一個預測值的完整流程。
這個過程非常直觀,就是將我們在第二章學到的所有知識串聯起來。
前向傳播的步驟
我們以一個簡單的、用于二元分類的網絡為例。假設它有一個輸入層、一個隱藏層和一個輸出層。
圖 3.1: 前向傳播流程示意圖。數據從輸入層(綠色)開始,流經隱藏層(藍色),最終到達輸出層(紅色)產生預測值。
對于一個輸入樣本 X
,其前向傳播的計算流程如下:
-
輸入層 -> 隱藏層
- 首先,隱藏層的每一個神經元都會接收來自輸入層所有神經元的信號。
- 對于隱藏層中的 第 j 個神經元,它會計算一個加權和
z_j
,這和我們在感知器中學到的一樣:
z j = ( ∑ i ( x i ? w i j ) ) + b j z_j = (\sum_{i} (x_i \cdot w_{ij})) + b_j zj?=(i∑?(xi??wij?))+bj?
其中,x_i
是第i
個輸入,w_ij
是從輸入層第i
個神經元到隱藏層第j
個神經元的權重,b_j
是隱藏層第j
個神經元的偏置。 - 然后,將這個加權和
z_j
通過一個激活函數(比如我們學過的 ReLU 或 Sigmoid)處理,得到該神經元的輸出a_j
:
a j = Activation ( z j ) a_j = \text{Activation}(z_j) aj?=Activation(zj?) - 對隱藏層中的所有神經元重復這個過程,我們就得到了整個隱藏層的輸出
A_hidden
。
-
隱藏層 -> 輸出層
- 現在,隱藏層的輸出
A_hidden
成為了輸出層的輸入。 - 輸出層的計算過程與隱藏層完全相同。假設我們的輸出層只有一個神經元(用于二元分類),它的計算過程是:
- 計算加權和
z_output
:
z output = ( ∑ j ( a j ? w j , output ) ) + b output z_{\text{output}} = (\sum_{j} (a_j \cdot w_{j,\text{output}})) + b_{\text{output}} zoutput?=(j∑?(aj??wj,output?))+boutput? - 應用激活函數得到最終預測
y_pred
:
y pred = Activation output ( z output ) y_{\text{pred}} = \text{Activation}_{\text{output}}(z_{\text{output}}) ypred?=Activationoutput?(zoutput?)
(對于二元分類,這里的激活函數通常是 Sigmoid)
- 計算加權和
- 現在,隱藏層的輸出
至此,一次完整的前向傳播就完成了。 我們從一個原始輸入 X
開始,通過網絡中預設的權重和偏置,一步步計算,最終得到了一個預測結果 y_pred
。
值得注意的是,在網絡未經訓練時,由于權重和偏置都是隨機初始化的,這個 y_pred
幾乎肯定是錯誤的。
那么,我們如何知道它"錯得有多離譜"?又該如何利用這個"錯誤"來指導網絡調整參數,讓下一次的預測更準一些呢?
這便是我們下一節要討論的 損失函數。
3.2 損失函數:衡量預測的"錯誤"程度
損失函數(Loss Function),有時也被稱為 成本函數(Cost Function) 或 目標函數(Objective Function),是神經網絡學習過程中的"導航員"和"裁判"。
它的作用非常明確:用一個具體的數值來量化模型的預測值(y_pred
)與真實值(y_true
)之間的差距。
這個差距,我們稱之為"損失"(Loss)或"誤差"(Error)。
- 損失值越大,說明模型的預測越不準確,離真實答案"越遠"。
- 損失值越小,說明模型的預測越精準,離真實答案"越近"。
因此,整個神經網絡訓練的 最終目標,就是通過調整權重和偏置,來 最小化這個損失函數的值。
選擇哪種損失函數取決于我們正在處理的任務類型。下面我們介紹兩種最常見的場景。
場景一:回歸問題(Regression)
在回歸任務中,我們的目標是預測一個連續的數值,比如房價、氣溫或者股票價格。對于這類問題,最常用的損失函數是 均方誤差(Mean Squared Error, MSE)。
工作原理:MSE 計算的是所有樣本的"預測值與真實值之差的平方"的平均值。
圖 3.2: 均方誤差(MSE)的可視化。它計算的是每個數據點(藍點)到模型預測線(紅線)的垂直距離(綠色虛線,即殘差)的平方的平均值。(來源: Neuromatch Academy)
數學公式(假設我們有 n
個樣本):
L MSE = 1 n ∑ i = 1 n ( y true ( i ) ? y pred ( i ) ) 2 L_{\text{MSE}} = \frac{1}{n} \sum_{i=1}^{n} (y_{\text{true}}^{(i)} - y_{\text{pred}}^{(i)})^2 LMSE?=n1?i=1∑n?(ytrue(i)??ypred(i)?)2
- 我們先計算每個樣本的預測值和真實值之差
(y_true - y_pred)
。 - 然后將這個差值平方,這有兩個好處:1) 保證結果是正數;2) 對較大的誤差給予更重的"懲罰"。
- 最后將所有樣本的平方誤差加起來,求一個平均值。
場景二:分類問題(Classification)
在分類任務中,我們的目標是預測一個離散的類別標簽,例如判斷一封郵件是否為垃圾郵件(二元分類),或者識別一張圖片中的動物是貓、狗還是鳥(多元分類)。
對于分類問題,最強大的損失函數是 交叉熵損失(Cross-Entropy Loss)。
工作原理:它的核心思想是:對于正確的預測,我們給予較小的"懲罰"(損失);對于錯誤的預測,我們給予巨大的"懲罰"。
圖 3.3: 不同損失函數在二元分類中的對比(當真實標簽為1時)。交叉熵損失(綠色實線)顯示,當模型對正確類別的預測概率接近1時,損失趨近于0;而當預測概率接近0時,損失會急劇增加,給予錯誤的預測巨大的懲罰。(來源: Wikimedia Commons)
對于最常見的 二元分類(Binary Classification),其交叉熵損失(也稱為 BCE Loss)公式如下:
L BCE = ? 1 n ∑ i = 1 n [ y true ( i ) log ? ( y pred ( i ) ) + ( 1 ? y true ( i ) ) log ? ( 1 ? y pred ( i ) ) ] L_{\text{BCE}} = - \frac{1}{n} \sum_{i=1}^{n} \left[ y_{\text{true}}^{(i)} \log(y_{\text{pred}}^{(i)}) + (1 - y_{\text{true}}^{(i)}) \log(1 - y_{\text{pred}}^{(i)}) \right] LBCE?=?n1?i=1∑n?[ytrue(i)?log(ypred(i)?)+(1?ytrue(i)?)log(1?ypred(i)?)]
讓我們來理解一下這個公式:
- 如果真實標簽
y_true
是 1:公式簡化為-log(y_pred)
。- 如果我們的預測
y_pred
也很接近 1(比如 0.99),那么log(y_pred)
接近 0,損失就很小。 - 如果我們的預測
y_pred
離譜地接近 0(比如 0.01),那么log(y_pred)
會趨近于負無窮,損失就會變得非常大。
- 如果我們的預測
- 如果真實標簽
y_true
是 0:公式簡化為-log(1 - y_pred)
。- 如果我們的預測
y_pred
也很接近 0(比如 0.01),那么1 - y_pred
接近 1,log(1-y_pred)
接近 0,損失就很小。 - 如果我們的預測
y_pred
離譜地接近 1(比如 0.99),那么1 - y_pred
接近 0,log(1-y_pred)
會趨近于負無窮,損失就會變得非常大。
- 如果我們的預測
這正是我們想要的:預測越有信心且越正確,損失越小;預測越有信心但越錯誤,損失就越大。
現在,我們有了一個明確的目標(最小化損失函數),也知道了如何衡量我們距離這個目標有多遠。
接下來的問題是:我們應該 如何 調整那成千上萬的權重和偏置,才能讓這個損失值降低呢?我們是應該把某個權重調高一點,還是調低一點?調多少才合適?
明確的目標(最小化損失函數),也知道了如何衡量我們距離這個目標有多遠。
接下來的問題是:我們應該 如何 調整那成千上萬的權重和偏置,才能讓這個損失值降低呢?我們是應該把某個權重調高一點,還是調低一點?調多少才合適?
這就是下一節 “梯度下降” 將要為我們解答的問題。
3.3 梯度下降:找到最小化損失的路徑
現在,我們站在了問題的核心:我們有了一個可以量化錯誤的損失函數,我們如何系統地調整網絡中成千上萬的參數(權重 w
和偏置 b
),來讓損失值變得越來越小呢?
暴力嘗試顯然是行不通的。我們需要一個聰明且高效的策略。這個策略就是 梯度下降(Gradient Descent)。
一個下山的類比
為了理解梯度下降,想象一個非常生動的場景:
你正置身于一座連綿起伏的山脈中,四周一片濃霧,你看不清山谷的最低點在哪里。你的任務是盡快到達山谷的底部。你該怎么辦?
一個非常直觀的策略是:環顧四周,找到腳下最陡峭的下坡方向,然后朝著這個方向邁出一步。 到達新位置后,你再次重復這個過程:環顧四周,找到新的最陡峭的下坡方向,再邁出一步。只要你堅持這么做,最終你將一步步地走到山谷的最低點。
這就是梯度下降算法的全部直覺。在這個類比中:
- 山脈的地形:就是我們的 損失函數。這是一個由所有網絡參數(權重和偏置)決定的復雜、高維度的"地形"。
- 你在山上的位置:由當前網絡的 所有參數值 決定。
- 你的海拔高度:就是當前參數所對應的 損失值。
- 我們的目標:找到這片地形的 最低點(Global Minimum),也就是損失函數的最小值。
- 最陡峭的下坡方向:這就是 梯度(Gradient) 的反方向。
圖 3.4: 梯度下降的可視化。在這個損失函數的"地形"上,無論從哪個點開始(紅點),梯度下降算法都會引導參數沿著最陡峭的下坡路徑前進,最終到達一個局部或全局的最低點。
核心概念:梯度與學習率
梯度下降算法的核心由兩個概念組成:
-
梯度(Gradient, ?)
在數學上,梯度是一個向量,它指向函數值 增長最快 的方向。換句話說,梯度就是函數在當前位置的 最陡峭的上坡方向。那么,最陡峭的 下坡方向 自然就是梯度的 反方向(
-?
)。在神經網絡中,這個"函數"就是我們的損失函數
L
。梯度?L
就是損失函數L
對所有參數(w_1, w_2, ..., b_1, b_2, ...
)求偏導數后組成的向量。這個向量告訴我們,在當前的位置,如何微調每一個參數,才能讓損失值上升得最快。而我們只需要沿著它的反方向更新參數,就能最高效地降低損失。 -
學習率(Learning Rate, α)
找到了下山的方向后,我們還需要決定 每一步該邁多大。這個步長,就是 學習率。它是一個超參數(需要我們手動設定的值),用來控制每次參數更新的幅度。- 學習率太小:我們會像個謹小慎微的嬰兒一樣,每次只挪動一小步。雖然方向是對的,但下山速度會非常非常慢,訓練過程會極其漫長。
- 學習率太大:我們可能會因為步子邁得太大而"沖過頭",直接越過了山谷的最低點,甚至可能跳到了對面的山坡上,導致損失值不降反升,永遠無法收斂到最低點。
因此,選擇一個合適的學習率是訓練神經網絡中最關鍵的環節之一。
梯度下降的更新規則
結合梯度和學習率,我們就得到了梯度下降的參數更新規則。對于網絡中的任何一個參數 θ
(它可以是任何權重 w
或偏置 b
),其更新過程如下:
θ new = θ old ? α ? ? θ L \theta_{\text{new}} = \theta_{\text{old}} - \alpha \cdot \nabla_{\theta}L θnew?=θold??α??θ?L
這個公式的含義是:
- 計算損失函數
L
在當前參數θ_old
處的梯度?_θ L
。 - 將梯度乘以學習率
α
,得到本次更新的步長。 - 從舊的參數值
θ_old
中減去這個步長,得到新的參數值θ_new
。
通過在整個訓練數據集上反復迭代這個過程(即,對于每個樣本或每個批次的樣本,都計算梯度并更新一次參數),網絡中的所有參數都會被逐步推向能使總損失最小化的最優值。
但是,這里又出現了一個巨大的挑戰:一個現代神經網絡的參數動輒成千上萬,甚至數百萬、數十億。我們該如何高效地計算出損失函數對這每一個參數的梯度呢?
這就要引出神經網絡優化中的最后一塊,也是最神奇的一塊拼圖——反向傳播。我們將在下一節詳細探討它。
3.4 反向傳播:高效的梯度計算引擎
**反向傳播(Backpropagation, BP)**是迄今為止訓練神經網絡最成功的算法。可以說,沒有反向傳播,就沒有深度學習的今天。它解決的正是上一節末尾提出的那個核心挑戰:如何在一個具有數百萬甚至數十億參數的復雜網絡中,快速、高效地計算出損失函數對每一個參數的梯度。
它的基本思想非常優雅,完全建立在微積分的 鏈式法則(Chain Rule) 之上。
直觀理解:責任的層層回溯
讓我們先拋開復雜的數學公式,用一個直觀的方式來理解反向傳播。
在前向傳播中,信息從輸入層流向輸出層,最終產生一個預測,并計算出總損失。現在,想象一下,這個最終的損失(誤差)是一個"責任",我們需要將這個"總責任"公平且準確地分配回網絡中的每一個參數(權重和偏置),看看它們各自對這個最終的錯誤貢獻了多少"力量"。
反向傳播做的就是這件事:它將損失 L
這個"總責任",從網絡的 輸出層開始,一層一層地向后傳遞,直到輸入層。
- 輸出層:在輸出層,我們可以直接計算出損失對該層參數(權重和偏置)的梯度。這相對簡單,因為它們離損失函數最近。
- 倒數第二層:這一層的參數并沒有直接影響最終的損失,而是通過影響了輸出層的輸出來間接影響損失。那么,這一層某個參數的"責任"有多大呢?根據鏈式法則,它的大小等于:(它對輸出層的影響) x (輸出層對最終損失的影響)。由于后者我們已經在第一步算出來了,我們只需要計算前者,就能得到當前層參數的梯度。
- 繼續向后:這個邏輯可以一直向后傳遞。任何一層參數的梯度,都可以通過它對下一層的影響,乘以【下一層已經計算出的梯度】來得到。
圖 3.5: 反向傳播中的梯度(或誤差信號 δ)流動示意圖。誤差從最后一層(右側)產生,并利用鏈式法則逐層向后傳遞,計算出每一層參數所應承擔的"責任"。
就這樣,誤差信號像漣漪一樣從后向前傳播,每經過一層,我們就利用鏈式法則計算出該層參數的梯度。當這個過程到達輸入層時,我們就已經擁有了網絡中所有參數的梯度。
反向傳播的兩個階段
因此,一次完整的參數更新(即梯度下降的一步)實際上包含兩個階段:
-
前向傳播(Forward Pass):
- 將一批訓練數據輸入網絡。
- 從輸入層開始,逐層計算,直到輸出層得到預測值。
- 根據預測值和真實值,計算出這一批數據的總損失。
- 在這個過程中,每一層的計算結果(比如加權和
z
和激活值a
)都需要被緩存下來,因為它們在反向傳播階段需要被用到。
-
反向傳播(Backward Pass):
- 從最終的損失開始,計算損失函數對輸出層參數的梯度。
- 利用鏈式法則,逐層向后計算每一層參數的梯度,直到輸入層。
- 這個過程會用到前向傳播中緩存的中間值。
當反向傳播完成后,我們就得到了所有參數的梯度。此時,我們就可以使用上一節學到的梯度下降更新規則,來更新所有的權重和偏置了:
θ new = θ old ? α ? ? θ L \theta_{\text{new}} = \theta_{\text{old}} - \alpha \cdot \nabla_{\theta}L θnew?=θold??α??θ?L
這個 “前向計算 -> 反向求導 -> 更新參數” 的循環,就是神經網絡訓練的核心。這個循環會不斷地重復,成千上萬次,直到損失函數的值收斂到一個足夠小的范圍,我們的網絡也就"學會"了如何處理這類任務。
至此,我們已經完整地解構了神經網絡學習的四大核心組件。在下一章,我們將把這些理論知識應用到實踐中,使用 PyTorch 這個強大的深度學習框架,來親手搭建和訓練我們的第一個神經網絡。