基本元素符号约定

  • 上标 [l][l][l]代表神经网络的层数 lthl^{th}lth ,比如a[L]a^{[L]}a[L] 是 [L][L][L]层的激活, W[L]W^{[L]}W[L]是[L][L][L]层的权重,b[L]b^{[L]}b[L]是[L][L][L]层的偏置。
  • 上标(i)(i)(i) 表示第ithi^{th}ith个样本,比如 x(i)x^{(i)}x(i)是第ithi^{th}ith个训练样本。
  • 下标 iii 表示 [l][l][l]层的第 ithi^{th}ith 项, 比如ai[l]a^{[l]}_iai[l]​ 表示第lthl^{th}lth层的第ithi^{th}ith个激活项

一、原理

多层神经网络搭建的流程图

1.1 为L-层神经网络初始化参数

W的维度 b的维度 激活值的计算 激活值的维度
第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)
第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)
⋮\vdots⋮ ⋮\vdots⋮ ⋮\vdots⋮ ⋮\vdots⋮ ⋮\vdots⋮
第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)
第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)
  • W[l]W^{[l]}W[l]:parameters['W' + str(l)] = np.random.randn(layer_dims[l], layer_dims[i - 1]) / np.sqrt(layer_dims[l - 1])

  • b[l]b^{[l]}b[l]:parameters['b' + str(l] = np.zeros(shape=(layer_dims[l], 1))

1.2 前向传播

1.2.1 前向传播的线性部分 Z[l]=WX+bZ^{[l]}=WX + bZ[l]=WX+b

W=[jklmnopqr]      X=[abcdefghi]      b=[stu]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}W=⎣⎡​jmp​knq​lor​⎦⎤​X=⎣⎡​adg​beh​cfi​⎦⎤​b=⎣⎡​stu​⎦⎤​

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]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} 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​⎦⎤​
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]=XA^{[0]} = XA[0]=X

1.2.2 计算前向传播的线激活函数部分

激活函数公式

  • Sigmoid: σ(Z)=σ(WA+b)=11+e−(WA+b)\sigma(Z) = \sigma(W A + b) = \frac{1}{ 1 + e^{-(W A + b)}}σ(Z)=σ(WA+b)=1+e−(WA+b)1​
  • Relu: A=RELU(Z)=max(0,Z)A = RELU(Z) = max(0, Z)A=RELU(Z)=max(0,Z)

计算出激活值A[l]A^{[l]}A[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]})A[l]=g(Z[l])=g(W[l]A[l−1]+b[l])

其中g()g()g()既可以是sigmoid()sigmoid()sigmoid()也可以是relu()relu()relu()

1.2.3 结合前向传播前两步

  1. 将前面两个步骤合并成一个新的[LINEAR->ACTIVATION]]前向函数,这里的ACTIVATION包括RELUSIGMOID
  2. 使用 [LINEAR->RELU]前向函数 L−1L-1L−1次,接着对第LLL层使用[LINEAR->SIGMOID] 前向函数使用一次。

1.3 计算误差

成本函数公式
J=−1m∑i=1m(y(i)log⁡(a[L](i))+(1−y(i))log⁡(1−a[L](i)))J =-\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)) J=−m1​i=1∑m​(y(i)log(a[L](i))+(1−y(i))log(1−a[L](i)))

1.4 反向传播

A[L]A^{[L]}A[L]它属于输出层,由A[L]=σ(Z[L])A^{[L]} = \sigma(Z^{[L]})A[L]=σ(Z[L])得来。A[L]A^{[L]}A[L]相对于成本函数的导数
dA[L]=∂L∂A[L]dA^{[L]} = \frac{\partial \mathcal{L}}{\partial A^{[L]}}dA[L]=∂A[L]∂L​.

dAL = - (np.divide(Y, AL) - np.divide(1 - Y, 1 - AL)) # derivative of cost with respect to AL

1.4.1 计算反向传播激活函数部分

dZ[L]=∂L∂Z[L]=dA[L]∗g′(Z[L])dZ^{[L]} = \frac{\partial \mathcal{L} }{\partial Z^{[L]}}=dA^{[L]} * g'(Z^{[L]}) dZ[L]=∂Z[L]∂L​=dA[L]∗g′(Z[L])其中g′()g'()g′()是激活函数的导数

  • sigmoid函数导数
    σ′(z)=(11+e−z)′=e−z(1+e−z)2=1+e−z−1(1+e−z)2=1(1+e−z)(1−1(1+e−z))=g(z)(1−g(z))\begin{aligned} \sigma'(z) &= (\frac{1}{1+e^{-z}})' = \frac{e^{-z}}{(1+e^{-z})^{2}} = \frac{1+e^{-z}-1}{(1+e^{-z})^{2}} \\ &= \frac{1}{(1+e^{-z})}(1-\frac{1}{(1+e^{-z})}) \\ &= g(z)(1-g(z)) \\ \end{aligned}σ′(z)​=(1+e−z1​)′=(1+e−z)2e−z​=(1+e−z)21+e−z−1​=(1+e−z)1​(1−(1+e−z)1​)=g(z)(1−g(z))​

  • ReLU导数:

ReLU′(Z)={0Z≤010&lt;ZReLU'(Z)=\left\{ \begin{array}{rcl} 0 &amp; &amp; {Z \leq 0}\\ 1 &amp; &amp; {0 &lt; Z} \end{array} \right. ReLU′(Z)={01​​Z≤00<Z​

1.4.2 计算反向传播线性部分

这里假设已经得到了dZ[l]dZ^{[l]}dZ[l]:
dW[L]=∂L∂W[l]=1mdZ[L]A[L−1]TdW^{[L]} = \frac{\partial \mathcal{L} }{\partial W^{[l]}} = \frac{1}{m} dZ^{[L]} A^{[L-1] T} dW[L]=∂W[l]∂L​=m1​dZ[L]A[L−1]Tdb[L]=∂L∂b[L]=1m∑i=1mdZ[L](i)db^{[L]} = \frac{\partial \mathcal{L} }{\partial b^{[L]}} = \frac{1}{m} \sum_{i = 1}^{m} dZ^{[L](i)}db[L]=∂b[L]∂L​=m1​i=1∑m​dZ[L](i)dZ[L−1]=W[L]TdZ[L]∗g′(Z[L−1])dZ^{[L-1]} =W^{[L] T} dZ^{[L]} * g'(Z^{[L-1]}) dZ[L−1]=W[L]TdZ[L]∗g′(Z[L−1])dW[L−1]=1mdZ[L−1]A[L−2]TdW^{[L-1]} =\frac{1}{m} dZ^{[L-1]} A^{[L-2] T} dW[L−1]=m1​dZ[L−1]A[L−2]Tdb[L−1]=1m∑i=1mdZ[L−1](i)db^{[L-1]} = \frac{1}{m} \sum_{i = 1}^{m} dZ^{[L-1](i)}db[L−1]=m1​i=1∑m​dZ[L−1](i)⋮\vdots⋮dZ[1]=W[2]TdZ[2]∗g′(Z1])dZ^{[1]} =W^{[2] T} dZ^{[2]} * g'(Z^{1]}) dZ[1]=W[2]TdZ[2]∗g′(Z1])dW1]=1mdZ[1]A[0]TdW^{1]} =\frac{1}{m} dZ^{[1]} A^{[0] T} dW1]=m1​dZ[1]A[0]Tdb[1]=1m∑i=1mdZ[1](i)db^{[1]} = \frac{1}{m} \sum_{i = 1}^{m} dZ^{[1](i)}db[1]=m1​i=1∑m​dZ[1](i)
其中dA[L−1]=∂L∂A[l−1]=W[L]TdZ[L]dA^{[L-1]} = \frac{\partial \mathcal{L} }{\partial A^{[l-1]}} = W^{[L] T} dZ^{[L]}dA[L−1]=∂A[l−1]∂L​=W[L]TdZ[L]

1.4.3 将反向传播前两步结合起来

  1. 将前面两个步骤合并成一个新的[LINEAR->ACTIVATION]]反向函数,这里的ACTIVATION包括RELU_BACKWARDSIGMOID_BACKWARD
  2. 首先对第LLL层使用[LINEAR->SIGMOID_BACKWARD] 前向函数使用一次,接着使用 [LINEAR->RELU_BACKWARD]前向函数 L−1L-1L−1次。

1.5 更新参数

W[l]=W[l]−αdW[l]W^{[l]} = W^{[l]} - \alpha \text{ } dW^{[l]} W[l]=W[l]−α dW[l]b[l]=b[l]−αdb[l]b^{[l]} = b^{[l]} - \alpha \text{ } db^{[l]} b[l]=b[l]−α db[l]其中α\alphaα是学习率


二、编程实现模型

2.1 准备软件包

  • numpy 是Python中进行科学计算的主要软件包。
  • matplotlib 是Python中用于绘制图形的库。
  • np.random.seed(1)用于保持所有随机函数调用的一致性,使每次计算出的结果一致。
import numpy as np
import h5py
import matplotlib.pyplot as pltnp.random.seed(1)  # 指定随机种子

2.2 初始化参数

  • LLL层神经网络的模型结构是 [LINEAR -> RELU] ×\times× (L-1) -> LINEAR -> SIGMOID,前 L−1L-1L−1层用ReluReluRelu函数激活,输出层用SigmoidSigmoidSigmoid函数激活
  • 权重WWW使用np.random.randn(shape)*0.01随机初始化
  • 偏置bbb用0初始化np.zero(shape)
  • 使用layer_dims储存每一层节点的数目,比如说, layer_dims 为 [2,4,1]: 那么就有两个输入节点,4个隐藏层节点,一个输出层节点。也接意味着W1的维度为 (4,2), b1 维度为 (4,1), W2 维度为 (1,4) 和 b2 维度为(1,1)。现在来实现LLL 层参数的初始化!
def initialize_parameters_deep(layer_dims):'''此函数是为了初始化多层网络参数:param layer_dims:包含我们网络中每个图层的节点的列表:return:parameters:包含参数“W1”,“b1”,“W2”……“WL”,"bL"的字典Wl:权重矩阵,维度为(layer_dims[l],layer_dims[l-1])bl:偏向量,维度为(layer_dims[l],1)'''np.random.seed(3)parameters = {}L = len(layer_dims)  # 网络的层数for i in range(1, L):  # range下标从0开始,如果L=3,则list(range(1,3))=[1,2]parameters['W' + str(i)] = np.random.randn(layer_dims[i], layer_dims[i - 1]) / np.sqrt(layer_dims[i - 1])  # 用除代替0.01# 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  # 包含的参数数是隐藏层数的两倍

2.3 前向传播函数

前向传播有三个步骤

  • 计算线性部分
  • 线性部分 -> 激活部分,其中激活函数将会使用Relu或者sigmoid
  • 一般来说,对整个模型使用L−1L-1L−1次[linear - > relu],1次[linera - > sigmoid]

2.3.1 线性部分[Linear]

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]

where A[0]=XA^{[0]} = XA[0]=X.

  • 两个矩阵乘用np.dot(W,A)
  • W.shape判断矩阵维度是否符合
def linear_forward(A, W, b):'''实现前向传播的线性部分:param A:来自上一层(或输入数据)的激活,维度为(上一层节点数,样本数):param W:权重矩阵,维度为(当前层的节点数,上一层的节点数):param b:偏向量,维度为(当前层的节点数,1):return:Z:激活函数的输入,也称为预激活参数cache:一个包含A,W,b的字典,储存它们以便后向传播的计算'''Z = np.dot(W, A) + bassert (Z.shape == (W.shape[0], A.shape[1]))cache = (A, W, b)  # cache是一个列表return Z, cache

2.3.2 线性激活部分【Linear -> Activation】

使用一下两个激活函数

  • Sigmoid: σ(Z)=σ(WA+b)=11+e−(WA+b)\sigma(Z) = \sigma(W A + b) = \frac{1}{ 1 + e^{-(W A + b)}}σ(Z)=σ(WA+b)=1+e−(WA+b)1​.

  • sigmoid函数导数
    σ′(z)=(11+e−z)′=e−z(1+e−z)2=1+e−z−1(1+e−z)2=1(1+e−z)(1−1(1+e−z))=g(z)(1−g(z))\begin{aligned} \sigma'(z) &amp;= (\frac{1}{1+e^{-z}})' = \frac{e^{-z}}{(1+e^{-z})^{2}} = \frac{1+e^{-z}-1}{(1+e^{-z})^{2}} \\ &amp;= \frac{1}{(1+e^{-z})}(1-\frac{1}{(1+e^{-z})}) \\ &amp;= g(z)(1-g(z)) \\ \end{aligned}σ′(z)​=(1+e−z1​)′=(1+e−z)2e−z​=(1+e−z)21+e−z−1​=(1+e−z)1​(1−(1+e−z)1​)=g(z)(1−g(z))​

A, activation_cache = sigmoid(Z)
def sigmoid(Z):"""Implements the sigmoid activation in numpyArguments:Z -- numpy array of any shapeReturns:A -- output of sigmoid(z), same shape as Zcache -- returns Z as well, useful during backpropagation"""A = 1/(1+np.exp(-Z))cache = Zreturn A, cache
  • ReLU函数:A=RELU(Z)=max(0,Z)A = RELU(Z) = max(0, Z)A=RELU(Z)=max(0,Z)
  • ReLU导数:

ReLU′(Z)={0Z≤010&lt;ZReLU'(Z)=\left\{ \begin{array}{rcl} 0 &amp; &amp; {Z \leq 0}\\ 1 &amp; &amp; {0 &lt; Z} \end{array} \right. ReLU′(Z)={01​​Z≤00<Z​

A, activation_cache = relu(Z)
def relu(Z):"""Implement the RELU function.Arguments:Z -- Output of the linear layer, of any shapeReturns:A -- Post-activation parameter, of the same shape as Zcache -- a python dictionary containing "A" ; stored for computing the backward pass efficiently"""A = np.maximum(0,Z)assert(A.shape == Z.shape)cache = Z return A, cache

以上两者的activation_cache都是ZZZ.

实现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]) 这里的激活函数ggg可以是 sigmoid()sigmoid()sigmoid() 或者 relu()relu()relu().

