神經網絡代碼實現:
- 讀取數據:
因爲要求y爲下圖的形式,所以要對y進行處理
import numpy as np
import pandas as pd
from scipy.io import loadmat
from sklearn.preprocessing import OneHotEncoder
path ='E:/Data/Ng/Coursera-ML-AndrewNg-Notes-master/code/ex4-NN back propagation/ex4data1.mat'
data = loadmat(path)
X = data['X']
y = data['y']
encoder = OneHotEncoder(sparse=False)
y_encoder = encoder.fit_transform(y) #可以直接利用OneHotEncoder進行轉換,比如說y當中一共有10個不同的元素,5000列,那麼轉化以後每一個數字都單獨爲1列,比如1就是[1,0,0,...,0],最後形狀爲(5000,10)
- 定義一個sigmoid函數和前向傳播函數
根據下列公式可進行編寫
def sigmoid(z):
return 1/(1+np.exp(-z))
def forward(X,y_encoder,theta1,theta2):
"""計算前向傳播的值
Params:
X:特徵值(5000*400)
y_encoder:輸出變量(5000*10)
theta1;theta2:初始化的值
return"""
m = X.shape[0] #獲取樣本總數,用於插入列的時候使用
a1 = np.insert(X,0,values=np.ones(m),axis=1) #插入一個x0=1的列,
z2 = a1 * theta1.T
#因爲 上圖中可以看到當輸入的樣本數爲1的時候z2= θ1*a1,即θ的每一行都要和樣本值相乘。但是這裏我們輸入的樣本值爲5000,θ1爲(25,401)的矩陣,也就是說我們的5000個樣本都要單獨和25行θ值對應相乘,所以這裏的θ要進行轉置。
a2 = np.insert(sigmoid(z2),0,values=np.ones(m),axis=1)
z3 = a2 *theta2.T
h = sigmoid(z3)
return a1,z2,a2,z3,h
- 根據公式編寫代價函數:
def cost(params,in_size,h_size,label_nums,X,y_encoder,l):
"""代價函數的前面部分
Params:
params:存放所有初始化theta1,theta2值的ndarray
in_size:特徵值的數量
h_szie:隱藏層的項數
label_nums:總分類數
X:特徵值數據
y_encoder:轉換後的y
l:lambda
return
J:代價函數"""
m = X.shape[0] #獲取樣本總數
X = np.mat(X)
y_encoder = np.mat(y_encoder)
theta1 = np.mat(np.reshape(params[:h_size*(in_size+1)],(h_size,in_size+1))) #(25,401)
theta2 = np.mat(np.reshape(params[h_size*(in_size+1):],(label_nums,h_size+1))) #(10,26)
a1,z2,a2,z3,h = forward(X,y_encoder,theta1,theta2) #獲取h
J= 0 #初始化代價函數
for i in range(m): #一個樣本一個樣本的計算代價函數
first_term = np.multiply(-y[i,:],np.log(h[i,:]))
second_term = np.multiply((1-y)[i,:],np.log(1-h[i,:]))
#注意神經網絡的代價函數要求兩次和,一次是m一次是k。這個循環是用來求m的,那麼在循環裏面要將k求好
J += np.sum(first_term-second_term) #累加求m,sum是在求k
J=J/m
J = J + (l/(2*m)*(np.sum(np.power(theta1[:,1:],2))+np.sum(np.power(theta2[:,1:],2)))
#加上正則優化項
return J
-
後向傳播的偏導數項
後向傳播比較難以理解,代碼中使用的是一個一個樣本計算的方法。
求代價函數的偏導數。先假設對一個樣本一個權重求偏導:
假設神經網絡有四層,中間兩層爲隱藏層
一般化可以這樣表示:
矢量化,因爲a此時不再只是一個數,而是所有樣本的數,所以要將其轉置。
再求前一層的偏導
代碼實現:
def sigmoid_gradient(z): #g的偏導數項
return np.multiply(sigmoid(z),(1-sigmoid(z)))
def bp(params,in_size,h_size,label_nums,X,y_encoder,l):
"""計算所有的代價函數對於θ的偏導數項
Params:
params:存放所有初始化theta1,theta2值的ndarray
in_size:特徵值的數量
h_szie:隱藏層的項數
label_nums:總分類數
X:特徵值數據
y_encoder:轉換後的y
l:lambda
return
J:代價函數
grad:梯度值""""""
m = X.shape[0]
X = np.mat(X)
y_encoder = np.mat(y_encoder)
theta1 = np.mat(np.reshape(params[:(in_size+1)*h_size],(h_size,in_size+1))) #(25,401)
theta2 = np.mat(np.reshape(params[(in_size+1)*h_size:],(label_nums,h_size+1)))#(10,26)
a1,z2,a2,z3,h=forward(X,y_encoder,theta1,theta2)
J=0
for i in range(m):
first = np.multiply(-y_encoder[i,:],np.log(h[i,:]))
second = np.multiply((1-y_encoder)[i,:],np.log(1-h[i,:]))
J += np.sum(first-second)
J = J/m
reg_term = (np.sum(np.power(theta1[:,1:],2)) + np.sum(np.power(theta2[:,1:],2)))*(float(l)/(2*m))
J += reg_term
delta2 = np.zeros(theta2.shape) #(25,401) #初始化梯度2
delta1 = np.zeros(theta1.shape) #(10,26) #初始化梯度1
for t in range(m): #一個樣本一個樣本的進行計算
a1t = a1[t,:] #(1,401) #拿出第一個樣本的值
z2t = z2[t,:] #(1,25)
a2t = a2[t,:] #(1,26)
z3t = z3[t,:] #(1,10)
ht = h[t,:] #(1,10)
yt = y_encoder[t,:] #(1,10)
d3 = ht - yt #(1,10) #計算δ(3)
z2t = np.insert(z2t,0,values=np.ones(1))
d2 = np.multiply((theta2.T * d3.T).T,sigmoid_gradient(z2t) )
#注意這裏是用的點乘,所以用multiply
delta1 = delta1 + d2[:,1:].T*a1t #偏導向1,因爲第一列不用計算
delta2 = delta2 + d3.T*a2t #偏導項2
delta1 = delta1/m
delta2 = delta2/m
delta1[:,1:] = delta1[:,1:] + (theta1[:,1:]*l)/m #正則優化
delta2[:,1:] = delta2[:,1:] + (theta2[:,1:]*l)/m
grad = np.concatenate((np.ravel(delta1),np.ravel(delta2)))
return J,grad
if __name__ == '__main__':
in_size = 400
h_size=25
label_nums=10
l=1
params = (np.random.random(size = h_size*(in_size+1)+label_nums*(h_size+1))-0.5)*0.25
J,grad = bp(params,in_size,h_size,label_nums,X,y_encoder,l)
print(grad.shape)
結果如下:
- 利用庫去擬合最優參數
from scipy.optimize import minimize
fmin = minimize(fun=bp,x0=params,args=(in_size,h_size,label_nums,X,y_encoder,l),method='TNC',jac=True,options={'maxiter':250})
fmin
- 通過h值來檢測準確度
X = np.mat(X)
theta1 = np.mat(np.reshape(fmin.x[:(in_size+1)*h_size],(h_size,in_size+1))) #(25,401)
theta2 = np.mat(np.reshape(fmin.x[(in_size+1)*h_size:],(label_nums,h_size+1)))#(10,26)
a1,z2,a2,z3,h=forward(X,y_encoder,theta1,theta2)
y_pred = np.array(np.argmax(h,axis=1)+1) #利用h來獲得結果,輸出可能性最大的值的索引,
#因爲我們的數字是從1開始到10
y_pred
- 測試精準度
correct = [1 if a == b else 0 for (a, b) in zip(y_pred, y)]
accuracy = (sum(map(int, correct)) / float(len(correct)))
print ('accuracy = {0}%'.format(accuracy * 100))
每一個代價函數對於權重的偏導數由兩個部分組成。
第一部分:z對於權重的偏導數
第二部分:代價函數對於z的偏導數
第一部分很好計算:
如果求z(i+1)對於權重 θ(i)的偏導,那就是a(i)
第二部分就比較難:
首先計算代價函數對於z的偏導可以利用鏈式求導法則,進行如下圖的分解。
因此,又分爲了兩個部分。
第一部分:a對於z求偏導
第二部分:代價函數對於a求偏導
第一部分:a(i)對於z(i)求偏導,由sigmoid函數的特點可知=a(i) *(1-a(i))
第二部分:若知道了最後一層的h值,則可以算出δ=h-y
然後從後往前推
比如現在又一個三層的神經網絡,我們通過前向傳播求出了a(3)
因此δ(3)的值就等於a(3)-y
又通過下圖可以知道,想要求出前面的一個代價函數關於z的偏導,就可以通過用δ(3) * θ(2)
然後將δ(3) * θ(2)和a(i) *(1-a(i))相乘就可以得出第二部分。然後再與第一部分相乘就可以得出第一個梯度。
然後令δ(3) * θ(2)*a(i) *(1-a(i))爲δ(2) 按照同樣的道理去計算出第二個梯度。
目前就只能理解到這裏。。。
參考資料: