Coursera吴恩达《神经网络与深度学习》课程笔记(4)-- 浅层神经网络

红色石头的我的网站:redstonewill.compython

上节课咱们主要介绍了向量化、矩阵计算的方法和python编程的相关技巧。并以逻辑回归为例,将其算法流程包括梯度降低转换为向量化的形式,从而大大提升了程序运算速度。本节课咱们将从浅层神经网络入手,开始真正的神经网络模型的学习。web

1. Neural Networks Overview

首先,咱们从总体结构上来大体看一下神经网络模型。算法

前面的课程中,咱们已经使用计算图的方式介绍了逻辑回归梯度降低算法的正向传播和反向传播两个过程。以下图所示。神经网络的结构与逻辑回归相似,只是神经网络的层数比逻辑回归多一层,多出来的中间那层称为隐藏层或中间层。这样从计算上来讲,神经网络的正向传播和反向传播过程只是比逻辑回归多了一次重复的计算。正向传播过程分红两层,第一层是输入层到隐藏层,用上标[1]来表示:编程

z [ 1 ] = W [ 1 ] x + b [ 1 ]

a [ 1 ] = σ ( z [ 1 ] )

第二层是隐藏层到输出层,用上标[2]来表示:网络

z [ 2 ] = W [ 2 ] a [ 1 ] + b [ 2 ]

a [ 2 ] = σ ( z [ 2 ] )

在写法上值得注意的是,方括号上标[i]表示当前所处的层数;圆括号上标(i)表示第i个样本。dom

一样,反向传播过程也分红两层。第一层是输出层到隐藏层,第二层是隐藏层到输入层。其细节部分咱们以后再来讨论。机器学习

这里写图片描述

2. Neural Network Representation

下面咱们以图示的方式来介绍单隐藏层的神经网络结构。以下图所示,单隐藏层神经网络就是典型的浅层(shallow)神经网络。svg

这里写图片描述

结构上,从左到右,能够分红三层:输入层(Input layer),隐藏层(Hidden layer)和输出层(Output layer)。输入层和输出层,顾名思义,对应着训练样本的输入和输出,很好理解。隐藏层是抽象的非线性的中间层,这也是其被命名为隐藏层的缘由。函数

在写法上,咱们一般把输入矩阵X记为 a [ 0 ] ,把隐藏层输出记为 a [ 1 ] ,上标从0开始。用下标表示第几个神经元,注意下标从1开始。例如 a 1 [ 1 ] 表示隐藏层第1个神经元, a 2 [ 1 ] 表示隐藏层第2个神经元,,等等。这样,隐藏层有4个神经元就能够将其输出 a [ 1 ] 写成矩阵的形式:学习

a [ 1 ] = [ a 1 [ 1 ] a 2 [ 1 ] a 3 [ 1 ] a 4 [ 1 ] ]

最后,相应的输出层记为 a [ 2 ] ,即 y ^ 。这种单隐藏层神经网络也被称为两层神经网络(2 layer NN)。之因此叫两层神经网络是由于,一般咱们只会计算隐藏层输出和输出层的输出,输入层是不用计算的。这也是咱们把输入层层数上标记为0的缘由( a [ 0 ] )。

关于隐藏层对应的权重 W [ 1 ] 和常数项 b [ 1 ] W [ 1 ] 的维度是(4,3)。这里的4对应着隐藏层神经元个数,3对应着输入层x特征向量包含元素个数。常数项 b [ 1 ] 的维度是(4,1),这里的4一样对应着隐藏层神经元个数。关于输出层对应的权重 W [ 2 ] 和常数项 b [ 2 ] W [ 2 ] 的维度是(1,4),这里的1对应着输出层神经元个数,4对应着输出层神经元个数。常数项 b [ 2 ] 的维度是(1,1),由于输出只有一个神经元。总结一下,第i层的权重 W [ i ] 维度的行等于i层神经元的个数,列等于i-1层神经元的个数;第i层常数项 b [ i ] 维度的行等于i层神经元的个数,列始终为1。

3. Computing a Neural Network’s Output