def linear_activation_forward(A_prev, W, b, activation):'''实现linear->activation这一层的前向传播:param A_prev:来自上一层(或输入层)的激活,维度为(上一层节点数,样本数):param W:权重矩阵,numpy数组,维度为(当前层节点数量,上一层节点数量):param b:偏向量,numpy阵列,维度为(当前层节点数量,1):param activation:选择在此层中的激活函数,字符串类型,【sigmoid,relu】:return:A:激活函数的输出,也称为激活后的值cache: (A, W, b,Z)一个包含'linear_cache'和'activation_cache'的字典,我们需要存储它以有效地计算后向传播'''if activation == "sigmoid":Z, linear_cache = linear_forward(A_prev, W, b)  # linear_cache = (A, W, b)A, activation_cache = sigmoid(Z)  # activation_cache = Zelif activation == "relu":Z, linear_cache = linear_forward(A_prev, W, b)  # linear_cache = (A, W, b)A, activation_cache = relu(Z)  # activation_cache = Zassert (A.shape == (W.shape[0], A.shape[1]))cache = (linear_cache, activation_cache)  # (A,W,b,Z),其实是个一列表return A, cache

2.3.3 LLL层神经网络前向传播

  1. 为了更加方便地实现LLL层神经网络,我们需要调用带RELU参数的linear_activation_forward(A_prev, W, b, 'relu')函数 L−1L-1L−1次
  2. 再调用一次带SIGMOID参数的linear_activation_forward(A_prev, W, b, 'sigmoid')函数一次。
  3. 使用caches列表保存过程中产生包含linear_cacheactivation_cachecache字典。 将新元素 c 添加到列表中的方法是list.append(c)

L层前向传播模型

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]). (T也称作 Yhat,数学上表示为 Y^\hat{Y}Y^。)

多层模型的前向传播模型代码如下

def L_model_forward(X, parameters):'''实现[LINEAR-> RELU] *(L-1) - > LINEAR-> SIGMOID计算前向传播,也就是多层网络的前向传播,为后面每一层都执行LINEAR和ACTIVATION也就是前L-1层用relu激活函数,输出层用sigmoid。:param X:输入数据,numpy数组,维度为(输入层节点数,样本数):param parameters:包含W1、b1,W2,b2...的字典,是initialize_parameters_deep(layer_dims)的输出,:return:AL:最后的激活值,也就是Yhatcaches:包含以下内容的缓存列表:linear_relu_forward()的每一个cache(缓存,Z),共有L-1个,索引从0-L-2linear_sigmoid_forward()的cache(Z),只有一个,索引为L-1'''caches = []  # 缓存是一个列表,也就是可变、可添加的A = XL = len(parameters) // 2  # 网络的层数,//是整除# 实现[LINEAR-> RELU] *(L-1),添加cache 到caches中for l in range(1, L):  # 1 -> L-1A_prev = A  # 与函数保持一致A, cache = linear_activation_forward(A_prev, W=parameters['W' + str(l)], b=parameters['b' + str(l)],activation="relu")caches.append(cache)  # list 添加元素要用append,缓存的元素是Z# 实现LINEAR-> SIGMOID,添加cache到caches列表中AL, cache = linear_activation_forward(A, W=parameters['W' + str(L)], b=parameters['b' + str(L)],activation="sigmoid")# cache = (linear_cache, activation_cache)  # (A_prev,W,b,Z)caches.append(cache)  # list 添加元素要用appendassert (AL.shape == (1, X.shape[1]))  # 维度为(1,样本数)return AL, caches

2.4 计算成本

计完成了两层模型的前向传播部分,我们需要计算成本(误差),以便确定它到底有没在学习,成本函数公式如下:
J=−1m∑i=1m(y(i)log⁡(a[L](i))+(1−y(i))log⁡(1−a[L](i)))J=-\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)) J=−m1​i=1∑m​(y(i)log(a[L](i))+(1−y(i))log(1−a[L](i)))

def compute_cost(AL, Y):'''计算成本函数:param AL: 与标签预测相对应的概率向量,维度为(1,样本数):param Y:标签向量(例如:如果是猫则为1,不是猫则为0),维度为(1,样本数):return:cost:交叉熵成本'''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)  # 让成本函数cost维度是所期望的,比如将[[17]]变成17assert (cost.shape == ())  # 一维return cost

2.5 反向传播

反向传播用于相对于损失函数的梯度,前向传播和后向传播的流程图

反向传播依然分为三步

  • Linear 线性部分后向计算
  • Linear -> activation后向计算,其中activation计算relu或者sigmoid的导数
  • 整个模型计算 [LINEAR -> RELU] × (L-1) -> LINEAR -> SIGMOID

2.5.1 反向传播的线性部分

反向传播的线性部分

对第lll层, 线性部分是: 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] ,这里的cache是线性部分的缓存,包含A[l−1]A^{[l-1]}A[l−1]、W[l]W^{[l]}W[l]、b[l]b^{[l]}b[l]。假设我们已经有了导数dZ[l]=∂L∂Z[l]dZ^{[l]} = \frac{\partial \mathcal{L} }{\partial Z^{[l]}}dZ[l]=∂Z[l]∂L​,我们想要得到(dW[l],db[l]dA[l−1])(dW^{[l]}, db^{[l]} dA^{[l-1]})(dW[l],db[l]dA[l−1]),那么可以用下面三个公式计算:
dW[l]=∂L∂W[l]=1mdZ[l]A[l−1]TdW^{[l]} = \frac{\partial \mathcal{L} }{\partial W^{[l]}} = \frac{1}{m} dZ^{[l]} A^{[l-1] T} dW[l]=∂W[l]∂L​=m1​dZ[l]A[l−1]Tdb[l]=∂L∂b[l]=1m∑i=1mdZ[l](i)db^{[l]} = \frac{\partial \mathcal{L} }{\partial b^{[l]}} = \frac{1}{m} \sum_{i = 1}^{m} dZ^{[l](i)}db[l]=∂b[l]∂L​=m1​i=1∑m​dZ[l](i)dA[l−1]=∂L∂A[l−1]=W[l]TdZ[l]dA^{[l-1]} = \frac{\partial \mathcal{L} }{\partial A^{[l-1]}} = W^{[l] T} dZ^{[l]} dA[l−1]=∂A[l−1]∂L​=W[l]TdZ[l]

def linear_backward(dZ, cache):'''为单层实现反向传播的线性部分(第l层):param dZ: 相对于(当前l层的)线性输出的成本梯度:param cache:来自当前层前向传播的值的元组(A_prev,W,b):return:dA_prev:相对于激活(前一层l-1)的成本梯度,与A_prev维度相同dW:相对于W(当前层l)的成本函数梯度,与w维度相同db:相对于b(当前层l)的成本函数梯度,与b维度相同'''A_prev, W, b = cachem = 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

