在掌握了逻辑回归算法后,先来学习浅层神经网络,之后再对深度神经网络进行学习。
1. 原理推导
1.1 神经网络表示
神经网络由输入层、隐含层和输出层构成。L层神经网络,隐含层为第1 ~ (L - 1)层,输出层为第L层。为了方便,将输入层写成第0层。
定义:上标[l]表示第l层,下标j表示第j个节点。
例如,下图为2层神经网络,包含1个隐藏层:
输入层和隐含层可以写成:
a[0]=x=⎡⎣⎢x1x2x3⎤⎦⎥,a[1]=⎡⎣⎢⎢⎢⎢⎢⎢a[1]1a[1]2a[1]3a[1]4⎤⎦⎥⎥⎥⎥⎥⎥(1)
1.2 正向传播(Forward Propagation)
计算过程与逻辑回归类似,只是多了隐含层,如下:
z[1]1=w[1]T1x+b[1]1,a[1]1=g(z[1]1)z[1]2=w[1]T2x+b[1]2,a[1]2=g(z[1]2)z[1]3=w[1]T3x+b[1]3,a[1]3=g(z[1]3)z[1]4=w[1]T4x+b[1]4,a[1]4=g(z[1]4)(2)
式中,g(z)为**函数。可将上式向量化为:
⎡⎣⎢⎢⎢⎢⎢⎢z[1]1z[1]2z[1]3z[1]4⎤⎦⎥⎥⎥⎥⎥⎥=⎡⎣⎢⎢⎢⎢⎢⎢−−−−w[1]T1w[1]T2w[1]T3w[1]T4−−−−⎤⎦⎥⎥⎥⎥⎥⎥⎡⎣⎢x1x2x3⎤⎦⎥+⎡⎣⎢⎢⎢⎢⎢⎢b[1]1b[1]2b[1]3b[1]4⎤⎦⎥⎥⎥⎥⎥⎥,⎡⎣⎢⎢⎢⎢⎢⎢a[1]1a[1]2a[1]3a[1]4⎤⎦⎥⎥⎥⎥⎥⎥=g⎛⎝⎜⎜⎜⎜⎜⎜⎡⎣⎢⎢⎢⎢⎢⎢z[1]1z[1]2z[1]3z[1]4⎤⎦⎥⎥⎥⎥⎥⎥⎞⎠⎟⎟⎟⎟⎟⎟(3)
即:
z[1]=W[1]x+b[1],a[1]=g(z[1])(4)
式中,
z[1]=⎡⎣⎢⎢⎢⎢⎢⎢z[1]1z[1]2z[1]3z[1]4⎤⎦⎥⎥⎥⎥⎥⎥,W[1]=⎡⎣⎢⎢⎢⎢⎢⎢−−−−w[1]T1w[1]T2w[1]T3w[1]T4−−−−⎤⎦⎥⎥⎥⎥⎥⎥,b[1]=⎡⎣⎢⎢⎢⎢⎢⎢b[1]1b[1]2b[1]3b[1]4⎤⎦⎥⎥⎥⎥⎥⎥,a[1]=⎡⎣⎢⎢⎢⎢⎢⎢a[1]1a[1]2a[1]3a[1]4⎤⎦⎥⎥⎥⎥⎥⎥(5)
因此,对于该2层神经网络的每个样本,给定a[0] = x,正向传播计算流程如下:
z[1]=W[1]a[0]+b[1]a[1]=g(z[1])z[2]=W[2]a[1]+b[2]a[2]=g(z[2])(6)
各矩阵维度如下表:
矩阵 |
a[0]
|
W[1]
|
b[1]
|
z[1]
|
a[1]
|
W[2]
|
b[2]
|
z[2]
|
a[2]
|
维度 |
(3, 1) |
(4, 3) |
(4, 1) |
(4, 1) |
(4, 1) |
(1, 4) |
(1, 1) |
(1, 1) |
(1, 1) |
以上推导仅仅针对单个样本,对于m个样本,以上标(m)表示第m个样本。则可以将公式改写为:
⎡⎣⎢|z[1](1)||z[1](2)||⋯||z[1](m)|⎤⎦⎥=⎡⎣⎢⎢⎢⎢⎢⎢⎢⎢−−−−w[1]1Tw[1]2Tw[1]3Tw[1]4T−−−−⎤⎦⎥⎥⎥⎥⎥⎥⎥⎥⎡⎣⎢|x(1)||x(2)||⋯||x(m)|⎤⎦⎥+b[1](7)
⎡⎣⎢|a[1](1)||a[1](2)||⋯||a[1](m)|⎤⎦⎥=g⎛⎝⎜⎡⎣⎢|z[1](1)||z[1](2)||⋯||z[1](m)|⎤⎦⎥⎞⎠⎟(8)
即:
Z[1]=W[1]X+b[1],A[1]=g(Z[1])(9)
式中,
Z[1]=⎡⎣⎢|z[1](1)||z[1](2)||⋯||z[1](m)|⎤⎦⎥,A[1]=⎡⎣⎢|a[1](1)||a[1](2)||⋯||a[1](m)|⎤⎦⎥(10)
因此,对于以上2层神经网络,针对m个样本,给定A[0] = X,正向传播计算流程如下:
Z[1]=W[1]A[0]+b[1]A[1]=g(Z[1])Z[2]=W[2]A[1]+b[2]A[2]=g(Z[2])(11)
各矩阵维度如下表:
矩阵 |
A[0]
|
W[1]
|
b[1]
|
Z[1]
|
A[1]
|
W[2]
|
b[2]
|
Z[2]
|
A[2]
|
维度 |
(3, m) |
(4, 3) |
(4, 1) |
(4, m) |
(4, m) |
(1, 4) |
(1, 1) |
(1, m) |
(1, m) |
从式中可以看出,矩阵Z和A在计算过程中,水平索引遍历所有样本集,垂直索引遍历神经网络每一层的各个节点。
1.3 **函数(Activation Function)
常见的4种**函数如下:
sigmoid函数:在0 ~ 1之间,很少用到,仅仅用于二分类问题。
tanh函数,在-1 ~ 1之间,均值为0,使得数据更集中。
sigmoid和tanh函数的一个共同缺点:当z很大或者很小时,函数的斜率都很小且接近0,从而使得梯度下降法的速度较慢。
ReLU(Rectified Linear Units)函数:通常比sigmoid和tanh函数效果好,是最常用的**函数。缺点在于,当很大的梯度经过一个ReLU神经元,更新过参数后,这个神经元再也不会对任何数据有**现象。对于较大的学习因子,经常会发生这个问题。因此,需要设置合适的、较小的学习因子。
Leaky ReLU函数:相比ReLU函数,设置了一个很小的常数α,保留了一些负轴的值,使得负轴信息不会全部丢失。
1.4 为什么**函数使用非线性函数?
假如使用线性函数g(z) = z:
则正向传播化简为:
z[1]=W[1]a[0]+b[1]a[1]=g(z[1])=z[1]z[2]=W[2]a[1]+b[2]=W[2]z[1]+b[2]a[2]=g(z[2])=z[2]=W[2](W[1]a[0]+b[1])+b[2]=(W[2]W[1])a[0]+(W[2]b[1]+b[2])=W′a[0]+b′(12)
可以看出,a[2]与z[1]等效,如果使用线性**函数,就无法发挥隐含层的作用。在深度神经网络中,使用线性**函数,多个隐含层与没有隐含层的效果相同。
因此,必须使用非线性**函数。
1.5 梯度下降(Gradient Descent)
以n[l]表示第l层的节点数,对于以上的2层神经网络:
n[0]=nx=3,n[1]=4,n[2]=1(13)
代价函数:
J(w,b)=1m∑i=1mL(a(i),y(i))=−1m∑i=1m[y(i)loga(i)+(1−y(i))log(1−a(i))](14)
梯度下降法流程如下:
Computeactivationa(i),i=1,⋯,mComputecostfunctionJdW[1]=dJdW[1],db[1]=dJdb[1],⋯W[1]=W[1]−αdW[1],b[1]=b[1]−αdb[1],⋯(15)
式中,α为学习因子。为了更新参数W和b,关键在于求解导数dW和db,可以通过反向传播来求解。
1.6 反向传播(Backward Propagation)
在正向传播中,已经计算得到了Z[1]、A[1]、Z[2]、A[2]。此处使用sigmoid函数来推导。对于2层神经网络,针对单个样本,正向传播为:
z[1]=W[1]a[0]+b[1]a[1]=g(z[1])z[2]=W[2]a[1]+b[2]a[2]=g(z[2])(16)
与逻辑回归类似,反向传播计算如下:
dz[2]=a[2]−ydW[2]=dz[2]a[1]Tdb[2]=dz[2]dz[1]=dJdz[2]⋅dz[2]da[1]⋅da[1]dz[1]=W[2]Tdz[2]∗g[1]′(z[1])dW[1]=dz[1]a[0]Tdb[1]=dz[1](17)
拓展到m个样本得到:
dZ[2]=A[2]−YdW[2]=1mdZ[2]A[1]Tdb[2]=1mnp.sum(dZ[2],axis=1,keepdims=True)dz[1]=W[2]TdZ[2]∗g[1]′(Z[1])dW[1]=1mdZ[1]A[0]Tdb[1]=1mnp.sum(dZ[1],axis=1,keepdims=True)(18)
式中,“*”表示元素点乘。通过上式求出梯度dW和db,并代入梯度下降法中,即可对参数W和b进行更新。
1.7 随机初始化
对于如下的2层神经网络。
如果将参数全部初始化为0:
W[1]=[0000],b[1]=[00](19)
那么,无论采用什么样本来训练,都会得到:
a[1]1=a[1]2,dz[1]1=dz[1]2(20)
在更新参数的过程中,W始终保持如下的对称形式:
W=[uuvv](21)
这样,a[1]1和a[1]2具体相同的功能,实际上只需要一个就够了。
所以,在初始化神经网络参数时,应当避免以上情况,将参数W随机初始化,为了方便,可以将b全部初始化为0。
另外,根据sigmoid或者tanh函数的特点,当输入较大时,函数的导数较大,会导致学习速度较慢。因此,通常将参数随机初始化为较小的参数:
W=np.random.randn(l,l−1)∗0.01b=np.zeros((l,1))(22)
2. 代码实现
案例:采用2层神经网络实现色点的二分类。
计算流程如下:
2.1 初始化initialize
对参数W进行随机初始化,将参数b初始化为0。核心代码如下:

