【零基礎】淺層神經網絡解析


回顧:

【零基礎】AI神經元解析(含實例代碼)

 

一、序言

  前兩天寫了關於單神經元的解析,這里再接再厲繼續淺層神經網絡的解析。淺層神經網絡即是“層次較少”的神經網絡,雖然層次少但其性能相對單神經元強大了不只一點。

  注:本文內容主要是對“床長”的系列教程進行總結,強烈推薦“床長”的人工智能系列教程(https://www.captainbed.net/)

二、淺層神經網絡的構成

  回顧前面單神經元的構成,我們知道神經元包含4個關鍵函數:

  1)傳播函數,由輸入x、偏置w、閾值b計算出a

  2)激活函數,將a映射到0~1之間的結果y,可理解為(是、否)的概率

  3)反向傳播函數,通過y、答案label計算出dw、db(用以更新w和b)

  4)損失函數,計算y與label間的誤差

 

   直觀上我們知道淺層神經網絡自然是由數個神經元構成的,對於一個簡單的兩層神經網絡其結構如下圖所示:

 

  它包含了輸入層(即X)、隱藏層、輸出層,其中輸入層不是神經元,所以說這是一個兩層的神經網絡。

  在實際實現時我們並不是挨個計算每一個經元的結果,最后再計算輸出的結果。我們是一次計算出一層所有的神經元結果,再將每一層的結果作為輸入計算下一層。而且第一層的傳播函數並不與第二層傳播函數分離,神經網絡的傳播函數包含了所有層的傳播計算,反向傳播函數也是包含了所有層的反向計算,所以從單神經元到神經網絡代碼的結構其實變化不大。

  下面我們直接上代碼來解析,如果你看明白了前面單神經元的解析,那這里是非常好理解的。

  (文末附完整代碼下載方式)

三、准備工作

  1)要處理的問題

  之前我們用單神經元要處理的問題是“從圖片中識別出數字9”,即使使用單神經元也有93%的正確率,所以這里我們要增加問題的難度。將問題改為“從圖片中識別出奇數”,代碼上只需要很小的修改,將下面代碼

  #將label不是9的數據全部轉為0,將9轉為1
  train_label = np.where(train_label==9,1,0)
  test_label = np.where(test_label==9,1,0)

  修改為:

  #找出圖片中的奇數將label中13579置為1、02468置為0
  train_label = np.where((train_label%2)!=0,1,0)
  test_label = np.where((test_label%2)!=0,1,0)

  使用之前的單神經元代碼執行后的結果為下圖所示,可以看到預測效果大幅下降。

  2)要使用的網絡結構

  首先要明確神經網絡的層數,這里我們是一個簡單的網絡,所以只需要兩層。其次是每一層神經元的個數,這里我們網絡第一層設置4個神經元,第二層是輸出層所以只要一個神經元。輸入層跟以前一樣,是784個輸入(一張28x28圖片的所有像素),網絡結構如下圖:

四、隨機初始化參數

#初始化參數w和b
def initialize_parameters(input_num, hide_num, out_num):
  #input_num 輸入層神經元個數
  #hide_num 隱藏層神經元個數
  #out_num 輸出層神經元個數
  np.random.seed(2)
  #隨機初始化第一層相關參數w、b
  W1 = np.random.rand(hide_num, input_num) * 0.01
  b1 = np.zeros(shape=(hide_num, 1))

  #隨機初始化第二層相關參數w、b
  W2 = np.random.randn(out_num, hide_num) * 0.01
  b2 = np.zeros(shape=(out_num, 1))

  return W1,b1,W2,b2

   淺層神經網絡與單神經元在初始化參數w、b的區別有以下幾點:

  1)每一層神經元使用不同的w和b,所以有w1、w2、b1、b2,如果是三層網絡則相應會有w3、b3.

  2)不同層神經元的w、b的數據形狀是不一樣的。比如我們輸入的圖片有784個像素,且第二層有4個神經元,所以第一層w的形狀是(4,784)、b的形狀是(4,1)。第二層神經元只有一個,來自第一層的輸入只有4個參數,所以第二層w的形狀是(1,4)、b的形狀是(1,1)。

  3)w1和w2是隨機生成的,之前單神經元結構中,w初始值為0,如果這里還使用全0的話,最終結果可能與單神經元一樣,而且還乘以0.01確保初始值足夠小。

五、傳播函數

#向前傳播函數
def forward(img, W1,b1,W2,b2):

  #第一層
  A1 = np.dot(W1, img) + b1
  Y1 = np.tanh(A1)#第一層和第二層使用不同的激活函數

  #第二層
  A2 = np.dot(W2, Y1) + b2
  Y2 = sigmoid(A2)
  return Y1,Y2

  傳播函數與單神經元結構類似,不過需要注意的是,這里第一層使用的激活函數為tanh、第二層依舊使用的是sigmoid。他們的區別在於,sigmoid是將輸出映射到0~1而tanh是將輸出映射到-1~1。具體原因和區別以后再說(因為我也沒搞清楚呢),這里只要知道有區別就行了。

 

 六、反向傳播函數