2.5.2 反向传播的线性激活部分【linear -> activation backward】

为了实现线性激活后向传播,提供了两个后向函数,这里的cache是线性激活部分的缓存,包含ZZZ.

  • sigmoid_backward,实现sigmoid的反向传播dZ = sigmoid_backward(dA, activation_cache)
def sigmoid_backward(dA, cache):"""Implement the backward propagation for a single SIGMOID unit.Arguments:dA -- post-activation gradient, of any shapecache -- 'Z' where we store for computing backward propagation efficientlyReturns:dZ -- Gradient of the cost with respect to Z"""Z = caches = 1/(1+np.exp(-Z))dZ = dA * s * (1-s)assert (dZ.shape == Z.shape)return dZ
  • relu_backward,实现relu()的反向传播dZ = relu_backward(dA, activation_cache)
def relu_backward(dA, cache):"""Implement the backward propagation for a single RELU unit.Arguments:dA -- post-activation gradient, of any shapecache -- 'Z' where we store for computing backward propagation efficientlyReturns:dZ -- Gradient of the cost with respect to Z"""Z = cachedZ = np.array(dA, copy=True) # just converting dz to a correct object.# When z <= 0, you should set dz to 0 as well. dZ[Z <= 0] = 0assert (dZ.shape == Z.shape)return dZ

如果g(.)是激活函数,那么sigmoid_backward和relu_backward可以这样计算:
dZ[l]=dA[l]∗g′(Z[l])dZ^{[l]} = dA^{[l]} * g'(Z^{[l]}) dZ[l]=dA[l]∗g′(Z[l])

def linear_activation_backward(dA, cache, activation):'''实现linear -> Activation 层的后向传播:param dA: 当前层激活后的梯度值:param cache: 我们存储用于有效计算反向传播的值的元组,值为(linear_cache(# linear_cache = (A, W, b)),activation_cache(# Z)):param activation:要在此层中使用的激活函数的名称,字符串类型,如["relu"|"sigmoid"]:return:dA_prev:相对于激活(前一层L-1)的成本梯度值,与A_prev的维度相同dW:相对于W(当前层l)的成本梯度值,与W维度相同db:相对于b(当前层l)的成本梯度值,与b维度相同'''linear_cache, activation_cache = cacheif activation == "relu":dZ = relu_backward(dA, activation_cache)  # activation_cache = Zif activation == "sigmoid":dZ = sigmoid_backward(dA, activation_cache)dA_prev, dW, db = linear_backward(dZ, linear_cache)return dA_prev, dW, db

2.5.3 LLL层模型的反向传播

在我们之前的前向传播L_model_forward(X, parameters)的第lll层计算中我们都将(A[l−1]A^{[l-1]}A[l−1],W[l]W^{[l]}W[l],b[l]b^{[l]}b[l], andZ[l]Z^{[l]}Z[l] )缓存到cache中,在反向传播中我们会使用那一层的cache来计算梯度值。LLL层模型的反向传播,流程图如下:

初始化反向向传播:
为了进行反向传播计算,我们已经知道输出值A[L]=σ(Z[L])A^{[L]} = \sigma(Z^{[L]})A[L]=σ(Z[L]),而dA[L]dA^{[L]}dA[L]是成本函数的导数J=−1m∑i=1m(y(i)log⁡(a[L](i))+(1−y(i))log⁡(1−a[L](i)))J =-\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))J=−m1​i=1∑m​(y(i)log(a[L](i))+(1−y(i))log(1−a[L](i)))的导数,即dA[L]=∂L∂A[L]dA^{[L]}= \frac{\partial \mathcal{L}}{\partial A^{[L]}}dA[L]=∂A[L]∂L​,我们可以用下面的代码计算:

dAL = - (np.divide(Y, AL) - np.divide(1 - Y, 1 - AL)) # derivative of cost with respect to AL

计算完dA[L]dA^{[L]}dA[L]之后,便可以用其继续后向计算,多层神经网络模型的反向传播模型函数:

def L_model_backward(AL, Y, caches):'''构建多层模型的后向传播函数,对[LINEAR->RELU] * (L-1) -> LINEAR -> SIGMOID组执行反向传播:param AL:概率向量,正向传播的输出(L_model_forward()):param Y:标签向量,true "label" vector (containing 0 if non-cat, 1 if cat):param caches:包含以下内容的cache列表linear_activation_forward("relu")的cache,不包含输出层linear_activation_forward("sigmoid")的cache #linear_cache, activation_cache)  # (A,W,b,Z),其实是个一列表:return:grads:包含梯度值的字典grads["dA" + str(l)] = ...grads["dW" + str(l)] = ...grads["db" + str(l)] = ...'''grads = {}L = len(caches)  # 网络的层数,隐藏层+输出层m = AL.shape[1]Y = Y.reshape(AL.shape)  # 因为有可能Y是个行向量,使之与AL保持一致# 初始化后向传播dAL = -(np.divide(Y, AL) - np.divide(1 - Y, 1 - AL))  # 成本函数的导数# Lth layer (SIGMOID -> LINEAR) gradients. Inputs: "AL, Y, caches". Outputs: "grads["dAL"], grads["dWL"], grads["dbL"]current_cache = caches[-1]  # caches的最后一个cachegrads["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)):  # [L-2,...,0]current_cache = caches[l]  # l=L-2dA_prev_temp, dW_temp, db_temp = linear_activation_backward(grads["dA" + str(l + 2)], current_cache,"relu")  # dALgrads["dA" + str(l + 1)] = dA_prev_temp  # l=L-2 , l+1=L-1grads["dW" + str(l + 1)] = dW_tempgrads["db" + str(l + 1)] = db_tempreturn grads

2.6 更新参数

前向反向传播都完成之后,那么就要更新W[l]W^{[l]}W[l] 和 b[l]b^{[l]}b[l] for l=1,2,...,Ll = 1, 2, ..., Ll=1,2,...,L. 的参数:
W[l]=W[l]−αdW[l]W^{[l]} = W^{[l]} - \alpha \text{ } dW^{[l]} W[l]=W[l]−α dW[l]b[l]=b[l]−αdb[l]b^{[l]} = b^{[l]} - \alpha \text{ } db^{[l]} b[l]=b[l]−α db[l]

其中α\alphaα 是学习率

def update_parameters(parameters, grads, learning_rate):'''使用梯度下降更新参数:param parameters:包含参数“W1”,“b1”,“W2”……“WL”,"bL"的字典:param grads:包含梯度值的字典,包含参数“dA1”,“dW1”,“db1”,“dW2”……“dWL”,"dbL",“dWL”:param learning_rate:学习参数:return::parameters:包含更新参数的字典parameters["W" + str(l)] = ...parameters["b" + str(l)] = ...'''L = len(parameters) // 2  # 整除for l in range(L):  # 0 -> L-1,这里l从0开始,所以下面就要加1.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

