勘误:
parameters["W" + str(i)] = np.random.randn(layer_dims[i], layer_dims[i - 1]) / np.sqrt(layer_dims[i - 1])
,这里暂时不需要理解这种初始化方式,在course2中理解即可,如果这里使用原来的初始化方式会导致梯度下降滞塞。在前面的作业中,我们已经训练了2层的神经网络(其中一层为隐藏层)。在这一篇内容中,我们将学习如何建立深度神经网络,该神经网络有着许多你想要的层!
在本次作业后,我们将学会:
我们在pyCharm中(这里使用Anaconda进行环境配置,使用PyCharm进行编程)导入如下的包:
如果你不知道怎么安装,可以参考我前边的文章:
这两篇文章中使用环境和库的安装可能会帮到你。
在main.py中输入一下下面这段代码看看所需的包是否都有了,如果有报红的,请卸载后重新安装或尝试其他的方法:
import numpy as np
import h5py
import matplotlib.pyplot as plt
from course1.week4.testCases import *
from course1.week4.dnn_utils import sigmoid, sigmoid_backward, relu, relu_backward
plt.rcParams['figure.figsize'] = (5.0, 4.0) # set default size of plots
plt.rcParams['image.interpolation'] = 'nearest'
plt.rcParams['image.cmap'] = 'gray'
np.random.seed(1)
为了建立神经网络,你需要完善很多函数。这些函数将会被在下一个任务建立两层和L层神经网络时用到。每一个小的函数都会帮助你完善接下来的介绍的这些步骤,这里有一个任务大纲,你将:
目标:创建并初始化一个两层的神经网络所需要的参数(初始化W1,b1,W2,b2)
引导步骤:
np.random.randn(shape)*0.01
来初始化np.zeros(shape)
可以尝试自己写一下函数initialize_parameters(n_x, n_h, n_y)
,其中:
需要返回一个parameters字典,包含以下信息:
尝试自己动手写一下吧!
这个初始化的写法如下:
def initialize_parameters(n_x, n_h, n_y):
np.random.seed(1)
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))
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.]]
理论基础:
L层神经网络的初始化非常复杂因为有很多权重矩阵和偏差向量。当完成下边的initialize_parameters_deep
函数时,你需要确定你的函数能够匹配任意层数的维度。回顾一下,n^ [l]是第l层的单元数,因此,例如我们输入的X的大小是(12288,209)(即m = 209个样本),则:
记住,当在Python中计算WX+b时,会触发广播机制,例如,如果有以下的W、X和b:
则WX+b将会为:
目标: 为L层神经网络进行初始化
引导步骤:
np.random.randn(shape) * 0.01
np.zeros(shape)
layer_dims
中。例如,在上一周我们练习平面数据集分类模型时,有2个输入,隐藏层有4个隐藏单元,输出层有1个输出单元,这意味着W1的维度是(4,2),b1的维度是(4,1),W2的维度是(1,4),b2的维度是(1,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))
这里我们要求指定随机种子:np.random.seed(3)
那么,尝试完善一下你的函数:initialize_parameters_deep(layer_dims)
吧!其中:
这个函数需要返回:
def initialize_parameters_deep(layer_dims):
np.random.seed(3)
parameters = {}
L = len(layer_dims)
for i in range(1, L):
parameters["W" + str(i)] = np.random.randn(layer_dims[i], layer_dims[i - 1]) / np.sqrt(layer_dims[i - 1])
parameters["b" + str(i)] = np.zeros(shape=(layer_dims[i], 1))
assert (parameters["W" + str(i)].shape == (layer_dims[i], layer_dims[i - 1]))
assert (parameters["b" + str(i)].shape == (layer_dims[i], 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.]]
现在你已经完成了参数的初始化,接下来,你将要做的是前向传播模块。你将从完善一些接下来你看到的基础函数开始一步步完善模型。在这个部分中你将要完成3个函数:
线性化模块(将所有的例子向量化)通过以下的等式计算:
在这其中,
目标: 建立线性部分的前向传播
提示: 在计算上边的等式时,你会发现np.dot()
很好用,如果你的维度不匹配,打印W.shape可能会对你有帮助。
那么,尝试完善一下你函数linear_forward(A, W, b)
吧~
其中:
要求你返回:
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
在这里说一下断言内的意思,为什么Z.shape == (W.shape[0], A.shape[1])
?
首先要明白,shape[0]和shape[1]是什么意思。举个例子,有一个矩阵的shape是(4,3),那么这个矩阵的shape[0]就是4,shape[1]就是3,即4行3列。
在矩阵相乘中,我们要求前边这个参数的列数要等于后边这个参数的行数,而乘出来的结果就是(前边参数的行数,后边参数的列数)。这里np.dot(W, A)
得到的结果就是(W.shape[0], A.shape[1])
,而b的shape为(W.shape[0],1)
(注意,b的行数是和W的行数保持一致的,从上边初始化的内容就能够理解)。在两个结果相加时,会触发广播机制,将b的一列复制为A.shape[1]列。从而得到Z的维度就是(W.shape[0], A.shape[1])
。
直接复制下边的代码,测试一下你的函数:
A, W, b = linear_forward_test_case()
Z, linear_cache = linear_forward(A, W, b)
print("Z = " + str(Z))
结果是(这里我和原作者【学习链接2】出现了偏差(可能是种子有问题),我复制原作者的代码与此结果相同):
Z = [[ 3.26295337 -1.23429987]]
在这里,你将要使用两个激活函数。
A, activation_cache = sigmoid(Z)
。A, activation_cache = relu(Z)
。为了更方便,你需要组合两个函数(Linear和Activation)到一个函数中(Linear->Activation
)。在这里,你将完善一个函数,它会完成线性前向传播步骤,随后是激活传播步骤。
目标: 完成前向传播层,实现LINEAR -> ACTIVATION。数学关系在这里:
这里的激活函数“g”就是sigmoid()或relu()。使用前边我们写好的linear_forward()
和正确的激活函数来完成linear_activation_forward(A_prev, W, b, activation)
其中:
返回:
def linear_activation_forward(A_prev, W, b, activation):
Z, linear_cache = linear_forward(A_prev, W, b)
if activation == "sigmoid":
A, activation_cache = sigmoid(Z)
elif activation == "relu":
A, activation_cache = relu(Z)
assert (A.shape == (W.shape[0], A_prev.shape[1]))
cache = (linear_cache, activation_cache)
return A, cache
经过激活,A的维度和Z的维度其实还是一样的。
直接复制下边的代码:测试一下你的函数是否正确:
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))
结果为(这里我和原作者【学习链接2】出现了偏差(可能是种子有问题),我复制原作者的代码与此结果相同):
With sigmoid: A = [[0.96890023 0.11013289]]
With ReLU: A = [[3.43896131 0. ]]
为了在完成L层神经网络时更便利,你需要一个函数来复制linear_activation_forward
with ReLU L - 1次,然后再使用linear_activation_forward
with SIGMOID.
目标: 完善上面模型中的前向传播
声明: 在接下来的代码中,AL代表
这有时候也称为Y帽。
提示:
list.append(c)
那么,请开始写你的函数:L_model_forward(X, parameters)
吧!
其中:
需要返回:
linear_relu_forward()
得到的缓存(一共有L-1个,索引从0到L-2)linear_sigmoid_forward()
得到的缓存(只有一个,索引为L-1)def L_model_forward(X, parameters):
caches = []
A = X
# 每一层都应该有W和B两个参数,所以parameters//2就是层数
L = len(parameters) // 2 # 这里的除号意思是“地板除”,除完后向下取整,见学习链接4
# 0-(L-1)层我们使用ReLU方法
for i in range(1, L):
A_prev = A
A, cache = linear_activation_forward(A_prev,
parameters["W" + str(i)],
parameters["b" + str(i)],
activation="relu")
caches.append(cache)
# 第L层(最后一层)我们使用sigmoid方法使最终结果处于0-1之间
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
博主认为倒数第二行的断言写下边这句更准确。因为parameters[“W” + str(L)].shape[0]就是神经网络第L层的单元数
assert (AL.shape == (parameters["W" + str(L)].shape[0], X.shape[1]))
复制下边的代码测试一下你的函数吧!:
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)))
这里中见到的“//”两个除号是地板除,详见:python中除号/和//的区别。即在完成除法操作后向下取整。
结果为(这里我和原作者【学习链接2】出现了偏差(可能是种子有问题),我复制原作者的代码与此结果相同):
AL = [[0.17007265 0.2524272 ]]
Length of caches list = 2
到现在为止,我们已经完成了完整的前向传播,输入X从而输出包含着预测结果的A^ [l],同时也记录了中间的缓存在“caches”中。使用这里的A^ [l],我们可以计算预测的损失。
到这里,你已经完成了前向传播,你需要计算成本,因为你需要通过计算成本来检查你的模型是否真的在学习。
目标: 计算叉乘损失J,只需要使用下面的公式:
接下来,请利用你已有的知识完成成本函数的计算吧!compute_cost(AL, Y)
其中,AL是预测的值,Y是真实的“标签”,需要你输出损失函数的值。
def compute_cost(AL, Y):
# m,即数据集的大小,Y的大小是(1,m)
m = Y.shape[1]
# 根据公式写出损失函数
cost = -(1 / m) * (np.sum(np.multiply(Y, np.log(AL)) + np.multiply(1 - Y, np.log(1 - AL))))
# 降维,保证损失是是一个数,而不是一个矩阵/数组一样的东西
cost = np.squeeze(cost)
return cost
拿下边的代码来测试一下你写的是否正确吧!
Y, AL = compute_cost_test_case()
print("cost = " + str(compute_cost(AL, Y)))
我得到的结果如下:
cost = 0.41493159961539694
如前向传播一样,你将要完善反向传播的函数。要记住反向传播是被用来计算损失函数的斜率从而更新函数的。
类似于前向传播,你建立反向传播一共有以下三步:
对于第L层,linear部分是这么计算的:
这一步后就是激活函数的计算了。
假设你已经计算了导数:
你想要得到
这些数。
我们通过一张图更直观的理解我们上边说的这些话。
三个输出(dW^ [l],db^ [l],dA^ [l])将通过dz^ [l]进行计算。这里有三个你可能需要用到的公式:
目标: 用上边给的三个公式来完成函数linear_backward(dZ, cache)
。
其中:
需要返回(这里没翻译好,反正就是上边那张图中公式的意思):
由我们写的前向传播函数可以知道,cache中放的东西包括A、W、b。
def linear_backward(dZ, cache):
A_prev, W, b = cache
# W.shape[0]是本层的神经单元数
# W.shape[1]是上一层的神经单元数
# 只有在通过A = g(WX + b)的计算之后,得到的A的shape[1]才是数量
m = A_prev.shape[1]
dW = np.dot(dZ, A_prev.T) / m
db = np.sum(dZ, axis=1, keepdims=True) / m
dA_prev = np.dot(W.T, dZ)
assert (dW.shape == W.shape)
assert (dA_prev.shape == A_prev.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))
结果为(这里我和原作者【学习链接2】出现了偏差(可能是种子有问题),我复制原作者的代码与此结果相同):
dA_prev = [[ 0.51822968 -0.19517421]
[-0.40506361 0.15255393]
[ 2.37496825 -0.89445391]]
dW = [[-0.10076895 1.40685096 1.64992505]]
db = 0.5062944750065832
接下来你需要写的函数包含了两个函数:linear_backward
和激活函数的反向传播步骤linear_activation_backward
。
为了帮助你完成linear_activation_backward
,我们提供了两个反向函数给你。
如果导数g(.)是激活函数,你可以使用sigomid_backward和relu_backward来计算:
目标: 完成LINEAR -> ACTIVATION层的反向传播。完成linear_activation_backward(dA, cache, activation)
函数。
其中:
这里cache的来源是我们上边写过的一个函数linear_activation_forward()
,我们写的这个函数是用于线性激活函数的正向传播。
1、linear_cache的来源就是linear_forward()
函数。
由此可见,linear_cache包含的就是线性正向传播中的(A,W,b)
2、activation_cache的来源是sigmoid()
和relu(
)函数。
由此看来,activation_cache实际上就是激活函数正向传播中的Z~
理解了以后,请根据公式大胆使用linear_cache和activation_cache吧!
要求返回:
def linear_activation_backward(dA, cache, activation):
linear_cache, activation_cache = cache
if activation == "sigmoid":
dZ = sigmoid_backward(dA, activation_cache)
elif activation == "relu":
dZ = relu_backward(dA, activation_cache)
dA_prev, dW, db = linear_backward(dZ, linear_cache)
return dA_prev, dW, db
有的人可能会有疑问,为什么cache里有linear_cache和activation_cache?这一点看一下正向传播时的linear_activation
函数就知道了。
直接复制这段代码试试你的函数是否可行,若不可行,根据错误逐步修改:
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))
结果为(这里我和原作者【学习链接2】出现了偏差(可能是种子有问题),我复制原作者的代码与此结果相同):
sigmoid:
dA_prev = [[ 0.11017994 0.01105339]
[ 0.09466817 0.00949723]
[-0.05743092 -0.00576154]]
dW = [[ 0.10266786 0.09778551 -0.01968084]]
db = -0.057296222176291135
relu:
dA_prev = [[ 0.44090989 0. ]
[ 0.37883606 0. ]
[-0.2298228 0. ]]
dW = [[ 0.44513824 0.37371418 -0.10478989]]
db = -0.2083789237027353
现在你将完善整个网络的反向传播函数。回顾当你完善L_model_forward
函数,在每一次迭代过程中,你都存储了一个包含(X,W,b 和 z)的cache。在反向传播模块中,你将使用这些变量来计算斜率。因此,在L_model_backward
函数中,你将从L层开始隐藏层的反向迭代。在每一步中,你将使用L层缓存中的值来进行反向传播。
初始化反向传播:通过这个网络进行反向传播,我们知道输出是这样的:
你的代码需要计算dAL,而dA^ [L]等于L对A^ [L]求导:
为了做到这一点,使用这个公式(这是使用微积分推导的,其实您不需要深入了解):
dAL = - (np.divide(Y, AL) - np.divide(1 - Y, 1 - AL)) # derivative of cost with respect to AL
你可以使用这个激活函数斜率dAL持续进行反向传播。正如上边的图所看到的,您可以将dAL输入到已经实现的LINEAR->SIGMOID backward函数中(该函数将使用L_model_forward函数的缓存值)。
在这之后,你将使用一个for循环通过使用LINAER->RELU来迭代其它层。你将存储dA、dW和db在grads中。为了这么做,使用下面的代码:
例如,对于 L= 3,dW^ [l]会存储在grads[“dW3”]中。
目标: 完善【LINEAR -> RELU】× (L-1)-> LINEAR -> SIGMOID模型的反向传播函数L_model_backward(AL, Y, caches)
,其中:
linear_activation_forward()
输出的缓存(caches[l],for l in range(L-1),i.e l = 0…L-2)应返回一个包含梯度信息的词典,如下:
请尝试写一写这个函数!
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,"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, "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
(这个函数博主认为理解起来不容易。。所以将自己的理解贴在这里)
大概意思就是,在第L层前向传播时,使用的是sigmoid函数。所以在反向传播时就用的也是sigmoid的导数。
剩下的在L-1层以前(因为有很多层,所以使用了for循环,这个是没有办法向量化的),前向传播用的都是ReLU函数,所以这里反向传播使用的也是ReLU函数的导数来求解。
写好后,可以跑一下下边的代码验证你的函数:
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]]
在这一部分,你将用梯度下降更新你模型中的参数:
其中,α是学习率。在计算完更新参数后,将它们存储在参数字典中。
目标: 完成update_parameters()
函数,使用梯度下降更新你的参数。
说明: 使用梯度下降更新每一个W^ [l]和b^ [l],l = 1,2,…,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]]
恭喜!你已经完成了所有建立一个深度神经网络所需要的函数!
我们都知道这很不容易,但是下半部分的任务就会简单许多
在下一个任务中,我们将会建立两个模型:
你将会实际运用这些模型来识别一张图片是猫/不是猫!
如果对本文章内容理解不深,建议多看几遍。
import numpy as np
import h5py
import matplotlib.pyplot as plt
from course1.week4.testCases import *
from course1.week4.dnn_utils import sigmoid, sigmoid_backward, relu, relu_backward
#
# plt.rcParams['figure.figsize'] = (5.0, 4.0) # set default size of plots
# plt.rcParams['image.interpolation'] = 'nearest'
# plt.rcParams['image.cmap'] = 'gray'
#
np.random.seed(1)
def initialize_parameters(n_x, n_h, n_y):
np.random.seed(1)
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))
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
def initialize_parameters_deep(layer_dims):
np.random.seed(3)
parameters = {}
L = len(layer_dims)
for i in range(1, L):
parameters["W" + str(i)] = np.random.randn(layer_dims[i], layer_dims[i - 1]) * 0.01
parameters["b" + str(i)] = np.zeros(shape=(layer_dims[i], 1))
assert (parameters["W" + str(i)].shape == (layer_dims[i], layer_dims[i - 1]))
assert (parameters["b" + str(i)].shape == (layer_dims[i], 1))
return parameters
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
def linear_activation_forward(A_prev, W, b, activation):
Z, linear_cache = linear_forward(A_prev, W, b)
if activation == "sigmoid":
A, activation_cache = sigmoid(Z)
elif activation == "relu":
A, activation_cache = relu(Z)
assert (A.shape == (W.shape[0], A_prev.shape[1]))
cache = (linear_cache, activation_cache)
return A, cache
def L_model_forward(X, parameters):
caches = []
A = X
# 每一层都应该有W和B两个参数,所以parameters//2就是层数
L = len(parameters) // 2
# 0-(L-1)层我们使用ReLU方法
for i in range(1, L):
A_prev = A
A, cache = linear_activation_forward(A_prev,
parameters["W" + str(i)],
parameters["b" + str(i)],
activation="relu")
caches.append(cache)
# 第L层(最后一层)我们使用sigmoid方法使最终结果处于0-1之间
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]))
# assert (AL.shape == (parameters["W" + str(L)].shape[0], X.shape[1]))
return AL, caches
def compute_cost(AL, Y):
# m,即数据集的大小,Y的大小是(1,m)
m = Y.shape[1]
# 根据公式写出损失函数
cost = -(1 / m) * (np.sum(np.multiply(Y, np.log(AL)) + np.multiply(1 - Y, np.log(1 - AL))))
# 降维,保证损失是是一个数,而不是一个矩阵/数组一样的东西
cost = np.squeeze(cost)
return cost
def linear_backward(dZ, cache):
A_prev, W, b = cache
# W.shape[0]是本层的神经单元数
# W.shape[1]是上一层的神经单元数
# 只有在通过A = g(WX + b)的计算之后,得到的A的shape[1]才是数量
m = A_prev.shape[1]
dW = np.dot(dZ, A_prev.T) / m
db = np.sum(dZ, axis=1, keepdims=True) / m
dA_prev = np.dot(W.T, dZ)
assert (dW.shape == W.shape)
assert (dA_prev.shape == A_prev.shape)
assert (db.shape == b.shape)
return dA_prev, dW, db
def linear_activation_backward(dA, cache, activation):
linear_cache, activation_cache = cache
if activation == "sigmoid":
dZ = sigmoid_backward(dA, activation_cache)
elif activation == "relu":
dZ = relu_backward(dA, activation_cache)
dA_prev, dW, db = linear_backward(dZ, linear_cache)
return dA_prev, dW, db
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,
"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, "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
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