2.2 正向传播forward
按照下式计算各个节点的Z和A:
Z[1]=W[1]A[0]+b[1]A[1]=g(Z[1])Z[2]=W[2]A[1]+b[2]A[2]=g(Z[2])(23)
核心代码如下:

2.3 计算代价函数compute_cost
采用下式计算代价函数:
J(w,b)=1m∑i=1mL(a(i),y(i))=−1m∑i=1m[y(i)loga(i)+(1−y(i))log(1−a(i))](24)
核心代码如下:

2.4 反向传播backward
按照下式计算参数的梯度dW和db:
dZ[2]=A[2]−YdW[2]=1mdZ[2]A[1]Tdb[2]=1mnp.sum(dZ[2],axis=1,keepdims=True)dz[1]=W[2]TdZ[2]∗g[1]′(Z[1])dW[1]=1mdZ[1]A[0]Tdb[1]=1mnp.sum(dZ[1],axis=1,keepdims=True)(25)
核心代码如下:

2.5 参数更新update_parameters
按照下式对参数W和b进行更新:
W[1]=W[1]−αdW[1],b[1]=b[1]−αdb[1]W[2]=W[2]−αdW[2],b[2]=b[2]−αdb[2](26)
核心代码如下:

2.6 模型构建nn_model
将以上几个模块进行整合,输入训练样本,得到最优参数W和b。关键代码如下:

2.7 预测predict
根据优化得到的参数W和b,输入样本x,得到预测值,如果预测概率大于0.5,则预测值为1,否则为0。关键代码如下:

2.8 样本测试
代码如下:

可得到如下输出,分类准确率为90.50%。
此外,对隐含层节点数量的影响作了分析,核心代码如下:

得到如下结果。可以看出,较多的隐含层节点无法提升分类准确率,甚至存在过拟合现象,5个隐含层节点是比较理想的结果。
代码下载地址:https://gitee.com/tuzhen301/Coursera-deeplearning.ai1-3