1 神经网络与深度学习
1.4 浅层神经网络
上节课我们主要介绍了向量化、矩阵计算的方法和python编程的相关技巧。并以逻辑回归为例,将其算法流程包括梯度下降转换为向量化的形式,从而大大提高了程序运算速度。本节课我们将从浅层神经网络入手,开始真正的神经网络模型的学习。
1.4.1 Neural Networks Overview
首先,我们从整体结构上来大致看一下神经网络模型。
前面的课程中,我们已经使用计算图的方式介绍了逻辑回归梯度下降算法的正向传播和反向传播两个过程。如下图所示。神经网络的结构与逻辑回归类似,只是神经网络的层数比逻辑回归多一层,多出来的中间那层称为隐藏层或中间层。这样从计算上来说,神经网络的正向传播和反向传播过程只是比逻辑回归多了一次重复的计算。正向传播过程分成两层,第一层是输入层到隐藏层,用上标[1]来表示:
z
[
1
]
=
W
[
1
]
x
+
b
[
1
]
a
[
1
]
=
σ
(
z
[
1
]
)
\begin{array}{l} {z^{[1]}} = {W^{[1]}}x + {b^{[1]}}\\ {\kern 1pt} {\kern 1pt} {\kern 1pt} {\kern 1pt} {\kern 1pt} {a^{[1]}} = \sigma ({z^{[1]}}) \end{array}
z[1]=W[1]x+b[1]a[1]=σ(z[1])
第二层是隐藏层到输出层,用上标[2]来表示:
z
[
2
]
=
W
[
2
]
a
[
1
]
+
b
[
2
]
a
[
2
]
=
σ
(
z
[
2
]
)
\begin{array}{l} {z^{[2]}} = {W^{[2]}}{a^{[1]}} + {b^{[2]}}\\ {\kern 1pt} {\kern 1pt} {\kern 1pt} {\kern 1pt} {\kern 1pt} {a^{[2]}} = \sigma ({z^{[2]}}) \end{array}
z[2]=W[2]a[1]+b[2]a[2]=σ(z[2])
在写法上值得注意的是,方括号上标[i]表示当前所处的层数;圆括号上标(i)表示第i个样本。
同样,反向传播过程也分成两层。第一层是输出层到隐藏层,第二层是隐藏层到输入层。其细节部分我们之后再来讨论。
1.4.2 Neural Network Representation
下面我们以图示的方式来介绍单隐藏层的神经网络结构。如下图所示,单隐藏层神经网络就是典型的浅层(shallow)神经网络。
结构上,从左到右,可以分成三层:输入层(Input layer),隐藏层(Hidden layer)和输出层(Output layer)。输入层和输出层,顾名思义,对应着训练样本的输入和输出,很好理解。隐藏层是抽象的非线性的中间层,这也是其被命名为隐藏层的原因。
在写法上,我们通常把输入矩阵X记为
a
[
0
]
{a^{[0]}}
a[0] ,把隐藏层输出记为
a
[
1
]
{a^{[1]}}
a[1] ,上标从0开始。用下标表示第几个神经元,注意下标从1开始。例如
a
1
[
1
]
a_1^{[1]}
a1[1] 表示隐藏层第1个神经元,
a
2
[
1
]
a_2^{[1]}
a2[1]表示隐藏层第2个神经元,,等等。这样,隐藏层有4个神经元就可以将其输出
a
[
1
]
{a^{[1]}}
a[1]写成矩阵的形式:
KaTeX parse error: Unknown column alignment: * at position 35: …{\begin{array}{*̲{20}{c}} {a_1^{…
最后,相应的输出层记为
a
[
2
]
{a^{[2]}}
a[2] ,即
y
^
\hat y
y^ 。这种单隐藏层神经网络也被称为两层神经网络(2 layer NN)。之所以叫两层神经网络是因为,通常我们只会计算隐藏层输出和输出层的输出,输入层是不用计算的。这也是我们把输入层层数上标记为0的原因(
a
[
0
]
{a^{[0]}}
a[0])。
关于隐藏层对应的权重
W
[
1
]
{W^{[1]}}
W[1]和常数项
b
[
1
]
{b^{[1]}}
b[1],
W
[
1
]
{W^{[1]}}
W[1]的维度是(4,3)。这里的4对应着隐藏层神经元个数,3对应着输入层x特征向量包含元素个数。常数项
b
[
1
]
{b^{[1]}}
b[1]的维度是(4,1),这里的4同样对应着隐藏层神经元个数。关于输出层对应的权重
W
[
2
]
{W^{[2]}}
W[2] 和常数项
b
[
2
]
{b^{[2]}}
b[2] ,
W
[
2
]
{W^{[2]}}
W[2] 的维度是(1,4),这里的1对应着输出层神经元个数,4对应着输出层神经元个数。常数项
b
[
2
]
{b^{[2]}}
b[2]的维度是(1,1),因为输出只有一个神经元。总结一下,第i层的权重
W
[
i
]
{W^{[i]}}
W[i] 维度的行等于i层神经元的个数,列等于i-1层神经元的个数;第i层常数项
b
[
i
]
{b^{[i]}}
b[i]维度的行等于i层神经元的个数,列始终为1。
1.4.3 Computing a Neural Network’s Output
接下来我们开始详细推导神经网络的计算过程。回顾一下,我们前面讲过两层神经网络可以看成是逻辑回归再重复计算一次。如下图所示,逻辑回归的正向计算可以分解成计算z和a的两部分:
z
=
W
T
x
+
b
a
=
σ
(
z
)
\begin{array}{l} z = {W^T}x + b\\ {\kern 1pt} {\kern 1pt} {\kern 1pt} {\kern 1pt} {\kern 1pt} a = \sigma (z) \end{array}
z=WTx+ba=σ(z)
对于两层神经网络,从输入层到隐藏层对应一次逻辑回归运算;从隐藏层到输出层对应一次逻辑回归运算。每层计算时,要注意对应的上标和下标,一般我们记上标方括号表示layer,下标表示第几个神经元。例如
a
i
[
l
]
a_i^{[l]}
ai[l] 表示第 层的第i个神经元。注意,i从1开始, 从0开始。
下面,我们将从输入层到输出层的计算公式列出来:
KaTeX parse error: Double superscript at position 40: …]} = w{_1^{[1]}^̲T}x + b_1^{[1]}…
然后,从隐藏层到输出层的计算公式为:
KaTeX parse error: Double superscript at position 23: …]} = w{_1^{[2]}^̲T}{a^{[1]}} + b…
其中
a
[
1
]
{a^{[1]}}
a[1]为:
KaTeX parse error: Unknown column alignment: * at position 35: …{\begin{array}{*̲{20}{c}} {a_1^{…
上述每个节点的计算都对应着一次逻辑运算的过程,分别由计算z和a两部分组成。
为了提高程序运算速度,我们引入向量化和矩阵运算的思想,将上述表达式转换成矩阵运算的形式:
KaTeX parse error: Double superscript at position 42: …} = W{_{}^{[1]}^̲{}}x + b_{}^{[1…
之前也介绍过,这里顺便提一下,
W
[
1
]
{W^{[1]}}
W[1]的维度是(4,3),
b
[
1
]
{b^{[1]}}
b[1] 的维度是(4,1),
W
[
2
]
{W^{[2]}}
W[2]的维度是(1,4),
b
[
2
]
{b^{[2]}}
b[2]的维度是(1,1)。这点需要特别注意。
1.4.4 Vectorizing across multiple examples
上一部分我们只是介绍了单个样本的神经网络正向传播矩阵运算过程。而对于m个训练样本,我们也可以使用矩阵相乘的形式来提高计算效率。而且它的形式与上一部分单个样本的矩阵运算十分相似,比较简单。
之前我们也介绍过,在书写标记上用上标(i)表示第i个样本,例如
x
(
i
)
,
z
(
i
)
,
a
[
2
]
(
i
)
{x^{(i)}},{z^{(i)}},{a^{[2](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
]
x
(
i
)
+
b
[
2
]
a
[
2
]
(
i
)
=
σ
(
z
[
2
]
(
i
)
)
\begin{array}{l} {z^{[1](i)}} = {W^{[1]}}{x^{(i)}} + {b^{[1]}}\\ {a^{[1](i)}} = \sigma ({z^{[1](i)}})\\ {z^{[2](i)}} = {W^{[2]}}{x^{(i)}} + {b^{[2]}}\\ {a^{[2](i)}} = \sigma ({z^{[2](i)}}) \end{array}
z[1](i)=W[1]x(i)+b[1]a[1](i)=σ(z[1](i))z[2](i)=W[2]x(i)+b[2]a[2](i)=σ(z[2](i))
不使用for循环,利用矩阵运算的思想,输入矩阵X的维度为
(
n
x
,
m
)
({n_x},m)
(nx,m)。这样,我们可以把上面的for循环写成矩阵运算的形式:
Z
[
1
]
=
W
[
1
]
X
+
b
[
1
]
A
[
1
]
=
σ
(
Z
[
1
]
)
Z
[
2
]
=
W
[
2
]
X
+
b
[
2
]
A
[
2
]
=
σ
(
Z
[
2
]
)
\begin{array}{l} {Z^{[1]}} = {W^{[1]}}X + {b^{[1]}}\\ {A^{[1]}} = \sigma ({Z^{[1]}})\\ {Z^{[2]}} = {W^{[2]}}X + {b^{[2]}}\\ {A^{[2]}} = \sigma ({Z^{[2]}}) \end{array}
Z[1]=W[1]X+b[1]A[1]=σ(Z[1])Z[2]=W[2]X+b[2]A[2]=σ(Z[2])
其中,
Z
[
1
]
{Z^{[1]}}
Z[1]的维度是(4,m),4是隐藏层神经元的个数; 的维度与 相同;
A
[
1
]
{A^{[1]}}
A[1]与
Z
[
1
]
{Z^{[1]}}
Z[1]的维度均为(1,m)。对上面这四个矩阵来说,均可以这样来理解:行表示神经元个数,列表示样本数目m。
1.4.5 Explanation for Vectorized Implementation
这部分Andrew用图示的方式解释了m个样本的神经网络矩阵运算过程。其实内容比较简单,只要记住上述四个矩阵的行表示神经元个数,列表示样本数目m就行了。
这部分Andrew用图示的方式解释了m个样本的神经网络矩阵运值得注意的是输入矩阵X也可以写成
A
[
0
]
{A^{[0]}}
A[0] 。
1.4.6 Activation functions
神经网络隐藏层和输出层都需要激活函数(activation function),在之前的课程中我们都默认使用Sigmoid函数
σ
(
x
)
\sigma (x)
σ(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)。
1.4.7 Why do you need non-linear activation functions
我们知道上一部分讲的四种激活函数都是非线性(non-linear)的。那是否可以使用线性激活函数呢?答案是不行!下面我们就来进行简要的解释和说明。
假设所有的激活函数都是线性的,为了简化计算,我们直接令激活函数
g
(
z
)
=
z
g(z) = z
g(z)=z ,即
a
=
z
a = 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
]
\begin{array}{l} {z^{[1]}} = {W^{[1]}}x + {b^{[1]}}\\ {a^{[1]}} = {z^{[1]}}\\ {z^{[2]}} = {W^{[2]}}{a^{[1]}} + {b^{[2]}}\\ {a^{[2]}} = {z^{[2]}} \end{array}
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]进行化简计算:
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
]
)
{a^{[2]}} = {z^{[2]}}{\rm{ = }}{W^{[2]}}{a^{[1]}} + {b^{[2]}}{\rm{ = }}{W^{[2]}}({W^{[1]}}x + {b^{[1]}}) + {b^{[2]}} = \left( {{W^{[2]}}{W^{[1]}}} \right)x + ({W^{[2]}}{b^{[1]}} + {b^{[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])
经过推导我们发现
a
[
2
]
{a^{[2]}}
a[2] 仍是输入变量x的线性组合。这表明,使用神经网络与直接使用线性模型的效果并没有什么两样。即便是包含多层隐藏层的神经网络,如果使用线性函数作为激活函数,最终的输出仍然是输入x的线性模型。这样的话神经网络就没有任何作用了。因此,隐藏层的激活函数必须要是非线性的。
另外,如果所有的隐藏层全部使用线性激活函数,只有输出层使用非线性激活函数,那么整个神经网络的结构就类似于一个简单的逻辑回归模型,而失去了神经网络模型本身的优势和价值。
值得一提的是,如果是预测问题而不是分类问题,输出y是连续的情况下,输出层的激活函数可以使用线性函数。如果输出y恒为正值,则也可以使用ReLU激活函数,具体情况,具体分析。
1.4.8 Derivatives of activation functions
在梯度下降反向计算过程中少不了计算激活函数的导数即梯度。
我们先来看一下sigmoid函数的导数:
对于tanh函数的导数:
对于ReLU函数的导数:
对于Leaky ReLU函数:
下转:https://blog.csdn.net/qq_41556318/article/details/88014571