【Numpy基礎】矩陣陣列相乘與神經網路的實現
# 矩陣乘以陣列
A = np.array([[1,2],[3,4],[5,6]])
A.shape # (3, 2)
B = np.array([7,8])
B.shape # (2,)
A.dot(B) # array([23, 53, 83])
B這個一維陣列會被當成列向量使用。
反過來,一維陣列在前,矩陣在後的情況如下:
X = np.array([1,2])
X.shape # (2,)
W = np.array([[1,3,5],[2,4,6]])
W.shape #(2,3)
np.dot(X,W) # array([ 5, 11, 17])
一維陣列在前,會被當做行向量使用。
同時,這裡很值得注意的是,用多維陣列實現神經網路時,開頭輸入的是一個一維陣列,進行一次計算後輸出結果也是一個一維陣列,只不過這個一維陣列的大小和神經元的數量是一樣的。那麼,再往後推演,道理是一樣的,相當於同樣的過程一直向後。
即,再加一層,也相當於一個一維陣列和它進行矩陣計算。
三層神經網路的實現
通過這個案例可以看出,神經網路真的在於細節,從最小的地方入手,理解透徹以後,再擴充套件,要容易理解得多。
符號的設定
符號能幫助我們將概念表達清楚,上面這個,是站在當前神經元的立場來看,所以下標中第一個數字表示當前的神經元排序,第二個數字才是前一層的神經元排序,上標表示層數。
另外,每一層都會有一個偏置神經元,具體設定如下:
的下標就一個值,因為每一層只有一個偏置。
的表達如下
這只是表達了一個神經元的計算方式,而從更大一點的視角來看——矩陣的角度:
其中各個變數的表達如下:
看起來非常複雜,其實就是前面的延伸,且無任何彎道。
X = np.array([1.0, 0.5])
W1 = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]])
B1 = np.array([0.1, 0.2, 0.3])
print(W1.shape) # (2, 3)
print(X.shape) # (2,)
print(B1.shape) # (3,)
A1 = np.dot(X, W1) + B1
A1.shape # (3,)
總結:一維陣列和矩陣點乘,陣列在前則視為行向量,即形狀視為 ;反過來,陣列在後,則形狀視為 ,其中 是一維陣列元素個數。
我們輸出一維陣列的形狀時,顯示都是 ,往往會有一種感覺是,如果將其視作矩陣,則是m行,其實不一定,這是錯誤的看法。
上面的程式碼只完成了線性計算,再加上啟用函式即可變成非線性的輸出:
# 第一層到第二層的傳遞
Z1 = sigmoid(A1)
print(A1) # [0.3 0.7 1.1]
print(Z1) # [0.57444252 0.66818777 0.75026011]
現在再向前推進一步:
W2 = np.array([[0.1, 0.4], [0.2, 0.5], [0.3, 0.6]])
B2 = np.array([0.1, 0.2])
print(Z1.shape)
print(W2.shape)
print(B2.shape)
A2 = np.dot(Z1, W2) + B2
Z2 = sigmoid(A2)
從第一層輸出的結果是一維陣列,元素有3個,現在我們再定義第一層向第二層傳遞時的引數,第一層的輸出可以認為是
的行向量,則W
的形狀是3行2列,即當前層的神經元個數列。
最後再定義第二層向輸出層傳遞:
# 輸出層的啟用函式:sigma
def identity_function(x):
return x
W3 = np.array([[0.1, 0.3],[0.2, 0.4]])
B3 = np.array([0.1, 0.2])
A3 = np.dot(Z2, W3) + B3
Y = identity_function(A3)
輸出層啟用函式定義
這裡簡單的梳理一下,輸出層的啟用函式,需要根據求解問題的性質來決定。
一般迴歸問題用的是恆等函式,二元分類問題用的是sigmoid函式
,而多元分類問題則用softmax
函式。
綜合
# 按照慣例的實現
# 定義網路引數
def init_network():
network = {}
network['W1'] = np.array([[0.1, 0.3, 0.5], [0.2, 0.4, 0.6]]) # 2x3
network['b1'] = [0.1, 0.2, 0.3] # 3,
network['W2'] = np.array([[0.1, 0.4], [0.2, 0.5], [0.3, 0.6]]) # 3x2
network['b2'] = [0.1, 0.2] # 2,
network['W3'] = np.array([[0.1, 0.3], [0.2, 0.4]]) # 2x2
network['b3'] = [0.1, 0.2] # 2,
return network
def forward(network, x):
W1,W2,W3 = network['W1'], network['W2'], network['W3']
b1, b2, b3 = network['b1'],network['b2'],network['b3']
a1 = np.dot(x,W1) + b1 # 線性
z1 = sigmoid(a1)
a2 = np.dot(z1, W2) + b2
z2 = sigmoid(a2)
a3 = np.dot(z2, W3) + b3
y = identity_function(a3)
return y # 最後的輸出
# 實際呼叫
network = init_network()
x = np.array([1.0, 0.5])
y = forward(network, x)
print(y) # [0.31682708 0.69627909]
這份綜合的程式碼,將網路的設計和使用講的非常通透,值得細細品味。其中,init_work()
函式用來對權重和偏置的初始化,儲存在network
字典中,forward()
函式則封裝了將輸入資料轉化為輸出訊號的處理過程。且這裡的forward
表徵的是資料的前向傳遞,後面訓練會用到的後向backward
。
總之,藉助於Numpy,我們可以快速實現一個神經網路。
END.
完全參考:
《深度學習入門:基於Python的理論和實現》