多層感知器(二)


雖然涉及一些數學運算,不過,你應該試著去理解過感知器的原理,如果你暫時沒那時間,或者是曾經試著從公式去理解而不得其門而入,以下來提供另一種理解方式。

在〈多層感知器(一)〉中使用了三個感知器,如果將三個感知器的 coef_intercept_ 顯示出來會得到:

print(p1.coef_, p1.intercept_)  # [[-60. 107.]] [0.]
print(p2.coef_, p2.intercept_)  # [[ 321. -800.]] [-1.]
print(p3.coef_, p3.intercept_)  # [[2. 2.]] [-1.]

在〈分類與感知器〉中提過,coef_ 是感知器的權重向量(在直線的情況下,也可以理為直線的法向量),而 intercept_ 其實是感知器的偏差(bias)(在直線的情況下,也可以理為直線的截距)。

對於一個感知器,它的輸出其實是透過 W . X + b,要輸出 1 或 0,W 表示權重向量,X 是變數值的向量,b 是偏差值,如果 W . X + b > 0 輸出 1,W . X + b <= 0 輸出 0。

既然〈多層感知器(一)〉中已經訓練好三個感知器,那麼就直接用它的權重與偏差吧!對於 p1p2 感知器可以寫成:

import numpy as np
import matplotlib.pyplot as plt

def a(x):
    return 1 if x > 0 else 0

def p1_predict(height, waist):
    w1 = np.array([-60, 107])
    b1 = 0
    return a(np.dot(w1, [height, waist]) + b1)

def p2_predict(height, waist):
    w2 = np.array([321, -800])
    b2 = -1
    return a(np.dot(w2, [height, waist]) + b2)

height = 165
waist = 85

print(p1_predict(height, waist)) # 顯示 0
print(p2_predict(height, waist)) # 顯示 0

現在 p1p2 各輸出為 0,接著將輸出接至 p3

def p3_predict(p1_o, p2_o):
    w3 = np.array([2, 2])
    b3 = -1
    return a(np.dot(w3, [p1_o, p2_o]) + b3)

# 顯示 0
print(
    p3_predict(
        p1_predict(height, waist), 
        p2_predict(height, waist)
    )
)

你可以試試不同的 heightwaist,看看 p3_predict 最後輸出結果是否符合分類;其實以上的過程,可以透過矩陣運算組合在一起:

import numpy as np
import matplotlib.pyplot as plt

def a(x):
    return np.where(x > 0, 1, 0)

def p1p2_predict(height, waist):
    # 建立矩陣
    w = np.array([
        [-60, 107],
        [321, -800]
    ]) 
    b = np.array([0, -1])
    # @ 是矩陣相乘
    return a(w @ [height, waist] + b) 

def p3_predict(p1p2_o):
    w3 = np.array([2, 2])
    b3 = -1
    return a(np.dot(w3, p1p2_o) + b3)

height = 165
waist = 85

print(
    p3_predict(
        p1p2_predict(height, waist)
    )
)

為了將一般化,來為這些感知器、權重、偏差等取名稱好了,為此編好號碼:

多層感知器(二)

每一層的感知器,是由上往下編號,對於第 1 層的 1 號感知器,權重為 [-60, 107],偏差為 0,給予名稱 [w111, w112],以及 b11,也就是說上標表示哪一層,下標表示哪一號感知器接收上一層哪一個輸入,例如 1 層 1 號感知器需要就是:

多層感知器(二)

類似地,1 層 2 號感知器需要就是:

多層感知器(二)

那麼方才的矩陣運算,就可以表示為 W(1) @ x + b(1)

多層感知器(二)

方才程式運算時的 a 函式,將運算結果轉為 0 或 1,稱為激勵函式,之所以這麼稱呼,是因為它控制了什麼樣的值,可以輸出 1,進一步激發下一個感知器,為什麼需要這個激勵函式呢?一個感知器的輸出是 W . X + b,可以處理線性的問題,將多個感知器的輸出直接作為其他感知器的輸入,最後組合出來的輸出,其實也會是 W . X + b 這樣的形式,還是只能處理線性的問題。

多個感知器的輸出若有了激勵函式的轉換,再作為其他感知器的輸入,組合出來的形式就有可能是 W . X + b 以外的形式,根據感知器的數量、激勵函式的選擇以及層數的不同,組合後的形式會是千變萬化,也就是說,你的分類線不再是直線,而會是各種可能的形狀,可以用來處理非線性的分類問題了。

從另一個角度來看,激勵函式是在改變特徵空間,將空間中的一組特徵映射至另一個空間成為另一組特徵,然後在該空間中尋找映射後特徵的線性邊界。

聽來很像是〈多元線性迴歸(二)〉中談過的 PolynomialFeatures,轉換後的特徵值可以更好地被線性模型 LinearRegression 擬合?某些程度上是類似的概念,只不過各種感知器的數量、激勵函式的選擇以及層數的不同下,各種特徵空間轉換的組合更為複雜,更加難以想像。

這就是為什麼,這邊要透過三個感知器來組合,因為比較容易理解激勵函式的作用,簡單來說,原本一個感知器只能有一條直線進行分類,透過激勵函式的轉換以及感知器的組合,就像是結合了兩條分類直線,來達到非線性分類的需求。

將激勵函式組合上去,就成為 a(W(1) * x + b(1)),就目前來說,結果會是個 1x2 矩陣,會是 2 層 1 號感知器的輸入,而對於 2 層 1 號感知器需要的是:

多層感知器(二)

根據上圖的標示,最後的輸出結果會是 a(W(2) @ a(W(1) @ x + b(1)) + b(2)),可以看到,若有更多層、更多感知器,這樣的表示方式下,就是矩陣大小以及相乘的層次問題。

另一方面,透過一些數學推導過程,權重矩陣與偏差矩陣的實際值,可以從資料中學習得到,有興趣瞭解推導過程,就自行找相關的理論書或文件…XD