本次编程练习的目的是创建一个大于2层的深度神经网络。
简单讲述一下具体的实现步骤:
-
初始化参数: 把W初始化为随机数,b初始化为0。
-
正向传播: 正着计算,得到最终预测值,同时记录下中间的各种参数,之后求导数要使用。
把整个过程分解成了三个函数:
linear_forward(A, W, b) --> 计算线性函数Z = W * A_pre + b
linear_activation_forward(A_pre, W, b, activation) --> 计算激活函数relu/sigmoid,其实也是计算当前层的所有参数,调用了函数linear_forward
L_model_forward(X, parameters) --> 计算出最终的预测值,并记录中间过程中的变量,调用了函数linear_activation_forward -
成本函数: 直接使用成本函数的计算公式计算即可
-
反向传播: 从最后一层开始,反着计算参数的梯度(也就是导数),最终要使用这些导数进行变量的更新。
整个过程也是分成了三个函数,可以类比正向传播的过程:
linear_backward(dZ, cache) —> 计算线性函数Z = W * A_pre + b相对于A的导数dA
linear_activation_backward(dA, cache, activation) —> 计算这一层的所有导数以及上一层的dA(是从正向传播角度来看的上一层),这一层的导数包括dW, db。调用了函数linear_backward
L_model_backward(AL, Y, caches)—>计算整个神经网络参数的导数,也就是每一层的dW, db,调用了函数linear_activation_backward -
更新参数: 使用公式:w = w - α * dw (此处的α为学习率)循环计算每一层的参数(参数b也是同样的道理,同样的α)
更详细的解释放在了代码注释当中
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
# 初始化双层神经网络(可以不看,本次关注的是深层神经网络)
def initialize_parameters(nx, nh, ny):
np.random.seed(1)
W1 = np.random.rand(nh, nx) * 0.01
b1 = np.zeros((nh, 1))
W2 = np.random.rand(ny, nh) * 0.01
b2 = np.zeros((ny, 1))
assert(W1.shape == (nh, nx))
assert(b1.shape == (nh, 1))
assert(W2.shape == (ny, nh))
assert(b2.shape == (ny, 1))
parameters = {'W1':W1,
'b1':b1,
'W2':W2,
'b2':b2}
return parameters
# 初始化L层神经网络
def initialize_parameters_deep(layer_dims):
"""
input:
layer_dims: 类型为list, 包含每层的神经元数量
output:
parameters: 类型为dict, 包含所有的参数
"""
L = len(layer_dims)
parameters = {}
for i in range(1, L):
W = np.random.rand(layer_dims[i], layer_dims[i - 1]) * 0.01
b = np.zeros((layer_dims[i], 1))
parameters['W' + str(i)] = W
parameters['b' + str(i)] = b
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):
"""
input:
A: 估计值
W: 权重
b: 偏置
output:
Z: 线性组合之后的值
cache: 类型为tuple, cache == (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_pre, W, b, activation):
"""
input:
A_pre: 上一层的估计值
W: 本层的权重
b: 本层的偏置
activation: 激活函数名称
output:
A: 本层的估计值
cache: ((A_pre, W, b), Z)
"""
Z, linear_cache = linear_forward(A_pre, 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_pre.shape[1]))
cache = (linear_cache, activation_cache)
return A, cache
# 正向传播-----最终函数
def L_model_forward(X, parameters):
"""
input:
X: 输入的样本特征
parameters: 参数(类型为dict)
output:
AL: 最终的估计值
caches: 每一层的所有参数(类型为list)[((A_pre, W, b), Z)...]
"""
caches = []
A = X
L = len(parameters) // 2
for i in range(1, L):
A, cache = linear_activation_forward(A, parameters['W' + str(i)], parameters['b' + str(i)], "relu")
caches.append(cache)
AL, cache = linear_activation_forward(A, parameters['W' + str(L)], parameters['b' + str(L)], "sigmoid")
caches.append(cache)
assert(AL.shape == (1, X.shape[1]))
return AL, caches
# 计算成本函数
def compute_cost(AL, Y):
m = AL.shape[1]
cost = -1 / m * np.sum(Y * np.log(AL) + (1 - Y) * np.log((1 - AL)))
cost = np.squeeze(cost)
assert(cost.shape == ())
return cost
# 反向传播----计算线性函数的导数
def linear_backward(dZ, cache):
"""
input:
dZ: Z对成本函数J的导数
cache:参数(A_pre, W, b)
output:
dA_pre:上一层A的导数
dW:W的导数
db:b的导数
"""
A_pre, W, b = cache
m = A_pre.shape[1]
dW = 1 / m * np.dot(dZ, A_pre.T)
db = 1 / m * np.sum(dZ, axis = 1, keepdims = True)
dA_pre = np.dot(W.T, dZ)
assert(dA_pre.shape == A_pre.shape)
assert(dW.shape == W.shape)
assert(db.shape == b.shape)
return dA_pre, dW, db
# 反向传播----计算激活函数的导数
def linear_activation_backward(dA, cache, activation):
"""
input:
dA: A的导数
cache: 参数((A_pre, W, b), Z)
activation: 激活函数的名字
output:
dA_pre: 上一层A的导数
dW:W的导数
db:b的导数
"""
linear_cache, activation_cache = cache
if activation == 'relu':
dZ = relu_backward(dA, activation_cache)
dA_pre, dW, db = linear_backward(dZ, linear_cache)
elif activation == 'sigmoid':
dZ = sigmoid_backward(dA, activation_cache)
dA_pre, dW, db = linear_backward(dZ, linear_cache)
return dA_pre, dW, db
# 反向传播----最终所有导数
def L_model_backward(AL, Y, caches):
"""
input:
AL:整个神经网络的最终预测值
Y:实际值
caches:参数(包含L个层的所有参数)[((A_pre, W, b), Z)...]
output:
grads:所有层的梯度(类型为dict),使用诸如grads['dW2']这种方式访问
"""
m = AL.shape[1]
Y = Y.reshape(AL.shape)
grads = {}
L = len(caches)
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_pre, dW, db = linear_activation_backward(grads['dA' + str(l + 2)], current_cache, 'relu')
grads['dA' + str(l + 1)] = dA_pre
grads['dW' + str(l + 1)] = dW
grads['db' + str(l + 1)] = db
return grads
# 更新参数
def update_parameters(parameters, grads, learning_rate):
"""
input:
parameters: 所有层的参数(包括W, b)
grads: 所有层的梯度
learning_rate: 学习率
output:
parameters: 更新过后的参数
"""
L = len(parameters) // 2
for l in range(L):
parameters['W' + str(l + 1)] -= learning_rate * grads['dW' + str(l + 1)]
parameters['b' + str(l + 1)] -= learning_rate * grads['db' + str(l + 1)]
return parameters