跳至主要內容

    從感知器到 MLP:神經網路是什麼函數?

    從一個加權和走到多層函數,手算前向傳播、非線性與參數數量

    sunny 桑尼· · 21 MIN READ · 148 次瀏覽

    從感知器到 MLP:神經網路是什麼函數?
    本文目錄

    看到神經網路圖上的圓圈和箭頭,很容易以為自己必須先理解大腦,才能理解模型。對實作而言,更直接的入口是把它看成一個有參數的函數:資料進來,經過幾次加權、加偏置與非線性轉換,輸出幾個數字。

    「學習」是在調這些參數;「前向傳播」是在參數暫時固定時,把輸出算出來。先把兩者拆開,你就能讀懂一層在做什麼、哪裡改變了函數的表達能力,也能辨認一個漂亮網路圖是否真的交代了如何訓練。

    一個神經元,先算加權和,再決定如何轉換

    圖1|圖中先算sum(w_i x_i)+b,再套用f。來源:Andrej Karpathy/CS231n Neural Networks Part 1,MIT。保留完整原圖,轉存PNG。圖上的生物詞彙是粗略類比;這張圖沒有指定f一定是硬閾值,也沒有展示學習規則。

    對一筆有d個特徵的資料,可以把一個單元寫成:

    z = w_1 x_1 + ... + w_d x_d + b
    h = f(z)
    

    權重w控制各特徵對這個加權和的方向與幅度;偏置b讓轉換的位置可以移動。它不是另外收集的一個資料欄位,而是模型要設定或學習的參數。

    例如x=[2,1]、w=[1,-2]、b=0.5,先算z=2-2+0.5=0.5。接下來套什麼f,才決定h:若用ReLU,h=0.5;若用硬閾值,輸出可能是0或1。相同的加權和,可以接不同的函數與任務。

    這種形式與前面學過的線性模型相連。線性迴歸可以直接輸出z,二元羅吉斯迴歸以sigmoid轉成模型的正類機率估計。神經網路往前多走一步:把許多這樣的單元組起來,讓中間算出的特徵繼續成為下一層的輸入。

    感知器的關鍵限制:一條直線不能解所有分類

    早期的感知器用線性分數配硬閾值做分類。以兩個二元輸入的AND為例,只有兩者都為1時答案才是1。手動選w=[1,1]、b=-1.5,再看分數是否大於0,就能分對四種輸入。

    x1x2x1+x2-1.5AND
    00-1.50
    01-0.50
    10-0.50
    110.51

    這組參數是手選的。經典感知器學習則會根據標記資料更新參數。例如用y∈{-1,+1},當y(w·x+b)≤0時更新,這個條件包含誤判與落在邊界的情況,eta是正的學習率:

    w <- w + eta * y * x
    b <- b + eta * y
    

    正類卻沒得到正分數時,更新把w往x方向推;負類則往反方向推。對有限、線性可分的訓練資料,經典感知器在相應前提下有有限次更新找到分隔面的保證。若資料不可線性分開,增加迭代上限並不能替它變出不存在的直線。

    XOR就是最小的反例:00與11為0,01與10為1。兩個正類點之間的線段與兩個負類點之間的線段,在正方形中心相交,無法用同一條直線嚴格分開。限制在模型能表達的分類邊界,並非你還沒把某個權重調得夠準。

    這也說明了從單個感知器走向多層網路的動機:先把輸入轉成一組新的中間特徵,再用它們作判斷。但單純多接幾個線性層,還不夠。

    多放幾層,為什麼還可能等於一層?

    先看沒有非線性轉換的兩層。本文把每筆樣本放在矩陣的一列,於是計算是X乘W:

    H = X W1 + b1
    O = H W2 + b2
      = X (W1 W2) + (b1 W2 + b2)
    

    定義W'=W1W2、b'=b1W2+b2,最後仍是O=XW'+b'。偏置讓嚴格名稱成為「仿射映射」;慣稱線性層時,通常也包含這個偏置。

    所以,多層圖不自動等於更豐富的函數族。如果中間層很窄,合成矩陣的秩甚至會受到限制。關鍵是加入不能一般性消去的非線性。

    圖2|ReLU(z)=max(0,z),負半邊為0、正半邊斜率為1。來源:Andrej Karpathy/CS231n,MIT,完整原圖轉存PNG。只採用教材自畫曲線,沒有使用旁邊引用他人論文的速度比較圖,也不由此宣稱固定加速倍數。

    加入ReLU之後:

    H = ReLU(X W1 + b1)
    O = H W2 + b2
    

    現在,哪些單元會把負值截成0,會隨輸入而改變。不同輸入區域可以走出不同的有效線性關係;整個網路不再一般性等價於單一仿射映射。這不是說每個權重設定都一定產生複雜邊界:若某些單元一直不啟動,或某個輸入範圍內啟動情況不變,局部仍可能很簡單。

    MLP把一組單元的輸出交給下一組

    圖3|3→4→2的前向全連接網路。每個隱藏單元接收三個輸入,每個輸出接收四個隱藏值;同層節點之間沒有畫連線。來源:Andrej Karpathy/CS231n,MIT,完整原圖轉存PNG。圖沒有列出實際權重,也沒有宣稱訓練完成。

    MLP是Multilayer Perceptron,多層感知器。在這種基本前向網路中,相鄰兩層全連接,中間層使用非線性轉換。它承接了感知器的加權計算想法,但現代MLP通常不把每個隱藏單元都做成硬閾值分類器。

    「隱藏」表示這些中間值由模型算出,不是資料直接提供的目標標籤。訓練時,我們通常不會事先規定第1個單元必須代表年齡、第2個必須代表收入。單元可能共同表達某種關係,不能只看網路圖就替每個圓圈命名。

    這張圖有一個隱藏層及一個輸出層,按不計輸入層的常見慣例,稱為兩層網路。若別人的「三層網路」把輸入層也算進去,可能指的是同一張圖。討論架構時,寫清楚3→4→2與隱藏層數,比只說深幾層更可靠。

    數參數:箭頭算權重,單元另有偏置

    第一層有3×4=12個權重、4個偏置;第二層有4×2=8個權重、2個偏置。總共26個參數。圖沒有把偏置畫成額外節點,並不代表模型沒有偏置。

    全連接層參數數量 = 輸入數 × 輸出數 + 輸出數
    3→4→2: (3×4+4) + (4×2+2) = 26
    

    這裡預設每層使用偏置。若程式設定不使用偏置,必須扣掉對應數量。一次送兩筆或兩百筆資料,都共用同一組參數,不會把26變成52或5200。

    圖4|另一個3→4→4→1網路,多一個隱藏層;權重12+16+4=32、偏置4+4+1=9,共41參數。來源:Andrej Karpathy/CS231n,MIT,完整原圖轉存PNG。所有箭頭向前,這不是循環神經網路。

    增加深度是在增加函數組合的步驟,增加寬度是在某一步提供更多單元。兩者如何影響任務,還取決於非線性、訓練方式、資料與計算限制,不能只靠圓圈多寡選模型。

    讓矩陣維度與網路圖對得起來

    先沿用3→4→2,但一次送兩筆樣本。以每列一筆資料的慣例,維度如下:

    物件形狀意義
    X(2,3)兩筆資料,每筆三欄
    W1、b1(3,4)、(4,)第一層權重與四個偏置
    H(2,4)每筆資料得到四個隱藏值
    W2、b2(4,2)、(2,)第二層權重與兩個偏置
    O(2,2)每筆得到兩個輸出分數

    NumPy的b1是形狀(4,)的一維陣列,透過broadcast加到每一列。這裡的O只是分數,可以為負,尚未經softmax,不要直接稱為機率。

    有些教材以單筆欄向量寫W x,W1形狀是(4,3);這與本文的列式X W並不矛盾。重點是整套符號與軸向一致,不要從兩種慣例各拿半個公式混在一起。

    還要注意:np.array([1,2,3])形狀是(3,),其.T仍是(3,)。它不是一個已經有列軸與欄軸的(3,1)矩陣;要建立後者,需明確reshape(-1,1)。這是形狀約定,不是轉置會神奇補出一個維度。

    圖5|換一個4→5→3的例子。來源:Aston Zhang、Zachary C. Lipton、Mu Li、Alexander J. Smola/Dive into Deep Learning,Multilayer Perceptrons,CC BY-SA 4.0。完整原SVG以3倍解析度轉PNG並加白底,這份圖像改作沿用CC BY-SA 4.0;節點、箭頭與標籤未改。

    這張圖只是用另一組數量練習同一套規則,不是前一張網路更新權重後的樣子。它的參數是:

    4→5:4×5+5 = 25
    5→3:5×3+3 = 18
    總共43個參數
    

    若batch有n筆,X是(n,4),W1是(4,5),H是(n,5),W2是(5,3),O是(n,3)。batch大小改的是中間陣列有幾列,沒有改變每筆樣本使用的函數。

    手算一筆前向計算,再把兩筆一起跑

    接下來用圖3相同的3→4→2結構,手設一組容易算的權重。這些數字是本站的教學構造,不是來源圖上的隱含權重,也沒有經過訓練。完整矩陣在後面的程式中。

    對第一筆x=[1,0,-1],第一層四個加權和依序是[2,-1,-1,0]。經過ReLU後變成h=[2,0,0,0]。第二層用這四個值加權,再加[0.5,-0.5]的偏置,得到[2.5,-0.5]。

    這個過程沒有用到正確答案y,因為前向傳播只是在既有參數下算輸出。要訓練,還得拿輸出與目標算損失、求參數梯度,再更新權重。後面的反向傳播篇會拆解那部分,不能把「我能算出輸出」寫成「模型已學會任務」。

    以下在WSL的Python 3.10.12、NumPy 1.26.3與scikit-learn 1.5.1實際執行。可在隔離環境安裝固定版本:

    numpy==1.26.3
    scipy==1.14.0
    scikit-learn==1.5.1
    joblib==1.4.2
    threadpoolctl==3.5.0
    
    python3 -m venv .venv
    source .venv/bin/activate
    # 將上方固定版本清單另存為requirements.txt
    python -m pip install -r requirements.txt
    # 將下方程式另存為neuron_forward.py
    python neuron_forward.py
    
    import numpy as np
    import sklearn
    from sklearn.linear_model import Perceptron
    
    print(f"numpy={np.__version__} sklearn={sklearn.__version__}")
    # A hand-specified 3->4->2 forward computation, not trained weights.
    X = np.array([[1., 0., -1.], [0., 1., 1.]])
    W1 = np.array([[1., -1., 0., 1.],
                   [0., 1., 1., -1.],
                   [-1., 0., 1., 1.]])
    b1 = np.zeros(4)
    W2 = np.array([[1., 0.], [0., 1.], [1., -1.], [-1., 1.]])
    b2 = np.array([0.5, -0.5])
    H = np.maximum(X @ W1 + b1, 0)
    O = H @ W2 + b2
    print("forward shapes:", X.shape, W1.shape, H.shape, W2.shape, O.shape)
    print("hand_set_hidden=", H.tolist())
    print("hand_set_outputs=", O.tolist())
    print("3_4_2_parameters=", sum(a.size for a in [W1, b1, W2, b2]))
    # If we remove the nonlinear activation, affine layers collapse exactly.
    affine = (X @ W1 + b1) @ W2 + b2
    collapsed = X @ (W1 @ W2) + (b1 @ W2 + b2)
    print("no_activation_equals_collapsed=", np.allclose(affine, collapsed))
    print("relu_output_equals_no_activation_for_this_batch=", np.allclose(O, affine))
    print("vector_shape=", X[0].shape, "transpose_shape=", X[0].T.shape,
          "column_shape=", X[0].reshape(-1, 1).shape)
    # Hand-designed two-ReLU representation of XOR on the four binary inputs.
    B = np.array([[0., 0.], [0., 1.], [1., 0.], [1., 1.]])
    xor_hidden = np.maximum(B @ np.array([[1., -1.], [-1., 1.]]), 0)
    xor_score = xor_hidden.sum(axis=1)
    print("hand_set_XOR_scores=", xor_score.tolist())
    print("hand_set_XOR_predictions=", (xor_score >= 0.5).astype(int).tolist())
    # The only fitted model in this script: a classical perceptron on AND.
    y_and = np.array([0, 0, 0, 1])
    model = Perceptron(max_iter=1000, tol=None, shuffle=False, random_state=0)
    model.fit(B, y_and)
    print("fitted_AND_predictions=", model.predict(B).tolist())
    print("D2L_4_5_3_parameters=", (4 * 5 + 5) + (5 * 3 + 3))
    

    本次實際輸出:

    numpy=1.26.3 sklearn=1.5.1
    forward shapes: (2, 3) (3, 4) (2, 4) (4, 2) (2, 2)
    hand_set_hidden= [[2.0, 0.0, 0.0, 0.0], [0.0, 1.0, 2.0, 0.0]]
    hand_set_outputs= [[2.5, -0.5], [2.5, -1.5]]
    3_4_2_parameters= 26
    no_activation_equals_collapsed= True
    relu_output_equals_no_activation_for_this_batch= False
    vector_shape= (3,) transpose_shape= (3,) column_shape= (3, 1)
    hand_set_XOR_scores= [0.0, 1.0, 1.0, 0.0]
    hand_set_XOR_predictions= [0, 1, 1, 0]
    fitted_AND_predictions= [0, 0, 0, 1]
    D2L_4_5_3_parameters= 43
    

    第一段手設網路的兩筆輸出,分別是[2.5,-0.5]與[2.5,-1.5]。no_activation_equals_collapsed=True驗證移除ReLU後,兩層可以合成單一仿射;後面的False只說這組權重、這兩筆資料的ReLU結果與移除後不同,不是聲稱每筆輸入都一定不同。

    程式最後唯一的fit是在AND四點上訓練經典Perceptron,並回測同四點。這不是MLP訓練,也沒有獨立保留資料。它學到的分隔線不必等於前面手設的w=[1,1]、b=-1.5;能分開這四點的直線有很多條。

    tol=None關閉相應的損失停止條件,本例按設定跑滿1000輪,不表示它需要1000輪才第一次分對。shuffle=False固定了資料順序,因此也不能把這次穩定輸出單獨歸功於random_state。這個小例子只驗證限定條件下的運算與訓練行為,不證明其他資料的泛化能力。

    XOR如何靠兩個ReLU變得可分?

    程式中的另一段沒有呼叫fit,而是手設兩個隱藏特徵:

    h1 = max(x1-x2, 0)
    h2 = max(x2-x1, 0)
    o  = h1+h2
    

    在四個二元輸入上,它們的結果如下:

    x1、x2h1h2oXOR
    000000
    010111
    101011
    110000

    原輸入上無法以單一直線嚴格分開的四點,經過非線性特徵後,讓o≥0.5表示正類就可以。兩個隱藏值的和也等於|x1-x2|,但本文只在這四個二元組合上對照XOR,不把任意實數輸入都叫作邏輯XOR。

    這是一個表達能力的構造:存在一組權重可以做出想要的函數。它沒有證明任意初始化、任意學習率與任意損失都能訓練出這組權重。把「能表示」與「能順利學到」分開,才不會把神經網路的能力講成保證。

    硬閾值本身也不是一般平滑反向傳播的好入口:幾乎處處導數為0,閾值處不連續。經典感知器有自己的更新規則;現代MLP則常用適合梯度訓練的非線性與相應損失,不能把兩種學習程序當成同一件事。

    啟動函數不只有ReLU,輸出函數也要分開看

    圖6|左為sigmoid、右為tanh;手機依相同順序上下排。來源:Andrej Karpathy/CS231n,MIT,兩張完整原曲線編排。曲線靠近端點不表示有限實數輸入會在數學上恰好到達端點。

    sigmoid(z)=1/(1+exp(-z)),數學上把有限實數映到(0,1);tanh映到(-1,1),且tanh(0)=0。它們都是非線性,也都可能在兩端變得很平,影響梯度傳遞。ReLU在正半邊不會像這兩條曲線一樣飽和,但負半邊為0,也有單元長期不啟動等問題。

    本篇先用它們辨認「中間轉換改變了什麼」,下一篇再詳談導數與損失搭配。不要把某個歷史範例的選擇升成所有任務的硬規則,也不要把隱藏層的啟動函數與輸出層的任務介面混在一起。

    例如回歸可以輸出任意實數,互斥多類分類可以先輸出logits再配softmax與交叉熵。這些選擇要與目標的意義及實作的損失函數一致。只是把數字壓進0到1,並不自動等於已校準的機率預測。

    圓圈更多,能畫的邊界更多,但哪個比較值得用?

    圖7|同一組二維toy分類資料,分別用3、6、20個隱藏單元得到不同決策區域;圓點是帶類別的資料,底色是模型分類。來源:Andrej Karpathy/CS231n,MIT。桌機完整原圖,手機依原三面板直排;未改資料、標題或區域。這是來源模型結果,不是本站程式輸出;原圖沒有保留集分數。

    更多隱藏單元讓模型有機會表達更細的分類區域,卻沒有單憑這張圖決定哪個對新資料比較好。二十個單元的圖貼合更多訓練點,也可能追著雜訊;三個單元的圖較簡單,也可能漏掉真實關係。要比較,得把訓練、驗證、正則化與測試安排好。

    你可能聽過「一個隱藏層就能逼近任何函數」。常見的通用逼近結果,在適當非線性、足夠寬度等條件下,討論對緊緻定義域上連續目標函數的任意精度逼近。它是一種存在性結果,不承諾有限資料下的訓練程序能找到該權重,也不保證需要的寬度、計算量或泛化誤差符合你的需求。

    因此,選MLP時仍要問具體問題:輸入如何縮放?基準模型表現如何?隱藏寬度、深度與正則化怎麼比較?訓練是否真的降低了適當的損失?保留資料的表現是否穩定?不要把「神經網路」四個字當成略過這些問題的理由。

    讀一張網路圖時,可以按三步檢查。先標出每層輸入輸出形狀與參數數量,再追蹤加權和在哪裡經過非線性,最後確認輸出是分數、數值還是經特定轉換的機率估計。這三步能讓圓圈與箭頭落到可計算的函數上;至於模型如何學習,就接著看損失與反向傳播。

    延伸閱讀與素材出處

    圖片授權聲明

    圖1–4、圖6–7及封面採Andrej Karpathy/CS231n教材自有圖,依MIT使用;原圖轉PNG、兩曲線左右/上下編排、寬度圖手機三面板重排,以及封面完整等比縮放加白邊,皆沒有重畫資料或冒稱原作者背書。下列保留完整實際MIT聲明。

    圖5採Dive into Deep Learning的MLP原SVG,完整轉PNG、加白底的圖像改作仍依CC BY-SA 4.0提供,署名上述四位作者並連回原章。原教材與這份圖像改作的權利標示可供後續使用者辨認;本文獨立撰寫的教學與程式不是把來源章節換句話說。CC授權完整原件另存本篇D2L-CC-BY-SA-4.0.txt。

    The MIT License (MIT)
    
    Copyright (c) 2015 Andrej Karpathy
    
    Permission is hereby granted, free of charge, to any person obtaining a copy
    of this software and associated documentation files (the "Software"), to deal
    in the Software without restriction, including without limitation the rights
    to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
    copies of the Software, and to permit persons to whom the Software is
    furnished to do so, subject to the following conditions:
    
    The above copyright notice and this permission notice shall be included in all
    copies or substantial portions of the Software.
    
    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
    SOFTWARE.
    

    留言0

    登入後就能留言。

    登入
    sunny 桑尼
    sunny 桑尼

    AI 資料科學 · 產品經理 · 蒙奇 D 桑尼

    專注於 AI 工具應用與資料科學教育,致力於讓每個人都能用 AI 提升工作效率。曾任職於科技業,現為全職教育創作者。

    讀更多文章 →想自己動手試?領免費 Starter Kit →