筆記1中我們利用 numpy 搭建了神經(jīng)網(wǎng)絡(luò)最簡單的結(jié)構(gòu)單元:感知機(jī)。筆記2將繼續(xù)學(xué)習(xí)如何手動搭建神經(jīng)網(wǎng)絡(luò)。我們將學(xué)習(xí)如何利用 numpy 搭建一個含單隱層的神經(jīng)網(wǎng)絡(luò)。單隱層顧名思義,即僅含一個隱藏層的神經(jīng)網(wǎng)絡(luò),抑或是成為兩層網(wǎng)絡(luò)。
繼續(xù)回顧一下搭建一個神經(jīng)網(wǎng)絡(luò)的基本思路和步驟:
定義網(wǎng)絡(luò)結(jié)構(gòu)(指定輸出層、隱藏層、輸出層的大?。?/span>
初始化模型參數(shù)
循環(huán)操作:執(zhí)行前向傳播/計算損失/執(zhí)行后向傳播/權(quán)值更新
定義網(wǎng)絡(luò)結(jié)構(gòu)
假設(shè) X 為神經(jīng)網(wǎng)絡(luò)的輸入特征矩陣,y 為標(biāo)簽向量。則含單隱層的神經(jīng)網(wǎng)絡(luò)的結(jié)構(gòu)如下所示:
網(wǎng)絡(luò)結(jié)構(gòu)的函數(shù)定義如下:
deflayer_sizes(X,Y): n_x=X.shape[0]#sizeofinputlayer n_h=4#sizeofhiddenlayer n_y=Y.shape[0]#sizeofoutputlayer return(n_x,n_h,n_y)
其中輸入層和輸出層的大小分別與 X 和 y 的 shape 有關(guān)。而隱層的大小可由我們手動指定。這里我們指定隱層的大小為4。
初始化模型參數(shù)
假設(shè) W1 為輸入層到隱層的權(quán)重數(shù)組、b1 為輸入層到隱層的偏置數(shù)組;W2 為隱層到輸出層的權(quán)重數(shù)組,b2 為隱層到輸出層的偏置數(shù)組。于是我們定義參數(shù)初始化函數(shù)如下:
def initialize_parameters(n_x, n_h, n_y):
W1 = np.random.randn(n_h, n_x)*0.01
b1 = np.zeros((n_h, 1))
W2 = np.random.randn(n_y, n_h)*0.01
b2 = np.zeros((n_y, 1))
assert (W1.shape == (n_h, n_x))
assert (b1.shape == (n_h, 1))
assert (W2.shape == (n_y, n_h))
assert (b2.shape == (n_y, 1))
parameters = {"W1": W1,
"b1": b1,
"W2": W2,
"b2": b2}
return parameters
其中對權(quán)值的初始化我們利用了 numpy 中的生成隨機(jī)數(shù)的模塊 np.random.randn ,偏置的初始化則使用了 np.zero 模塊。通過設(shè)置一個字典進(jìn)行封裝并返回包含初始化參數(shù)之后的結(jié)果。
前向傳播
在定義好網(wǎng)絡(luò)結(jié)構(gòu)并初始化參數(shù)完成之后,就要開始執(zhí)行神經(jīng)網(wǎng)絡(luò)的訓(xùn)練過程了。而訓(xùn)練的第一步則是執(zhí)行前向傳播計算。假設(shè)隱層的激活函數(shù)為
tanh 函數(shù),
輸出層的激活函數(shù)為 sigmoid 函數(shù)。則前向傳播計算表示為:
定義前向傳播計算函數(shù)為:
def forward_propagation(X, parameters): # Retrieve each parameter from the dictionary "parameters"
W1 = parameters['W1']
b1 = parameters['b1']
W2 = parameters['W2']
b2 = parameters['b2']
# Implement Forward Propagation to calculate A2 (probabilities)
Z1 = np.dot(W1, X) + b1
A1 = np.tanh(Z1)
Z2 = np.dot(W2, Z1) + b2
A2 = sigmoid(Z2)
assert(A2.shape == (1, X.shape[1]))
cache = {"Z1": Z1,
"A1": A1,
"Z2": Z2,
"A2": A2}
return A2, cache
從參數(shù)初始化結(jié)果字典里取到各自的參數(shù),然后執(zhí)行一次前向傳播計算,將前向傳播計算的結(jié)果保存到 cache 這個字典中, 其中 A2 為經(jīng)過 sigmoid 激活函數(shù)激活后的輸出層的結(jié)果。
計算當(dāng)前訓(xùn)練損失
前向傳播計算完成后我們需要確定以當(dāng)前參數(shù)執(zhí)行計算后的的輸出與標(biāo)簽值之間的損失大小。與筆記1一樣,損失函數(shù)同樣選擇為交叉熵?fù)p失:
定義計算損失函數(shù)為:
def compute_cost(A2, Y, parameters):
m = Y.shape[1] # number of example
# Compute the cross-entropy cost
logprobs = np.multiply(np.log(A2),Y) + np.multiply(np.log(1-A2), 1-Y)
cost = -1/m * np.sum(logprobs)
cost = np.squeeze(cost) # makes sure cost is the dimension we expect.
assert(isinstance(cost, float))
return cost
執(zhí)行反向傳播
當(dāng)前向傳播和當(dāng)前損失確定之后,就需要繼續(xù)執(zhí)行反向傳播過程來調(diào)整權(quán)值了。中間涉及到各個參數(shù)的梯度計算,具體如下圖所示:
根據(jù)上述梯度計算公式定義反向傳播函數(shù):
def backward_propagation(parameters, cache, X, Y):
m = X.shape[1]
# First, retrieve W1 and W2 from the dictionary "parameters".
W1 = parameters['W1']
W2 = parameters['W2']
# Retrieve also A1 and A2 from dictionary "cache".
A1 = cache['A1']
A2 = cache['A2']
# Backward propagation: calculate dW1, db1, dW2, db2.
dZ2 = A2-Y
dW2 = 1/m * np.dot(dZ2, A1.T)
db2 = 1/m * np.sum(dZ2, axis=1, keepdims=True)
dZ1 = np.dot(W2.T, dZ2)*(1-np.power(A1, 2))
dW1 = 1/m * np.dot(dZ1, X.T)
db1 = 1/m * np.sum(dZ1, axis=1, keepdims=True)
grads = {"dW1": dW1,
"db1": db1,
"dW2": dW2,
"db2": db2}
return grads
將各參數(shù)的求導(dǎo)計算結(jié)果放入字典 grad 進(jìn)行返回。
這里需要提一下的是涉及到的關(guān)于數(shù)值優(yōu)化方面的知識。在機(jī)器學(xué)習(xí)中,當(dāng)所學(xué)問題有了具體的形式之后,機(jī)器學(xué)習(xí)就會形式化為一個求優(yōu)化的問題。不論是梯度下降法、隨機(jī)梯度下降、牛頓法、擬牛頓法,抑或是
Adam 之類的高級的優(yōu)化算法,這些都需要花時間掌握去掌握其數(shù)學(xué)原理。
權(quán)值更新
迭代計算的最后一步就是根據(jù)反向傳播的結(jié)果來更新權(quán)值了,更新公式如下:
由該公式可以定義權(quán)值更新函數(shù)為:
def update_parameters(parameters, grads, learning_rate = 1.2): # Retrieve each parameter from the dictionary "parameters"
W1 = parameters['W1']
b1 = parameters['b1']
W2 = parameters['W2']
b2 = parameters['b2']
# Retrieve each gradient from the dictionary "grads"
dW1 = grads['dW1']
db1 = grads['db1']
dW2 = grads['dW2']
db2 = grads['db2']
# Update rule for each parameter
W1 -= dW1 * learning_rate
b1 -= db1 * learning_rate
W2 -= dW2 * learning_rate
b2 -= db2 * learning_rate
parameters = {"W1": W1,
"b1": b1,
"W2": W2,
"b2": b2}
return parameters
這樣,前向傳播-計算損失-反向傳播-權(quán)值更新的神經(jīng)網(wǎng)絡(luò)訓(xùn)練過程就算部署完成了。當(dāng)前了,跟筆記1一樣,為了更加 pythonic 一點(diǎn),我們也將各個模塊組合起來,定義一個神經(jīng)網(wǎng)絡(luò)模型:
def nn_model(X, Y, n_h, num_iterations = 10000, print_cost=False):
np.random.seed(3)
n_x = layer_sizes(X, Y)[0]
n_y = layer_sizes(X, Y)[2]
# Initialize parameters, then retrieve W1, b1, W2, b2. Inputs: "n_x, n_h, n_y". Outputs = "W1, b1, W2, b2, parameters".
parameters = initialize_parameters(n_x, n_h, n_y)
W1 = parameters['W1']
b1 = parameters['b1']
W2 = parameters['W2']
b2 = parameters['b2']
# Loop (gradient descent)
for i in range(0, num_iterations):
# Forward propagation. Inputs: "X, parameters". Outputs: "A2, cache".
A2, cache = forward_propagation(X, parameters)
# Cost function. Inputs: "A2, Y, parameters". Outputs: "cost".
cost = compute_cost(A2, Y, parameters)
# Backpropagation. Inputs: "parameters, cache, X, Y". Outputs: "grads".
grads = backward_propagation(parameters, cache, X, Y)
# Gradient descent parameter update. Inputs: "parameters, grads". Outputs: "parameters".
parameters = update_parameters(parameters, grads, learning_rate=1.2)
# Print the cost every 1000 iterations
if print_cost and i % 1000 == 0:
print ("Cost after iteration %i: %f" %(i, cost))
return parameters
以上便是本節(jié)的主要內(nèi)容,利用 numpy 手動搭建一個含單隱層的神經(jīng)網(wǎng)路。從零開始寫起,打牢基礎(chǔ),待到結(jié)構(gòu)熟練,原理吃透,再去接觸一些主流的深度學(xué)習(xí)框架才是學(xué)習(xí)深度學(xué)習(xí)的最佳途徑。
本文來自《自興動腦人工智能》項目部:凱文。
-
神經(jīng)網(wǎng)絡(luò)
+關(guān)注
關(guān)注
42文章
4772瀏覽量
100809 -
AI
+關(guān)注
關(guān)注
87文章
30947瀏覽量
269217 -
人工智能
+關(guān)注
關(guān)注
1791文章
47314瀏覽量
238654
發(fā)布評論請先 登錄
相關(guān)推薦
評論