接下来咱们开始详细推导神经网络的计算过程。回顾一下,咱们前面讲过两层神经网络能够当作是逻辑回归再重复计算一次。以下图所示,逻辑回归的正向计算能够分解成计算z和a的两部分:

z = w T x + b

a = σ ( z )

这里写图片描述

对于两层神经网络,从输入层到隐藏层对应一次逻辑回归运算;从隐藏层到输出层对应一次逻辑回归运算。每层计算时,要注意对应的上标和下标,通常咱们记上标方括号表示layer,下标表示第几个神经元。例如 a i [ l ] 表示第l层的第i个神经元。注意,i从1开始,l从0开始。

下面,咱们将从输入层到输出层的计算公式列出来:

z 1 [ 1 ] = w 1 [ 1 ] T x + b 1 [ 1 ] ,   a 1 [ 1 ] = σ ( z 1 [ 1 ] )

z 2 [ 1 ] = w 2 [ 1 ] T x + b 2 [ 1 ] ,   a 2 [ 1 ] = σ ( z 2 [ 1 ] )

z 3 [ 1 ] = w 3 [ 1 ] T x + b 3 [ 1 ] ,   a 3 [ 1 ] = σ ( z 3 [ 1 ] )

z 4 [ 1 ] = w 4 [ 1 ] T x + b 4 [ 1 ] ,   a 4 [ 1 ] = σ ( z 4 [ 1 ] )

而后,从隐藏层到输出层的计算公式为:

z 1 [ 2 ] = w 1 [ 2 ] T a [ 1 ] + b 1 [ 2 ] ,   a 1 [ 2 ] = σ ( z 1 [ 2 ] )

其中 a [ 1 ] 为:

a [ 1 ] = [ a 1 [ 1 ] a 2 [ 1 ] a 3 [ 1 ] a 4 [ 1 ] ]

上述每一个节点的计算都对应着一次逻辑运算的过程,分别由计算z和a两部分组成。

为了提升程序运算速度,咱们引入向量化和矩阵运算的思想,将上述表达式转换成矩阵运算的形式:

z [ 1 ] = W [ 1 ] x + b [ 1 ]

a [ 1 ] = σ ( z [ 1 ] )

z [ 2 ] = W [ 2 ] a [ 1 ] + b [ 2 ]

a [ 2 ] = σ ( z [ 2 ] )

这里写图片描述

以前也介绍过,这里顺便提一下, W [ 1 ] 的维度是(4,3), b [ 1 ] 的维度是(4,1), W [ 2 ] 的维度是(1,4), b [ 2 ] 的维度是(1,1)。这点须要特别注意。

4. Vectorizing across multiple examples

上一部分咱们只是介绍了单个样本的神经网络正向传播矩阵运算过程。而对于m个训练样本,咱们也可使用矩阵相乘的形式来提升计算效率。并且它的形式与上一部分单个样本的矩阵运算十分类似,比较简单。

以前咱们也介绍过,在书写标记上用上标(i)表示第i个样本,例如 x ( i ) z ( i ) a [ 2 ] ( i ) 。对于每一个样本i,可使用for循环来求解其正向输出:

for i = 1 to m:
        z [ 1 ] ( i ) = W [ 1 ] x ( i ) + b [ 1 ]
        a [ 1 ] ( i ) = σ ( z [ 1 ] ( i ) )
        z [ 2 ] ( i ) = W [ 2 ] a [ 1 ] ( i ) + b [ 2 ]
        a [ 2 ] ( i ) = σ ( z [ 2 ] ( i ) )

不使用for循环,利用矩阵运算的思想,输入矩阵X的维度为( n x ,m)。这样,咱们能够把上面的for循环写成矩阵运算的形式:

Z [ 1 ] = W [ 1 ] X + b [ 1 ]

A [ 1 ] = σ ( Z [ 1 ] )

Z [ 2 ] = W [ 2 ] A [ 1 ] + b [ 2 ]

A [ 2 ] = σ ( Z [ 2 ] )

