0%

DNN 笔记一

关于第一个专项课程的笔记。

这里先简单的定义为DNN就是层数很多的神经网络。


激活函数

神经网络的激活函数一般有以下四种:

image

它们的方程写为:

从图中可以看出,对于sigmoid函数,它的输出大于1,这对于神经网络来说是不利的。 tanh就能对sigmoid函数进行改进,使得输出值的范围在-1到1之间。

但是,对于sigmoidtanh来说,当输出值Z过大或者过小时, 当我们在求取梯度的时候,这个地方的梯度值就几乎为0,这样就使得在网络层数较多时, 梯度值在某一层就会变为十分接近0,影响到梯度的反向传播。

所以,一般情况下,使用ReluLeaky Relu来作为神经网络的激活函数, 它不仅能改善梯度的反向传播,还可以加快训练速度,因为作为函数,它的计算算是非常简单。

对它们进行求导,看它们的导函数长什么样:

对于$g(z) = \frac {1}{1+e^{-z}}$,它的导函数为:

对于$g(z) = tanh(z)$,

对于$g(z) = Rule(z) = max(0,z)$,

最后对于$g(z) = LeakyRelu(z) = max(0.01z, z)$ ,

看得出上面的导数其实都很简单,这也有利于快速的计算。


两层神经网络

在week_3的作业中,需要构建一个两层的神经网络,这里将它拿出来先分析一下。

image

注意到这里的激活函数是tanh加上sigmoid,最后一层使用sigmoid的原因, 主要是为了输出一个0~1的值,就可以把它看成概率来处理。(由图中可以看出其中的上标 $[i]$就代表了层数)

参数初始化

这里需要初始化的参数有:

  • 学习速率$\alpha$。
  • 链接权值$w^{[i]}$。
  • 偏置$b^{[i]}$。

对于学习速率$\alpha$,一般将其定义为一个确定的值即可,可能在0.01~1之间, 视情况而定,需要不断调整来获得它较优的取值。

对于偏置$b^{[i]}$,直接将它初始化为0就可以了。

对于权值$w^{[i]}$,这里就不能将它们初始化0,或者可以说不能将它们初始化为一个相同的值, 因为如果它们的初始值相同,它们之间的更新将不会有差异,这样它们会一直取到相同的值, 显然不行。 (如果使用drop-out策略,初始化为相同的值应该也无所谓)

对于权值的初始化,它的好坏对网络的训练也是会有很大影响的,后面再谈。

这里直接随机初始化就行,

1
2
3
w = np.random.randn(dx,dy) * 0.01

b = np.zeros((dx,1))

这里权值$w$先以标准正态来随机取值,然后乘以0.01,防止权值过大造成计算出的数值过大。 偏置$b$直接初始化为0即可。

前向传播

对于一个输入$x^{(i)}$,它的前向传播过程如下:

这里的输出是一个$a^{[2]}$,它是$z^{[2]}$通过sigmoid函数求出来的, 输出的值可以看出一个概率值,所以这里使用一个交叉熵函数来估计最后的损失。

反向传播求其梯度:

最重要的,就是函数的反向传播过程了,这也是最难的地方, 但实际上它就是一个求取梯度的过程,但是由于函数嵌套太多, 所以容易引起困扰。

这里先忽略前面的系数,就当输入只有一个样本,所以去掉所有的上标$(i)$。

直接看一个最难的,结合上面的图与公式进行推导,

上面就是链式法则,显然要求$w^{[1]}$的偏导,首先要将前面的偏导数全部求出来, 一个一个的写出来。

首先对$a^{[2]}$进行求导,它是在上面的交叉熵函数里面,

再对$z^{[2]}$进行求导,它通过了一个sigmoid函数,

继续对$a^{[1]}$进行求导,它通过了一个线性运算,

接着对$z^{[1]}$进行求导,这里通过了一个tanh运算,

