更多笔记请火速前往 DeepLearning.ai学习笔记汇总 本周我们将学习如何配置训练/验证/测试集,如何分析方差&偏差,如何处理高偏差、高方差或者二者共存的问题,如何在神经网络中应用不同的正则化方法(如L2正则化、Dropout),梯度检测。
一般来说为了充分利用已有数据以及让模型预测的更加一般化,通常将数据划分成训练/验证/测试集,划分比例一般为60%-20%-20%。
在大数据时代,我们很容易有上百万甚至上千万的数据,这时假设我们使用训练集模拟出了若干个模型,现在需要比较不同模型的拟合效果,如果仍然将原数据的20%作为验证集,那么这将是一非常大的数据量,其实是没必要,因为一般来说只要选取一万或者几万(即1%-3%)的数据就可以了。 同理,测试集若在大数据的情况下,也只需要取0.5%-3%左右即可。
举个栗子来解释一下这个问题。加入我们要实现一个识别汽车的应用。我的在训练的时候采用的都是高清无码,拍的很好看的车子。可以到了测试集我们可能用一些手机拍的照片,这些照片相比起来可能比较模糊,暗淡,像素低。很显然,这两类数据分布不同。这种情况该怎么解决呢?Andrew大大的建议是让验证集数据与测试集数据同分布,既属于同一类数据。,用他的原话就是:
Make sure dev set and test set come from the same distribute. 但是如何做到这一点呢?咱们可以用爬虫之类的工具在网上怕资源呀~~~
引用原话: Not having a test set might be okay.(Only dev set) 是的,即使没有测试集也不要紧。因为测试集的作用就是对通过验证集最终选定的神经网络做出无偏估计所以如果你根本就不需要对模型进行无偏估计,那么测试集没有也无妨。。
这两个易学易混淆的概念可以参考Andrew Ng机器学习课程笔记--week6(精度&召回率&偏差&方差)
这一节主要介绍在训练模型时,遇到问题该如何解决的过程。如下图所示
解释一下上面流程图的意思:
这一节内容讲的比较简单,主要介绍的是 \(L2\)正则化 。具体的可参考这篇博文来理解正则化。 Andrew Ng机器学习课程笔记--week3(逻辑回归&正则化参数)
下一节将会介绍为什么要使用正则化,以及直观理解正则化是如何预防过拟合的。
如上图所示。假设最开始我们训练的模型得到的是最右边的结果,很显然这是过拟合的。我们现在通过加入正则项来改善过拟合。但是为什么能改善呢?
我们首先做一个极端的假设,假设正则项系数 \(λ\) 非常大,那么为了满足\(minJ(w,b)\)这个运算,则必须使得\(w^{[l]}≈0\)
但是这样会极大的简化神经网络的结构,简单地说可能就是从非常复杂的非线性结构转变成了线性结构,这时得到了最左边的模型,但显然也不行,这是欠拟合
所以需要取中间的值,让模型恰到好处,就如中间的模型一样。
下面从另一个角度,即结合损失函数公式和激活函数曲线来理解。 在之前的笔记中已经介绍过激活函数tanh(z)和sigmoid(z),下面以tanh(z) 为例来介绍正则化如何预防过拟合
tanh(z) 曲线如上图所示,我们可以很清楚地看到\(z\)在比较小的时候激活函数是呈现线性关系的。(这个原因要记住了,后面就开始一整条生态链的推导了~~2333)
又由正则化损失函数可以知道,当我们把\(λ\)的值设为很大的值时,权重\(w\)就会相应的减小 而又因为\(z=wx+b\),所以\(z\)就很小 而\(z\)很小,那么激活函数\(tanh(z)\)就处于线性关系啦,bingo!!整个神经网络不就趋向于线性网络了吗?
为方便说明我们假设需要计算的是3层神经网络,其中令
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%。
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,神经网络结构会被简化,从而达到预防过拟合的效果。
以紫色节点为例,它有很多输入节点,但是每个节点都有可能被删除,所以它不能完全依靠某一个节点,就像古话说的,不能将所有鸡蛋放在同一个篮子里(我的天。。。顿时升华了有木有!2333)。
既然不能完全依靠某一个节点,那么就需要将权重传播开来,即每个输入节点的权重都增加那么一丢丢,这样就会产生 收缩权重的平方范数(shrinking the squared norm of the weights) 的效果,这与\(L2正则化\)有一样的效果,都能压缩权重。
回归到最重要的问题:为什么dropout效果这么好。Hinton大神的解释是dropout减少了节点之间的共适应。共适应这个词说起来好专业,我举个例子来说一下我的理解:
假设一个网络中有10个节点,有一个perfect节点,它的取值刚刚好,另外9个节点的取值还需要调整,也就是所谓的一神带9坑!这个时候网络的输出层往回传递误差,这10个节点都不知道自己现在的取值是不是合适的啊,毕竟咱们开了上帝视角,而它们没有。所以它们就根据传回来的误差更新自己的取值,虽然其他9个节点可能有更合适的取值,但是这个perfect的值就破坏了啊。而且,在更新取值的时候,其他9个坑逼节点心想“这个误差是咱们10个共同造成的,嗯,我只要把我那份误差更新掉就行”,而实际上最终的误差是9个节点造成的,也就是说这些个坑逼节点对自己的错误认识还不够充分!不行,不能这么宠着它们!一个很简单的想法,就是让perfect不工作,得了,您歇着吧!这个时候9个节点就可以更好的更新自己权值,直到出现下一个perfect节点。
但是,问题是咱们也不知道哪个节点是perfect节点啊,咱们训练的时候别说上帝视角了,有时候就连哪些个节点是dead node都看不穿啊。那怎么办呢?就让部分节点先不工作吧,先富带后富。假设不工作的节点全是坑壁节点,那对于perfect节点就是好事啊,毕竟最后的误差就小了。如果不工作的节点恰好有perfect节点,那对于那些个正在工作的菜鸡节点就是好事,让他们能正确认识到自己的错误!这样网络就能训练得更好了。
直接上链接算了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}\),如果