Couresra-吴恩达-深度学习-第一章:Neural Network and Deep Learning- 第四周
本周学习内容
- Key Concepts on Deep Neural Network
- Deep L-layer neural network (深层神经网络)
- Forward Propagation in a Deep Network (前向算法)
- Getting your matrix dimensions right (核对矩阵的维数)
- Why deep representations (为什么用深层表示)
- Building blocks of deep neural networks (搭建深层神经网络)
- Forward and Backward Propagation (前向和后向算法)
- Parameters vs Hyperparameters (参数 vs 超参数)
- What does this have to do with brain (这和大脑有什么关系)
测验
本周题目都比较简单,主要考察参数的维度:
W
[
l
]
:
(
n
[
l
]
,
n
[
l
−
1
]
)
W^{[l]} : (n^{[l]},n^{[l-1]})
W[l]:(n[l],n[l−1])
b
[
l
]
:
(
n
[
l
]
,
1
)
b^{[l]} : (n^{[l]},1)
b[l]:(n[l],1)
代码练习
任务1:Building your Deep Neural Network: Step by Step
目标
- Develop an intuition of the over all structure of a neural network.
- Write functions (e.g. forward propagation, backward propagation, logistic loss, etc…) that would help you decompose your code and ease the process of building a neural network.
- Initialize/update parameters according to your desired structure.
大纲(Outline of the Assignment)
我们将构建两个神经网络,一个是两层的,一个是多层的。必须的步骤有:
- 初始化参数
- 前向传播(图中紫色部分)
- LINEAR part - 生成
Z
[
l
[
Z^{[l[}
Z[l[
Z = np.dot(W,A) + b
- LINEAR > ACTIVATION forward function
- LINEAR > ReLU (L-1层)
A = relu(Z)
- LINEAR > Sigmoid(最后一层L)
A = sigmoid(Z)
- LINEAR > ReLU (L-1层)
- LINEAR part - 生成
Z
[
l
[
Z^{[l[}
Z[l[
- 计算损失函数
- 反向传播 (图中红色部分)
- 线性部分的反向传播
- 激活函数部分的反向传播
- 结合线性部分与激活函数的反向传播公式
- 更新参数
1. 初始化参数(Initialization)
两层的:
def initialize_parameters(n_x,n_h,n_y):
"""
此函数是为了初始化两层网络参数而使用的函数。
参数:
n_x - 输入层节点数量
n_h - 隐藏层节点数量
n_y - 输出层节点数量
返回:
parameters - 包含你的参数的python字典:
W1 - 权重矩阵,维度为(n_h,n_x)
b1 - 偏向量,维度为(n_h,1)
W2 - 权重矩阵,维度为(n_y,n_h)
b2 - 偏向量,维度为(n_y,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
L层的参数初始化稍复杂,另外涉及到一点broadcasting,但只要记住
w
,
b
w,b
w,b的维度即可
def initialize_parameters_deep(layers_dims):
"""
此函数是为了初始化多层网络参数而使用的函数。
参数:
layers_dims - 包含我们网络中每个图层的节点数量的列表
返回:
parameters - 包含参数“W1”,“b1”,...,“WL”,“bL”的字典:
W1 - 权重矩阵,维度为(layers_dims [1],layers_dims [1-1])
bl - 偏向量,维度为(layers_dims [1],1)
"""
np.random.seed(3)
parameters = {}
L = len(layers_dims)
for l in range(1,L):
parameters["W" + str(l)] = np.random.randn(layers_dims[l], layers_dims[l - 1]) / np.sqrt(layers_dims[l - 1])
parameters["b" + str(l)] = np.zeros((layers_dims[l], 1))
#确保我要的数据的格式是正确的
assert(parameters["W" + str(l)].shape == (layers_dims[l], layers_dims[l-1]))
assert(parameters["b" + str(l)].shape == (layers_dims[l], 1))
return parameters
2.前向传播
线性(Linear)
def linear_forward(A,W,b):
"""
实现前向传播的线性部分。
参数:
A - 来自上一层(或输入数据)的激活,维度为(上一层的节点数量,示例的数量)
W - 权重矩阵,numpy数组,维度为(当前图层的节点数量,前一图层的节点数量)
b - 偏向量,numpy向量,维度为(当前图层节点数量,1)
返回:
Z - 激活功能的输入,也称为预激活参数
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
线性激活(Linear-Activation)
def linear_activation_forward(A_prev,W,b,activation):
"""
实现LINEAR-> ACTIVATION 这一层的前向传播
参数:
A_prev - 来自上一层(或输入层)的激活,维度为(上一层的节点数量,示例数)
W - 权重矩阵,numpy数组,维度为(当前层的节点数量,前一层的大小)
b - 偏向量,numpy阵列,维度为(当前层的节点数量,1)
activation - 选择在此层中使用的激活函数名,字符串类型,【"sigmoid" | "relu"】
返回:
A - 激活函数的输出,也称为激活后的值
cache - 一个包含“linear_cache”和“activation_cache”的字典,我们需要存储它以有效地计算后向传递
"""
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
我们把两层模型需要的前向传播函数做完了,那多层模型的呢?
我们调用上面的两个函数实现它,为了在实现L层神经网络时更加方便,我们需要一个函数来复制前一个函数(带有RELU的linear-activation_forward)L-1次,最后用一个sigmoid函数。
def L_model_forward(X,parameters):
"""
实现[LINEAR-> RELU] *(L-1) - > LINEAR-> SIGMOID计算前向传播,也就是多层网络的前向传播,为后面每一层都执行LINEAR和ACTIVATION
参数:
X - 数据,numpy数组,维度为(输入节点数量,示例数)
parameters - initialize_parameters_deep()的输出
返回:
AL - 最后的激活值
caches - 包含以下内容的缓存列表:
linear_relu_forward()的每个cache(有L-1个,索引为从0到L-2)
linear_sigmoid_forward()的cache(只有一个,索引为L-1)
"""
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)], "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
3.计算损失函数
def compute_cost(AL,Y):
"""
实施等式(4)定义的成本函数。
参数:
AL - 与标签预测相对应的概率向量,维度为(1,示例数量)
Y - 标签向量(例如:如果不是猫,则为0,如果是猫则为1),维度为(1,数量)
返回:
cost - 交叉熵成本
"""
m = Y.shape[1]
cost = -np.sum(np.multiply(np.log(AL),Y) + np.multiply(np.log(1 - AL), 1 - Y)) / m
cost = np.squeeze(cost)
assert(cost.shape == ())
return cost
4.后向传播
需要使用 d Z [ l ] dZ^{[l]} dZ[l]来计算三个输出 d W [ l ] dW^{[l]} dW[l], d b [ l ] db^{[l]} db[l], d A [ l ] dA^{[l]} dA[l], 需要用到以下三个公式:
d W [ l ] = ∂ L ∂ W [ l ] = 1 m d Z [ l ] A [ l − 1 ] T d W^{[l]}=\frac{\partial \mathcal{L}}{\partial W^{[l]}}=\frac{1}{m} d Z^{[l]} A^{[l-1] T} dW[l]=∂W[l]∂L=m1dZ[l]A[l−1]T
d b [ l ] = ∂ L ∂ b [ l ] = 1 m ∑ i = 1 m d Z [ l ] ( i ) d b^{[l]}=\frac{\partial \mathcal{L}}{\partial b^{[l]}}=\frac{1}{m} \sum_{i=1}^{m} d Z^{[l](i)} db[l]=∂b[l]∂L=m1∑i=1mdZ[l](i)
d A [ l − 1 ] = ∂ L ∂ A [ l − 1 ] = W [ l ] T d Z [ l ] d A^{[l-1]}=\frac{\partial \mathcal{L}}{\partial A^{[l-1]}}=W^{[l] T} d Z^{[l]} dA[l−1]=∂A[l−1]∂L=W[l]TdZ[l]
线性部分
def linear_backward(dZ,cache):
"""
为单层实现反向传播的线性部分(第L层)
参数:
dZ - 相对于(当前第l层的)线性输出的成本梯度
cache - 来自当前层前向传播的值的元组(A_prev,W,b)
返回:
dA_prev - 相对于激活(前一层l-1)的成本梯度,与A_prev维度相同
dW - 相对于W(当前层l)的成本梯度,与W的维度相同
db - 相对于b(当前层l)的成本梯度,与b维度相同
"""
A_prev, W, b = cache
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 (dA_prev.shape == A_prev.shape)
assert (dW.shape == W.shape)
assert (db.shape == b.shape)
return dA_prev, dW, db
线性激活部分(Linear-Activation backward)
此部分需要两个函数sigmoid_backward
和 relu_backward
def linear_activation_backward(dA,cache,activation="relu"):
"""
实现LINEAR-> ACTIVATION层的后向传播。
参数:
dA - 当前层l的激活后的梯度值
cache - 我们存储的用于有效计算反向传播的值的元组(值为linear_cache,activation_cache)
activation - 要在此层中使用的激活函数名,字符串类型,【"sigmoid" | "relu"】
返回:
dA_prev - 相对于激活(前一层l-1)的成本梯度值,与A_prev维度相同
dW - 相对于W(当前层l)的成本梯度值,与W的维度相同
db - 相对于b(当前层l)的成本梯度值,与b的维度相同
"""
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
5.更新参数
参数更新公式:
W
[
l
]
=
W
[
l
]
−
α
d
W
[
l
]
W^{[l]}=W^{[l]}-\alpha d W^{[l]}
W[l]=W[l]−αdW[l]
b
[
l
]
=
b
[
l
]
−
α
d
b
[
l
]
b^{[l]}=b^{[l]}-\alpha d b^{[l]}
b[l]=b[l]−αdb[l]
def update_parameters(parameters, grads, learning_rate):
"""
使用梯度下降更新参数
参数:
parameters - 包含你的参数的字典
grads - 包含梯度值的字典,是L_model_backward的输出
返回:
parameters - 包含更新参数的字典
参数[“W”+ str(l)] = ...
参数[“b”+ str(l)] = ...
"""
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
至此为止,我们已经定义了神经网络所需要的函数,接下来的作业当中将使用这些函数来构成一个神经网络。