最后对$w^{[1]}$进行求导,这也是同样的一个线性运算,

对于偏执$b^{[1]}, b^{[2]}$,它们的导数同理,可以写为,

其中,


综合上面的所有式子,可以将各个参数的导数写为,


所以在计算正向传播时,需要将中间计算得到的各个$a^{[i]}$的值保存下来, 因为在反向传播时,会用到它们。

在反向传播的计算中,一层一层从后向前计算梯度, 就可以利用链式法则计算出各个参数的梯度了。

参数更新:

这里先不引入高级的参数更新方式,就正常的更新,


更多层的神经网络

简单的理解,深度神经网络就是层数比较多的神经网络, 所以基本的操作可以说和上面的两层神经网络是一样的,

下面按照编程题过一遍整个流程,首先是网络结构图,

image

图中就是一个深度神经网络的结构,它中间神经元的激活函数是ReLU, 最后一层是由sigmoid神经元组成,这一般适用于进行多分类。

由图中,可以看出整个神经网络的一个训练步骤:

  1. 初始化各层的权值$W$和$b$。
  2. 输入样本,进行前向传播,先进过一系列的ReLU激活函数, 最后再通过一个sigmoid,得到最后得输出。
  3. 对于得到的输出,计算它与样本的标签值的交叉熵,得到当前的损失。
  4. 将损失反向传播。
  5. 更新各个参数,继续从第2步开始。

注意在前向传播的过程中,要存下每一层的输出,因为在反向传播时需要用到它们。

各层参数的维度:

明白各层参数的维度,对于理解神经网络的代码很重要,这里写出它们的矩阵维度。

首先,假设一个输入样本是一个12288长度的行向量,它是由一幅64*64*3的图片展开而成的, 同时假设输入共有209个样本。

同时需要假设如下的一些东西:

  • 网络一共有$L$层。
  • 样本数$m = 209$。
  • 样本维度$n = 12288$。
  • 第$i$层的神经元数量有$n^{[i]}$个。
  • 最后一层就是输出层,按上面的定义,它神经元的个数就是$n^{[L]}$个。

从输入开始,假设每个样本是一个列向量,那么输入就是一行输入样本,

其中$x^{[i]}$就是一个样本,它是一个列向量,

所以输入$X$的维度是,$X \in R^{n \times m}$。

为了方便线性运算,也就是之间$WX + b$来计算,这里把每个神经元定义为一个行向量, 并且它的长度等于它上一层的神经元数。

其中$W^{[i](j)}$就表示第$i$层的第$j$个神经元。那么对于$W^{[i]}$,它就等于,

所以第$i$层的神经元的权值矩阵$W^{[i]}$的维度为,$W^{[i]} \in R^{n^{[i]} \times n^{[i-1]}}$,另外,


将它们的维度总结如下,当然它们所对应的导数和它们的维度一致,


前向传播:

只需要注意保存中间变量。

反向传播:

image

结合对两层神经网络的描述,其实反向传播的计算也很简单, 就从后向前计算导数就好了。

Hyperparameter:

超参数,就是一些需要在训练前就确定好的参数,并且可以认为一旦训练开始, 就不能再改变它们的值了,在神经网络中,超参数控制着$W$和$b$的最终取值, 可以认为它们决定了网络的最终性能。

例如下面的一些参数,

  • 学习速率$\alpha$。
  • 迭代次数$t$。
  • 网络层数$L$。
  • 某一层的神经元数$n^{[1]},n^{[2]},…$
  • 激活函数的选择。

对于这样一些的参数的选择,通常情况都得按着经验来,现在的很多研究也都是围绕着如何调参来展开的。

一般情况只能一个一个的去试,看哪一组参数最合适,所以这是一个很耗时间的过程。


总结

第一个专项的课程,主要还是在于引入深度神经网络,讲了最为重要的前向传播和反向传播, 理解好前向与反向传播,对于神经网络的学习至关重要。