1. 程式人生 > >【Numpy基礎】矩陣陣列相乘與神經網路的實現

【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])

一維陣列在前,會被當做行向量使用。

同時,這裡很值得注意的是,用多維陣列實現神經網路時,開頭輸入的是一個一維陣列,進行一次計算後輸出結果也是一個一維陣列,只不過這個一維陣列的大小和神經元的數量是一樣的。那麼,再往後推演,道理是一樣的,相當於同樣的過程一直向後。

即,再加一層,也相當於一個一維陣列和它進行矩陣計算。

三層神經網路的實現

在這裡插入圖片描述

通過這個案例可以看出,神經網路真的在於細節,從最小的地方入手,理解透徹以後,再擴充套件,要容易理解得多。

符號的設定
在這裡插入圖片描述

符號能幫助我們將概念表達清楚,上面這個,是站在當前神經元的立場來看,所以下標中第一個數字表示當前的神經元排序,第二個數字才是前一層的神經元排序,上標表示層數。

另外,每一層都會有一個偏置神經元,具體設定如下:

在這裡插入圖片描述

b 1 ( 1 ) b_1^{(1)}

的下標就一個值,因為每一層只有一個偏置。

a 1 ( 1 ) a_1^{(1)} 的表達如下

a 1 ( 1 ) = w 11 ( 1 ) x 1 + w 12 ( 1 ) x 2 + b 1 ( 1 ) a_1^{(1)} = w_{11}^{(1)}x_1 + w_{12}^{(1)}x_2 + b_1^{(1)}

這只是表達了一個神經元的計算方式,而從更大一點的視角來看——矩陣的角度:

A ( 1 ) = X W ( 1 ) + B ( 1 ) A^{(1)} = XW^{(1)} + B^{(1)}

其中各個變數的表達如下:

在這裡插入圖片描述

看起來非常複雜,其實就是前面的延伸,且無任何彎道。

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,)

總結:一維陣列和矩陣點乘,陣列在前則視為行向量,即形狀視為 1 × m 1\times m ;反過來,陣列在後,則形狀視為 m × 1 m \times 1 ,其中 m m 是一維陣列元素個數。

我們輸出一維陣列的形狀時,顯示都是 ( m , ) (m,) ,往往會有一種感覺是,如果將其視作矩陣,則是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個,現在我們再定義第一層向第二層傳遞時的引數,第一層的輸出可以認為是 1 × 3 1\times 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的理論和實現》