DeepLearning.ai学习笔记(二)改善深层神经网络:超参数调试、正则化以及优化--Week1深度学习的实用层面...
更多笔记请火速前往 DeepLearning.ai学习笔记汇总
本周我们将学习如何配置训练/验证/测试集,如何分析方差&偏差,如何处理高偏差、高方差或者二者共存的问题,如何在神经网络中应用不同的正则化方法(如L2正则化、Dropout),梯度检测。
一、训练/验证/测试集(Train/dev/test sets)
一般来说为了充分利用已有数据以及让模型预测的更加一般化,通常将数据划分成训练/验证/测试集,划分比例一般为60%-20%-20%。
1.数据划分比例需要注意的问题
在大数据时代,我们很容易有上百万甚至上千万的数据,这时假设我们使用训练集模拟出了若干个模型,现在需要比较不同模型的拟合效果,如果仍然将原数据的20%作为验证集,那么这将是一非常大的数据量,其实是没必要,因为一般来说只要选取一万或者几万(即1%-3%)的数据就可以了。
同理,测试集若在大数据的情况下,也只需要取0.5%-3%左右即可。
2.训练/测试集分布不匹配
举个栗子来解释一下这个问题。加入我们要实现一个识别汽车的应用。我的在训练的时候采用的都是高清无码,拍的很好看的车子。可以到了测试集我们可能用一些手机拍的照片,这些照片相比起来可能比较模糊,暗淡,像素低。很显然,这两类数据分布不同。这种情况该怎么解决呢?Andrew大大的建议是让验证集数据与测试集数据同分布,既属于同一类数据。,用他的原话就是:
Make sure dev set and test set come from the same distribute.
但是如何做到这一点呢?咱们可以用爬虫之类的工具在网上怕资源呀~~~
3.没有测试集肿么办?
引用原话: Not having a test set might be okay.(Only dev set)
是的,即使没有测试集也不要紧。因为测试集的作用就是对通过验证集最终选定的神经网络做出无偏估计所以如果你根本就不需要对模型进行无偏估计,那么测试集没有也无妨。。
二、偏差 & 方差
这两个易学易混淆的概念可以参考Andrew Ng机器学习课程笔记--week6(精度&召回率&偏差&方差)
三、机器学习基础
这一节主要介绍在训练模型时,遇到问题该如何解决的过程。如下图所示
解释一下上面流程图的意思:
- 1.High bais --> bigger network:
首先如果我们构建的模型有较大的偏差(high bias),那么我们需要构建一个更大的网络,如增加隐藏层的数量,或者增加隐藏层单元的数量。 - 2.High variance --> more data or regularization
解决了高偏差的问题后,还需要检查方差是否过大,如果方差过大,可以增加数据来解决。但是有的时候可能没有更多的数据,那么也可以通过正则化来改善高方差的问题。
四、正则化
这一节内容讲的比较简单,主要介绍的是 \(L2\)正则化 。具体的可参考这篇博文来理解正则化。
Andrew Ng机器学习课程笔记--week3(逻辑回归&正则化参数)
下一节将会介绍为什么要使用正则化,以及直观理解正则化是如何预防过拟合的。
五、为什么要正则化
1.直观理解(1)
如上图所示。假设最开始我们训练的模型得到的是最右边的结果,很显然这是过拟合的。我们现在通过加入正则项来改善过拟合。但是为什么能改善呢?
我们首先做一个极端的假设,假设正则项系数 \(λ\) 非常大,那么为了满足\(minJ(w,b)\)这个运算,则必须使得\(w^{[l]}≈0\)
但是这样会极大的简化神经网络的结构,简单地说可能就是从非常复杂的非线性结构转变成了线性结构,这时得到了最左边的模型,但显然也不行,这是欠拟合
所以需要取中间的值,让模型恰到好处,就如中间的模型一样。
2.直观理解(2)
下面从另一个角度,即结合损失函数公式和**函数曲线来理解。
在之前的笔记中已经介绍过**函数tanh(z)和sigmoid(z),下面以tanh(z) 为例来介绍正则化如何预防过拟合
tanh(z) 曲线如上图所示,我们可以很清楚地看到\(z\)在比较小的时候**函数是呈现线性关系的。(这个原因要记住了,后面就开始一整条生态链的推导了~~2333)
又由正则化损失函数可以知道,当我们把\(λ\)的值设为很大的值时,权重\(w\)就会相应的减小
而又因为\(z=wx+b\),所以\(z\)就很小
而\(z\)很小,那么**函数\(tanh(z)\)就处于线性关系啦,bingo!!整个神经网络不就趋向于线性网络了吗?
六、Dropout正则化
1) Dropout("随机失活") 操作过程
- 1.每层每个节点以某一概率(这里以50%为例)被选中为需要删除的节点(如下图中标上X的节点)
- 2.被选中为删除的节点,不仅要删除节点,与之相连的线段也要删除,如下图示
- 3.使用反向传播算法对精简后的神经网络进行权重更新计算
- 4.恢复被删节点,然后循环往复上面的步骤,直到得到我们想要的结果。
2) 最常用实现方法--反向随机失活(Inverted Dropout)
为方便说明我们假设需要计算的是3层神经网络,其中令
- 保留节点的概率\(keepProb=0.8\)
- \(a3\)表示三层网络各节点的值, \(a3 = [a^{[1]}, a^{[2]}, a^{[3]}]\).
- \(d3\)表示一个三层的dropout向量,其维度与\(a3\)相同,用python实现如下:
keepProb = 0.8
# d3元素值为True或False
d3 = np.random.rand(a3.shape[0], a3.shape[1]) < keepProb
# 在相乘运算时,python会自动将True转化为1,False转化为0
# 所以可以选出概率小于keepProb的节点继续留下来进行计算(这个大于小于可以根据你自己的想法来实现,不一定是小于)
a3 = np.multiply(a3, d3)
a3 /= keepProb
四行代码,前面三行已经在注释中解释,下面着重解释一下第四行代码的作用(该行代码便是Inverted Dropout的关键步骤)。
我们假设网络的隐藏层,即\(a^{[2]}\)有50个units,那么按照\(keepProb=0.8\)可以知道需要删除10个units,也就是说\(a^{[2]}\)会减少20%,那么我们在计算下一层,即\(z^{[3]}=w^{[3]}a^{[2]}+b^{[3]}\)时就会使得\(z^{[3]}\)的期望值(均值)发生变化,为了不影响\(z^{[3]}\)的期望值,我们需要用\(w^{[3]}a^{[2]}\div 0.8\)来修正或弥补我们所需的20%。
3) 补充说明
1.另外需要注意的是在测试阶段,我们不需要再使用dropout,而是像之前一样直接将各层的权重,偏差带入计算出预测值即可。
2.上面所提到的keepProb也可以是跟着各层节点数变化的,以下面的神经网络为例。
\(W^{[1]} \in R^{7×3},W^{[2]} \in R^{7×7},W^{[3]} \in R^{3×7},……\)
可知第二层的系数最多,所以最有可能造成过拟合,所以该层的keepProb应该取得比较小,如0.5.而其他的层则可以取为0.8,对于完全不会产生过拟合的就可以取1.
七、理解Dropout
下面给出好几种直观理解,看你喜欢那种咯~~
1. 简化网络结构
和正则化一样,通过dropout,神经网络结构会被简化,从而达到预防过拟合的效果。
2. 权重扩散
以紫色节点为例,它有很多输入节点,但是每个节点都有可能被删除,所以它不能完全依靠某一个节点,就像古话说的,不能将所有鸡蛋放在同一个篮子里(我的天。。。顿时升华了有木有!2333)。
既然不能完全依靠某一个节点,那么就需要将权重传播开来,即每个输入节点的权重都增加那么一丢丢,这样就会产生 收缩权重的平方范数(shrinking the squared norm of the weights) 的效果,这与\(L2正则化\)有一样的效果,都能压缩权重。
3. 1神带9坑
回归到最重要的问题:为什么dropout效果这么好。Hinton大神的解释是dropout减少了节点之间的共适应。共适应这个词说起来好专业,我举个例子来说一下我的理解:
假设一个网络中有10个节点,有一个perfect节点,它的取值刚刚好,另外9个节点的取值还需要调整,也就是所谓的一神带9坑!这个时候网络的输出层往回传递误差,这10个节点都不知道自己现在的取值是不是合适的啊,毕竟咱们开了上帝视角,而它们没有。所以它们就根据传回来的误差更新自己的取值,虽然其他9个节点可能有更合适的取值,但是这个perfect的值就破坏了啊。而且,在更新取值的时候,其他9个坑逼节点心想“这个误差是咱们10个共同造成的,嗯,我只要把我那份误差更新掉就行”,而实际上最终的误差是9个节点造成的,也就是说这些个坑逼节点对自己的错误认识还不够充分!不行,不能这么宠着它们!一个很简单的想法,就是让perfect不工作,得了,您歇着吧!这个时候9个节点就可以更好的更新自己权值,直到出现下一个perfect节点。
但是,问题是咱们也不知道哪个节点是perfect节点啊,咱们训练的时候别说上帝视角了,有时候就连哪些个节点是dead node都看不穿啊。那怎么办呢?就让部分节点先不工作吧,先富带后富。假设不工作的节点全是坑壁节点,那对于perfect节点就是好事啊,毕竟最后的误差就小了。如果不工作的节点恰好有perfect节点,那对于那些个正在工作的菜鸡节点就是好事,让他们能正确认识到自己的错误!这样网络就能训练得更好了。
4. 知乎
直接上链接算了Dropout解决过拟合问题
八、其他正则化方法
1.Data augmentation数据扩增
简单地说就是在无法增加额外训练数据的情况下,可以对已有数据进行翻转、放大,扭曲等处理得到新的数据来扩充原数据集,从而达到正则化的目的。如图示
2. early stopping
首先假设我们训练集的训练误差随着迭代次数递减,曲线如图蓝色曲线所示。这看起来貌似不错,所以接下来我们看看验证集的效果。
验证集误差在刚开始是递减的,但是在箭头所指处发生转向(开始增加),所以我们发现错误就要及时纠正,那我们训练到这就及时停下来吧~~
但是这样是有明显的缺点的,因为我们在实现神经网络的时候一般是按照如下两个方面步骤进行的:
- 1.优化costfunction(减小偏差)
- 具体的方法有Gradient,momentum,Adam等
- 2.避免过拟合(减小方差)
- 具体的方法有正则化、数据扩增、dropout等
虽然early stopping让我们及时的避免了过拟合(减少方差),但是我们也停止了误差的下降(减小偏差)。这种一个时间解决一个问题的思路称为正交化(Orthogonalization)。
前面提到的 \(L2\)正则化 正则化虽然可以同时优化方差和偏差,但是他需要花费较大精力去找到合适的参数λ,但是吴大大说他自己更加倾向于用 \(L2\)正则化 这个方法。
九、正则化输入
- 正则化数据前后的数据分布特点
- 正则化前后梯度下降的区别
十、梯度消失与梯度爆炸
假如有如下图示的深度神经网络:
为了直观理解梯度消失和梯度爆炸,我们假设所有**函数为线性**函数,即\(g(z)=z\)。并假设前\(L-1\)个权重矩阵都相等,即为\(W_{linear}\) ,所以可以得到 \(y_{hat}=W_{linear}^{L-1}W_{L}X\)。
假设\(W_{linear}\)都等于这个:,那么则有\(y_{hat}=1.5^{L-1}W_LX\),很显然当\(L\)很大时则会出现梯度爆炸。
同理若将权重的值设置为小于1,那么则会出现梯度消失。
十一、神经网络的权重初始化
这篇文章主要就是介绍了权重如何初始化,但是并没有给出推导过程,而是直接给了结论。所以想更加了解如何初始化权重可以看一下我翻译的这篇文章神经网络权重初始化问题,其中很详细的介绍了权重初始化问题。
十二、梯度的数值逼近
梯度其实就是微分求导,但是由于求导结果可能比较复杂,所以使用微积分中的求导的定义来计算梯度。
即假设损失函数\(J(θ)=θ^3\),那么梯度\(\frac{\partial J(θ)}{\partial θ}_{|θ=1}=3\),那如果用求到的定义是怎么计算的呢?如下:
\[\frac{\partial J(θ)}{\partial θ}_{|θ=1} = \frac{J(θ+ε)-J(θ+ε)}{2ε}\]
当\(ε=0.01\)时,上式结果为3.0001
很显然ε越小,就越会逼近真实的梯度值。
这篇笔记有详细介绍-->Andrew Ng机器学习课程笔记--week5(下)
十三、梯度检验
上面说到了如何实现梯度的数值逼近,之所以这样做是为了判断我们的反向传播算法是否错误,这可以为我们的代码调试提供依据。但是怎么判断梯度计算正确呢?
因为通常使用矢量进行计算,所以下面的变量均为矢量
如上图所示,梯度逼近值用\(dθ_{approx}[i]\)表示,我们通过欧几里得距离,即\(\frac{||dθ_{approx}[i]-dθ||_2}{||dθ_{approx}[i]||_2+||dθ||_2}\)。我们假设我们选取的\(ε=10^{-7}\),如果
- 距离计算的结果是\(10^{-7}\)与\(ε\)相近,那么可以认为这个梯度计算是正确的。
- 但如果计算出来的逼近值是\(10^{-5}\)左右,那么此时我们就可能需要谨慎起来,可以查看每一项的误差是不是都差不多,如果有一项或者好几项的误差异常大,那么就需要开始找bug了
- 如果计算出来的距离是\(10^{-3}\),那么肯定是有bug的。。。啥都不用说了,开始找bug吧
十四、关于梯度检验实现的建议
- 1.上面提到的梯度检测只是用来让你判断反向传播算法是否正常,所以并不能用在训练模型的算法中,因为它运行效率真的很慢
- 2.如果梯度检测发现有问题,那么需要定位到误差较大的那一层进行debug(虽然并不太好定位)
- 3.如果损失函数使用了正则化,那么在计算梯度逼近值的时候也请记得加上正则项
- 4.不要和Dropout一起使用。因为Dropout会随机删除节点,所以根本就不容易计算梯度,也就是说梯度是会随机变化的。所以如果你的代码中已经使用了Dropout,那么当你运行梯度检测的时候,记得将Dropout的keepProb参数设为\(\vec 1\),这样就不会删除节点了。
MARSGGBO原创
2017-9-3