其中, Z [ 1 ] 的维度是(4,m),4是隐藏层神经元的个数; A [ 1 ] 的维度与 Z [ 1 ] 相同; Z [ 2 ] A [ 2 ] 的维度均为(1,m)。对上面这四个矩阵来讲,都可以这样来理解:行表示神经元个数,列表示样本数目m。

5. Explanation for Vectorized Implementation

这部分Andrew用图示的方式解释了m个样本的神经网络矩阵运算过程。其实内容比较简单,只要记住上述四个矩阵的行表示神经元个数,列表示样本数目m就好了。

值得注意的是输入矩阵X也能够写成 A [ 0 ]

6. Activation functions

神经网络隐藏层和输出层都须要激活函数(activation function),在以前的课程中咱们都默认使用Sigmoid函数 σ ( x ) 做为激活函数。其实,还有其它激活函数可供使用,不一样的激活函数有各自的优势。下面咱们就来介绍几个不一样的激活函数 g ( x )

  • sigmoid函数

这里写图片描述

  • tanh函数

这里写图片描述

  • ReLU函数

这里写图片描述

  • Leaky ReLU函数

这里写图片描述

如上图所示,不一样激活函数形状不一样,a的取值范围也有差别。

如何选择合适的激活函数呢?首先咱们来比较sigmoid函数和tanh函数。对于隐藏层的激活函数,通常来讲,tanh函数要比sigmoid函数表现更好一些。由于tanh函数的取值范围在[-1,+1]之间,隐藏层的输出被限定在[-1,+1]之间,能够当作是在0值附近分布,均值为0。这样从隐藏层到输出层,数据起到了归一化(均值为0)的效果。所以,隐藏层的激活函数,tanh比sigmoid更好一些。而对于输出层的激活函数,由于二分类问题的输出取值为{0,+1},因此通常会选择sigmoid做为激活函数。

观察sigmoid函数和tanh函数,咱们发现有这样一个问题,就是当|z|很大的时候,激活函数的斜率(梯度)很小。所以,在这个区域内,梯度降低算法会运行得比较慢。在实际应用中,应尽可能避免使z落在这个区域,使|z|尽量限定在零值附近,从而提升梯度降低算法运算速度。

为了弥补sigmoid函数和tanh函数的这个缺陷,就出现了ReLU激活函数。ReLU激活函数在z大于零时梯度始终为1;在z小于零时梯度始终为0;z等于零时的梯度能够当成1也能够当成0,实际应用中并不影响。对于隐藏层,选择ReLU做为激活函数可以保证z大于零时梯度始终为1,从而提升神经网络梯度降低算法运算速度。但当z小于零时,存在梯度为0的缺点,实际应用中,这个缺点影响不是很大。为了弥补这个缺点,出现了Leaky ReLU激活函数,可以保证z小于零是梯度不为0。

最后总结一下,若是是分类问题,输出层的激活函数通常会选择sigmoid函数。可是隐藏层的激活函数一般不会选择sigmoid函数,tanh函数的表现会比sigmoid函数好一些。实际应用中,一般会会选择使用ReLU或者Leaky ReLU函数,保证梯度降低速度不会过小。其实,具体选择哪一个函数做为激活函数没有一个固定的准确的答案,应该要根据具体实际问题进行验证(validation)。

7. Why do you need non-linear activation functions

咱们知道上一部分讲的四种激活函数都是非线性(non-linear)的。那是否可使用线性激活函数呢?答案是不行!下面咱们就来进行简要的解释和说明。

假设全部的激活函数都是线性的,为了简化计算,咱们直接令激活函数 g ( z ) = z ,即 a = z 。那么,浅层神经网络的各层输出为:

z [ 1 ] = W [ 1 ] x + b [ 1 ]

a [ 1 ] = z [ 1 ]

z [ 2 ] = W [ 2 ] a [ 1 ] + b [ 2 ]

a [ 2 ] = z [ 2 ]

咱们对上式中 a [ 2 ] 进行化简计算:

a [ 2 ] = z [ 2 ] = W [ 2 ] a [ 1 ] + b [ 2 ] = W [ 2 ] ( W [ 1 ] x + b [ 1 ] ) + b [ 2 ] = ( W [ 2 ] W [ 1 ] ) x + ( W [ 2 ] b [ 1 ] + b [ 2 ] ) = W x + b

