改善深层神经网络:超参数调试、正则化以及优化

第一节 训练、验证、测试集

对于数据,我们可以划分为以下3类:

(1)训练集(Train Set),顾名思义,用来训练模型的数据。

(2)交叉检验集(hold-out cross validation set),选出最好的模型。

(3)测试集,最后利用测试集对模型进行测试,获取模型运行的无偏估计。

一般情况下,要求交叉检验集和测试集越小越好(当然在数据较小的时候可以采用7/2/1的比例)。

验证集的目的是为了验证不同的算法哪种更加有效,所以验证集只要足够大能够验证大约2-10种算法哪种更好就足够了,不需要使用20%的数据作为验证集。如百万数据中抽取1万的数据作为验证集就可以了。

测试集的主要目的是评估模型的效果,如在单个分类器中,往往在百万级别的数据中,我们选择其中1000条数据足以评估单个模型的效果。

注意:

(1)建议验证集要和训练集来自于同一个分布,可以使得机器学习算法变得更快;

(2)如果不需要用无偏估计来评估模型的性能,则可以不需要测试集。

第二节 偏差与方差

1517847540761604.jpg


高偏差代表着拟合效果太差,需要加深网络层数。

高方差代表着过拟合(overfitting),可以采取增加数据量(成本太高)或者采取正则化。关于正则化后面会说。

关于训练情况是高偏差还是高方差,可以通过以下几个例子来说明:

表2.1 训练举例

Train set error

1%

15%

15%

0.5%

Dev set error

11%

16%

30%

1%

类型

高方差(过拟合)

高偏差(欠拟合)

高偏差(欠拟合)

低偏差

低方差

第三节 机器学习的基本方法(解决高偏差和高方差问题)

(1)存在高偏差:

增加网络结构,例如增加隐藏层数目等

训练更长时间

寻找更加合适的网络结构

(2)存在高方差:

       –获取更多数据

       –正则化

       –寻找合适的网络

第四节 正则化