#反向傳播函數
def backward(img, label, W1,b1,W2,b2, Y1,Y2):
  m = img.shape[1]
  #第二層
  dZ2 = Y2 - label
  dW2 = np.dot(dZ2, Y1.T)/m
  db2 = np.sum(dZ2, axis=1, keepdims=True)/m
  #第一層
  dZ1 = np.multiply(np.dot(W2.T, dZ2), 1-np.power(Y1, 2))
  dW1 = np.dot(dZ1, img.T)/m
  db1 = np.sum(dZ1, axis=1, keepdims=True)/m

  return dW1,db1,dW2,db2

  與傳播函數方向相反,這里是先計算第二層的反向傳播再計算第一層的反向傳播。需要注意的是,由於第一層使用的激活函數是tanh,所以其反向計算公式與第一層的公式不一樣,因為使用不同激活函數其反向求導也就不一樣了。

  另外np.sum中的兩個參數axis=1、keepdims=True是為了確保db1的數據形式為(1,4),其中axis=1的意思是按行求和、keepdims=True的意思是保留矩陣的形狀,不同參數的np.sum計算示意如下:

  np.sum(dZ1)/m                 0.0016664927232987162

  np.sum(dZ1, axis=1)/m            [0.00026393,0.00077922,0.0003274 ,0.00029595]

  np.sum(dZ1, axis=1, keepdims=True)/m   

                        [[0.00026393]
                        [0.00077922]
                        [0.0003274 ]
                        [0.00029595]]

七、梯度下降

#梯度下降 更新w、b參數
def update(W1,b1,W2,b2, dW1,db1,dW2,db2, learning_rate=1.2):
  W1 = W1 - learning_rate*dW1
  b1 = b1 - learning_rate*db1
  W2 = W2 - learning_rate*dW2
  b2 = b2 - learning_rate*db2

  return W1,b1,W2,b2

  梯度下降與單神經元的情況差不多。

八、損失函數

#損失函數
def costCal(Y2, label):
  m = label.shape[1]
  logprobs = np.multiply(np.log(Y2), label) + np.multiply((1-label), np.log(1-Y2))
  cost = -np.sum(logprobs)/m
  return cost

  損失函數與單神經元的情況也差不多,需要注意的是np.multiply就是將兩個矩陣做對應元素的乘。

九、預測函數

#預測函數
def predict(W1,b1,W2,b2, img):
  Y1,Y2 = forward(img, W1,b1,W2,b2)
  predictions = np.round(Y2)#對結果四舍五入
  return predictions

  與單神經元的情況類似,預測函數其實就是做一次“向前傳播”。

十、訓練模型並預測

#訓練模型
def model(img, label, hide_num, num_iterations = 1000, learning_rate=0.1, print_cost = False):
  np.random.seed(3)
  input_num = img.shape[0]
  out_num = label.shape[0]

  #初始化參數
  W1,b1,W2,b2 = initialize_parameters(input_num,hide_num,out_num)
  #循環若干次完成訓練
  for i in range(0, num_iterations):
    #向前傳播
    Y1,Y2 = forward(img, W1,b1,W2,b2)
    #計算本次成本
    cost = costCal(Y2, label)
    #反向傳播,得到梯度
    dW1,db1,dW2,db2 = backward(img, label, W1,b1,W2,b2, Y1,Y2)
    #參數優化
    W1,b1,W2,b2 = update(W1,b1,W2,b2, dW1,db1,dW2,db2, learning_rate)
    # 將本次訓練的成本打印出來
    if print_cost and i % 100 == 0:
    print ("在訓練%i次后,成本是: %f" % (i, cost))

  return W1,b1,W2,b2

#調用訓練模型

W1,b1,W2,b2 = model(train_img, train_label, 4, num_iterations=2000, learning_rate=1, print_cost=True)

#調用預測函數

predictions = predict(W1,b1,W2,b2, test_img)
print ('預測准確率是: %d' % float((np.dot(test_label, predictions.T) + np.dot(1 - test_label, 1 - predictions.T)) / float(test_label.size) * 100) + '%')

  需要注意的是這里的learning_rate=1,而單神經元時的learning_rate為0.005。

十一、總結回顧

  通過實現一個簡單的二層神經網絡我們發現,其實代碼並沒有修改很多,整體的結構也變化不大,其中最主要的變化在於第一層使用的激活函數變為tanh,由此導致反向傳播的計算也有了較大的變化。

  運行后我們可以發現,預測的准確度較單神經元有了較大幅度的提升:

在訓練0次后,成本是: 0.693817
在訓練100次后,成本是: 0.251725
在訓練200次后,成本是: 0.176756
在訓練300次后,成本是: 0.110538
在訓練400次后,成本是: 0.372297
在訓練500次后,成本是: 0.128188
在訓練600次后,成本是: 0.091792
在訓練700次后,成本是: 0.075769
在訓練800次后,成本是: 0.064764
在訓練900次后,成本是: 0.055826
在訓練1000次后,成本是: 0.132452
在訓練1100次后,成本是: 0.102556
在訓練1200次后,成本是: 0.131425
在訓練1300次后,成本是: 0.086445
在訓練1400次后,成本是: 0.178343
在訓練1500次后,成本是: 0.077496
在訓練1600次后,成本是: 0.093846
在訓練1700次后,成本是: 0.071567
在訓練1800次后,成本是: 0.070109
在訓練1900次后,成本是: 0.060202
預測准確率是: 94%

  關注公眾號“零基礎愛學習”回復"AI5"可獲得完整代碼。后面我們還會繼續更新“如何構建深度神經網絡”,以及對目前還未明晰的問題解析。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM