续前一节1.3 浅层神经网络1
5. 向量化实现的解释(Explanation for vectorized implementation)
本节课主要对前一节课第4节写出的多样本神经网络正向传播计算过程向量化的正确性进行证明。具体证明过程此处不作详细说明。
6. 激活函数(Activation functions)
到目前为止,我们一直用sigmoid函数作为激活函数,但实际上,有些时候,其他激活函数的效果可能更好一些。本节课就对可选激活函数进行了简单介绍。
在之前的神经网络和Logistic回归中使用的sigmoid函数就是其中一种激活函数。
在更一般的情况下,我们可以使用不同的函数,记为g(z),那么在前一节课中的第4节推导出的a的计算公式
可以改写为
其中,g可以是非线性函数,不一定是sigmoid函数,在前面的课程学习中,我们知道,sigmoid函数介于0和1之间。有个激活函数一般情况下都比sigmoid函数表现更好一些,这个函数就是tanh函数(tanh function)或者称为双曲正切函数(hyperbolic tangent function),它的值介于-1和1之间,公式为:
tanh函数的图像如下图所示:
由于tanh函数的输出介于-1和1之间,并且具有类似数据中心化的效果,激活函数的平均值更接近于0,这就使得下一层的学习更加方便,从而使得tanh函数在神经网络中的表现大部分情况下都比sigmoid函数更好。具体的算法优化将在下一部分课程【改善深层神经网络:超参数调试、正则化以及优化】中讲解。
Andrew目前已经很少会使用sigmoid函数,因为tanh函数几乎在所有场合的表现都更加优越。其中,有一种例外情况就是输出层,因为训练集中的输出结果y的值要么是0,要么是1,那么输出层的结果y帽介于0与1之间会更加合理,这里就不适合使用tanh函数。sigmoid函数在二元分类场景中使用会比较合适,即输出层的激活函数使用sigmoid函数比较合理。也就是说,在神经网络中,很有可能不同层的激活函数是不同的,如下图所示:
sigmoid函数和tanh函数都有的一个缺点是:如果z非常大或者非常小,那么导数的梯度或者说这个函数的斜率可能会很小,即接近于0。这样就会拖慢梯度下降算法。
在机器学习里,最受欢迎的一个函数是修正线性单元(Rectified Linear Unit,ReLU),它的公式为:
现在机器学习中,一般输出层的激活函数用sigmoid函数,其他层全部默认使用ReLU。在不确定神经网络隐藏层使用哪一种激活函数时,默认直接选择使用ReLU即可。但还是有一部分人隐藏层的激活函数使用的是tanh函数。
ReLU的一个缺点是:当z为负数时,导数等于0,在实践过程中这个没有问题。但ReLU还有另一个版本,叫做带泄漏的ReLU(Leaky ReLU),这个函数值在z为负数时不为0,而是一个很平缓的斜率。这个函数通常比ReLU表现更好,但实际应用并不多。
如果ReLU和带泄漏的ReLU一定要选一个,Andrew建议使用ReLU。
这两个函数的优点是:对于很多z空间,激活函数的导数或者叫斜率与0相差很大,这样神经网络的学习速度会比使用tanh激活函数或者sigmoid激活函数快得多。主要原因是:ReLU减少了函数导数或者斜率趋向于0的概率,这样就能避免学习速度在导数为0时的明显下降。
理论上说,z的一半范围的取值都会使得ReLU的斜率为0,但在实践中,有足够多的隐藏单元令z大于0,所以对大多数的训练样本来说,学习速度还是很快的。下图为ReLU和带泄漏的ReLU的图像:
下面对本节课的学习内容简单总结一下:
- sigmoid函数只用于二元分类的输出层,其他情况都不要使用,或者几乎从来不用;
- tanh函数一般情况下都比sigmoid函数表现更好;
- ReLU是最常用的默认激活函数;
- 带泄漏的ReLU也可以尝试应用一下,看下实际的应用效果,它的公式一般是:a=max(0.01z,z),其中,常数0.01也可以自己进行调整,或者改成学习率参数都可以,比较下不同取值的表现。
深度学习的其中一个特点是在建立神经网络时,经常有很多不同的选择,比如隐藏单元数、激活函数、如何初始化权重等,而这些内容的选择,并没有一个准则来确定究竟什么样的参数组合更适合解决你的问题,在Andrew的这门课中,他将介绍哪些是热门选择,哪些用的比较少。对于不确定哪种激活函数最有效时,可以先试试,在保留交叉验证数据集或开发集上跑跑试试,哪个参数效果更好就用哪个。这样才能够构建一个更适合要解决的问题特性的神经网络架构。
7. 为什么需要非线性激活函数?(Why do you need non-linear activation fucntions)
上一节课我们介绍了多种热门的激活函数,那么为什么神经网络需要非线性的激活函数呢?本节课就对这个问题给出了解答。
事实证明,要让神经网络能够计算出有趣的函数,就必须使用非线性激活函数。
神经网络正向传播的方程为:
我们为什么不去掉函数g,然后令a[1]=z[1],或者令g(z)=z呢?这种函数被称为线性激活函数,或者更学术一点的称为恒等激活函数,因为它们直接将输入输出了。
为了说明问题,假设
则将a[1]的定义带入a[2],得到如下方程:
从上述方程可以看出,如果使用线性激活函数(恒等激活函数),那么,神经网络只是把输入线性组合再输出。在后续会讲到的深度网络中,有很多神经网络和隐藏层,如果使用线性激活函数或不使用激活函数,那么神经网络中有多少层,一直做得都只是计算线性激活函数。这样的话,还不如直接去掉所有隐藏层。
此外,如果在双层神经网络中,隐藏层全部使用线性激活函数,输出层使用sigmoid激活函数,则这个双层神经网络的效果与Logistic回归的效果是一样的。
这就说明,线性隐层是没有任何作用的。多个线性函数组合最终还是一个线性函数,除非引入非线性函数。
只有一种场景可以在输出层使用线性激活函数即g(z)=z,即机器学习的对象是回归问题。此时,隐藏层要使用其他非线性激活函数,如ReLU、tanh、带泄漏的ReLU等。
与压缩有关的一些非常特殊的情况可能会在隐藏层中使用线性激活函数。除此之外,使用线性激活函数的情况非常少见。
8. 激活函数的导数(Derivatives of activation functions)
当实现神经网络的反向传播时,需要计算激活函数的斜率或者导数。下面我们来看看激活函数的选择以及如何计算这些函数的斜率。
- sigmoid激活函数导数计算过程
如果激活函数g(z)是一个sigmoid函数,即
则g(z)的导数为:
为了证明上述等式的合理性,我们取两个极值做一个验证:
- 当z足够大时,假设z=10,此时
而此时导数为0,推导过程如下:
从图6中我们可以看出,当z值足够大时,g(z)的斜率几乎是趋近于0的,这与上面的公式推导过程得出的结论是一致的。
- 相反的,如果z=-10,从图6可以看到z很小时,g(z)的值很接近0,此时,g(z)的斜率也很接近于0,斜率或导数的结果推导过程如下:
- 当z=0时,g(z)=1/2,此时,导数为:
导数除了d/dz g(z)这种表示法以外,还可以有另一种表示方法:g'(z)。而g(z)可表示为a,即a=g(z),则sigmoid激活函数的导数的另一种表示为:
这种表示法的好处是,如果已知g(z)的斜率,则可根据上述公式快速计算得出g'(z)的值,即g(z)的斜率。
- tanh激活函数导数计算过程
对tanh激活函数进行求导,得到如下公式:
从图7的tanh激活函数的图像中可以看出,当z足够大或者足够小时,导数g'(z)的值趋向于0。这里取z的两个极值和一个特殊值,来验证下导数公式的正确性:
- 当z=10时,tanh(z)的值约等于1,将这个值代入求导公式g'(z),得到斜率约等于0,这与在图7中看到的趋势是一致的;
- 当z=-10时,tanh(z)的值约等于-1,将这个值代入求导公式g'(z),得到斜率约等于0,这与在图7中看到的趋势是一致的;
- 当z=0时,tanh(z)=0,将这个值代入求导公式g'(z),得到斜率等于1,这与在图7中看到的趋势是一致的;
令a=tanh(z),则
使用上述公式,只要已知a的值,即可快速计算出对应的导数或斜率。
- ReLU导数计算过程
ReLU的公式为:g(z)=max(0,z),在坐标系中的图像如图8所示。
它的导数计算公式为:
从导数计算公式可以看出当z=0时,无法计算导数或者斜率,这在数学上可能不是百分之百的正确,但在软件实现的过程中,这个激活函数是可行的。我们可以人为设定当z=0时,令导数为0或者为1。此时,g'就变成激活函数g(z)的次梯度,这样梯度下降法仍然有效。在实际应用场景中,z精确问0的概率非常小,所以将z=0处的导数设成哪个值都无所谓。所以在实践中,一般定义z的导数为如下公式:
- 带泄漏的ReLU导数计算过程
带泄漏的ReLU的公式为:g(z)=max(0.01z,z),在坐标系中的图像如图9所示。
它的导数计算公式为:
其中,当z=0时的梯度处理方式与ReLU一致,这里不再赘述。
9. 神经网络的梯度下降法(Gradient descent for neural networks)
在本节课中,介绍了单隐层神经网络的反向传播或者说梯度下降算法的具体实现,并说明为什么这几个特定的方程是精准的方程。
单隐层神经网络的参数包括:w[1]、b[1]、w[2]、b[2],并且神经网络有nx=n[0]个输入特征,n[1]个隐藏单元,n[2]个输出单元,到目前为止,我们接触到的单隐层神经网络的n[2]=1。
- 矩阵w[1]的维度为(n[1],n[0]);
- 矩阵b[1]的维度为(n[1],1),就是n[1]维列向量;
- 矩阵w[2]的维度为(n[2],n[1]);
- 矩阵b[1]的维度为(n[2],1),就是n[2]维列向量;
目前还是假设神经网络处理的是二元分类问题,在这种情况下,神经网络的参数的成本函数定义为:
即损失函数的平均值,L表示当神经网络预测出y帽时的损失函数,因此这里的y帽其实就是a[2],其基本真值标签等于y。如果是解决二元分类问题,则损失函数就可以与之前做得Logistic回归完全一致。
所以为了训练神经网络的参数,需要做梯度下降。在训练神经网络时,随机初始化参数很重要,而不是全部初始化为0。当参数初始化成某些值之后,每个梯度下降循环都会计算预测值及导数。每次迭代都包含三步:
- 计算i=1到m的预测值y帽;
- 计算导数,包括成本函数关于参数w[i]的导数和成本函数关于参数b[i]的导数。
- 梯度下降算法最后会更新w[i]和b[i]的值,更新公式为:
上述计算过程经过不断迭代,直至参数开始收敛。
在以前的课程中,已经讨论过如何计算预测值,如何计算输出层以及如何用向量化方式去实现这些操作,那么,接下来的关键就在于如何计算这些偏导项。接下来将给出计算偏导项所需要的公式。
10. (选修)直观理解反向传播(Backpropagation intuition(Optional))
11. 随机初始化(Random Initialization)
初始化权重为0
随机初始化