4.1 关于正则化(regularization

逻辑回归:

加入正则化项的代价函数:

1517847701123216.png

注意:lambda在python中属于保留字,所以在编程的时候,用“lambd”代表这里的正则化因子λ。

加入正则化项的代价函数:

1517848507473069.jpg

后面||W||F2为W的F范数,W的维度为(n[l],n[l-1])

正则化的格式:

(1)梯度变为:

dW[l]=(form_backprop)+(λ/m)W[l]

(2)梯度更新变为:

W[l]:=W[l]αdW[l]

(3)代入得到:

W[l]=(1αλm)W[l]α(form_backprop)

其中,(1αλm)为一个<1的项,会给原来的W[l]一个衰减的参数,所以L2范数正则化也被称为“权重衰减(Weight decay)”。

4.2 为何正则化可以减少方差

过拟合状态如图所示:

1517847779101145.jpg

便于理解的解释:

加入正则化项,可以这样理解,正则化因子λ在设置的足够大的情况下,为了使代价函数最小化,权重矩阵W就会被设置为接近于0的值。那么这相当于消除了很多神经元的影响,那么图中的大的神经网络就会变成一个较小的网络。当然实际上这些神经元仍然存在,只不过是权重很小了,被我们给近似忽略而已。

数学解释:

假设神经元中使用的激活函数为g(z)=tanh(z),在加入正则化项后:

1517847820115058.png

当λ大,导致W[l]减小,Z[l]=W[1]a[l-1]+b[l]便会减小,由上图可知,在z较小的区域里,tanh(z)函数近似线性,所以每层的函数就近似线性函数,整个网络就成为一个简单的近似线性的网络,从而不会发生过拟合。

4.3 Dropout正则化

Dropout的中文名称为“随机失活”,即利用一定的概率来实现上面说的那个节点不起作用。最常用的Dropout正则化算法就是Inverted Dropout正则化。其代码如下:

keep_prob = 0.8  # 设置神经元保留概率d3 = np.random.rand(a3.shape[0], a3.shape[1]) < keep_prob#d3是一个bool量,即随机生成的数中,小于0.8设置为1,大于等于0.8设置为0。a3 = np.multiply(a3, d3)#这就是将a3中需要置为零的节点置0a3 /= keep_prob#通过对“a3 /= keep_prob”,则保证无论keep_prob设置为多少,都不会对Z[4]的期望值产生影响

注意:在测试阶段不要用dropout,因为那样会使得预测结果变得随机。

另外我们可以这样理解,单个神经元的工作就是接收输入,并产生一些有意义的输出,但是加入了Dropout以后,输入的特征都是有可能会被随机清除的,所以该神经元不会再特别依赖于任何一个输入特征,也就是说不会给任何一个输入设置太大的权重。所以通过传播过程,dropout将产生和L2范数相同的收缩权重的效果。

利用这种正则化的缺点是:代价函数不能再被明确的定义,以为每次迭代都会随机消除一些神经元结点,所以我们无法绘制出每次迭代J(W,b)下降的图。

使用Dropout的步骤:

(1)关闭dropout功能,即设置 keep_prob = 1.0;

(2)运行代码,确保J(W,b)函数单调递减;

(3)再打开dropout函数

4.4 其它正则化方法

(1)数据扩增(Data augmentation):对图片做一些例如反转等操作,得到更多的训练集和验证集。这种方式可以很简单的或者可靠的新数据,但是价值不大。

(2)Early stopping:在交叉验证集的误差上升之前的点停止迭代,避免过拟合。这种方法的缺点是无法同时解决bias和variance之间的最优。

第五节 归一化输入

步骤:

(1)计算每个特征所有样本数据的均值:μ=1m∑i=1mx(i);

(2)减去均值得到对称的分布:x:=xμ

(3)归一化方差:σ2=1m∑i=1mx(i)2,x=x/σ2

    归一化输入是为了防止某个参数过大,导致出现问题。

第六节 梯度爆炸和梯度消失

1517847950283289.png

为了便于理解,我们做出以下假设:

(1)b[l]=0

(2)g(z)=z

(3)W[L]=W[L1]==W[2]=W[1]=W

所以对于目标函数输出有:

y^=W[L]W[L1]W[2]W[1]X=WnX

我们可以看出以下两点:

(1)假如W的各数值大于1,哪怕只大于1一点点,当n趋向于无穷的时候,Wn也会趋近于无穷,由此造成梯度爆炸;

(2)假如W的各数值小于1,哪怕只小于1一点点,当n趋向于无穷的时候,Wn也会趋近于0,由此造成梯度消失。

我们可以利用初始化来缓解这个问题,但是无法消除。

以单个神经元为例:

1517847992799263.png

由上图可知,当输入的数量n较大时,我们希望每个wi的值都小一些,这样它们的和得到的z也较小。

我们想到可以取Wi=1/n,这被称为Xavier初始化。

代码如下:

WL = np.random.randn(WL.shape[0],WL.shape[1])* np.sqrt(1/n)

这样的原理便是:如果激活函数的输入x近似设置成均值为0,标准方差1的情况,输出z也会调整到相似的范围内。虽然没有解决梯度消失和爆炸的问题,但其在一定程度上确实减缓了梯度消失和爆炸的速度。

常用的设置:

·        激活函数使用RELU:Var(wi)=2/n

·        激活函数使用tanh:Var(wi)=1/n

注意:其中n是输入的神经元个数,也就是n[l−1]

第七节 梯度检验

7.1 单边求导VS双边求导

1517848043896804.png

由图我们可知,双边求导的方法更接近于实际导数,因此常用双边求导。接下来进入提督检验的整体。

7.2  梯度检验

步骤:

(1)连接参数

①将W[1],b[1],…, W[L],b[L]重组为一个大的向量θ;

②将dW[1],db[1],…, dW[L],db[L]重组为一个大的向量dθ。

(2)进行梯度检验

1517848080717658.png

判断近似相等的公式:

1517848200137658.jpg

注意事项:

· 不要在训练过程中使用梯度检验,只在debug的时候使用,使用完毕关闭梯度检验的功能;

· 如果算法的梯度检验出现了错误,要检查每一项,找出错误,也就是说要找出哪个dθapprox[i]与dθ的值相差比较大;

· 不要忘记了正则化项;

· 梯度检验不能与dropout同时使用,因为每次迭代的过程中,dropout会随机消除隐层单元的不同神经元,这时是难以计算dropout在梯度下降上的代价函数J;

· 在随机初始化的时候运行梯度检验,或许在训练几次后再进行。


本笔记基于DEEPLEARNING.AI中Andrew Ng课程笔记整理而成,部分内容参考了http://blog.csdn.net/koala_tree/article/details/78125697内容,在此对其表示感谢~