Processing math: 0%

Shallow Neural Networks-1

本文为 Andrew Ng 深度学习课程第一部分神经网络和深度学习的笔记,对应第三周浅层神经网络的相关课程。

Neural Network Overview

本周,你将学会如何实现神经网络。上周,我们讨论了对数几率回归 (logistic regression) ,并且使用计算图 (computation graph) 的方式了解了梯度下降算法的正向传播和反向传播的两个过程,如下图所示 :

而神经网络 (Neural Network) 是这个样子,如下图 :

我们可以把很多 sigmoid 单元堆叠起来来构成一个神经网络。在之前所学的对数几率回归中,每一个节点对应着两个计算步骤:首先计算 z=w^{T}x + b​ ,然后计算 a=\sigma(z)​ 。在这个神经网络中,三个竖排堆叠的节点就对应着这两部分的计算,那个单独的节点也对应着另一个类似的 z, a​ 的计算。

在神经网络中,所用的符号也会有些不一样。我们还是用 x 来表示输入特征,用 W^{[1]}, b^{[1]} 来表示参数,这样你就可以计算出 z^{[1]} = W^{[1]}x + b^{[1]} 。这里右上角的 [1] 代表着节点所属的层,你可以认为层数从 0 开始算起,如上图中的 x_1, x_2, x_3 就代表着第 0 层(也称为输入层),三个竖排的节点就属于第 1 层(也称为隐藏层),单独的那个节点属于第 2 层(也称为输出层)。需要注意的是,这与之前用来标注第 i 个训练样本 (x^{(i)}, y^{(i)}) 不同,这里用的是方括号。

那么,在这个神经网络模型中,正向传播就分为两层。

  • 从输入层到隐藏层:在使用类似对数几率回归的方法计算了 z^{[1]} 之后,再计算 a^{[1]}=\sigma(z^{[1]})​
  • 从隐藏层到输出层:使用相同的方法计算 z^{[2]}=W^{[2]}a^{[1]}+b^{[2]} 。当然,这里的参数 W^{[2]}, b^{[2]}W^{[1]}, b^{[1]} 不同,且第 1 层的输出 a^{[1]} 作为第 2 层的输入 x ,接着同样计算 a^{[2]}=\sigma(z^{[2]}) ,得到的 a^{[2]}​ 就是整个神经网络的输出。

同样,还需要通过反向传播计算 da^{[2]}, dz^{[2]} 等等,这些将会在后面详细讨论。

One hidden layer Neural Network

下图是一张单隐藏层的神经网络,也称为双层神经网络 (2 layer NN) 。我们把最左边的 x1, x2, x3 称为输入层 (Input Layer) ,中间称为隐藏层 (Hidden Layer) ,最右边只有一个节点的称为输出层 (Output Layer) ,负责输出预测值 \hat{y} 。在计算神经网络的层数时,不算入输入层。

由于在训练过程中,我们看不到这些中间节点的真正数值,不像输入,输出层那样,所以称为隐藏层。

之前,我们用 x 来表示输入,其实它还有一种表示方式 a^{[0]} ,这个 a 有 activation (激活) 的意思,意味这它把不同层的值传递给下一层,起到了激活的作用。用上标 [i] 表示在第 i 层,用下标 j 表示这层中第 j 个节点,如 a^{[1]}_{2} 即表示第 1 层的第 2 个节点。那么上图中隐藏层的4个节点可以写成矩阵的形式:

Computing a Neural Network’s Output

接下来,我们来看神经网络的输出是如何计算出来的。我们可以把神经网络的计算看作对数几率回归的多次重复计算。

我们先来回顾一下对数几率回归的计算过程,如下图:

这里的圆圈代表了,对数几率回归的两个步骤。我们先隐去其他节点,如右图,那么它就和对数几率回归非常相似,我们可以计算出 z^{[1]}_{1} = w^{[1]T}_{1}x+b^{[1]}_{1}a^{[1]}_{1} = \sigma(z^{[1]}_{1}) ,上标代表层数,下标表示这层上的第几个节点。

以此类推,我们可以写出:

回想起之前所讲的向量化,如果我们想让程序高效的运行,就必须将其向量化。

