如果打不开,也可以复制链接到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个模型来实现猫图片的识别。