通过推导咱们发现 a [ 2 ] 还是输入变量x的线性组合。这代表,使用神经网络与直接使用线性模型的效果并无什么两样。即使是包含多层隐藏层的神经网络,若是使用线性函数做为激活函数,最终的输出仍然是输入x的线性模型。这样的话神经网络就没有任何做用了。所以,隐藏层的激活函数必需要是非线性的。

另外,若是全部的隐藏层所有使用线性激活函数,只有输出层使用非线性激活函数,那么整个神经网络的结构就相似于一个简单的逻辑回归模型,而失去了神经网络模型自己的优点和价值。

值得一提的是,若是是预测问题而不是分类问题,输出y是连续的状况下,输出层的激活函数可使用线性函数。若是输出y恒为正值,则也可使用ReLU激活函数,具体状况,具体分析。

8. Derivatives of activation functions

在梯度降低反向计算过程当中少不了计算激活函数的导数即梯度。

咱们先来看一下sigmoid函数的导数:

g ( z ) = 1 1 + e ( z )

g ( z ) = d d z g ( z ) = g ( z ) ( 1 g ( z ) ) = a ( 1 a )

对于tanh函数的导数:

g ( z ) = e ( z ) e ( z ) e ( z ) + e ( z )

g ( z ) = d d z g ( z ) = 1 ( g ( z ) ) 2 = 1 a 2

对于ReLU函数的导数:

g ( z ) = m a x ( 0 , z )

