回顧:html
1、序言dom
前兩天寫了關於單神經元的解析,這裏再接再礪繼續淺層神經網絡的解析。淺層神經網絡便是「層次較少」的神經網絡,雖然層次少但其性能相對單神經元強大了不僅一點。ide
注:本文內容主要是對「牀長」的系列教程進行總結,強烈推薦「牀長」的人工智能系列教程(https://www.captainbed.net/)函數
2、淺層神經網絡的構成post
回顧前面單神經元的構成,咱們知道神經元包含4個關鍵函數:性能
1)傳播函數,由輸入x、偏置w、閾值b計算出a學習
2)激活函數,將a映射到0~1之間的結果y,可理解爲(是、否)的機率優化
3)反向傳播函數,經過y、答案label計算出dw、db(用以更新w和b)人工智能
4)損失函數,計算y與label間的偏差
直觀上咱們知道淺層神經網絡天然是由數個神經元構成的,對於一個簡單的兩層神經網絡其結構以下圖所示:
它包含了輸入層(即X)、隱藏層、輸出層,其中輸入層不是神經元,因此說這是一個兩層的神經網絡。
在實際實現時咱們並非挨個計算每個經元的結果,最後再計算輸出的結果。咱們是一次計算出一層全部的神經元結果,再將每一層的結果做爲輸入計算下一層。並且第一層的傳播函數並不與第二層傳播函數分離,神經網絡的傳播函數包含了全部層的傳播計算,反向傳播函數也是包含了全部層的反向計算,因此從單神經元到神經網絡代碼的結構其實變化不大。
下面咱們直接上代碼來解析,若是你看明白了前面單神經元的解析,那這裏是很是好理解的。
(文末附完整代碼下載方式)
3、準備工做
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置爲一、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圖片的全部像素),網絡結構以下圖:
4、隨機初始化參數
#初始化參數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,因此有w一、w二、b一、b2,若是是三層網絡則相應會有w三、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確保初始值足夠小。
5、傳播函數
#向前傳播函數
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。具體緣由和區別之後再說(由於我也沒搞清楚呢),這裏只要知道有區別就好了。
6、反向傳播函數
#反向傳播函數
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=一、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]]
7、梯度降低
#梯度降低 更新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
梯度降低與單神經元的狀況差很少。
8、損失函數
#損失函數
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就是將兩個矩陣作對應元素的乘。
9、預測函數
#預測函數
def predict(W1,b1,W2,b2, img):
Y1,Y2 = forward(img, W1,b1,W2,b2)
predictions = np.round(Y2)#對結果四捨五入
return predictions
與單神經元的狀況相似,預測函數其實就是作一次「向前傳播」。
10、訓練模型並預測
#訓練模型
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。
11、總結回顧
經過實現一個簡單的二層神經網絡咱們發現,其實代碼並無修改不少,總體的結構也變化不大,其中最主要的變化在於第一層使用的激活函數變爲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"可得到完整代碼。後面咱們還會繼續更新「如何構建深度神經網絡」,以及對目前還未明晰的問題解析。