我们首先先将 w 向量化,由于有4个对数几率回归单元,而每一个回归单元都有其对应的参数向量 w ,且每一个回归单元都有输入 x_1, x_2, x_3​ ,所以我们可以得到:

那么,我们可以得到如下式子:

在本神经网络中,你就应该计算 (为了更好理解,右下角标注了矩阵的形状) :

记得我们之前说过,可以用 a^{[0]}​表示 x​ ,所以 z^{[1]}​ 有可以写成 z^{[1]} = W^{[1]}a^{[0]}+ b^{[1]}​ ,那么可以用同样的方法推导出:

所以在代码中,我们只需实现上述的4行代码。然而这是对单个样本的,即对于输入的特征向量 x ,可以计算出 \hat{y} = a^{[2]}​ ,对于整个训练集的向量化将在接下来的部分介绍。

Vectorizing across multiple examples

接下来,我们实现对整个训练集的向量化。假设你有 m​ 个训练样本,理解了上述理论,那么我们可以通过输入 x^{(i)}​ 计算得出 \hat{y}^{(i)} = a^{[2](i)}​ 其中 i \in [1,m]​

还记得我们之前把输入的特征向量 x^{(1)}, x^{(2)}, …, x^{(m)}​ 横向堆叠起来,得到了一个 (n_x \times m)​ 的矩阵 X​ ,即

同样的,我们把 x^{(1)}, x^{(2)}, …, x^{(m)}​ 横向堆叠起来,那么我们需要计算的式子变为:

其中,Z^{[1]}​ 的形状为 (4 \times m)​ ,你可以这样理解,每一个样本对应着矩阵的一列,所以有 m​ 列,隐藏层中的每一个节点对应着矩阵的一行,所以 Z^{[1]}​4​ 行,规律同样适用于 X, A​

Activation functions

到目前为止,我们一直选择 sigmoid 函数作为 activation function (激活函数) ,但有时使用其他函数效果要好得多,它们各自有不同的特点,下面我们来介绍几个不同的激活函数 g(x)

  • tanh (双曲正切函数),实际上是 sigmoid 函数向下平移后再经过拉伸得到的。对于隐藏单元,如果你选择 tanh 作为激活函数,它的表现几乎总是比 sigmoid 函数要好,因为 tanh 函数的输出介于 (-1,1) 之间,激活函数的平均值更接近于 0 ,而不是 0.5 ,这让下一层的学习更方便一点。所以之后我们几乎不再使用 sigmoid 作为激活函数了,但有一个例外,即选择输出层的激活函数的时候,因为二分类问题的输出为 \{0,1\} ,你更希望 \hat{y} 介于 0,1 之间,所以一般会选择 sigmoid 函数。
  • 所以之前所举的例子中,你可以使用 tanh 函数作为隐藏层的激活函数,而选择 sigmoid 函数作为输出层的激活函数。同样的,可以使用上标来表示每一层的激活函数,如:g^{[1]}(x) = tanh(z), g^{[2]} = \sigma(z)​
  • 然而,不管是 tanh 还是 sigmoid 函数,都有一个缺点,如果 z​ 特别大或者特别小,那么在这一点的函数导数会很小,因此会拖慢梯度下降算法。为了弥补这个缺点,就出现了 ReLU (rectified linear unit) 函数,该函数有如下特点:当 z​ 为正,导数为 1​,当 z​ 为负,导数为 0​ ,当 z​0​ 时,导数不存在,但在实际使用中, z​ 几乎不会等于 0​ ,当然你可以在程序中直接把在 z=0​ 点的导数赋为 0​1​
  • 但 ReLU 也有一个缺点,当 z 为负时,导数恒等于 0 。所以就有了 Leaky ReLU 函数,通常表现比 ReLU 函数更好,但实际使用中频率没那么高。
  • ReLU 和 Leaky ReLU 的优势在于,对于 z 的许多取值,激活函数的导数和 0 差的很远,这也就意味着,在实践中你的神经网络的学习速度会快很多。
  • 总结一下,在我们一般选择 sigmoid 作为输出层的激活函数,而选择 ReLU 作为其他层的激活函数,ReLU 如今被人们广泛使用。
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×