三、搭建多层神经网络

L层神经网络的结构:

这个模型可以被总结为: [LINEAR -> RELU] × (L-1) -> LINEAR -> SIGMOID -> OUTPUT

  • 维度为 (64,64,3)的图像被平整为 (12288,1) 大小的向量
  • 输入数据[x0,x1,...,x12287]T[x_0,x_1,...,x_{12287}]^T[x0​,x1​,...,x12287​]T和大小为 (n[1],12288)(n^{[1]}, 12288)(n[1],12288)的权重矩阵 W[1]W^{[1]}W[1]矩阵乘
  • 加上偏置 b[1]b^{[1]}b[1]之后用relu函数激活后得到:A[1]=[a0[1],a1[1],...,an[1]−1[1]]TA^{[1]}=[a_0^{[1]}, a_1^{[1]},..., a_{n^{[1]}-1}^{[1]}]^TA[1]=[a0[1]​,a1[1]​,...,an[1]−1[1]​]T.
  • 以上的过程可以对(W[l],b[l])(W^{[l]}, b^{[l]})(W[l],b[l]) 进行(L-1) 次,
  • 最后用sigmoidsigmoidsigmoid函数激活最后的线性单元Z[L]Z^{[L]}Z[L]得到A[L]A^{[L]}A[L](或者Y^\hat{Y}Y^)),如果结果大于0.5,则分类为cat,否则就为noncat

具体步骤

  1. 初始化参数
  2. 循环迭代 num_iterations次:
    a. 前向传播
    b. 计算成本函数
    c. 后向传播
    d. 更新参数(使用 parameters, 和后向传播中得到的梯度grads )
  3. 使用训练好的参数进行预测

代码如下

def L_layer_model(X, Y, layers_dims, learning_rate=0.0075, num_iterations=3000, print_cost=False, isPlot=True):'''实现一个L层神经网络,[Linear -> Relu] *(L -1) -> Linera -> sigmoid.:param X:输入的数据,维度为(n_x,样本数):param Y:标签向量,维度为(1,数量):param layers_dims:层数的向量,维度为(n_x,n_h,……,n_y):param learning_rate:学习率:param num_iterations:迭代的次数:param print_cost:是否打印:param isPlot:是否绘制出误差值的图谱:return:parameters:模型学习的参数,它们可以用来预测。'''np.random.seed(1)costs = []parameters = initialize_parameters_deep(layers_dims)for i in range(0, num_iterations):AL, caches = L_model_forward(X, parameters) # 前向传播cost = compute_cost(AL, Y) # 计算成本grads = L_model_backward(AL, Y, caches)  # 后向传播,cache= linear_cache = (A, W, b) + activation_cache(Z) parameters = update_parameters(parameters, grads, learning_rate) # 更新参数# 打印成本值if i % 100 == 0:costs.append(cost)if print_cost:print("第", i, "次迭代,成本值为:", np.squeeze(cost))# 绘制成本值图形if isPlot:plt.plot(np.squeeze(costs))plt.ylabel('cost')plt.xlabel('iterations(per tens)')plt.title("Learning rate =" + str(learning_rate))plt.show()return parameters

四、正式训练

4.1 导入数据

我们现有一个数据集"data.h5",其中包含训练集“train_catvnoncat.h5”和测试集“test_catvnoncat.h5”

  • 标签值为0(noncat)或1(cat)的m_train个样本的训练集
  • 标签值为0(noncat)或1(cat)的m_test个样本的训练集
  • 每张图片的维度为(num_px, num_px, 3),其中的3代表RGB
  • 训练样本数m_train:209
  • 测试样本数m_test:50
  • 每幅图像大小:(64,64,3)
  • train_set_x_orig shape:(209,64,64,3)
  • train_set_y_orig shape:(1, 209)
  • test_set_x_orig shape: (50, 64, 64, 3)
  • test_set_y_orig shape: (1, 50)
def load_dataset():train_dataset = h5py.File('datasets/train_catvnoncat.h5', "r")  # 读取训练集数据train_set_x_orig = np.array(train_dataset["train_set_x"][:])  # 训练集特征 (m_train(209),num_px, num_px, 3)train_set_y_orig = np.array(train_dataset["train_set_y"][:])  # 训练集标签 (m_train(209),1)test_dataset = h5py.File('datasets/test_catvnoncat.h5', "r")  # 读取测试集数据test_set_x_orig = np.array(test_dataset["test_set_x"][:])  # 测试集特征 (m_test(50),num_px, num_px, 3)test_set_y_orig = np.array(test_dataset["test_set_y"][:])  # 测试集标签 (m_test(50),1)classes = np.array(test_dataset["list_classes"][:])  # 字符串numpy数组,包含'cat'和'noncat'train_set_y_orig = train_set_y_orig.reshape((1, train_set_y_orig.shape[0]))  # 维度变为(1,m_train(209))test_set_y_orig = test_set_y_orig.reshape((1, test_set_y_orig.shape[0]))  # 维度变为(1,m_test(50))return train_set_x_orig, train_set_y_orig, test_set_x_orig, test_set_y_orig, classes

4.2 标准化数据

通常,在将数据送入神经网络之前,我们需要改变图像维度并且将它们标准化。

  • train_x shape: (12288, 209)
  • test_x shape: (12288, 50)
  • 其中12288=64 * 64 * 3,刚好是一个图像成向量排布的大小
# 加载数据
train_x_orig, train_y, test_x_orig, test_y, classes = load_dataset()
# 改变训练样本和测试样本维度
train_x_flatten = train_x_orig.reshape(train_x_orig.shape[0], -1).T  # -1表示维度可以通过数据进行判断,注意有转置
test_x_flatten = test_x_orig.reshape(test_x_orig.shape[0], -1).T
# 标准化数据,使值介于0 — 1之间
train_x = train_x_flatten / 255
test_x = test_x_flatten / 255

4.3 进行训练

# 数据加载完成,开始进行L层网络的训练
layers_dims = [12288, 20, 7, 5, 1]  # 5-layer model
parameters = L_layer_model(train_x, train_y, layers_dims, num_iterations=2500, print_cost=True, isPlot=True)

训练结果

第 0 次迭代,成本值为: 0.7157315134137129
第 100 次迭代,成本值为: 0.6747377593469114
第 200 次迭代,成本值为: 0.6603365433622127
第 300 次迭代,成本值为: 0.6462887802148751
:
:第 2000 次迭代,成本值为: 0.08505631034960733
第 2100 次迭代,成本值为: 0.0575839119860166
第 2200 次迭代,成本值为: 0.04456753454691873
第 2300 次迭代,成本值为: 0.03808275166596694
第 2400 次迭代,成本值为: 0.03441074901839676

4.4 结果分析

4.4.1 进行预测

在以上代码运行完成之后,我们的训练模型就算是训练好了,但实际效果如何还需要检验。

  • 首先我们要用这个模型对训练集进行一次预测,查看模型对训练集的吻合程度
  • 对测试集进行预测,查看准确率

