欢迎来到第4周作业的第1部分!在此之前你已经训练了一个2层的神经网络(只有一个隐藏层)。本周,你将学会构建一个任意层数的深度神经网络!
完成此任务后,你将能够:
ReLU等非线性单位来改善模型符号说明:
让我们开始吧!
让我们首先导入作业过程中需要的所有包。
In [1]:
cd ../input/deeplearning46278
/home/kesci/input/deeplearning46278
In [2]:
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
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
%autoreload 2
np.random.seed(1)
为了构建你的神经网络,你将实现几个“辅助函数”。这些辅助函数将在下一个作业中使用,用来构建一个两层神经网络和一个L层的神经网络。你将实现的每个函数都有详细的说明,这些说明将指导你完成必要的步骤。此作业的大纲如下:
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Py4aQ89D-1662895428829)(L1W4%E4%BD%9C%E4%B8%9A1%20%E9%80%90%E6%AD%A5%E6%9E%84%E5%BB%BA%E4%BD%A0%E7%9A%84%E6%B7%B1%E5%BA%A6%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C.assets/960.png)]](https://1000bd.com/contentImg/2023/11/05/211148624.png)
图1
注意:对于每个正向函数,都有一个对应的反向函数。 这也是为什么在正向传播模块的每一步都将一些值存储在缓存中的原因。缓存的值可用于计算梯度。 然后,在反向传导模块中,你将使用缓存的值来计算梯度。 此作业将指导说明如何执行这些步骤。
首先编写两个辅助函数用来初始化模型的参数。 第一个函数将用于初始化两层模型的参数。 第二个将把初始化过程推广到L层模型上。
练习:创建并初始化2层神经网络的参数。
说明:
np.random.randn(shape)* 0.01。np.zeros(shape)。In [3]:
# GRADED FUNCTION: initialize_parameters
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:
W1 -- weight matrix of shape (n_h, n_x)
b1 -- bias vector of shape (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((n_h,1))
W2 = np.random.randn(n_y, n_h)*0.01
b2 = np.zeros((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
In [4]:
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.]]
预期输出:
W1 = [[ 0.01624345 -0.00611756]
[-0.00528172 -0.01072969]]
b1 = [[0.]
[0.]]
W2 = [[ 0.00865408 -0.02301539]]
b2 = [[0.]]
更深的L层神经网络的初始化更加复杂,因为存在更多的权重矩阵和偏差向量。 完成 initialize_parameters_deep后,应确保各层之间的维度匹配。 回想一下,
n
[
l
]
n^{[l]}
n[l]是l层中的神经元数量。 因此,如果我们输入的 X 的大小为(12288,209)(以m=209为例),则:
| Shape of W | Shape of b | Activation | Shape of Activation | |
|---|---|---|---|---|
| Layer 1 | ( n [ 1 ] , 12288 ) (n^{[1]},12288) (n[1],12288) | ( n [ 1 ] , 1 ) (n^{[1]},1) (n[1],1) | Z [ 1 ] = W [ 1 ] X + b [ 1 ] Z^{[1]} = W^{[1]} X + b^{[1]} Z[1]=W[1]X+b[1] | ( n [ 1 ] , 209 ) (n^{[1]},209) (n[1],209) |
| Layer 2 | ( n [ 2 ] , n [ 1 ] ) (n^{[2]}, n^{[1]}) (n[2],n[1]) | ( n [ 2 ] , 1 ) (n^{[2]},1) (n[2],1) | Z [ 2 ] = W [ 2 ] A [ 1 ] + b [ 2 ] Z^{[2]} = W^{[2]} A^{[1]} + b^{[2]} Z[2]=W[2]A[1]+b[2] | ( n [ 2 ] , 209 ) (n^{[2]}, 209) (n[2],209) |
| ⋮ | ⋮ | ⋮ | ⋮ | ⋮ |
| Layer L-1 | ( n [ L − 1 ] , n [ L − 2 ] ) (n^{[L-1]}, n^{[L-2]}) (n[L−1],n[L−2]) | ( n [ L − 1 ] , 1 ) (n^{[L-1]}, 1) (n[L−1],1) | Z [ L − 1 ] = W [ L − 1 ] A [ L − 2 ] + b [ L − 1 ] Z^{[L-1]} = W^{[L-1]} A^{[L-2]} + b^{[L-1]} Z[L−1]=W[L−1]A[L−2]+b[L−1] | ( n [ L − 1 ] , 209 ) (n^{[L-1]}, 209) (n[L−1],209) |
| Layer L | ( n [ L ] , n [ L − 1 ] ) (n^{[L]}, n^{[L-1]}) (n[L],n[L−1]) | ( n [ L ] , 1 ) (n^{[L]}, 1) (n[L],1) | Z [ L ] = W [ L ] A [ L − 1 ] + b [ L ] Z^{[L]} = W^{[L]} A^{[L-1]} + b^{[L]} Z[L]=W[L]A[L−1]+b[L] | ( n [ L ] , 209 ) (n^{[L]}, 209) (n[L],209) |
当我们在python中计算 W X + b W X + b WX+b时,使用广播,比如:
W = [ j k l m n o p q r ] X = [ a b c d e f g h i ] b = [ s t u ] (2) W = [jklmnopqr]\;\;\; X = [abcdefghi] \;\;\; b =[stu]\tag{2} W=⎣ ⎡jmpknqlor⎦ ⎤X=⎣ ⎡adgbehcfi⎦ ⎤b=⎣ ⎡stu⎦ ⎤(2)
Then W X + b WX+b WX+b will be:
W X + b = [ ( j a + k d + l g ) + s ( j b + k e + l h ) + s ( j c + k f + l i ) + s ( m a + n d + o g ) + t ( m b + n e + o h ) + t ( m c + n f + o i ) + t ( p a + q d + r g ) + u ( p b + q e + r h ) + u ( p c + q f + r i ) + u ] (3) WX + b = [(ja+kd+lg)+s(jb+ke+lh)+s(jc+kf+li)+s(ma+nd+og)+t(mb+ne+oh)+t(mc+nf+oi)+t(pa+qd+rg)+u(pb+qe+rh)+u(pc+qf+ri)+u]\tag{3} WX+b=⎣ ⎡(ja+kd+lg)+s(ma+nd+og)+t(pa+qd+rg)+u(jb+ke+lh)+s(mb+ne+oh)+t(pb+qe+rh)+u(jc+kf+li)+s(mc+nf+oi)+t(pc+qf+ri)+u⎦ ⎤(3)
练习:实现L层神经网络的初始化。
说明:
模型的结构为 [LINEAR -> RELU] × (L-1) -> LINEAR -> SIGMOID。也就是说,L−1层使用ReLU作为激活函数,最后一层采用sigmoid激活函数输出。
随机初始化权重矩阵。使用np.random.rand(shape)* 0.01。
零初始化偏差。使用np.zeros(shape)。
我们将在不同的layer_dims变量中存储
n
[
l
]
n^{[l]}
n[l],即不同层中的神经元数。例如,上周“二维数据分类模型”的layer_dims为[2,4,1]:即有两个输入,一个隐藏层包含4个隐藏单元,一个输出层包含1个输出单元。因此,W1的维度为(4,2),b1的维度为(4,1),W2的维度为(1,4),而b2的维度为(1,1)。现在你将把它应用到L层!
这是L=1(一层神经网络)的实现。以启发你如何实现通用的神经网络(L层神经网络)。
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))
In [5]:
# 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":
Wl -- weight matrix of shape (layer_dims[l], layer_dims[l-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
In [6]:
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.]]
预期输出:
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.]]
现在,你已经初始化了参数,接下来将执行正向传播模块。 首先实现一些基本函数,用于稍后的模型实现。按以下顺序完成三个函数:
线性正向模块(在所有数据中均进行向量化)的计算按照以下公式:
Z [ l ] = W [ l ] A [ l − 1 ] + b [ l ] (4) Z^{[l]} = W^{[l]}A^{[l-1]} +b^{[l]}\tag{4} Z[l]=W[l]A[l−1]+b[l](4)
其中 A [ 0 ] = X A^{[0]} = X A[0]=X
练习:建立正向传播的线性部分。
提醒:
该单元的数学表示为
Z
[
l
]
=
W
[
l
]
A
[
l
−
1
]
+
b
[
l
]
Z^{[l]} = W^{[l]}A^{[l-1]} +b^{[l]}
Z[l]=W[l]A[l−1]+b[l],你可能会发现np.dot()有用。 如果维度不匹配,则可以printW.shape查看修改。
In [7]:
# GRADED FUNCTION: linear_forward
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)
W -- weights matrix: numpy array of shape (size of current layer, size of previous layer)
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
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
In [8]:
A, W, b = linear_forward_test_case()
Z, linear_cache = linear_forward(A, W, b)
print("Z = " + str(Z))
Z = [[ 3.26295337 -1.23429987]]
预期输出:
Z = [[ 3.26295337 -1.23429987]]
在此笔记本中,你将使用两个激活函数:
Sigmoid:
σ
(
Z
)
=
σ
(
W
A
+
b
)
=
1
1
+
e
−
(
W
A
+
b
)
\sigma(Z) = \sigma(W A + b) = \frac{1}{ 1 + e^{-(W A + b)}}
σ(Z)=σ(WA+b)=1+e−(WA+b)1。 我们为你提供了“ Sigmoid”函数。 该函数返回两项值:激活值"a"和包含"Z"的"cache"(这是我们将馈入到相应的反向函数的内容)。 你可以按下述方式得到两项值:
A, activation_cache = sigmoid(Z)
ReLU:ReLu的数学公式为
A
=
R
E
L
U
(
Z
)
=
m
a
x
(
0
,
Z
)
A = RELU(Z) = max(0, Z)
A=RELU(Z)=max(0,Z)。我们为你提供了relu函数。 该函数返回两项值:激活值“A”和包含“Z”的“cache”(这是我们将馈入到相应的反向函数的内容)。 你可以按下述方式得到两项值:
A, activation_cache = relu(Z)
为了更加方便,我们把两个函数(线性和激活)组合为一个函数(LINEAR-> ACTIVATION)。 因此,我们将实现一个函数用以执行LINEAR正向步骤和ACTIVATION正向步骤。
练习:实现 LINEAR->ACTIVATION 层的正向传播。 数学表达式为: A [ l ] = g ( Z [ l ] ) = g ( W [ l ] A [ l − 1 ] + b [ l ] ) A^{[l]} = g(Z^{[l]}) = g(W^{[l]}A^{[l-1]} +b^{[l]}) A[l]=g(Z[l])=g(W[l]A[l−1]+b[l]),其中激活"g" 可以是sigmoid()或relu()。 使用linear_forward()和正确的激活函数。
In [9]:
# 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)
W -- weights matrix: numpy array of shape (size of current layer, size of previous layer)
b -- bias vector, numpy array of shape (size of the current layer, 1)
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
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
In [10]:
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. ]]
预期输出:
With sigmoid: A = [[0.96890023 0.11013289]]
With ReLU: A = [[3.43896131 0. ]]
注意:在深度学习中,"[LINEAR->ACTIVATION]"计算被视为神经网络中的单个层,而不是两个层。
为了方便实现L层神经网络,你将需要一个函数来复制前一个函数(使用RELU的linear_activation_forward)L−1次,以及复制带有SIGMOID的linear_activation_forward。
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CWKSYeNY-1662895428831)(L1W4%E4%BD%9C%E4%B8%9A1%20%E9%80%90%E6%AD%A5%E6%9E%84%E5%BB%BA%E4%BD%A0%E7%9A%84%E6%B7%B1%E5%BA%A6%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C.assets/960-1662529885234-1.png)]](https://1000bd.com/contentImg/2023/11/05/211148562.png)
图2 : [LINEAR -> RELU] × (L-1) -> LINEAR -> SIGMOID 模型
练习:实现上述模型的正向传播。
说明:在下面的代码中,变量AL表示
A
[
L
]
=
σ
(
Z
[
L
]
)
=
σ
(
W
[
L
]
A
[
L
−
1
]
+
b
[
L
]
)
A^{[L]} = \sigma(Z^{[L]}) = \sigma(W^{[L]} A^{[L-1]} + b^{[L]})
A[L]=σ(Z[L])=σ(W[L]A[L−1]+b[L])(有时也称为Yhat,即
Y
^
\hat{Y}
Y^。)
提示:
c添加到list中,可以使用list.append(c)。In [11]:
# GRADED FUNCTION: L_model_forward
def L_model_forward(X, parameters):
"""
Implement forward propagation for the [LINEAR->RELU]*(L-1)->LINEAR->SIGMOID computation
Arguments:
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)
"""
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
In [12]:
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
预期输出:
AL = [[0.17007265 0.2524272 ]]
Length of caches list = 2
现在,你有了一个完整的正向传播模块,它接受输入X并输出包含预测的行向量 A [ L ] A^{[L]} A[L]。 它还将所有中间值记录在"caches"中以计算预测的损失值。
现在,你将实现模型的正向和反向传播。 你需要计算损失,以检查模型是否在学习。
练习:使用以下公式计算交叉熵损失J:
−
1
m
∑
i
=
1
m
(
y
(
i
)
log
(
a
[
L
]
(
i
)
)
+
(
1
−
y
(
i
)
)
log
(
1
−
a
[
L
]
(
i
)
)
)
(7)
-\frac{1}{m} \sum\limits_{i = 1}^{m} (y^{(i)}\log\left(a^{[L] (i)}\right) + (1-y^{(i)})\log\left(1- a^{[L](i)}\right)) \tag{7}
−m1i=1∑m(y(i)log(a[L](i))+(1−y(i))log(1−a[L](i)))(7)
In [13]:
# GRADED FUNCTION: compute_cost
def compute_cost(AL, Y):
"""
Implement the cost function defined by equation (7).
Arguments:
AL -- probability vector corresponding to your label predictions, shape (1, number of examples)
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(Y * np.log(AL) + (1-Y) * np.log(1-AL),axis=1,keepdims=True)
### 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
In [14]:
Y, AL = compute_cost_test_case()
print("cost = " + str(compute_cost(AL, Y)))
cost = 0.41493159961539694
预期输出:
cost = 0.41493159961539694
就像正向传播一样,你将实现辅助函数以进行反向传播。 请记住,反向传播用于计算损失函数相对于参数的梯度。
提醒:
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zbNE7TBV-1662895428833)(L1W4%E4%BD%9C%E4%B8%9A1%20%E9%80%90%E6%AD%A5%E6%9E%84%E5%BB%BA%E4%BD%A0%E7%9A%84%E6%B7%B1%E5%BA%A6%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C.assets/960-1662529885235-2.png)]](https://1000bd.com/contentImg/2023/11/05/211148594.png)
图3:
LINEAR->RELU->LINEAR->SIGMOID 的正向和反向传播,紫色块代表正向传播,红色块代表反向传播。
对于那些精通微积分的人(不必进行此作业),可以使用微积分的链式规则来得出2层网络中的损失 L \mathcal{L} L相对于 z [ 1 ] z^{[1]} z[1]的导数,如下所示:
d L ( a [ 2 ] , y ) d z [ 1 ] = d L ( a [ 2 ] , y ) d a [ 2 ] d a [ 2 ] d z [ 2 ] d z [ 2 ] d a [ 1 ] d a [ 1 ] d z [ 1 ] (8) \frac{d \mathcal{L}(a^{[2]},y)}{{dz^{[1]}}} = \frac{d\mathcal{L}(a^{[2]},y)}{{da^{[2]}}}\frac{{da^{[2]}}}{{dz^{[2]}}}\frac{{dz^{[2]}}}{{da^{[1]}}}\frac{{da^{[1]}}}{{dz^{[1]}}} \tag{8} dz[1]dL(a[2],y)=da[2]dL(a[2],y)dz[2]da[2]da[1]dz[2]dz[1]da[1](8)
为了计算梯度 d W [ 1 ] = ∂ L ∂ W [ 1 ] dW^{[1]} = \frac{\partial L}{\partial W^{[1]}} dW[1]=∂W[1]∂L,请使用上一个链规则,然后执行 d W [ 1 ] = d z [ 1 ] × ∂ z [ 1 ] ∂ W [ 1 ] dW^{[1]} = dz^{[1]} \times \frac{\partial z^{[1]} }{\partial W^{[1]}} dW[1]=dz[1]×∂W[1]∂z[1]。 在反向传播的每个步骤中,你都将当前梯度乘以对应层的梯度,以获得所需的梯度。
同样地,为了计算梯度 d b [ 1 ] = ∂ L ∂ b [ 1 ] db^{[1]} = \frac{\partial L}{\partial b^{[1]}} db[1]=∂b[1]∂L,你使用前一个链规则,然后执行 d b [ 1 ] = d z [ 1 ] × ∂ z [ 1 ] ∂ b [ 1 ] db^{[1]} = dz^{[1]} \times \frac{\partial z^{[1]} }{\partial b^{[1]}} db[1]=dz[1]×∂b[1]∂z[1]。
这也是为什么我们称之为反向传播。
现在,类似于正向传播,你将分三个步骤构建反向传播:
ReLU或sigmoid 的导数计算对于层 l l l,线性部分为: Z [ l ] = W [ l ] A [ l − 1 ] + b [ l ] Z^{[l]} = W^{[l]} A^{[l-1]} + b^{[l]} Z[l]=W[l]A[l−1]+b[l](之后是激活)。
假设你已经计算出导数 d Z [ l ] = ∂ L ∂ Z [ l ] dZ^{[l]} = \frac{\partial \mathcal{L} }{\partial Z^{[l]}} dZ[l]=∂Z[l]∂L。 你想获得 ( d W [ l ] , d b [ l ] d A [ l − 1 ] ) (dW^{[l]}, db^{[l]} dA^{[l-1]}) (dW[l],db[l]dA[l−1])。
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tKWyKw63-1662895428834)(L1W4%E4%BD%9C%E4%B8%9A1%20%E9%80%90%E6%AD%A5%E6%9E%84%E5%BB%BA%E4%BD%A0%E7%9A%84%E6%B7%B1%E5%BA%A6%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C.assets/960-1662529885235-3.png)]](https://1000bd.com/contentImg/2023/11/05/211148706.png)
图4
使用输入 d Z [ l ] dZ^{[l]} dZ[l]计算三个输出 ( d W [ l ] , d b [ l ] , d A [ l ] ) (dW^{[l]}, db^{[l]}, dA^{[l]}) (dW[l],db[l],dA[l])。以下是所需的公式:
d W [ l ] = ∂ L ∂ W [ l ] = 1 m d Z [ l ] A [ l − 1 ] T (8) dW^{[l]} = \frac{\partial \mathcal{L} }{\partial W^{[l]}} = \frac{1}{m} dZ^{[l]} A^{[l-1] T} \tag{8} dW[l]=∂W[l]∂L=m1dZ[l]A[l−1]T(8)
d b [ l ] = ∂ L ∂ b [ l ] = 1 m ∑ i = 1 m d Z [ l ] ( i ) (9) db^{[l]} = \frac{\partial \mathcal{L} }{\partial b^{[l]}} = \frac{1}{m} \sum_{i = 1}^{m} dZ^{[l](i)}\tag{9} db[l]=∂b[l]∂L=m1i=1∑mdZ[l](i)(9)
d A [ l − 1 ] = ∂ L ∂ A [ l − 1 ] = W [ l ] T d Z [ l ] (10) dA^{[l-1]} = \frac{\partial \mathcal{L} }{\partial A^{[l-1]}} = W^{[l] T} dZ^{[l]} \tag{10} dA[l−1]=∂A[l−1]∂L=W[l]TdZ[l](10)
练习:使用上面的3个公式实现linear_backward()。
In [15]:
# GRADED FUNCTION: linear_backward
def linear_backward(dZ, cache):
"""
Implement the linear portion of backward propagation for a single layer (layer l)
Arguments:
dZ -- Gradient of the cost with respect to the linear output (of current layer l)
cache -- tuple of values (A_prev, W, b) coming from the forward propagation in the current layer
Returns:
dA_prev -- Gradient of the cost with respect to the activation (of the previous layer l-1), same shape as A_prev
dW -- Gradient of the cost with respect to W (current layer l), same shape as W
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 = 1 / m * np.dot(dZ ,A_prev.T)
db = 1 / m * np.sum(dZ,axis = 1 ,keepdims=True)
dA_prev = np.dot(W.T,dZ)
### END CODE HERE ###
assert (dA_prev.shape == A_prev.shape)
assert (dW.shape == W.shape)
assert (db.shape == b.shape)
return dA_prev, dW, db
In [16]:
# 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.50629448]]
预期输出:
dA_prev = [[ 0.51822968 -0.19517421]
[-0.40506361 0.15255393]
[ 2.37496825 -0.89445391]]
dW = [[-0.10076895 1.40685096 1.64992505]]
db = [[0.50629448]]
接下来,创建一个合并两个辅助函数的函数:linear_backward 和反向步骤的激活 linear_activation_backward。
为了帮助你实现linear_activation_backward,我们提供了两个反向函数:
sigmoid_backward:实现SIGMOID单元的反向传播。 你可以这样使用:dZ = sigmoid_backward(dA, activation_cache)
relu_backward:实现RELU单元的反向传播。 你可以这样使用:dZ = relu_backward(dA, activation_cache)
如果
g
(
.
)
g(.)
g(.)是激活函数,
sigmoid_backward和relu_backward计算
d
Z
[
l
]
=
d
A
[
l
]
∗
g
′
(
Z
[
l
]
)
(11)
dZ^{[l]} = dA^{[l]} * g'(Z^{[l]}) \tag{11}
dZ[l]=dA[l]∗g′(Z[l])(11)
练习:实现LINEAR->ACTIVATION 层的反向传播。
In [17]:
# GRADED FUNCTION: linear_activation_backward
def linear_activation_backward(dA, cache, activation):
"""
Implement the backward propagation for the LINEAR->ACTIVATION layer.
Arguments:
dA -- post-activation gradient for current layer l
cache -- tuple of values (linear_cache, activation_cache) we store for computing backward propagation efficiently
activation -- the activation to be used in this layer, stored as a text string: "sigmoid" or "relu"
Returns:
dA_prev -- Gradient of the cost with respect to the activation (of the previous layer l-1), same shape as A_prev
dW -- Gradient of the cost with respect to W (current layer l), same shape as W
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)
dA_prev, dW, db = linear_backward(dZ, linear_cache)
### END CODE HERE ###
elif activation == "sigmoid":
### START CODE HERE ### (≈ 2 lines of code)
dZ = sigmoid_backward(dA, activation_cache)
dA_prev, dW, db = linear_backward(dZ, linear_cache)
### END CODE HERE ###
return dA_prev, dW, db
In [18]:
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.05729622]]
relu:
dA_prev = [[ 0.44090989 0. ]
[ 0.37883606 0. ]
[-0.2298228 0. ]]
dW = [[ 0.44513824 0.37371418 -0.10478989]]
db = [[-0.20837892]]
sigmoid 输出:
dA_prev = [[ 0.11017994 0.01105339]
[ 0.09466817 0.00949723]
[-0.05743092 -0.00576154]]
dW = [[ 0.10266786 0.09778551 -0.01968084]]
db = [[-0.05729622]]
relu 输出:
dA_prev = [[ 0.44090989 0. ]
[ 0.37883606 0. ]
[-0.2298228 0. ]]
dW = [[ 0.44513824 0.37371418 -0.10478989]]
db = [[-0.20837892]]
现在,你将为整个网络实现反向传播函数。 回想一下,当你实现L_model_forward函数时,在每次迭代中,你都存储了一个包含(X,W,b和z)的缓存。 在反向传播模块中,你将使用这些变量来计算梯度。 因此,在L_model_backward函数中,你将从L层开始向后遍历所有隐藏层。在每个步骤中,你都将使用
l
l
l层的缓存值反向传播到层
l
l
l。 图5展示了反向传播过程。
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mUEV3xUW-1662895428835)(L1W4%E4%BD%9C%E4%B8%9A1%20%E9%80%90%E6%AD%A5%E6%9E%84%E5%BB%BA%E4%BD%A0%E7%9A%84%E6%B7%B1%E5%BA%A6%E7%A5%9E%E7%BB%8F%E7%BD%91%E7%BB%9C.assets/960-1662529885235-4.png)]](https://1000bd.com/contentImg/2023/11/05/211148648.png)
图5:反向流程
初始化反向传播:
为了使网络反向传播,我们知道输出是
A
[
L
]
=
σ
(
Z
[
L
]
)
A^{[L]} = \sigma(Z^{[L]})
A[L]=σ(Z[L])。因此,你的代码需要计算dAL
=
∂
L
∂
A
[
L
]
= \frac{\partial \mathcal{L}}{\partial A^{[L]}}
=∂A[L]∂L。
为此,请使用以下公式(不需要深入的微积分知识):
dAL = - (np.divide(Y, AL) - np.divide(1 - Y, 1 - AL)) # derivative of cost with respect to AL
然后,你可以使用此激活后的梯度dAL继续反向传播。如图5所示,你现在可以将dAL输入到你实现的LINEAR-> SIGMOID反向函数中(它将使用L_model_forward函数存储的缓存值)。之后,你得通过for循环,使用LINEAR-> RELU反向函数迭代所有其他层。同时将每个dA,dW和db存储在grads词典中。为此,请使用以下公式:
g r a d s [ " d W " + s t r ( l ) ] = d W [ l ] (15) grads["dW" + str(l)] = dW^{[l]}\tag{15} grads["dW"+str(l)]=dW[l](15)
例如,当
l
=
3
l=3
l=3时,它将在grads["dW3"]中存储
d
W
[
l
]
dW^{[l]}
dW[l]。
练习:实现 [LINEAR->RELU] × (L-1) -> LINEAR -> SIGMOID 模型的反向传播。
In [19]:
# GRADED FUNCTION: L_model_backward
def L_model_backward(AL, Y, caches):
"""
Implement the backward propagation for the [LINEAR->RELU] * (L-1) -> LINEAR -> SIGMOID group
Arguments:
AL -- probability vector, output of the forward propagation (L_model_forward())
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])
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[L - 1]
dA_prev_temp, dW_temp, db_temp = linear_activation_backward(dAL, current_cache, "sigmoid")
grads["dA" + str(L-1)] = dA_prev_temp
grads["dW" + str(L)] = dW_temp
grads["db" + str(L)] = db_temp
### 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_activation_backward(grads["dA" + str(l+1)], current_cache, activation = "relu")
grads["dA" + str(l)] = dA_prev_temp
grads["dW" + str(l+1)] = dW_temp
grads["db" + str(l+1)] = db_temp
### END CODE HERE ###
return grads
In [20]:
AL, Y_assess, caches = L_model_backward_test_case()
grads = L_model_backward(AL, Y_assess, caches)
print ("dW1 = "+ str(grads["dW1"]))
print ("db1 = "+ str(grads["db1"]))
print ("dA1 = "+ str(grads["dA1"]))
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]]
预期输出:
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]]
在本节中,你将使用梯度下降来更新模型的参数:
W [ l ] = W [ l ] − α d W [ l ] (16) W^{[l]} = W^{[l]} - \alpha \text{ } dW^{[l]} \tag{16} W[l]=W[l]−α dW[l](16)
b [ l ] = b [ l ] − α d b [ l ] (17) b^{[l]} = b^{[l]} - \alpha \text{ } db^{[l]} \tag{17} b[l]=b[l]−α db[l](17)
其中 α 是学习率。 在计算更新的参数后,将它们存储在参数字典中。
练习:实现update_parameters()以使用梯度下降来更新模型参数。
说明:
对于
l
=
1
,
2
,
.
.
.
,
L
l = 1, 2, ..., L
l=1,2,...,L,使用梯度下降更新每个
W
[
l
]
W^{[l]}
W[l] 和
b
[
l
]
b^{[l]}
b[l]的参数。
In [21]:
# GRADED FUNCTION: update_parameters
def update_parameters(parameters, grads, learning_rate):
"""
Update parameters using gradient descent
Arguments:
parameters -- python dictionary containing your parameters
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
In [22]:
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"]))
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]]
预期输出:
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]]
恭喜你实现了构建深度神经网络所需的所有函数!
我们知道这是一项艰巨的任务,但是继续前进将变得更好。 下一部分的作业相对容易。
在下一个作业中,你将使用这些函数构建两个模型用于分类猫图像和非猫图像: