如果打不開,也可以複製鏈接到https://nbviewer.jupyter.org中打開。
一步一步搭建你的深度NN Building your Deep Neural Network: Step by Step
在前面你已經學習了訓練單隱層NN。
本節課你將搭建一個深層NN,至於用多少層,你說了算。
- 本節中,你將實現幾個函數用於搭建深層NN
- 這些函數將在下節編程中用於搭建一個深層NN,實現圖像識別分類
完成本節編程後,你可以
- 使用非線性單元ReLU改善你的模型
- 使用超過一個隱藏層搭建深度NN
- 實現一個方便使用的NN分類器
符號說明
-
上標表示第 層
例如: 表示第 層的激活函數;和都是第 層的參數。 -
上標 表示第 個樣本
例如:是第 個訓練樣本 -
小寫 表示向量的第 個輸入
例如: 表示第 層激活函數的 第 個輸入
1.本文涉及的基本庫
- numpy :是用Python進行科學計算的基本軟件包
- matplotlib:是一個著名的庫,用於在Python中繪製圖表
- dnn_utils_v2:提供了在本作業中會使用的一些必要的函數
- testCases_v2:提供了一些測試樣本來評估你的函數的正確性
- np.random.seed(1): 用於保持所有隨機函數的一致性. 請不要修改
import numpy as np
import h5py
import matplotlib.pyplot as plt
from testCases_v2 import *
from dnn_utils_v2 import sigmoid, sigmoid_backward, relu, relu_backward
#%matplotlib inline #Jupyter Notebook中使用
plt.rcParams['figure.figsize'] = (5.0, 4.0) # set default size of plots
plt.rcParams['image.interpolation'] = 'nearest'
plt.rcParams['image.cmap'] = 'gray'
#%load_ext autoreload #Jupyter Notebook中使用
#%autoreload 2 #Jupyter Notebook中使用
np.random.seed(1)
2.任務概述
爲了搭建你的NN,你將實現幾個輔助函數。這些輔助函數將在下一節中用於搭建一個2層NN和1個 層NN。
這些輔助函數會有詳細的說明,指導你完成必要的步驟。
下面是任務概述,你將會
- 爲1個2層網絡和1個 層網絡初始化參數
- 實現前向傳播模塊(如下圖紫色部分)
- 計算一層的中線性部分(結果保存在 )
- 激活函數會提供給你(relu/sigmoid)
- 把前面兩步(線性–>激活)合併入一個前向傳播函數
- 從第1層到第 層執行 (線性–>Relu)前向傳播函數次,最後第層執行(線性–>sigmod)前向傳播函數。最終會給你一個新的層模型前向傳播函數L_model_forward function
- 計算損失
- 實現反向傳播函數(如下圖紅色部分)
- 計算一層反向傳播步驟中的線性部分
- 激活函數的梯度會提供給你(relu_backward/sigmoid_backward)
- 把前面兩步(線性–>激活)合併入一個反向傳播函數
- 執行 (線性–>Relu)反向傳播函數次,再加上執行一次(線性–>sigmod)反向傳播函數。最終會給你一個新的層模型反向傳播函數L_model_backward function
- 更新參數
注意:每個前向函數都關聯一個反向函數。這就是爲什麼你前向模塊中的每一步都需要保存一些值到cache中的原因。這些cache中的值將用於在反向模塊中計算梯度。下面會向你詳細展示如何執行這些步驟。
3.初始化
你將要編寫2個函數來初始化你模型的參數。
第一個函數初始化2層模型參數。
第二個函數泛化初始化過程到層模型。
3.1 2層NN
創建和初始化2層NN的參數。
說明:
- 模型的結構是:LINEAR -> RELU -> LINEAR -> SIGMOID
- 隨機初始化權重矩陣,
np.random.randn(shape)*0.01
- 偏移值b初始化爲0,
np.zeros(shape)
該函數在訓練單隱層NN中已經構建過,代碼如下
def initialize_parameters(n_x, n_h, n_y):
"""
此函數是爲了初始化兩層網絡參數而使用的函數
Argument:
n_x -- size of the input layer 輸入層大小(節點數量)
n_h -- size of the hidden layer 隱藏層
n_y -- size of the output layer 輸出層
Returns:
parameters -- python dictionary containing your parameters: 包含你的參數的python字典:
W1 -- weight matrix of shape (n_h, n_x) 權重矩陣,維度爲(n_h,n_x)
b1 -- bias vector of shape (n_h, 1) 偏向量,維度爲(n_h,1)
W2 -- weight matrix of shape (n_y, n_h)
b2 -- bias vector of shape (n_y, 1)
"""
np.random.seed(1)
### START CODE HERE ### (≈ 4 lines of code)
W1 = np.random.randn(n_h, n_x) * 0.01
b1 = np.zeros(shape=(n_h, 1))
W2 = np.random.randn(n_y, n_h) * 0.01
b2 = np.zeros(shape=(n_y, 1))
### END CODE HERE ###
#使用斷言確保我的數據格式是正確的
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
測試一下
parameters = initialize_parameters(2,2,1)
print("W1 = " + str(parameters["W1"]))
print("b1 = " + str(parameters["b1"]))
print("W2 = " + str(parameters["W2"]))
print("b2 = " + str(parameters["b2"]))
結果如下
W1 = [[ 0.01624345 -0.00611756]
[-0.00528172 -0.01072969]]
b1 = [[ 0.]
[ 0.]]
W2 = [[ 0.00865408 -0.02301539]]
b2 = [[ 0.]]
3.2 L層NN
L層NN的初始化會更加複雜,因爲會有更多的權重矩陣和偏移矢量。完成參數初始化,你需要確保每層(矩陣)之間維度匹配。再次說明一下, 表示 層的單元數量。
例:輸入矩陣 維度是 ,樣本數量 ,所以
請記住,在python中會通過廣播機制計算 。
例:
就是
下面我們來實現層NN的初始化。
說明:
-
模型的結構是[LINEAR -> RELU] (L-1) -> LINEAR -> SIGMOID。它有 層使用Relu激活函數和輸出層使用sigmoid激活函數。
-
隨機初始化權重矩陣,
np.random.randn(shape)*0.01
-
偏移值b初始化爲0,
np.zeros(shape)
-
使用變量layer_dims保存每一層的單元數。
例:在平面數據分類中,layer_dims= [2,4,1]。表示:2個輸入,包含4個單元的1個隱藏層,和包含1個輸出單元的輸出層。所以W1維度是(4,2),b1維度是(4,1),W2維度是(1,4),b2維度是(1,1)。 -
以下是 (一層NN)的實現。你可以參照實現層NN。
if L == 1:
parameters["W" + str(L)] = np.random.randn(layer_dims[1], layer_dims[0]) * 0.01
parameters["b" + str(L)] = np.zeros((layer_dims[1], 1))
層實現代碼如下
# GRADED FUNCTION: initialize_parameters_deep
def initialize_parameters_deep(layer_dims):
"""
此函數是爲了初始化多層網絡參數而使用的函數。
Arguments:
包含我們網絡中每個圖層的節點數量的列表
layer_dims -- python array (list) containing the dimensions of each layer in our network
Returns:
parameters -- python dictionary containing your parameters "W1", "b1", ..., "WL", "bL":
權重矩陣,維度爲(layers_dims [1],layers_dims [1-1])
Wl -- weight matrix of shape (layer_dims[l], layer_dims[l-1])
偏向量,維度爲(layers_dims [1],1)
bl -- bias vector of shape (layer_dims[l], 1)
"""
np.random.seed(3)
parameters = {}
L = len(layer_dims) # number of layers in the network
for l in range(1, L):
### START CODE HERE ### (≈ 2 lines of code)
parameters['W' + str(l)] = np.random.randn(layer_dims[l], layer_dims[l - 1]) * 0.01
parameters['b' + str(l)] = np.zeros((layer_dims[l], 1))
### END CODE HERE ###
#確保我要的數據的格式是正確的
assert(parameters['W' + str(l)].shape == (layer_dims[l], layer_dims[l - 1]))
assert(parameters['b' + str(l)].shape == (layer_dims[l], 1))
return parameters
測試一下
parameters = initialize_parameters_deep([5,4,3])
print("W1 = " + str(parameters["W1"]))
print("b1 = " + str(parameters["b1"]))
print("W2 = " + str(parameters["W2"]))
print("b2 = " + str(parameters["b2"]))
運行結果如下
W1 = [[ 0.01788628 0.0043651 0.00096497 -0.01863493 -0.00277388]
[-0.00354759 -0.00082741 -0.00627001 -0.00043818 -0.00477218]
[-0.01313865 0.00884622 0.00881318 0.01709573 0.00050034]
[-0.00404677 -0.0054536 -0.01546477 0.00982367 -0.01101068]]
b1 = [[ 0.]
[ 0.]
[ 0.]
[ 0.]]
W2 = [[-0.01185047 -0.0020565 0.01486148 0.00236716]
[-0.01023785 -0.00712993 0.00625245 -0.00160513]
[-0.00768836 -0.00230031 0.00745056 0.01976111]]
b2 = [[ 0.]
[ 0.]
[ 0.]]
4.前向傳播模塊
4.1線性部分
完成參數初始化後,你可以開始實現前向傳播模塊。
你將依次實現以下3個函數
- LINEAR
- LINEAR -> ACTIVATION,ACTIVATION是ReLU或者Sigmoid
- [LINEAR -> RELU] (L-1) -> LINEAR -> SIGMOID,全模型。
全部樣本線性前向模塊矢量化計算公式如下
其中
調試過程中,你可以利用打印W.shape
來確保維度匹配。
線性部分實現代碼如下
def linear_forward(A, W, b):
"""
Implement the linear part of a layer's forward propagation. 實現前向傳播的線性部分。
Arguments:
來自上一層(或輸入數據)的激活,維度爲(上一層的節點數量,樣本的數量)
A -- activations from previous layer (or input data): (size of previous layer, number of examples)
權重矩陣,numpy數組,維度爲(當前層的節點數量,前一層的節點數量)
W -- weights matrix: numpy array of shape (size of current layer, size of previous layer)
偏移向量,numpy數組,維度爲(當前層節點數量,1)
b -- bias vector, numpy array of shape (size of the current layer, 1)
Returns:
激活函數的輸入,也稱爲預激活參數
Z -- the input of the activation function, also called pre-activation parameter
一個包含“A”,“W”和“b”的字典,存儲這些變量以有效地計算後向傳遞
cache -- a python dictionary containing "A", "W" and "b" ; stored for computing the backward pass efficiently
"""
### START CODE HERE ### (≈ 1 line of code)
Z = np.dot(W, A) + b
### END CODE HERE ###
assert(Z.shape == (W.shape[0], A.shape[1]))
cache = (A, W, b)
return Z, cache
testCases_v2.py的測試函數linear_forward_test_case()測試一下效果
A, W, b = linear_forward_test_case()
Z, linear_cache = linear_forward(A, W, b)
print("Z = " + str(Z))
運行結果如下
Z = [[ 3.26295337 -1.23429987]]
4.2 激活部分
在這裏,你使用了2個激活函數。
- Sigmoid函數:。在dnn_utils_v2.py中已經有實現好的函數sigmoid。函數返回2項,一個是激活函數的結果值,另一個是包含”Z” 的”cache“值 ,這個我們在後向傳播過程需要用到。調用方法如下
A, activation_cache = sigmoid(Z)
- ReLU函數:。在dnn_utils_v2.py中已經有實現好的函數relu。函數返回2項,一個是激活函數的結果值,另一個是包含”Z” 的”cache“值 ,這個我們在後向傳播過程需要用到。調用方法如下
A, activation_cache = relu(Z)
4.2.1 2層前向(線性->激活)步驟實現
爲了便於使用,我們把線性和激活2個部分合併到一個函數中。這個函數中先進行線性前向步驟,再進行激活步驟。
數學公式是: ,激活函數"g"可以是sigmoid()或者relu()。
實現代碼如下
# GRADED FUNCTION: linear_activation_forward
def linear_activation_forward(A_prev, W, b, activation):
"""
實現一層中的前向傳播線性-->激活
Implement the forward propagation for the LINEAR->ACTIVATION layer
Arguments:
來自上一層(或輸入層)的激活函數輸出,維度爲(上一層的節點數量,樣本數)
A_prev -- activations from previous layer (or input data): (size of previous layer, number of examples)
權重矩陣,numpy數組,維度爲(當前層的節點數量,前一層的大小)
W -- weights matrix: numpy array of shape (size of current layer, size of previous layer)
偏移向量,numpy數組,維度爲(當前層的節點數量,1)
b -- bias vector, numpy array of shape (size of the current layer, 1)
當前層中使用的激活函數名,字符串類型,【"sigmoid" | "relu"】
activation -- the activation to be used in this layer, stored as a text string: "sigmoid" or "relu"
Returns:
激活函數的輸出,也稱爲激活後的值
A -- the output of the activation function, also called the post-activation value
一個包含“linear_cache”和“activation_cache”的字典,我們需要存儲它以有效地計算後向傳遞
cache -- a python dictionary containing "linear_cache" and "activation_cache";
stored for computing the backward pass efficiently
"""
if activation == "sigmoid":
# Inputs: "A_prev, W, b". Outputs: "A, activation_cache".
### START CODE HERE ### (≈ 2 lines of code)
Z, linear_cache = linear_forward(A_prev, W, b)
A, activation_cache = sigmoid(Z)
### END CODE HERE ###
elif activation == "relu":
# Inputs: "A_prev, W, b". Outputs: "A, activation_cache".
### START CODE HERE ### (≈ 2 lines of code)
Z, linear_cache = linear_forward(A_prev, W, b)
A, activation_cache = relu(Z)
### END CODE HERE ###
assert (A.shape == (W.shape[0], A_prev.shape[1]))
cache = (linear_cache, activation_cache)
return A, cache
測試一下
A_prev, W, b = linear_activation_forward_test_case()
A, linear_activation_cache = linear_activation_forward(A_prev, W, b, activation = "sigmoid")
print("With sigmoid: A = " + str(A))
A, linear_activation_cache = linear_activation_forward(A_prev, W, b, activation = "relu")
print("With ReLU: A = " + str(A))
運行結果
With sigmoid: A = [[ 0.96890023 0.11013289]]
With ReLU: A = [[ 3.43896131 0. ]]
注意:在DL中,“線性->激活”2步計算被視爲NN的單層,而不是2層
4.2.2 層前向(線性->激活)步驟實現
爲了方便層NN的實現,你需要實現一個函數,其中用傳入Relu的linear_activation_forward()重複次,然後再使用1次傳入 SIGMOID的linear_activation_forward()。
要實現的前向傳播過程如上圖。
說明:變量AL表示 ,或者稱爲,預測值。
注意:
- 使用前面已經實現的函數linear_activation_forward()
- 循環 [LINEAR->RELU] (L-1) 次
- 使用
list.append(c)
在cache中追加一個值
實現代碼如下
# GRADED FUNCTION: L_model_forward
def L_model_forward(X, parameters):
"""
實現[LINEAR-> RELU] *(L-1) - > LINEAR-> SIGMOID計算前向傳播,也就是L層網絡的前向傳播,
Implement forward propagation for the [LINEAR->RELU]*(L-1)->LINEAR->SIGMOID computation
Arguments:
數據,numpy數組,維度爲(輸入節點數量,示例數)
X -- data, numpy array of shape (input size, number of examples)
parameters -- output of initialize_parameters_deep()
Returns:
AL -- last post-activation value 最後的激活值(預測值)
caches -- list of caches containing:
every cache of linear_relu_forward() (there are L-1 of them, indexed from 0 to L-2)
the cache of linear_sigmoid_forward() (there is one, indexed L-1)
包含以下內容的緩存列表:
linear_relu_forward()的每個cache(有L-1個,索引爲從0到L-2)
linear_sigmoid_forward()的cache(只有一個,索引爲L-1)
"""
caches = []
A = X
L = len(parameters) // 2 # number of layers in the neural network
# Implement [LINEAR -> RELU]*(L-1). Add "cache" to the "caches" list.
for l in range(1, L):
A_prev = A
### START CODE HERE ### (≈ 2 lines of code)
A, cache = linear_activation_forward(A_prev,
parameters['W' + str(l)],
parameters['b' + str(l)],
activation='relu')
caches.append(cache)
### END CODE HERE ###
# Implement LINEAR -> SIGMOID. Add "cache" to the "caches" list.
### START CODE HERE ### (≈ 2 lines of code)
AL, cache = linear_activation_forward(A,
parameters['W' + str(L)],
parameters['b' + str(L)],
activation='sigmoid')
caches.append(cache)
### END CODE HERE ###
assert(AL.shape == (1, X.shape[1]))
return AL, caches
測試一下
X, parameters = L_model_forward_test_case()
AL, caches = L_model_forward(X, parameters)
print("AL = " + str(AL))
print("Length of caches list = " + str(len(caches)))
運行結果如下
AL = [[ 0.17007265 0.2524272 ]]
Length of caches list = 2
5.損失函數
現在你將要實現前向和反向傳播過程。你需要計算損失,因爲你要知道你的模型是不是真的學習的夠好。
使用以下公式計算交叉熵損失J。
該函數在Planar data classification with one hidden layer中已經介紹過,代碼如下
# GRADED FUNCTION: compute_cost
def compute_cost(AL, Y):
"""
Implement the cost function defined by equation (7).
Arguments:
與標籤預測相對應的概率向量,維度爲(1,樣本數量)
AL -- probability vector corresponding to your label predictions, shape (1, number of examples)
標籤向量(例如:如果不是貓,則爲0,如果是貓則爲1),維度爲(1,數量)
Y -- true "label" vector (for example: containing 0 if non-cat, 1 if cat), shape (1, number of examples)
Returns:
cost -- cross-entropy cost 交叉熵成本
"""
m = Y.shape[1]
# Compute loss from aL and y.
### START CODE HERE ### (≈ 1 lines of code)
cost = (-1 / m) * np.sum(np.multiply(Y, np.log(AL)) + np.multiply(1 - Y, np.log(1 - AL)))
### END CODE HERE ###
cost = np.squeeze(cost) # To make sure your cost's shape is what we expect (e.g. this turns [[17]] into 17).
assert(cost.shape == ())
return cost
測試一下
Y, AL = compute_cost_test_case()
print("cost = " + str(compute_cost(AL, Y)))
運行結果如下
cost = 0.414931599615
6.反向傳播模塊
和前向傳播一樣,你需要爲了反向傳播構建輔助函數。
反向傳播用於計算損失函數相對於各個參數的梯度。
和前向傳播類似,你將按照以下3步構建反向傳播
- 線性反向
- LINEAR -> ACTIVATION反向,ACTIVATION計算ReLU或者sigmoid激活函數的梯度
- [LINEAR -> RELU] (L-1) -> LINEAR -> SIGMOID反向過程,全模塊
6.1線性反向
對於層,線性部分是
假設你已經計算了導數,現在你想獲得。
3個輸出 都要使用來計算,公式如下
利用上面3個公式實現函數linear_backward(),代碼如下。
注意,這裏和原文有2處不一樣。如果使用原文,層返現函數測試時候會報錯assert (isinstance(db, float)) AssertionError
# GRADED FUNCTION: linear_backward
def linear_backward(dZ, cache):
"""
爲單層實現反向傳播的線性部分(第L層)
Implement the linear portion of backward propagation for a single layer (layer l)
Arguments:
相對於(當前第l層的)線性輸出的成本梯度
dZ -- Gradient of the cost with respect to the linear output (of current layer l)
來自當前層前向傳播的值的元組(A_prev,W,b)
cache -- tuple of values (A_prev, W, b) coming from the forward propagation in the current layer
Returns:
相對於激活(前一層l-1)的成本梯度,與A_prev維度相同
dA_prev -- Gradient of the cost with respect to the activation (of the previous layer l-1), same shape as A_prev
相對於W(當前層l)的成本梯度,與W的維度相同
dW -- Gradient of the cost with respect to W (current layer l), same shape as W
相對於b(當前層l)的成本梯度,與b維度相同
db -- Gradient of the cost with respect to b (current layer l), same shape as b
"""
A_prev, W, b = cache
m = A_prev.shape[1]
### START CODE HERE ### (≈ 3 lines of code)
dW = np.dot(dZ, cache[0].T) / m
#db = np.squeeze(np.sum(dZ, axis=1, keepdims=True)) / m
db = np.sum(dZ,axis = 1, keepdims=True)/m
dA_prev = np.dot(cache[1].T, dZ)
### END CODE HERE ###
assert (dA_prev.shape == A_prev.shape)
assert (dW.shape == W.shape)
assert (db.shape == b.shape)
#assert (isinstance(db, float))
return dA_prev, dW, db
測試一下
# Set up some test inputs
dZ, linear_cache = linear_backward_test_case()
dA_prev, dW, db = linear_backward(dZ, linear_cache)
print ("dA_prev = "+ str(dA_prev))
print ("dW = " + str(dW))
print ("db = " + str(db))
運行結果
dA_prev = [[ 0.51822968 -0.19517421]
[-0.40506361 0.15255393]
[ 2.37496825 -0.89445391]]
dW = [[-0.10076895 1.40685096 1.64992505]]
db = 0.506294475007
6.2激活反向
下面你要構建一個函數linear_activation_backward,把線性部分和激活部分的反向合併在一起
爲了幫助你構建函數linear_activation_backward,提供了2個已經實現好的反向函數給你
- sigmoid_backward: SIGMOID單元的反向傳播,調用方式如下:
dZ = sigmoid_backward(dA, activation_cache)
- relu_backward: RELU單元的反向傳播,調用方式如下:
dZ = relu_backward(dA, activation_cache)
sigmoid_backward和relu_backward計算如下,表示激活函數
.
LINEAR->ACTIVATION層反向傳播實現代碼如下
# GRADED FUNCTION: linear_activation_backward
def linear_activation_backward(dA, cache, activation):
"""
實現LINEAR-> ACTIVATION層的後向傳播。
Implement the backward propagation for the LINEAR->ACTIVATION layer.
Arguments:
當前層l的激活後的梯度值
dA -- post-activation gradient for current layer l
存儲的用於有效計算反向傳播的值的元組(值爲linear_cache,activation_cache)
cache -- tuple of values (linear_cache, activation_cache) we store for computing backward propagation efficiently
要在此層中使用的激活函數名,字符串類型,【"sigmoid" | "relu"】
activation -- the activation to be used in this layer, stored as a text string: "sigmoid" or "relu"
Returns:
相對於激活(前一層l-1)的成本梯度值,與A_prev維度相同
dA_prev -- Gradient of the cost with respect to the activation (of the previous layer l-1), same shape as A_prev
相對於W(當前層l)的成本梯度值,與W的維度相同
dW -- Gradient of the cost with respect to W (current layer l), same shape as W
相對於b(當前層l)的成本梯度值,與b的維度相同
db -- Gradient of the cost with respect to b (current layer l), same shape as b
"""
linear_cache, activation_cache = cache
if activation == "relu":
### START CODE HERE ### (≈ 2 lines of code)
dZ = relu_backward(dA, activation_cache)
### END CODE HERE ###
elif activation == "sigmoid":
### START CODE HERE ### (≈ 2 lines of code)
dZ = sigmoid_backward(dA, activation_cache)
### END CODE HERE ###
# Shorten the code
dA_prev, dW, db = linear_backward(dZ, linear_cache)
return dA_prev, dW, db
測試一下
AL, linear_activation_cache = linear_activation_backward_test_case()
dA_prev, dW, db = linear_activation_backward(AL, linear_activation_cache, activation = "sigmoid")
print ("sigmoid:")
print ("dA_prev = "+ str(dA_prev))
print ("dW = " + str(dW))
print ("db = " + str(db) + "\n")
dA_prev, dW, db = linear_activation_backward(AL, linear_activation_cache, activation = "relu")
print ("relu:")
print ("dA_prev = "+ str(dA_prev))
print ("dW = " + str(dW))
print ("db = " + str(db))
運行結果
sigmoid:
dA_prev = [[ 0.11017994 0.01105339]
[ 0.09466817 0.00949723]
[-0.05743092 -0.00576154]]
dW = [[ 0.10266786 0.09778551 -0.01968084]]
db = -0.0572962221763
relu:
dA_prev = [[ 0.44090989 -0. ]
[ 0.37883606 -0. ]
[-0.2298228 0. ]]
dW = [[ 0.44513824 0.37371418 -0.10478989]]
db = -0.208378923703
6.3層反向
現在你可以實現整個NN的反向部分函數。
前面已經說過,L_model_forward函數在每一次迭代中,需要把一些變量值保存在cache中,包括X,W,b, z。在反向傳播模塊中,你會用到這些變量來計算梯度。
在L_model_backward函數中,你要從層開始反向遍歷所有的隱藏層。每一步都要取出對應cache值計算當前層梯度。下圖顯示了反向傳播流程。
反向傳播初始化
對於反向傳播,我們知道NN的輸出是,所以我們需要計算dAL 。實現代碼如下
dAL = - (np.divide(Y, AL) - np.divide(1 - Y, 1 - AL)) # derivative of cost with respect to AL
計算完之後,我們可以用這個激活後的梯度 dAL 繼續進行反向計算。正如上面圖中所示,你可以把dAL送入 LINEAR->SIGMOID反向過程函數L_model_forward,然後再使用for循環遍歷計算所有剩餘層的LINEAR->RELU反向過程函數。在此過程中,你可以把dA, dW, db保存在字典grads中,代碼如下
grads["dW"+str(l)]=dW[l]
例如: 對於層, 保存形式爲 grads[“dW3”]
實現[LINEAR->RELU] (L-1) -> LINEAR -> SIGMOID 模型代碼如下
注意:代碼和原文有3處改變。
def L_model_backward(AL, Y, caches):
"""
對[LINEAR-> RELU] *(L-1) - > LINEAR - > SIGMOID組執行反向傳播
Implement the backward propagation for the [LINEAR->RELU] * (L-1) -> LINEAR -> SIGMOID group
Arguments:
概率向量,正向傳播的輸出(L_model_forward())
AL -- probability vector, output of the forward propagation (L_model_forward())
標籤向量(例如:如果不是貓,則爲0,如果是貓則爲1),維度爲(1,數量)
Y -- true "label" vector (containing 0 if non-cat, 1 if cat)
caches -- list of caches containing:
every cache of linear_activation_forward() with "relu" (it's caches[l], for l in range(L-1) i.e l = 0...L-2)
the cache of linear_activation_forward() with "sigmoid" (it's caches[L-1])
包含以下內容的cache列表:
linear_activation_forward("relu")的cache,不包含輸出層
linear_activation_forward("sigmoid")的cache
Returns:
grads -- A dictionary with the gradients 具有梯度值的字典
grads["dA" + str(l)] = ...
grads["dW" + str(l)] = ...
grads["db" + str(l)] = ...
"""
grads = {}
L = len(caches) # the number of layers
m = AL.shape[1]
Y = Y.reshape(AL.shape) # after this line, Y is the same shape as AL
# Initializing the backpropagation
### START CODE HERE ### (1 line of code)
dAL = - (np.divide(Y, AL) - np.divide(1 - Y, 1 - AL))
### END CODE HERE ###
# Lth layer (SIGMOID -> LINEAR) gradients. Inputs: "AL, Y, caches". Outputs: "grads["dAL"], grads["dWL"], grads["dbL"]
### START CODE HERE ### (approx. 2 lines)
#current_cache = caches[-1]
current_cache = caches[L-1]
#grads["dA" + str(L)], grads["dW" + str(L)], grads["db" + str(L)] = linear_backward(sigmoid_backward(dAL,
# current_cache[1]),
# current_cache[0])
grads["dA" + str(L)], grads["dW" + str(L)], grads["db" + str(L)] = linear_activation_backward(dAL, current_cache, activation = "sigmoid")
### END CODE HERE ###
for l in reversed(range(L-1)):
# lth layer: (RELU -> LINEAR) gradients.
# Inputs: "grads["dA" + str(l + 2)], caches". Outputs: "grads["dA" + str(l + 1)] , grads["dW" + str(l + 1)] , grads["db" + str(l + 1)]
### START CODE HERE ### (approx. 5 lines)
current_cache = caches[l]
#dA_prev_temp, dW_temp, db_temp = linear_backward(sigmoid_backward(dAL, current_cache[1]), current_cache[0])
dA_prev_temp, dW_temp, db_temp = linear_activation_backward(grads["dA" + str(l + 2)], current_cache, activation = "relu")
grads["dA" + str(l + 1)] = dA_prev_temp
grads["dW" + str(l + 1)] = dW_temp
grads["db" + str(l + 1)] = db_temp
### END CODE HERE ###
return grads
測試一下。注意:這裏和原文不一樣。原文的測試案例估計和我的不一樣。
print("==============測試L_model_backward==============")
AL, Y_assess, caches = L_model_backward_test_case1()
grads = L_model_backward(AL, Y_assess, caches)
print ("dW1 = "+ str(grads["dW1"]))
print ("db1 = "+ str(grads["db1"]))
print ("dA1 = "+ str(grads["dA1"]))
結果如下
==============測試L_model_backward==============
dW1 = [[ 0.41010002 0.07807203 0.13798444 0.10502167]
[ 0. 0. 0. 0. ]
[ 0.05283652 0.01005865 0.01777766 0.0135308 ]]
db1 = [[-0.22007063]
[ 0. ]
[-0.02835349]]
dA1 = [[ 0. 0.52257901]
[ 0. -0.3269206 ]
[ 0. -0.32070404]
[ 0. -0.74079187]]
6.4更新參數
使用梯度下降更新模型參數,構建函數update_parameters()。
表示學習率。更新後,保存到參數字典。
代碼如下
# GRADED FUNCTION: update_parameters
def update_parameters(parameters, grads, learning_rate):
"""
使用梯度下降更新參數
Update parameters using gradient descent
Arguments:
parameters -- python dictionary containing your parameters 包含你的參數的字典
包含梯度值的字典,是L_model_backward的輸出
grads -- python dictionary containing your gradients, output of L_model_backward
Returns:
parameters -- python dictionary containing your updated parameters 包含更新參數的字典
parameters["W" + str(l)] = ...
parameters["b" + str(l)] = ...
"""
L = len(parameters) // 2 # number of layers in the neural network
# Update rule for each parameter. Use a for loop.
### START CODE HERE ### (≈ 3 lines of code)
for l in range(L):
parameters["W" + str(l + 1)] = parameters["W" + str(l + 1)] - learning_rate * grads["dW" + str(l + 1)]
parameters["b" + str(l + 1)] = parameters["b" + str(l + 1)] - learning_rate * grads["db" + str(l + 1)]
### END CODE HERE ###
return parameters
測試一下。注意:這裏和原文不一樣,沒有W3和b3。
print("==============測試update_parameters==============")
parameters, grads = update_parameters_test_case()
parameters = update_parameters(parameters, grads, 0.1)
print ("W1 = " + str(parameters["W1"]))
print ("b1 = " + str(parameters["b1"]))
print ("W2 = " + str(parameters["W2"]))
print ("b2 = " + str(parameters["b2"]))
運行結果
==============測試update_parameters==============
W1 = [[-0.59562069 -0.09991781 -2.14584584 1.82662008]
[-1.76569676 -0.80627147 0.51115557 -1.18258802]
[-1.0535704 -0.86128581 0.68284052 2.20374577]]
b1 = [[-0.04659241]
[-1.28888275]
[ 0.53405496]]
W2 = [[-0.55569196 0.0354055 1.32964895]]
b2 = [[-0.84610769]]
7.總結
到這裏,我們的搭建深度NN網絡所需要的函數都已經完成了。
後面的編程練習,我們會利用它們來構築2個模型
- 2層NN
- 層NN
你可以利用這2個模型來實現貓圖片的識別。