预测函数如下:

def predict(X, Y, parameters):'''该函数用于预测L层神经网络的结果:param X: 测试集:param Y: 标签:param parameters: 训练模型的参数:return:p:给定数据集X的预测'''m = X.shape[1]  # 测试集的样本数n = len(parameters) // 2  # 神经网络的层数p = np.zeros((1, m))  # 同标签维度相同的预测# 根据参数进行前向传播AL, caches = L_model_forward(X, parameters)for i in range(0, AL.shape[1]):  # i: 0 -> 样本数-1if AL[0, i] > 0.5:p[0, i] = 1else:p[0, i] = 0print("准确度为" + str(float(np.sum(p == Y) / m)))

进行预测

pred_train = predict(train_x, train_y, parameters)  # 训练集
pred_test = predict(test_x, test_y, parameters)  # 测试集

预测结果

准确度为0.9952153110047847
准确度为0.78

五、完整代码

import numpy as np
import h5py
import matplotlib.pyplot as plt
from testCases import *  # 测试函数np.random.seed(1)  # 指定随机种子def initialize_parameters_deep(layer_dims):'''此函数是为了初始化多层网络参数:param layer_dims:包含我们网络中每个图层的节点的列表:return:parameters:包含参数“W1”,“b1”,“W2”……“WL”,"bL"的字典Wl:权重矩阵,维度为(layer_dims[l],layer_dims[l-1])bl:偏向量,维度为(layer_dims[l],1)'''np.random.seed(3)parameters = {}L = len(layer_dims)  # 网络的层数for i in range(1, L):  # range下标从0开始,如果L=3,则list(range(1,3))=[1,2]parameters['W' + str(i)] = np.random.randn(layer_dims[i], layer_dims[i - 1]) / np.sqrt(layer_dims[i - 1])  # 用除代替0.01# 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):'''实现前向传播的线性部分:param A:来自上一层(或输入数据)的激活,维度为(上一层节点数,样本数):param W:权重矩阵,维度为(当前层的节点数,上一层的节点数):param b:偏向量,维度为(当前层的节点数,1):return:Z:激活函数的输入,也称为预激活参数cache:一个包含A,W,b的字典,储存它们以便后向传播的计算'''Z = np.dot(W, A) + bassert (Z.shape == (W.shape[0], A.shape[1]))cache = (A, W, b)  # cache是一个列表return Z, cachedef sigmoid(Z):"""Implements the sigmoid activation in numpyArguments:Z -- numpy array of any shapeReturns:A -- output of sigmoid(z), same shape as Zcache -- returns Z as well, useful during backpropagation"""A = 1 / (1 + np.exp(-Z))cache = Zreturn A, cachedef relu(Z):"""Implement the RELU function.Arguments:Z -- Output of the linear layer, of any shapeReturns:A -- Post-activation parameter, of the same shape as Zcache -- a python dictionary containing "A" ; stored for computing the backward pass efficiently"""A = np.maximum(0, Z)assert (A.shape == Z.shape)cache = Zreturn A, cachedef linear_activation_forward(A_prev, W, b, activation):'''实现linear->activation这一层的前向传播:param A_prev:来自上一层(或输入层)的激活,维度为(上一层节点数,样本数):param W:权重矩阵,numpy数组,维度为(当前层节点数量,上一层节点数量):param b:偏向量,numpy阵列,维度为(当前层节点数量,1):param activation:选择在此层中的激活函数,字符串类型,【sigmoid,relu】:return:A:激活函数的输出,也称为激活后的值cache:一个包含'linear_cache'和'activation_cache'的字典,我们需要存储它以有效地计算后向传播'''if activation == "sigmoid":Z, linear_cache = linear_forward(A_prev, W, b)  # linear_cache = (A, W, b)A, activation_cache = sigmoid(Z)  # activation_cache = Zelif activation == "relu":Z, linear_cache = linear_forward(A_prev, W, b)  # linear_cache = (A, W, b)A, activation_cache = relu(Z)  # activation_cache = Zassert (A.shape == (W.shape[0], A.shape[1]))cache = (linear_cache, activation_cache)  # (A,W,b,Z),其实是个一列表return A, cachedef L_model_forward(X, parameters):'''实现[LINEAR-> RELU] *(L-1) - > LINEAR-> SIGMOID计算前向传播,也就是多层网络的前向传播,为后面每一层都执行LINEAR和ACTIVATION也就是前L-1层用relu激活函数,输出层用sigmoid。:param X:输入数据,numpy数组,维度为(输入层节点数,样本数):param parameters:包含W1、b1,W2,b2...的字典,是initialize_parameters_deep(layer_dims)的输出,:return:AL:最后的激活值,也就是Yhatcaches:包含以下内容的缓存列表:linear_relu_forward()的每一个cache(缓存,Z),共有L-1个,索引从0-L-2linear_sigmoid_forward()的cache(Z),只有一个,索引为L-1'''caches = []  # 缓存是一个列表,也就是可变、可添加的A = XL = len(parameters) // 2  # 网络的层数,//是整除# 实现[LINEAR-> RELU] *(L-1),添加cache 到caches中for l in range(1, L):  # 1 -> L-1A_prev = A  # 与函数保持一致A, cache = linear_activation_forward(A_prev, W=parameters['W' + str(l)], b=parameters['b' + str(l)],activation="relu")caches.append(cache)  # list 添加元素要用append,缓存的元素是Z# 实现LINEAR-> SIGMOID,添加cache到caches列表中AL, cache = linear_activation_forward(A, W=parameters['W' + str(L)], b=parameters['b' + str(L)],activation="sigmoid")caches.append(cache)  # list 添加元素要用appendassert (AL.shape == (1, X.shape[1]))  # 维度为(1,样本数)return AL, cachesdef compute_cost(AL, Y):'''计算成本函数:param AL: 与标签预测相对应的概率向量,维度为(1,样本数):param Y:标签向量(例如:如果是猫则为1,不是猫则为0),维度为(1,样本数):return:cost:交叉熵成本'''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)  # 让成本函数cost维度是所期望的,比如将[[17]]变成17assert (cost.shape == ())  # 一维return costdef linear_backward(dZ, cache):'''为单层实现反向传播的线性部分(第l层):param dZ: 相对于(当前l层的)线性输出的成本梯度:param cache:来自当前层前向传播的值的元组(A_prev,W,b):return:dA_prev:相对于激活(前一层l-1)的成本梯度,与A_prev维度相同dW:相对于W(当前层l)的成本函数梯度,与w维度相同db:相对于b(当前层l)的成本函数梯度,与b维度相同'''A_prev, W, b = cachem = 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, dbdef sigmoid_backward(dA, cache):"""Implement the backward propagation for a single SIGMOID unit.Arguments:dA -- post-activation gradient, of any shapecache -- 'Z' where we store for computing backward propagation efficientlyReturns:dZ -- Gradient of the cost with respect to Z"""Z = caches = 1 / (1 + np.exp(-Z))dZ = dA * s * (1 - s)assert (dZ.shape == Z.shape)return dZdef relu_backward(dA, cache):"""Implement the backward propagation for a single RELU unit.Arguments:dA -- post-activation gradient, of any shapecache -- 'Z' where we store for computing backward propagation efficientlyReturns:dZ -- Gradient of the cost with respect to Z"""Z = cachedZ = np.array(dA, copy=True)  # just converting dz to a correct object.# When z <= 0, you should set dz to 0 as well.dZ[Z <= 0] = 0assert (dZ.shape == Z.shape)return dZdef linear_activation_backward(dA, cache, activation):'''实现linear -> Activation 层的后向传播:param dA: 当前层激活后的梯度值:param cache: 我们存储用于有效计算反向传播的值的元组,值为(linear_cache(# linear_cache = (A, W, b)),activation_cache(# Z)):param activation:要在此层中使用的激活函数的名称,字符串类型,如["relu"|"sigmoid"]:return:dA_prev:相对于激活(前一层L-1)的成本梯度值,与A_prev的维度相同dW:相对于W(当前层l)的成本梯度值,与W维度相同db:相对于b(当前层l)的成本梯度值,与b维度相同'''linear_cache, activation_cache = cacheif activation == "relu":dZ = relu_backward(dA, activation_cache)  # activation_cache = Zif activation == "sigmoid":dZ = sigmoid_backward(dA, activation_cache)dA_prev, dW, db = linear_backward(dZ, linear_cache)return dA_prev, dW, dbdef L_model_backward(AL, Y, caches):'''构建多层模型的后向传播函数,对[LINEAR->RELU] * (L-1) -> LINEAR -> SIGMOID组执行反向传播:param AL:概率向量,正向传播的输出(L_model_forward()):param Y:标签向量,true "label" vector (containing 0 if non-cat, 1 if cat):param caches:包含以下内容的cache列表linear_activation_forward("relu")的cache,不包含输出层linear_activation_forward("sigmoid")的cache #linear_cache, activation_cache)  # (A,W,b,Z),其实是个一列表:return:grads:包含梯度值的字典grads["dA" + str(l)] = ...grads["dW" + str(l)] = ...grads["db" + str(l)] = ...'''grads = {}L = len(caches)  # 网络的层数,隐藏层+输出层m = AL.shape[1]Y = Y.reshape(AL.shape)  # 因为有可能Y是个行向量,使之与AL保持一致# 初始化后向传播dAL = -(np.divide(Y, AL) - np.divide(1 - Y, 1 - AL))  # 成本函数的导数# Lth layer (SIGMOID -> LINEAR) gradients. Inputs: "AL, Y, caches". Outputs: "grads["dAL"], grads["dWL"], grads["dbL"]current_cache = caches[-1]  # caches的最后一个cachegrads["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)):  # [L-2,...,0]current_cache = caches[l]  # l=L-2dA_prev_temp, dW_temp, db_temp = linear_activation_backward(grads["dA" + str(l + 2)], current_cache,"relu")  # dALgrads["dA" + str(l + 1)] = dA_prev_temp  # l=L-2 , l+1=L-1grads["dW" + str(l + 1)] = dW_tempgrads["db" + str(l + 1)] = db_tempreturn gradsdef update_parameters(parameters, grads, learning_rate):'''使用梯度下降更新参数:param parameters:包含参数“W1”,“b1”,“W2”……“WL”,"bL"的字典:param grads:包含梯度值的字典,包含参数“dA1”,“dW1”,“db1”,“dW2”……“dWL”,"dbL",“dWL”:param learning_rate:学习参数:return::parameters:包含更新参数的字典parameters["W" + str(l)] = ...parameters["b" + str(l)] = ...'''L = len(parameters) // 2  # 整除for l in range(L):  # 0-L-1parameters["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 parametersdef L_layer_model(X, Y, layers_dims, learning_rate=0.0075, num_iterations=3000, print_cost=False, isPlot=True):'''实现一个L层神经网络,[Linear -> Relu] *(L -1) -> Linera -> sigmoid.:param X:输入的数据,维度为(n_x,样本数):param Y:标签向量,维度为(1,数量):param layers_dims:层数的向量,维度为(n_x,n_h,……,n_y):param learning_rate:学习率:param num_iterations:迭代的次数:param print_cost:是否打印:param isPlot:是否绘制出误差值的图谱:return:parameters:模型学习的参数,它们可以用来预测。'''np.random.seed(1)costs = []parameters = initialize_parameters_deep(layers_dims)for i in range(0, num_iterations):AL, caches = L_model_forward(X, parameters)  # 前向传播cost = compute_cost(AL, Y)  # 计算成本grads = L_model_backward(AL, Y, caches)  # 后向传播,cache= linear_cache = (A, W, b) + activation_cache(Z)parameters = update_parameters(parameters, grads, learning_rate)  # 更新参数# 打印成本值if i % 100 == 0:costs.append(cost)if print_cost:print("第", i, "次迭代,成本值为:", np.squeeze(cost))# 绘制成本值图形if isPlot:plt.plot(np.squeeze(costs))plt.ylabel('cost')plt.xlabel('iterations(per tens)')plt.title("Learning rate =" + str(learning_rate))plt.show()return parametersdef load_dataset():train_dataset = h5py.File('datasets/train_catvnoncat.h5', "r")  # 读取训练集数据train_set_x_orig = np.array(train_dataset["train_set_x"][:])  # 训练集特征 (m_train(209),num_px, num_px, 3)train_set_y_orig = np.array(train_dataset["train_set_y"][:])  # 训练集标签 (m_train(209),1)test_dataset = h5py.File('datasets/test_catvnoncat.h5', "r")  # 读取测试集数据test_set_x_orig = np.array(test_dataset["test_set_x"][:])  # 测试集特征 (m_test(50),num_px, num_px, 3)test_set_y_orig = np.array(test_dataset["test_set_y"][:])  # 测试集标签 (m_test(50),1)classes = np.array(test_dataset["list_classes"][:])  # 字符串numpy数组,包含'cat'和'noncat'train_set_y_orig = train_set_y_orig.reshape((1, train_set_y_orig.shape[0]))  # 维度变为(1,m_train(209))test_set_y_orig = test_set_y_orig.reshape((1, test_set_y_orig.shape[0]))  # 维度变为(1,m_test(50))return train_set_x_orig, train_set_y_orig, test_set_x_orig, test_set_y_orig, classesdef predict(X, Y, parameters):'''该函数用于预测L层神经网络的结果:param X: 测试集:param Y: 标签:param parameters: 训练模型的参数:return:p:给定数据集X的预测'''m = X.shape[1]  # 测试集的样本数n = len(parameters) // 2  # 神经网络的层数p = np.zeros((1, m))  # 同标签维度相同的预测# 根据参数进行前向传播AL, caches = L_model_forward(X, parameters)for i in range(0, AL.shape[1]):  # i: 0 -> 样本数-1if AL[0, i] > 0.5:p[0, i] = 1else:p[0, i] = 0print("准确度为" + str(float(np.sum(p == Y) / m)))# def print_mislabeled_images(classes, X, y, p):
#     """
#     绘制预测和实际不同的图像。
#         X - 数据集
#         y - 实际的标签
#         p - 预测
#     """
#     a = p + y
#     mislabeled_indices = np.asarray(np.where(a == 1))
#     plt.rcParams['figure.figsize'] = (40.0, 40.0) # set default size of plots
#     num_images = len(mislabeled_indices[0])
#     for i in range(num_images):
#         index = mislabeled_indices[1][i]
#
#         plt.subplot(2, num_images, i + 1)
#         plt.imshow(X[:,index].reshape(64,64,3), interpolation='nearest')
#         plt.axis('off')
#         plt.title("Prediction: " + classes[int(p[0,index])].decode("utf-8") + " \n Class: " + classes[y[0,index]].decode("utf-8"))
#
#
# print_mislabeled_images(classes, test_x, test_y, pred_test)# 加载数据
train_x_orig, train_y, test_x_orig, test_y, classes = load_dataset()
# 改变训练样本和测试样本维度
train_x_flatten = train_x_orig.reshape(train_x_orig.shape[0], -1).T  # -1表示维度可以通过数据进行判断,注意有转置
test_x_flatten = test_x_orig.reshape(test_x_orig.shape[0], -1).T
# 标准化数据,使值介于0 — 1之间
train_x = train_x_flatten / 255
test_x = test_x_flatten / 255# 数据加载完成,开始进行L层网络的训练
layers_dims = [12288, 20, 7, 5, 1]  # 5-layer model
parameters = L_layer_model(train_x, train_y, layers_dims, num_iterations=2500, print_cost=True, isPlot=True)pred_train = predict(train_x, train_y, parameters)  # 训练集
pred_test = predict(test_x, test_y, parameters)  # 测试集

Course 1 神经网络和深度学习 Week4 搭建多层神经网络识别猫图相关推荐

  1. 01.神经网络和深度学习 W3.浅层神经网络

    文章目录 1. 神经网络概览 2. 神经网络的表示 3. 神经网络的输出 4. 多样本向量化 5. 激活函数 6. 为什么需要 非线性激活函数 7. 激活函数的导数 8. 随机初始化 作业 参考: 吴 ...

  2. 吴恩达深度学习deeplearning.ai——第一门课:神经网络与深度学习——第二节:神经网络基础(上)

    2.1 二元分类 (Binary Classification) 这周我们将学习神经网络的基础知识,其中需要注意的是,当实现一个神经网络的时候,我们需要知道一些非常重要的技术和技巧.例如有一个包含 m ...

  3. 深入浅出神经网络与深度学习--神经元感知机单层神经网络介绍(-)

    1     概述 写神经网络与深度学习方面的一些知识,是一直想做的事情.但本人比较懒惰,有点时间想玩点游戏呀什么的,一直拖到现在. 也由于现在已经快当爸了,心也沉了下来,才能去梳理一些东西.本文会深入 ...

  4. 吴恩达深度学习作业编程1【识别猫片】

    1.jumpyter基本操作 shift+enter执行程序 2.向量化和非向量化的区别 import numpy as np a=np.array([1,2,3,4])#创建一个数组 print(a ...

  5. 01.神经网络和深度学习 W3.浅层神经网络(作业:带一个隐藏层的神经网络)

    文章目录 1. 导入包 2. 预览数据 3. 逻辑回归 4. 神经网络 4.1 定义神经网络结构 4.2 初始化模型参数 4.3 循环 4.3.1 前向传播 4.3.2 计算损失 4.3.3 后向传播 ...

  6. 吴恩达神经网络和深度学习

    [前言] 在学习了深度学习和神经网络之后,为什么我要以博客的形式来做笔记?这CSDN有那么多的优秀文章,我自己写的都比不上 别人的我写的真的有意义吗,为什么我要浪费大量的时间去做这项工作?我相信一句话 ...

  7. 神经网络与深度学习笔记汇总二

    神经网络与深度学习笔记汇总二 正交化(方便调整参数) 迭代 单实数评估指标(判断几种手段/方法哪个更好) 指标选取 训练集.开发集.测试集作用与用途 评估指标 判断算法是好是坏 迁移学习 总结 往期回 ...

  8. 吴恩达-神经网络和深度学习课程-学习心得(一)

    前言: 陆陆续续学完了吴恩达老师在网易云课堂上开设的深度学习工程师微专业的部分内容(01.神经网络与深度学习,04.卷积神经网络,02.改善深层神经网络),在进一步应用实践之前,对之前的学习的内容做个 ...

  9. 循环神经网络matlab程序设计,神经网络及深度学习(包含matlab代码).pdf

    神经网络及深度学习(包含matlab代码) 神经网络及深度学习 (包含 MATLAB 仿真) 人工神经网络(Artificial Neural Network,即 ANN ), 作为对人脑最简单的一种 ...

