逐步构建你的深度神经网络
在此之前你已经训练了一个2层的神经网络(只有一个隐藏层)。本周,你将学会构建一个任意层数的深度神经网络!
- 在此作业中,你将实现构建深度神经网络所需的所有函数。
- 在下一个作业中,你将使用这些函数来构建一个用于图像分类的深度神经网络。
完成此任务后,你将能够:
- 使用ReLU等非线性单位来改善模型
- 建立更深的神经网络(具有1个以上的隐藏层)
- 实现一个易于使用的神经网络类
符号说明:
- 上标
[
l
]
[l]
[l]表示与
l
t
h
l^{th}
lth层相关的数量。
- 示例: a [ L ] a^{[L]} a[L]是 L t h L^{th} Lth层的激活。 W [ L ] W^{[L]} W[L]和 b [ L ] b^{[L]} b[L]是 L t h L^{th} Lth层参数。
- 上标
(
i
)
(i)
(i)表示与
i
t
h
i^{th}
ith示例相关的数量。
- 示例: x ( i ) x^{(i)} x(i)是第 i t h i^{th} ith的训练数据。
- 下标
i
i
i表示
i
t
h
i^{th}
ith的向量。
- 示例: a i [ l ] a_i^{[l]} ai[l]表示 l t h l^{th} lth层激活的 i t h i^{th} ith输入。
1 安装包
让我们首先导入作业过程中需要的所有包。
- numpy是Python科学计算的基本包。
- matplotlib是在Python中常用的绘制图形的库。
- dnn_utils为此笔记本提供了一些必要的函数。
- testCases提供了一些测试用例来评估函数的正确性
- np.random.seed(1)使所有随机函数调用保持一致。 这将有助于我们评估你的作业,请不要改变seed。
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)
2 作业大纲
为了构建你的神经网络,你将实现几个“辅助函数”。这些辅助函数将在下一个作业中使用,用来构建一个两层神经网络和一个L层的神经网络。你将实现的每个函数都有详细的说明,这些说明将指导你完成必要的步骤。此作业的大纲如下:
- 初始化两层的网络和 L L L层的神经网络的参数。
- 实现正向传播模块(在下图中以紫色显示)。
- 完成模型反向传播步骤的LINEAR部分。
- 提供的ACTIVATE函数的梯度(relu_backward / sigmoid_backward)
- 将前两个步骤组合成新的[LINEAR-> ACTIVATION]反向函数。
- 将[LINEAR-> RELU]向后堆叠L-1次,并在新的L_model_backward函数中后向添加[LINEAR-> SIGMOID]
- 最后更新参数。
注意:对于每个正向函数,都有一个对应的反向函数。 这也是为什么在正向传播模块的每一步都将一些值存储在缓存中的原因。缓存的值可用于计算梯度。 然后,在反向传导模块中,你将使用缓存的值来计算梯度。 此作业将指导说明如何执行这些步骤。
3 初始化
首先编写两个辅助函数来初始化模型的参数。第一个函数将用于初始化两层模型的参数。第二个将把初始化过程推广到 L L L层模型上。
3.1 2层神经网络
练习:创建并初始化2层神经网络的参数。
说明:
- 模型的结构为:Linear -> ReLu -> Linear -> Sigmoid。
- 随机初始化权重矩阵。确保准确的维度,使用np.random.randn(shape) * 0.01。
- 将偏差初始化为0。使用np.zeros(shape)。
def initialize_parameters(n_x, n_h, n_y):
"""
参数:
n_x -- 输入层的大小
n_h -- 隐藏层的大小
n_y -- 输出层的大小
"""
np.random.seed(1)
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
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层神经网络
更深的L层神经网络的初始化更加复杂,因为存在更多的权重矩阵和偏差向量。 完成 initialize_parameters_deep后,应确保各层之间的维度匹配。 回想一下, n [ l ] n^{[l]} n[l]是层中的神经元数量。 因此,如果我们输入的 X X X的大小为 ( 12288 , 209 ) (12288, 209) (12288,209)(以 m = 209 m=209 m=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 = \begin{bmatrix} j & k & l\\ m & n & o \\ p & q & r \end{bmatrix}\;\;\; X = \begin{bmatrix} a & b & c\\ d & e & f \\ g & h & i \end{bmatrix} \;\;\; b =\begin{bmatrix} s \\ t \\ u \end{bmatrix}\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 = \begin{bmatrix} (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 \end{bmatrix}\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] × \times × (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 L层!
- 这是 L = 1 L=1 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))
def initialize_parameters_deep(layer_dims):
"""
参数:
layer_dims -- Python数组(列表),包含网络中每一层的维度
"""
np.random.seed(3)
parameters = {}
L = len(layer_dims)
for l in range(1, L):
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))
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 线性正向
现在,你已经初始化了参数,接下来将执行正向传播模块。 首先实现一些基本函数,用于稍后的模型实现。按以下顺序完成三个函数:
- Linear
- Linear -> Activation,其中激活函数采用ReLu或者Sigmoid。
- [Linear - ReLu] × \times × (L-1) -> Linear -> Sigmoid(整个模型)
线性正向模块(在所有数据中均进行向量化)的计算按照以下公式:
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]
其中
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()有用。如果维度不匹配,则可以print(W.shape)查看修改。
def linear_forward(A, W, b):
Z = np.dot(W, A) + b
assert(Z.shape == (W.shape[0], A.shape[1]))
cache = (A, W, b)
return Z, cache
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 正向线性激活
在此笔记本中,你将使用两个激活函数:
- 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, 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()和正确的激活函数。
def linear_activation_forward(A_prev, W, b, activation):
"""
参数:
A_prev -- 来自前一层(或输入数据)的激活:(前一层的大小,示例数)
W -- 权重矩阵:形状numpy数组(当前层大小,前一层大小)
b -- 偏置向量,形状数组(当前层大小,1)
activation -- 要在这个层中使用的激活,存储为一个文本字符串:"sigmoid"或"relu"
"""
if activation == "sigmoid":
Z, linear_cache = linear_forward(A_prev, W, b)
A, activation_cache = sigmoid(Z)
elif activation == "relu":
Z, linear_cache = linear_forward(A_prev, W, b)
A, activation_cache = relu(Z)
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. ]]
注意:在深度学习中,"[LINEAR->ACTIVATION]"计算被视为神经网络中的单个层,而不是两个层。
4.3 L层模型
为了方便实现
L
L
L层神经网络,你将需要一个函数来复制前一个函数(使用ReLu的linear_activation_forward)
L
−
1
L-1
L−1次,以及复制带有Sigmoid的linear_activation_forward。
图2:[Linear -> ReLu] × \times ×(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^。)
提示:
- 使用你先前编写的函数
- 使用for循环复制[LINEAR-> RELU](L-1)次
- 不要忘记在“cache”列表中更新缓存。 要将新值 c添加到list中,可以使用list.append©。
def L_model_forward(X, parameters):
caches = []
A = X
L = len(parameters) // 2 # 神经网络的层数
for l in range(1, L):
A_prev = A
A, cache = linear_activation_forward(A_prev, parameters['W' + str(l)], parameters['b' + str(l)], activation = "relu")
caches.append(cache)
# 输出层
AL, cache = linear_activation_forward(A, parameters['W' + str(L)], parameters['b' + str(L)], activation = "sigmoid")
caches.append(cache)
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
现在,你有了一个完整的正向传播模块,它接受输入X并输出包含预测的行向量 A [ L ] A^{[L]} A[L]。它还将所有中间值记录在"caches"中以计算预测的损失值。
5 损失函数
现在,你将实现模型的正向和反向传播。你需要计算损失,以检查模型是否在学习。
练习:使用以下计算交叉熵损失
J
J
J:
−
1
m
∑
i
=
1
m
(
y
(
i
)
log
(
a
[
L
]
(
i
)
)
+
(
1
−
y
(
i
)
)
log
(
1
−
a
[
L
]
(
i
)
)
)
-\frac {1}{m}\sum_{i=1}^{m}(y^{(i)}\log(a^{[L](i)})+(1-y^{(i)})\log(1-a^{[L](i)}))
−m1i=1∑m(y(i)log(a[L](i))+(1−y(i))log(1−a[L](i)))
def compute_cost(AL, Y):
"""
参数:
AL -- 最后激活后价值,形状(1,样例数量)
Y -- True "label"向量(例如:如果非cat则为0,如果cat则为1),形状(1,示例数量)
"""
m = Y.shape[1]
cost = -1 / m * np.sum(Y * np.log(AL) + (1 - Y) * np.log(1 - AL), axis = 1, keepdims = True)
cost = np.squeeze(cost) # 确保你的成本符合我们的预期(例如,将[[17]]变为17)。
assert(cost.shape == ())
return cost
Y, AL = compute_cost_test_case()
print("cost = " + str(compute_cost(AL, Y)))
cost = 0.41493159961539694
6 反向传播模块
就像正向传播一样,你将实现辅助函数以进行反向传播。 请记住,反向传播用于计算损失函数相对于参数的梯度。
提醒:
图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]。
这也是为什么我们称之为反向传播。
现在,类似于正向传播,你将分三个步骤构建反向传播:
- Linear backward
- Linear -> activation backward,其中激活函数使用ReLu或Sigmoid的导数计算
- [Linear -> ReLu] × \times × (L - 1) -> Linear -> Sigmoid backward (整个模型)
6.1 线性反向
对于层 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])。
图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()。
def linear_backward(dZ, cache):
A_prev, W, b = cache
m = A_prev.shape[1]
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)
assert (dA_prev.shape == A_prev.shape)
assert (dW.shape == W.shape)
assert (db.shape == b.shape)
return dA_prev, dW, db
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]]
6.2 反向线性激活
接下来,创建一个合并两个辅助函数的函数:linear_brackward
和反向步骤的激活`linear_activation_backward``。
为了帮助你实现linear_activation_backward
,我们提供了两个反向函数:
sigmoid_brackward
:实现Sigmoid单元的反向传播。你可以这样使用:dZ = sigmoid_backward(dA, activation_cache)
relu_brackward
:实现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层的反向传播。
def linear_activation_backward(dA, cache, activation):
linear_cache, activation_cache = cache
if activation == "relu":
dZ = relu_backward(dA, activation_cache)
dA_prev, dW, db = linear_backward(dZ, linear_cache)
elif activation == "sigmoid":
dZ = sigmoid_backward(dA, activation_cache)
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.05729622]]
relu:
dA_prev = [[ 0.44090989 0. ]
[ 0.37883606 0. ]
[-0.2298228 0. ]]
dW = [[ 0.44513824 0.37371418 -0.10478989]]
db = [[-0.20837892]]
6.3 反向L层模型
现在,你将为整个网络实现反向传播函数。回想一下,当你实现L_model_forward函数时在每次迭代中,你都存储了一个包含(X, W, b和z)的缓存。在反向传播模块中,你将使用这些变量来计算梯度。因此,在L_model_backward函数中,你将从
L
L
L层开始向后遍历所有隐藏层。每个步骤中,你都将使用
l
l
l层的缓存值反向传播到层
l
l
l。图5展示了反向传播过程。
图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)) # 成本对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] × \times × (L - 1) -> Linear -> Sigmoid模型的反向传播。
def L_model_backward(AL, Y, caches):
grads = {}
L = len(caches)
m = AL.shape[1]
Y = Y.reshape(AL.shape)
dAL = -(np.divide(Y, AL) - np.divide(1 - Y, 1 - AL))
current_cache = caches[L - 1]
grads["dA" + str(L)], grads["dW" + str(L)], grads["db" + str(L)] = linear_activation_backward(dAL, current_cache, activation = "sigmoid")
for l in reversed(range(L - 1)):
current_cache = caches[l]
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
return grads
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]]
6.4 更新参数
在本节中,你将使用梯度下降来更新模型的参数:
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)
其中
α
\alpha
α是学习率。在计算更新的参数后,将它们存储在参数字典中。
练习:实现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]的参数。
def update_parameters(parameters, grads, learning_rate):
L = len(parameters) // 2
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)]
return 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"]))
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 结论
恭喜你实现了构建深度神经网络所需的所有函数!
我们知道这是一项艰巨的任务,但是继续前进将变得更好。 下一部分的作业相对容易。
在下一个作业中,你将使用这些函数构建两个模型用于分类猫图像和非猫图像:
- 两层神经网络
- L层神经网络