g ( z ) = { 0 , z < 0 1 , z 0

对于Leaky ReLU函数:

g ( z ) = m a x ( 0.01 z , z )

g ( z ) = { 0.01 , z < 0 1 , z 0

9. Gradient descent for neural networks

接下来看一下在神经网络中如何进行梯度计算。

仍然是浅层神经网络,包含的参数为 W [ 1 ] b [ 1 ] W [ 2 ] b [ 2 ] 。令输入层的特征向量个数 n x = n [ 0 ] ,隐藏层神经元个数为 n [ 1 ] ,输出层神经元个数为 n [ 2 ] = 1 。则 W [ 1 ] 的维度为( n [ 1 ] , n [ 0 ] ), b [ 1 ] 的维度为( n [ 1 ] ,1), W [ 2 ] 的维度为( n [ 2 ] , n [ 1 ] ), b [ 2 ] 的维度为( n [ 2 ] ,1)。

该神经网络正向传播过程为:

Z [ 1 ] = W [ 1 ] X + b [ 1 ]

A [ 1 ] = g ( Z [ 1 ] )

Z [ 2 ] = W [ 2 ] A [ 1 ] + b [ 2 ]

A [ 2 ] = g ( Z [ 2 ] )

其中, g ( ) 表示激活函数。

反向传播是计算导数(梯度)的过程,这里先列出来Cost function对各个参数的梯度:

d Z [ 2 ] = A [ 2 ] Y

d W [ 2 ] = 1 m d Z [ 2 ] A [ 1 ] T

d b [ 2 ] = 1 m n p . s u m ( d Z [ 2 ] , a x i s = 1 , k e e p d i m = T r u e )

d Z [ 1 ] = W [ 2 ] T d Z [ 2 ] g ( Z [ 1 ] )

d W [ 1 ] = 1 m d Z [ 1 ] X T

d b [ 1 ] = 1 m n p . s u m ( d Z [ 1 ] , a x i s = 1 , k e e p d i m = T r u e )

反向传播的具体推导过程咱们下一部分再进行详细说明。

10. Backpropagation intuition(optional)

咱们仍然使用计算图的方式来推导神经网络反向传播过程。记得以前介绍逻辑回归时,咱们就引入了计算图来推导正向传播和反向传播,其过程以下图所示:

这里写图片描述

因为多了一个隐藏层,神经网络的计算图要比逻辑回归的复杂一些,以下图所示。对于单个训练样本,正向过程很容易,反向过程能够根据梯度计算方法逐一推导。

d z [ 2 ] = a [ 2 ] y

d W [ 2 ] = d z [ 2 ] z [ 2 ] W [ 2 ] = d z [ 2 ] a [ 1 ] T

d b [ 2 ] = d z [ 2 ] z [ 2 ] b [ 2 ] = d z [ 2 ] 1 = d z [ 2 ]

d z [ 1 ] = d z [ 2 ] z [ 2 ] a [ 1 ] a [ 1 ] z [ 1 ] = W [ 2 ] T d z [ 2 ] g [ 1 ] ( z [ 1 ] )

d W [ 1 ] = d z [ 1 ] z [ 1 ] W [ 1 ] = d z [ 1 ] x T

d b [ 1 ] = d z [ 1 ] z [ 1 ] b [ 1 ] = d z [ 1 ] 1 = d z [ 1 ]

这里写图片描述

总结一下,浅层神经网络(包含一个隐藏层),m个训练样本的正向传播过程和反向传播过程分别包含了6个表达式,其向量化矩阵形式以下图所示:

这里写图片描述

11. Random Initialization

神经网络模型中的参数权重W是不能所有初始化为零的,接下来咱们分析一下缘由。

举个简单的例子,一个浅层神经网络包含两个输入,隐藏层包含两个神经元。若是权重 W [ 1 ] W [ 2 ] 都初始化为零,即:

W [ 1 ] = [ 0 0 0 0 ]

W [ 2 ] = [ 0 0 ]

这样使得隐藏层第一个神经元的输出等于第二个神经元的输出,即 a 1 [ 1 ] = a 2 [ 1 ] 。通过推导获得 d z 1 [ 1 ] = d z 2 [ 1 ] ,以及 d W 1 [ 1 ] = d W 2 [ 1 ] 。所以,这样的结果是隐藏层两个神经元对应的权重行向量 W 1 [ 1 ] W 2 [ 1 ] 每次迭代更新都会获得彻底相同的结果, W 1 [ 1 ] 始终等于 W 2 [ 1 ] ,彻底对称。这样隐藏层设置多个神经元就没有任何意义了。值得一提的是,参数b能够所有初始化为零,并不会影响神经网络训练效果。

这里写图片描述

咱们把这种权重W所有初始化为零带来的问题称为symmetry breaking problem。解决方法也很简单,就是将W进行随机初始化(b可初始化为零)。python里可使用以下语句进行W和b的初始化:

W_1 = np.random.randn((2,2))*0.01
b_1 = np.zero((2,1))
W_2 = np.random.randn((1,2))*0.01
b_2 = 0

这里咱们将 W 1 [ 1 ] W 2 [ 1 ] 乘以0.01的目的是尽可能使得权重W初始化比较小的值。之因此让W比较小,是由于若是使用sigmoid函数或者tanh函数做为激活函数的话,W比较小,获得的|z|也比较小(靠近零点),而零点区域的梯度比较大,这样能大大提升梯度降低算法的更新速度,尽快找到全局最优解。若是W较大,获得的|z|也比较大,附近曲线平缓,梯度较小,训练过程会慢不少。

固然,若是激活函数是ReLU或者Leaky ReLU函数,则不须要考虑这个问题。可是,若是输出层是sigmoid函数,则对应的权重W最好初始化到比较小的值。

12. Summary

本节课主要介绍了浅层神经网络。首先,咱们简单概述了神经网络的结构:包括输入层,隐藏层和输出层。而后,咱们以计算图的方式推导了神经网络的正向输出,并以向量化的形式概括出来。接着,介绍了不一样的激活函数并作了比较,实际应用中根据不一样须要选择合适的激活函数。激活函数必须是非线性的,否则神经网络模型起不了任何做用。而后,咱们重点介绍了神经网络的反向传播过程以及各个参数的导数推导,并以矩阵形式表示出来。最后,介绍了权重随机初始化的重要性,必须对权重W进行随机初始化操做。

更多AI资源请关注公众号:红色石头的机器学习之路(ID:redstonewill)
这里写图片描述