最新文章

  1. 你只使用到了 VS Code 20% 的功能?听听 VS Code 首著作者怎么说
  2. Vue-router 的使用 (vue3.x版本)
  3. Spring Security使用数据库管理资源整理
  4. c#写图像tif gdal_Gdal系列 (二)读取图像基本操作b + 简单波段合成
  5. hdu 5584 gcd/lcm/数学公式
  6. 字节跳动 CEO 张楠谈遭微信封禁;传蚂蚁集团将重组 ;Apache ECharts 5 发布| 极客头条...
  7. “医检助手”诚聘互联网运营总监
  8. python数据爬虫项目
  9. 如何实施一个BI项目(附项目管理模板)
  10. 原型图APP尺寸大小
  11. 民生服务是“双创”永恒主题 且听“鸿雁旅居网”、“熊猫中医”谈背后心路历程...
  12. 【故障分析】基于主成分分析实现三容水箱故障诊断附matlab代码
  13. js设计程序实现摄氏度和华氏度转换
  14. 互联网金融热浪下丨看看美国的互联网金融怎么玩?
  15. 正则匹配过滤字母和数字
  16. Spring5框架-IOC容器
  17. java2 day03 XML DOM4J
  18. 基于百度接口的手机无广告浏览器设计
  19. <Zhuuu_ZZ>HIVE(终)总结大全:是兄弟就来三连我
  20. 共享充电语音提醒功能如何实现?

热门文章

  1. 元学习:实现通用人工智能的关键!
  2. python读取csv文件表头_Python读取CSV文件
  3. NRF52832学习笔记(11)——蓝牙MAC地址
  4. linux mmc驱动
  5. css背景图铺满整个屏幕
  6. 【Requests】获取本地的请求IP和域名解析的IP
  7. 项目启动成功,但是Eureka页面不显示
  8. linkMap深度解析
  9. 收到面试通知后,如何准备可以大大提升面试成功率?
  10. 谈谈红楼梦(第1-5回)