吴恩达深度学习(四)
设置优化问题
第一课:规范化输入
当训练神经网络时,有一种加速训练过程的方法,那就是对输入进行归一化。
对输入归一化包含两个步骤:
第一步是将减去均值,或者说将均值归零。
第二部是将方差进行归一化。(使x1和x2这两个特征的方差都等于1)
一个提示:如果你用这种方法来对数据进行缩放,那么务必对测试集和训练集都是用同样地u和б2。即不应该使用不同的方式去归一化训练集和测试集。因为你的所有数据包括训练和测试样本都得经过同样的变换,即由同样的u和б2定义。
为什么要对输入特征进行归一化呢?
因为由于特征值的范围不一致,将会导致w1和w2的取值范围有很大的不同,那么代价函数就会变成一个扁平的碗,这样你就需要选择一个非常小的学习率,梯度下降法就需要经历许多步,反复辗转。如果你进行特征归一化以后,你的代价函数通常看起来就会更对称,梯度下降法几乎能直接朝向最小值而去,你可以在梯度下降中采用更长的步长。
当然在实践中,w是一个高维向量,但大体上的感觉是你的代价函数会更圆,优化过程更容易进行,因为各种特征的尺度会比较接近。
当特征值的范围相差很大时,输入特征归一化就能保证所有的特征尺度都相近,这通常就能帮助学习算法运行的更快。
如果输入特征相差很大,那么归一化就非常重要,如果输入特征本来就尺度相近,那么这一步就没那么重要。不过由于没有坏处,我总是会这么做。
加速神经网络训练的方法?
第二课:消失/爆炸渐变
当训练神经网络时,特别是非常深的神经网络时的问题之一是梯度的消失和爆炸。即当你在训练神经网络的时,损失函数的导数或者说斜率,有时会非常大,或者非常小,甚至是指数级减小,这使训练变得困难。
梯度爆炸和消失是什么意思?
如何谨慎的选择随机初始化的权重来显著减少这种问题的发生?
我们假设每一个权重矩阵w[L]都只是比单位矩阵稍微大一些(1.5),然后进行相乘之后发现,当你的神经网络非常大时,y的值(1.5L)就会爆炸。如果你的w[L]变成0.5,那么y的值就会变成0.5L次方,激活函数的值就会呈指数级下降,就会消失。
对于权重参数w,如果它们只比1大一点点,那在一个非常深的网络中,激活函数就会爆炸。反之,小一点点就会消失。
虽然这个论证是激活函数随着L指数级的增加或减少,同样地论证可以用来表明计算机算出的倒数或者梯度也会指数级增加或者减少,这会让训练别的非常困难。尤其是当你的梯度比L要小指数级别,梯度下降将会用非常非常小的步走,梯度下降会用很长的时间才能有学习。
作为总结,你到现在理解了深层的网络会受制于梯度消失或者爆炸的问题,事实上,这个问题很久以来都是训练神经网络的巨大壁垒。
应该如何谨慎选择初始化权重的方法?
第三课:深度网络的权重初始化
对于非常深的网络会出现梯度消失和梯度爆炸的问题,事实上有一种方法可以针对性的解决这个问题,虽然不能完全解决它,但是帮助很大。
该方法就是更好、更细致地随机初始化你的神经网络,从初始化单个神经元开始,然后再把它应用到深度网络中:
当n越多的时候,我们就希望w的值越小。所以一个合理的做法就是让变量wi等于1/n,这里的n是指输入一个神经元的特征数,在实践中,你可以使用np.random.randn函数,去设置某一层的权重矩阵W。这样就会降低梯度消失和梯度爆炸问题的程度,因为这种做法通过设置权重矩阵W,使得W不会比1大很多,也不会比1小很多,因此梯度不会过快地膨胀或者消失。
对于不同的激活函数,会有不同的初始化公式。
有很多的公式可以给你一个出发点,它让权重矩阵的初始化变量有一个默认值,这些变量参数可以成为超参数的一部分,你可以通过调优确定使用哪个版本,有时候调优这个超参数会影响模型的规模。
如何使用适合的方法来初始化权重,从而让你的权重或者梯度不会过快地消失或者爆炸。
第四课:梯度的数值逼近
当你实现梯度逆传播时,你会发现一个测试,名叫梯度检验,可以帮助你确保你的梯度逆传播的实现是正确的。
如何在数值上近似计算梯度?
以后再谈如何实现梯度检验,从而确保逆传播的实现是正确的?
进行梯度计算的时候,取双侧的差值而不是单侧的差值。
用双侧取值来近似导数,你会发现结果更加接近,从而g(θ)很可能就是求f导数的正确实现。当你把这个方法用于梯度检验和逆传播时,它运行起来很可能比单侧差值要慢两倍,但从实践的角度,我认为这个方法值得一用。
为什么我们做梯度检验时采用双侧差值,而不是使用这个不够精确的单侧差值?
从导数的定义式也可以看到,采用双侧的取值要比单侧精确很多。
那么如何验证你的逆传播的实现是正确的?
第五课:梯度检查
梯度检查省了我大量的时间,还可以帮助我多次找出反向传播代码中的错误。怎么用梯度检查来调试代码并检验你的反向传播代码?
要实现梯度检查算法,首先要把你的所有参数,重新拼成一个巨大的参数向量θ,你要把W矩阵转化为一个向量,把所有的W矩阵转化为向量后,把它们首尾相接拼在一起,成为一个巨大的参数向量θ。
之前代价函数J是所有W和b的函数,经过向量转化后,它就变成了θ的函数。
W和b按照同样地顺序转化后,你也可以把dW,db等参数都转换为和θ的维度相同的向量dθ的形式。dθ和θ的维度相同。
现在的问题是θ是代价函数J的梯度或者斜率么?
如何编写梯度检查算法?
实现梯度检查算法时,我们首先要写一个循环,然后检查dθi的近似和dθi是否相等?通过求欧几里得距离,如果这个结果大于10的负3次方,那么很可能这个代码有错误。就应该仔细检查每个分量,看看是不是有某个i使得dθi的近似和dθi很不一样,然后继续跟踪调试,看看你的求导部分是不是有错误。
在写神经网络时,我经常写前向传播和反向传播,梯度检查可能返回一个非常大的值,那么程序有big,调试调试调试,直到它通过梯度检查。
实现梯度检查的小窍门?
第六课:梯度检查实施说明
分享一些实用技巧或一些关于如何实际为您的神经网络实现此功能的注意事项。
首先,不要在训练中使用梯度下降,而仅仅在调试的时候使用。
因为计算dθi的近似,计算当i取所有制的情况,这是一个非常慢的计算。因此要使用梯度下降,你会运用反向传播来计算dθ即运用反向传播来计算导数,只有当你在调试时,才会需要计算dθ的近似,来保证它和dθ足够接近。但当你完成时,你需要关掉梯度检验,别在每一次进行梯度下降迭代的时候都运行梯度检验,因为这实在是太慢了。
第二,如果一个算法没有通过梯度检测,你需要检查它的组成。检查每一个组成成分,尝试找出漏洞。即如果dθi的近似和dθi差距很大,我会检查不同的i值,看看哪些dθi的近似和dθi值差距最大。可能来自于某一层的b或者w,这也许能帮助你找到漏洞的位置。
如果你使用了正则化,别忘记正则化,即你需要记住加入这个项。
第四,梯度检验不能与随机失活一起使用,因为在每一次迭代中,随机失活将随机消除隐藏层单元的不同子集,在使用随机失活进行梯度下降的过程中,并不存在一个容易计算的代价函数J。随机失活可以被视为对于代价函数的优化,但是这个代价函数的定义是在每一次的迭代中对所有非常大的可消除节点集进行求和,所以这个代价函数是很难计算的。
因此,使用梯度检验来检查包含了随机失活的运算是很困难的。所以我常常在使用梯度检验的同时,不使用随机失活(把keep-prob和dropout设为1.0)。
还有一些别的事可以做:比如修正那些舍弃节点的模式,可以使用梯度检验来检查它们的模式是否正确。但是我通常不会这么做,所以我的建议是:关掉随机失活,使用梯度检验来检查你的算法在没有dropout情况下至少是正确的,然后再打开dropout。
最后这个内容非常微妙,很少发生,但并不是没有可能。你对于梯度下降的使用是正确的,同时w和b在随机初始化的时候,是很接近0的数,但是随着梯度下降的进行,w和b有所增大,也许你的反向传播算法,在w和b接近0的时候是正确的,但是当w和b变大的时候,算法精确度有所下降,所以虽然我不经常使用它,但是你可以尝试的一个方法是:
在随机初始化的时候,进行梯度检验。然后训练网络一段时间,那么w和b将会在0附近摇摆一段时间,即很小的随机初始值。在进行几次训练的迭代后,再运行梯度检验。
在本周,你学会了如何设置训练、开发和测试集,如何分析高偏差/高方差的情况,以及面对高偏差或者高方差你应该如何应对?如何运用不同形式的正则化?比如L2正则化还有drop out正则化,以及对你的神经网络如何进行随机失活?即一些加速神经网络训练的技巧,最后是梯度检验的内容。