这周已经正式结束了深度学习的第一门课程(深度学习与神经网络),开启了深度学习第二门课程(改善深层次的神经网络——超参数(α学习率、隐藏层单元数、隐藏层层数、激活函数的选择、梯度下降迭代的次数)的调整、正则化以及优化)。

1、训练集、验证集、测试集

在前面我们已经学习了如何构建深层次的神经网络,现在将要学习如何有效的使用深层次的神经网络,包括超参数的调整、如何构建数据,还包括优化学习算法确保其在合理的时间内完成学习。

在配置训练、验证、测试集的时候做出正确的决策能够极大层度的提高深层次神经网络的效率。在训练神经网络时我们常常会遇到很多的决策,如激活函数的选择,梯度下降迭代的次数、隐藏层的层数、各个隐藏层的隐藏单元数、学习率等等。在创建一个新应用的时候,我们通常是不能准确预测这些配置信息或者其他超参数的。实际上深度学习的实际应用是一个高度迭代的过程,首先我们会有自己的一个想法,比如构建特定层数的神经网络、特定单元数的神经网络以及数据个数,然后是去编辑代码并运行,通过运行调试得到神经网络或者是这些配置信息的运行结果,并根据这些结果去完善自己的想法,或者是为了得到一个更好的神经网络不断的去迭代测试。

深度学习已经在计算机视觉、自然语言处理、语音识别、结构化数据等诸多领域有广泛的应用,结构化数据又包括广告、网络搜索(网络搜索引擎、购物网站)、计算安全以及物流等等。那么一个领域中对于深度学习的直觉和经验(超参数的设定)到另一个领域中是不能发挥作用的,最佳的决策还是取决于你的数据量、输入计算机配置的特征数以及选择GPU还是CPU等等。那么对于一个新神经网络的超参数是不好设定的,因此需要不断的迭代循环已得到一个较好的参数配置,因此循环过程的效率也非常的重要,配置高质量的训练集、验证集和测试集也是提高循环效率的关键因素。

在深度学习当中我们通常将数据划分为3部分,分别是训练集、验证集(简单的交叉验证集0)、测试集。首先利用训练集训练模型,或者叫做算法,然后利用验证集验证出不同模型的最优模型,最后将得出的最优模型在测试机上进行评估。在小数据量的机器学习时代,通常将数据划分为70%的验证集和30%的测试集,如果没对验证集做明确的规定也将其划分为60%的训练集、20%的验证集和20%的测试集,这是机器学习领域当时都认可的最好的实践方法。对于少量的数据100、1000、10000,这种划分法还比较有用,但是对于现在的深度学习时代,数据量至少是百万级别的,所以验证集和测试集在总数居上占据的比例就更小。验证集的目的就是验证不同的算法,找出最优的,那么验证集就要足够大才能够验证2-10种算法,并迅速得出最优算法,但是验证集在大也不需要占20%,一百万的总数据的话,一万的验证集就足够了,就足以找出最优的1-2种算法了。测试集的目的是为了评估最终模型的性能,百万数据只需要一万的测试集就足以准确评估最终模型的性能了。百万数据集中训练集占了99%,验证集和测试集都占了1%。如果是过百万数据的应用,99.5%都是训练集,验证集和测试集各占0.25%,或者是验证集占0.4%,测试集占0.1%。

现在深度学习的一个趋势是越来越多的人在训练集和测试集根本不匹配的情况下就进行训练,假如有一个用户可以上传大量图片从而判别猫图片的应用程序,其训练集是从网上找的,而验证集和测试集是用户上传的图片,那么训练集可能有非常高的分辨率以及很好的后期制作,但是验证集和测试集是用户上传的,可能分辨率低、模糊等,那么对于这两类不同的数据集,吴恩达老师的建议是验证集和测试集一定来自同一分布,因为验证集验证不同的模型得出最优的模型,测试集评估最终模型的性能,那么验证集和测试集来自同一分布会好一点。由于训练集非常大,为了得到这么大的训练集我们通常采用一些创新策略,例如网页抓取,但是最终可能导致训练集、验证集和测试集不来自同一分布。但是只要依照这一法则,神经网络模型的效率还是比较高的。

那么在实际情况中如果没有测试集也没有关系,因为测试集是对最终的模型进行无偏估计。如果你不需要无偏估计就不需要设置测试集。那么就通过训练集训练出不同框架的模型,然后用验证集得出最后的最适用的模型,其实验证集是包含测试集的,只是不在提供无偏估计的功能。在现实生活中,如果没有测试集,人们将训练集叫做训练集,将验证集叫做测试集,只是将测试集当作验证集用,并无术语的实际的无偏估计功能,只是将验证集过度拟合到了测试集中。也有的团队将其叫做训练验证集。

2、偏差和方差

搭建高质量的训练集、验证集和测试集,可以帮助衡量神经网络模型的方差和偏差,进而可以找到更合适的方法优化算法。

所有的机器学习从业人员都期望深刻理解偏差和方差,这两个概念易学难精,即使你自己认为已经理解了偏差和方差的基本概念,却总有一些意想不到的新东西出现。关于深度学习的误差问题,另一个趋势是对偏差和方差的权衡研究甚浅,我们总是分别考虑偏差和方差,却很少谈及偏差和方差的权衡问题。

如上图的叉和圆圈是我们的就是我们的数据集,最左边的我们可以为其拟合一条直线得到一个简单的逻辑回归拟合,但是效果不怎么好,这是偏差高的情况,叫做欠拟合。最右边我们拟合了一个较为复杂的分类器,可能是一个深层次的神经网络,或者带有较多的神经单元,他有较好的拟合效果,这也是方差较高的表现叫做过拟合。那么中间的是一个复杂程度适中的分类器,其拟合效果也适中,叫做适度拟合。

像这种只有一个特征或者两个特征的二维数据集,我们可以通过绘制数据得到可视化的方差和偏差。对于多维数据空间,要绘制数据和可视化分类边界是不可能的。

通过设定几个指标来更好的理解方差和偏差。还是沿用判别猫图片这个例子,一张是猫,一张不是,这里首先做一个假设就是我们的人眼是具有很好的辨别能力的,几乎不会出错,误差率是0%(是用来衡量后面训练集和验证集的误差的)。那么衡量偏差和方差重要的两个数据就是训练集和验证集的误差。假如训练集的误差是1%,而验证集误差为11%,那么可以看出在训练集上有较好的拟合效果,而在验证集上拟合效果较差,这样叫做在训练集上过拟合,是方差高的表现。那么假如训练集的误差是15%,而验证集误差为16%,可以看出模型在训练集上的误差比较大,在训练集上就没有很好的拟合效果,但是验证集误差只增加了1%,倒是于训练集的误差相差不大,这种叫做欠拟合,是偏差高的表现。那么假如训练集的误差是15%,而验证集误差为30%,可以看出模型在训练集和验证集上的误差都比较大,所以在训练集和验证集上都没有好的拟合效果,因此这个模型的偏差和方差都很高。那么假如训练集的误差是0.5%,而验证集误差为1%,可以看出模型在训练集和验证集上的误差都比较小,所以在训练集和验证集上都有很好的拟合效果,因此这个模型的偏差和方差都几乎没有。注意这里的结论都是基于一个假设就是我们人眼的判别误差是0%,这是最优误差,也叫做贝叶斯误差,那么就如最优误差是15%,那么训练集的误差是15%,而验证集误差为16%这个模型是有较好的拟合效果的,偏差和方差的都比较低。那么假如没有分类器能够使用,因为图片过于模糊,人眼和系统都无法很好的进行识,那么最初的贝叶斯误差就会更高。这样的分析过程是设置了较小的贝叶斯误差,并且训练集、测试集来自同一分布,如果没有这样的前提,分析过程会更加的复杂。

总结:过拟合为高方差(训练集上拟合效果好,验证集上拟合效果不好)、欠拟合为高偏差(训练集上拟合效果不好)。

上图描述了高偏差高方差的分类器。首先如果是一条逻辑回归的直线,它的拟合效果很差,为欠拟合,是偏差高的表现,只要类似于线性的分类器,其偏差都很高,那么对这条直线中间部分稍作修改,那么紫色的线条就对这个数据进行了过拟合,有着较高的方差,但是它有形如线性的分类器,因此偏差也很高。那么看着上图的蓝色二次曲线有较好的拟合效果

而采用曲线函数或二次元函数会产生高方差,因为它曲线灵活性太高以致拟合了这两个错误样本和中间这些活跃数据。在二维数据上看不太自然,但对于高维数据,有些数据区域偏差高,有些数据区域方差高,所以在高维数据中采用这种分类器看起来就不会那么牵强了。

3、机器学习基础

在训练神经网络时有一个基本的方法:对于初始训练出来的模型,首先辨别其偏差,如果偏差较高,对于训练集都没有很好的拟合效果,那么就需要选择一个新的神经网络架构(更多的隐藏层和隐藏单元),或者是训练更长的时间。一般来说更大的神经网络对于改进模型是很有帮助的,如果模型够大,那么至少对于训练集是拟合或者过拟合的,但是延长训练时间一般是没有帮助的,但也不会有害。训练学习算法时,要不断尝试这些方法,直到解决掉偏差问题,这是最低标准,反复尝试,直到可以拟合数据为止,至少能够拟合训练集。

在得到一个可接受的偏差之后,就需要考虑方差。因而要评估验证集的性能,如果方差过高,应对的方法有获取更多的数据,或者是正则化,或者是找到更合适的神经网络框架(可能会一箭双雕,同时减少方差和偏差)。

在训练神经网络时,我们必须注意两点。第一是高偏差和高方差是两种完全不同的情况,也决定了我们后面尝试改进的方法,但是一般是首先通过训练集和验证集来判定算法是否有偏差和方差的问题,然后再决定使用什么方法。比如说模型有高偏差的问题,那么增加数据和正则化就没有用,更有用的是扩大神经网络模型或者是延长训练的时间。第二点在机器学习初期,方差和偏差的权衡就很常见,可以同时减少方差和偏差,也可以增加偏差,减少方差。但是在深度学习初期,只想减少方差和偏差其中一个而不影响另一个不太可能。

在深度学习和大数据时代,如果正则适度就可以选择一个较大的神经网络,在不怎么影响方差的同时减小偏差,在选择更多数据而不对偏差有过多影响的情况下减少误差,这也是现在很多深度学习工具的原理,在减少偏差或者方差时,对另一个的影响很小,这也是深度学习不用过多考虑偏差和方差权衡的原因。

4、正则化

在调整好了方差和偏差之后,我们将得到一个规范化的网络。接下来谈正则化,不管多大的神经网络都不会有负面影响,只是增加了计算时间,前提是规范的神经网咯。正则化是一种减少方差的实用方法,有时候也会在正则化的过程中考虑偏差和方差的权衡问题,偏差可能会增加,但是只要神经网络够大,那么偏差的增加部分是微乎其微的。另一个解决高方差的方法是获得更多的数据,但是获得更多的数据是不容易的。

利用logistic回归来讲述正则化。在logistic回归当中我们需要求取成本函数的最小值,成本函数里包含了每个样本的预测值,损失值,以及参数W、b,其中W是n维的向量,b是一个实数。要使成本函数正则化,只需要加入一个正则化:(λ/2m)||W||22,其中λ为正则化参数,||W||22为欧几里得范数的平方,它等于W中每个元素平方求和,也可以表示为WTW。这样的正则化叫做L2正则化。那么这里只加上了正则化参数W,那么b呢?也可以写上(b/2m)b2,但是这一项通常省略,因为W是高维的参数矢量,已经可以表示高方差的问题,W几乎涵盖了所有的参数,而b只是一个实数,所以这一项写不写都行。

L2正则化是最常用的正则化类型,其中的||W||22也被称为L2范数。我们可能还听过L1正则化,它加入的是(λ/2m)||W||1,其中的||W||1等于W所有元素之和,也叫做L1范数。L1正则化最后会使得W变得稀疏,也就是里面很多的零,这样有利于压缩模型,因为很多零导致模型占用的存储内存也变少了。但是L1使得W变稀疏,也没有很明显的减少的内存的占用。因此L2正则化更流行。

λ是正则化参数,我们通常通过验证集来确定它,需要不断的尝试找到最好的λ,并且要权衡训练集,因此要将它设为最小的值,以免过拟合。在python当中lambda是保留字段,因此在编写正则化参数的代码时,我们将其写为lambd,这样就可以避免与python保留字段的冲突。λ也是一个需要调整的超参数。

L2正则化的过程已经了解清楚。那么如何在神经网络中实现L2正则化,假设神经网络的成本函数是J,其中包括了每一层级的参数W[l],b[l],即J(W[1],b[1]……,W[l],b[l]),它的值为m个样本的损失值之和在乘上1/m,再加个上正则化(λ/2m)Σ||W[l]||F2,其中的范数不在是欧几里得范数,而是弗罗贝尼乌斯范数,因此下标变为了F不在是2,其值等于W中每个元素的平方求和,W[l]的维度是(n[l-1],n[l])。

那么怎么对弗罗贝尼乌斯范数求导数dW[l]?利用backprop使得成本函数J对求W[l]导数,再加上新增的正则项(λ/m)W[l],那么dW[l]=backprop+(λ/m)W[l],在进行参数更新W[l]=W[l]-αdW[l]=W[l]-α(backprop+(λ/m)W[l])=(1-αλ/m)W[l]-α(backprop),无论W[l]为多少这个更新参数的式子都在是W[l]变小,相当于W[l]减去系数αλ/m乘上W[l],而系数αλ/m小于1,这也是L2正则化被叫做权重衰减的原因。

5、为什么正则化可以减少过拟合

为什么正则化可以减少过拟合,或者说是减少高方差?现在给了两个例子,上图的下面部分最左边是欠拟合(高偏差),最右边是过拟合(高方差),中间是适度拟合。那么为了模仿过拟合,设置了一个很庞大的神经网络(上图的很小,但是可以将其看作有很多的神经元和隐藏层),其成本函数是J(W,b)=∑L(y(i),y^(i))/m+(λ/2m)||W[l]||F2,其中λ越大,权重参数矩阵的就越接近于0(W[l]=(1-αλ/m)W[l]-α(backprop)),这样就可以将庞大的神经网络简化为一个很简单的神经网络,如同logistic回归,但是又有很深的深度,这就将一个过拟合的神经网络变成了一个欠拟合的神经网络。

λ也会有一个中间值,使得模型对于数据是适中拟合的状态。

λ过大就会使得W接近于0,进而消除或者是减少一些神经元,从而简化神经网络,但是实际上这些神经单元并没有消除,只是影响变小了,就是因为一些神经元的作用变小了,才减少了产生过拟合的的可能性。

现在来直观感受一下为什么正则化可以减少过拟合。假如神经网络的每一层的激活函数都选择的是tanh函数,也就是g(z)=tanh(z),那么z很小的时候对应g(z)红线的线性部分,z很大的时候对应g(z)的非线性部分。那么当λ很大时,W就很小,那么对应的z就会很小,就会对应激活函数g(z)的线性部分,前面讲了如果隐藏层的激活函数选择线性函数的话,整个网络只会进行线性计算,就如同只有一层隐藏层一样,无法做出复杂的决策,更别说过拟合数据集上的非线性决策边界,相当于就是简化了神经网络,进而减少了过拟合的可能性。

在正则化的过程中,我们一定要注意我们已经定义了一个新的成本函数,J(W,b)=∑L(y(i),y^(i))/m+(λ/2m)||W[l]||F2,其中加了一个正则化项,目的是为了防止权重过大。那么如果是一个梯度下降函数,在调试梯度下降的过程中,定义J也是其中的一步,它代表着梯度下降调幅的数量,新定义的成本函数使得每个调幅都是单调递减的,只会使得J越来越小。那么如果J没加正则化项,那么不可能在调整梯度下降的过程中每个调幅都是单调递减的。

6、Dropout正则化

在训练深度学习模型时最常用的一种方法就是L2正则化。在深度学习中,还有一种方法也用到了正则化,就是 dropout (随机失活)正则化。

左图是一个过拟合的神经网络。那么Dropout要做的事情就是遍历神经网络每一层,为神经网络的每一个节点设置消除的概率,可以以抛硬币的概率来设置概率,那么每个节点保留或者消除的概率都是50%。那么在设置概率之后,将消除一些节点以及这些节点的连线,得到一个更简单、规模更小的神经网络,然后在采用backprop进行梯度下降更新参数,这是网络节点精简的一个样本,对于每个节点都可以采用抛硬币设置节点消除概率,保留一类节点,消除另一类节点。对于每个样本都使用精简的网络训练它,单纯的遍历每个节点,概率也是随机的,但是真的有效。(注意上面谈及的是神经网络的训练过程,在神经网络的训练过程中可以使用Dropout正则化,但是如果是测试阶段就不要使用Dropout正则化)

如何实施Dropout?这里讲述最常用的方法:反向的Dropout。以一个3层的神经网络为例,编程中可能会出现很多3,但是这里只对其中一层进行反向Dropout说明。第一步是定义一个d[3]向量(第三层的Dropout向量),d[3]=np.random.rand(a[3].shape(0),a[3].shape(1)),然后定义一个值keep.prop,看d[3]是否小于它,它代表的是节点保留的概率,在本例子中为0.8,那么节点消除的概率就是0.2。d[3]是一个随机矩阵,它的形状和a相同(a.shape[0]为a的行数,a.shape[0]为a的列数)。第三层的每个神经单元或者是样本的d[3]向量等于1的概率是0.8,等于0的概率是0.2。第二步是得到第三层的激活函数a[3],其中包含了计算的激活函数(与原来的a[3]=g[3](z[3])不一样),a[3]=np.multiply(a[3],d[3]),注意这里是元素乘,目的是为了过滤d[3]为0的节点,因为相乘之后就将d[3]为0的节点的激活值设为了0,那么这个节点就失活了,就会被消去。其实d[3]在python中对应着布尔类型,也就是不是Ture就是False。也可以进行乘法,因为python会将Ture和False自动的翻译为0和1。第三步是扩展a[3],也就是使得a[3]=a[3]/keep.prop,这样做的原因是:假设第三层有50个隐藏单元,因为keep.prop为0.8,那么说明至少有10节点的激活值会为0,那么又因为z[4]=W[4]a[3]+b[4],a[3]会减少20%,为了不影响z的期望值,只有将W[4]a[3]/keep.prop,这样才可以弥补和修正z的期望值。第三步就是反向的Dropout,它的作用就是除以keep.prop,使得z的期望值不变。当keep.prop为1的时候,没有Dropout,因为它会保留所有节点。实际上反向的Dropout使得测试阶段,也就是评估模型性能时更加的容易,因为它减少了数据扩展的问题。(注意上面谈及的是神经网络的训练过程,在神经网络的训练过程中可以使用Dropout正则化,但是如果是测试阶段就不要使用Dropout正则化)

在测试阶段如何训练算法?在测试阶段,首先给定变量x,或者是想要预测的变量,使得第0层的激活函数a[0]等于预测样本x。在整个测试阶段都不使用Dropout,测试阶段为z[1]=W[1]a[0]+b[1]、a[1]=g[1](z[1])、z[2]=W[2]a[1]+b[2]、a[2]=g[2](z[2])、……、y^。因此在整个测试阶段都不用抛硬币决定失活概率,进而消除一些隐藏神经单元,测试阶段不希望使用Dropout是因为它不希望输出的结果是随机的,如果使用了Dropout整个测试阶段都会受到干扰。

反向的Dropout使得激活函数除以keep.prop时可以记住上一步的操作,目的就是为了使得测试阶段不用Dropout调整数值范围,且激活函数的期望值也不会变,这样测试阶段就不用额外的加入Dropout正则化。

7、理解Dropout正则化

Dropout可以随机的删除神经网络的节点,这是很疯狂的,但是它确实有用,这是为什么呢?

通过上节课会有两个直观的认识。第一个直观的认识是对于Dropout随机删除神经网络节点的直观认识,在每一次训练迭代的过程后神经网络都会变小,那么使用小规模的神经网络似乎和正则化的效果一样。第二个直观理解,首先看到上图左侧,从一个神经元入手,它的工作就是将四个输入变为有意义的输出,但是经过Dropout过程后,它的四个输入都可能被消除,因此一个神经元不要依赖于其他的特征,因为这些特征很可能被随机删除,只有不依赖其他特征才可能被传播下去,并且通常不会给某个节点的权重过大,因为它很可能被随机消除,只给每个节点赋予一点权重,神经网络会传播每个节点的权重,那么Dropout就会产生压缩权重范数平方效果,和L2正则化一样都会压缩权重,产生预防过拟合的外层正则化效果。L2正则化对于权重的衰减是取决于激活函数的倍增大小的。

总结一下,Dropout正则化与L2正则化类似,与L2正则化不同的是当应用不同时,Dropout也会有所不同,并且有可能更使用于不同的输入范围。

Dropout正则化还有细节需要注意,首先看到上图的右下角。以一个五层的神经网络为例,每层的神经元个数为n[0]=3、n[1]=7、n[2]=7、n[3]=3、n[4]=2、n[5]=1,每层的的权重矩阵维数为W[1]=(7,3)、W[2]=(7,7)、W[3]=(3,7)、W[4]=(2,3)、W[5]=(1,2)。要实施Dropout正则化就要设置一个参数keep.prop,它代表每一层神经元的保留概率,且神经网络每一层的keep.prop可以不一样,如W[2]=(7,7),这一层的神经元过多,W权重矩阵也是最复杂的,那么为了防止这一层过拟合就要将其keep.prop设置小一点为0.5,其他的层级如果过拟合没那么容易发生,就可以将keep.prop设为0.7,如果某层级完全不用担心过拟合(只有一个神经元),那么可以将keep.prop设为1,上图用紫色框圈出了每一层级的keep.prop值。

值得注意的是keep.prop的值为1代表保留该层级所有的神经元,不使用Dropout。对于容易产生过拟合的层级就要将keep.prop设置小一点,发挥更强大的Dropout,这有点像处理L2正则化中的λ参数。理论上是可以对输入层也使用Dropout的,可以随机消除一些输入特征,但是一般我们都不这样做,如果要在输入层使用Dropout,就要将keep.prop设置为1或者0.9,因为删除很多的输入特征是不可能的。

总结一下,如果担心某一层产生过拟合,就可以将keep.prop设置得比其他层级低,但是这样做的缺点就是每一层都使用了Dropout,都增加了超参数keep.prop。还有另一种方法就是一些层使用Dropout,一些层不使用Dropout,只有使用了Dropout的层级才有超参数keep.prop。

Dropout实施的两个技巧。第一Dropout在计算机视觉领域得到了广泛的应用,几乎是默认的选择,因为计算机视觉的输入量非常大,导致输入了过多的像素,而没有足够多的数据,极易产生过拟合,值得注意的是Dropout是一种正则化方法,用来预防过拟合的,因此如果算法没有过拟合一般不会用它,所以在其他的领域使用Dropout比较少。第二Dropout有一个很大的缺点就是成本函数J没有明确的定义,因为每一次迭代都会随机删除一些节点,那么J根本无法明确定义,J无法明确定义的话梯度下降的性能也无法很好的衡量,明确定义的成本函数J会有明确的下降趋势,还可以绘制如上图中的J的趋势图。但是如果使用了Dropout就无法明确的写出J的公式,更别说绘制它的趋势图了。那么通常的做法就是先关闭Dropout,也就是将所有层级的keep.prop设置为1,然后运行代码,确保成本函数J是下降的的之后,在将各层级的keep.prop设置为原来的值,也就是开启Dropout。

8、其他的正则化方法

8.1 数据扩增

除了L2正则化和Dropout(随机失活)正则化以外还有其他的正则化方法可以减少过拟合。如上图在训练一个猫咪图片分类器,如果想要通过扩增训练集数据,从而减少过拟合是比较困难的,因为扩增数据的代价比较高,且有时候我们根本没办法进行扩增,那么就可以通过添加这类图片进行数据集的扩增,比如可以将猫咪图片进行旋转,如果将训练集的每张猫咪图片都旋转,训练集将会扩大一倍,虽然这会导致训练集冗余,但是会减少了获取一组新的猫咪图片的高花销。除此之外,还可以对图片进行任意裁剪,扩大训练集。那么通过这种翻转和随意裁剪的方式可以扩增数据,得到额外的假训练数据,虽然这些假数据不包含那多的信息,但是几乎是0花销,除了一些对抗性代价。通过这样迭代方法扩大数据集,从而正则化数据集,是减少过拟合的一种廉价方式。

对于光学符号识别,我们通常将数字随意进行旋转或者扭曲后加入数据集,以达到扩增数据集的目的。上面的数字看起来过于扭曲,实际上我们只对字符作微小的变化,这里是为了方便说明。变形后的数字加入训练集中仍然是数字。

数据扩增也是一种正则化方法,效果与其也差不多。

8.2 early stopping

在运行梯度下降的过程中,我们可以绘制训练误差,也可以绘制成本函数J的优化过程,我们希望绘制的图像不管是针对于误差还是J的都单调下降。

early stopping除了绘制上述的内容还可以绘制验证集的误差,包括验证集分类误差、代价函数、对数损失、逻辑损失等,但是对于验证集误差的绘制,开始会出现下降的趋势,然后到达某个节点之后就开始上升(出现了过拟合)。那么early stopping的作用就是找到这个节点,例如在上图当中,还未对神经网络进行训练之前,w还是一个接近于0的值,因为w的初始值就是一个比较小的随机值,随着神经网络的训练,那么w的值会越来越大,early stopping的作用就是在验证集误差还未上升时的那个节点停止迭代过程,进而得到对应的中值w,进而得到中值w的范数。

early stopping这个术语的意思就是提早停止迭代神经网络。early stopping也有一些缺点,接下来对其进行描述。

在机器学习的多个步骤当中,其中有一个就是选择一个算法来优化代价函数 J,如梯度下降等等,那么优化了代价函数J之后我们也不希望发生过拟合,就可以通过正则化和扩增数据来完成。首先由于机器学习超参数激增,找到可行的算法越来越复杂,但是会发现只要采取了一组工具优化代价函数,机器学习就会变得简单,只需要关注J(w,b)的值,只要是越来越小就行,其他的不用管。 其次未来减少方差,预防过拟合,我们需要采用另一套工具来实现,名为正交化,其意思就是在一个时间里面只做一个事。

那么early stopping它的缺点就是不能单独的处理优化J和减少方差这两个过程,因为其为你预防过拟合过早的停止了梯度迭代,而不能对J继续进行优化。那么想要同时考虑这两个过程就是不使用early stopping,而是L2正则化,这会使得训练时间很长,使得超参数的搜索空间更容易分解和搜索,但是缺点就是要尝试很多的正则化参数λ的值导致计算代价变大。early stopping的优点就是只用运行一次梯度下降,就可以找到w的较小、中等、较大的值,不用像L2正则化那样尝试很多的正则化超参数λ的值。

9、归一化输入

归一化输入是为了加快神经网络的训练速度。

9.1 归一化输入的步骤

假设有一个训练集,输入特征有两个:X=[x1 x2]T,红色的是数据集的散点(上图中的第一个散点图)。

归一化输入有两个步骤,第一步是零均值化:

,u是一个向量,X等于每个样本减去u 。也就是先求得样本的均值,在让整个训练集中的每个样本移动减去均值,完成零均值处理(零均值化处理之后为第二个散点图)。第二步是归一化方差,注意x1方向的方差比x2大得多:

(注意这里的x(i)是已经进行零均值化处理的),σ2是一个向量,在让已经零均值化的整个训练集的每个样本除以σ2。也就是先求样本的方差,然后让整个训练集中的每个样本除以方差,进而完成归一化方差处理(归一化方差处理之后为第三个散点图)。归一化输入的结果就是输入的x1和x2的方差都为1。

值得注意的是如果用归一化输入调整了训练集的数据,那么一定要用相同的u、σ2归一化测试集,而不要重新去预估u、σ2,因为不论是对于训练集还是测试集,我们都希望用相同的u、σ2定义的相同的数据转换,u、σ2都是通过训练数据计算得来的。

9.2 为什么要归一化输入特征

右上角是代价函数公式,假如输入的是非归一化的特征,那么J的形状会非常的细长狭窄,如上图是左上角所示,要找的J的最小值在红色箭头指向的那里。假如输入的特征是不同的范围x1:1-100,x2:0-1,那么其对应得出的参数W的值也会有很大的差异,其二维平面上的两个轴应该是w1、w2,未来方便理解写的w、b。右下角是该函数对应的轮廓,像一个细长的碗。如果输入的是归一化的特征,那么代价函数J的图像就非常的匀称,如上图的右上角所示,右下角是它的函数轮廓。那么对于这两个图要实施梯度下降,首先对于左边来说,需要设置一个较小的学习率,因为可能需要反复迭代才能够找到J的最小值,其次对于右边这种均匀的球形轮廓,可以设置一个较大的学习率,因为它可以很快很直接的找到J的最小值。

W参数是一个高维的向量,用二维平面描述不能有一个直观的理解,但是总的直观理解就是J的形状更圆更容易优化,前提是输入的特征范围相似,只有-1到1的相似偏差,这样会使J的优化更简单迅速。例如x1:0~1,x2:-1~1,x3:1~2,这样的输入特征对于J的优化比较友好,当然如果输入的特征的相似偏差超出了-1~1,就可以使用归一化输入,将其设为相同的方差即可。

10、梯度消失和梯度爆炸

在训练神经网络,特别是深层次的神经网络的时候,可能会发生梯度爆炸和梯度消失,也就是说有时候梯度或者导数会变得很大,有时候又变得很小,甚至是呈指数式的减小,这就增加了训练的困难。

假设在训练如上图的一个极深的神经网络,每一层只有两个节点是为了节约幻灯片的位置。首先是输入了两个特征值x1和x2,然后每一层对应的权重矩阵分别是W[1]、W[2]、……、W[l],且假设b[l]=0和每一层的激活函数g[l]都是线性函数,即g[l](z[l])=z[l]。那么预测值y^=W[1]W[2]……W[l]X(因为z[1]=W[1]X,a[1]=g[1](z[1])=z[1]=W[1]X,z[2]=W[2]a[1],a[2]=g[2](z[2])=z[2]=W[2]W[1]X,依次内推就得到预测值y^)。

假如权重矩阵为1.5倍的单位矩阵,注意W[l]的维度为(n[l],n[l-1]),对于上述网络除了最后一层的W[l]的维度为(1,2),其余层的W[1]~W[l-1]的维度都是(2,2)。那么假设W[1]~W[l-1]都为1.5倍的单位矩阵

,那么y^=

(忽略了最后一层的权重矩阵,关系不大,只是为了表述梯度下降和梯度爆炸的一个思想)=1.5L-1X,因此如果神经网络的层数L很大,那么y^的值就会很大,其值呈1.5L-1指数级增长,因此y^的值是呈爆炸式增长的,又因为g[l](z[l])=z[l]=W[l]X,那么激活函数的值也呈指数级的爆炸式增长。那么如果将1.5都改为了0.5,那么W[1]~W[l-1]都为0.5倍的单位矩阵

,那么y^=0.5L-1X,其值呈0.5L-1指数级减少,因此y^的值是呈指数级减少的,又因为g[l](z[l])=z[l]=W[l]X,那么激活函数的值也呈指数级减少。

总之就是当权重矩阵的值比1大一点的时候,L层神经网络的激活函数会呈指数级的爆炸式增长,比1小一点的时候,如

,L层神经网络的激活函数就可能呈指数级的减少。这里是针对于的激活函数,那么对于梯度函数和导数是一样的。

11、 神经网络初始化权重

对于梯度下降和梯度爆炸没有能够彻底解决的办法,下面会提出一个不完整的解决方法,虽然没有很完美的效果,但是还是帮助我们更加谨慎的为神经网络初始化权重参数。

以单个神经元的神经网络为例(如logistic回归)讲述解决梯度下降和爆炸的方法,在引申到深层次的神经网络。首先看到单个神经元的神经网络有四个输入x1~x4,然后经过激活函数g(z)之后得到a值,进而算出最后的预测值y^(后面引申到深层次的神经网络的时候,要将输入特征x1~x4变为a[l])。那么z=W1x1+W2x2+W3x3+W4x4(b记为0),如果不希望z的值很大或者很小,那么就希望n值越大的时候,Wi的值就可以越小,那么就可以将Wi的值设置为1/n,其中n是该神经元输入特征的个数,表示为代码的话就是Wi=np.random.rand(shape)*np.sqrt(1/n[l-1]),n[l-1]就是第l-1层的神经元个数,也就是l层每个神经元输入特征的个数。

但是如果激活函数选用的是Relu,而不是1/n,那么就可以将Wi的值设置为2/n,其中n是该神经元输入特征的个数,表示为代码的话就是Wi=np.random.rand(shape)*np.sqrt(2/n[l-1]),其中np.random.rand()是高斯随机变量,然后乘上W初始值的平方根。

对于变体函数,刚刚提到的函数是 Relu 激活函数,其权重参数W的初始化就按照公式np.random.rand(shape)*np.sqrt(1/n[l-1])。对于几个其它变体函数,如 tanh 激活函数,其权重参数W用公式

进行初始化。还有人提出使用公式

进行权重参数W的初始化。

12、梯度的数值逼近

在进行反向传播的时候有一个测试,叫做梯度检验,它是为了确保反向传播过程能够顺利的进行。因为有时候我们虽然写下了有关反向传播的公式,但是我们不能够100%的保证反向传播的各个细节都不会出错。再讲梯度检验之前,先讲一下如何计算梯度做数值逼近。

首先画出f的函数,f(x)=x3。假设θ=1,那么f(θ)=θ3,取一个ε=0.01,那么θ+ε=1.01,θ-ε=0.99。θ和θ+ε对应一个三角形,其长比宽就是梯度。θ和θ-ε也对应一个三角形,其长比宽也是梯度。θ+ε和θ-ε也对应一个三角形,其长比宽也是梯度,这个大三角形的梯度比两个小三角形都更能代表f的θ处的导数(梯度),因为它包含了两个小三角形,计算的不是单边公差,而是双边公差。

那么让我们来计算一下这个大三角形的长比宽。首先长为f(θ+ε)-f(θ-ε),其次宽为2ε,所以长比宽的比值为(f(θ+ε)-f(θ-ε))/2ε,这就是梯度值,记为g(θ)=f‘(θ),那么讲θ=1,ε=0.001的值带进去,可以得到g(θ)=3.0001,那么真实的g(θ)=3,所以逼近误差只有0.0001。但是如果做的单边公差,也就是f(θ+ε)-f(θ))/ε(标准的求导公式,但是误差太大),那么g(θ)=3.0301,逼近误差就是0.0301,比0.0001大多了。

总之双边公差的方法可以非常逼近导数值,如果在反向传播和梯度检验中使用这个方法,它的速度与计算两次单边公差的速度一样。

13、梯度检验

梯度检验可以帮忙减少神经网络的训练时间,还可以减少反向传播当中出现bug的概率。

假如存在一个神经网络,其中包含参数W[1]、b[1]……W[l]、b[l]。要进行梯度检验的第一步就是将所有的参数组成一个巨大的向量,也就是将所有矩阵W和所有向量b全部组成一个大向量θ。代价函数本来是以W、b为参数的:J(W[1],b[1]……W[l],b[l]),现在变为了以θ为参数:J(θ)。然后经过反向传播后会得到和参数W[1]、b[1]……W[l]、b[l]一样顺序的梯度:dW[1]、db[1]……dW[l]、db[l](维度也与W[1],b[1]……W[l],b[l]相同),同样也将所有的梯度组成一个大向量dθ(与θ维度一样)。

那么求出的dθ与梯度和代价函数有什么关系?可以先将J(θ)写作J(θ1……θi),且记住J是超参数θ的函数。要实施梯度检验(grad check),就要执行循环,计算J中每个θi的双边公差dθapprox[i],即for each i:dθapprox[i]=[J(θ1,……,θi+ε,……)-J(θ1,……,θi-ε,……)]/2ε。通过上一节的梯度的数值逼近知道双边公差dθapprox[i]是梯度值

的逼近值。那么最后所有θi的双边公差dθapprox[i]组成向量dθapprox,就得到两个值dθ和dθapprox(两个与θ的维度都一样),梯度检验要做的事情就是检验dθapprox这个向量是否逼近向量dθ。

那么如何定义两个向量是否真的彼此邻近?一般会做以下运算,首先计算两个向量距离的欧几里得范数:||dθapprox-dθ||2,这里没有平方,这里的具体计算是求取误差的平方和,然后取平方根得到欧式距离。然后用向量长度的欧几里得范数做归一化处理:||dθapprox||2+||dθ||2。故最后的式子为||dθapprox-dθ||2/(||dθapprox||2+||dθ||2),分母是为了防止这个向量过大或过小,并且使这个式子变为了比率。在这个计算过程中,一般会取ε为10-7,如果最后计算式子的值也为10-7或者更小,那么代表这两个向量是逼近的,dθ没有问题,反向传播可以正确实施。但是如果计算式子的值为10-5,可能这个值是对的,但是还是仔细检查每一项θi,避免误差过大,出现bug。如果计算式子的值为10-3,则极有可能存在bug,必须检查每一个θi项,看看是不是存在一个θi使得dθapprox[i]和dθ[i]相差的比较大,并追踪它的求导计算看有没有出错。总之,经过最终的调试,式子||dθapprox-dθ||2/(||dθapprox||2+||dθ||2)值应该是10-7或者更小,才是正确的。

总结:在实现一个神经网络的时候,会有前向传播和反向传播,经过这些过程后如果梯度检验的值比较大,那么就要不断的调试,知道梯度检验的值比较小,就可以认为神经网络的实施过程是正确的了。

14、关于梯度检验实现的注记

梯度检验的过程也是有实用技巧和注意事项的。

第一点,梯度下降的检验不能在训练神经网络的时候使用,只能是在调试神经网络的时候使用,因为计算每个θi的双边公差dθapprox是一个非常漫长的过程,并且还需要调用梯度下降计算dθ,最终还要验证两个向量是否邻近。并且调试完成之后要关闭梯度检验,因为它太慢了。

第二点,如果梯度检验失败,就需要检验每一项值θi,看是哪个θi导致dθapprox[i]与dθ[i]的值相差较大。比如发现有一个db[l]的值对应的θl导致dapproxθ与dθ的值相差较大,但是dW[l]周围都比较相近(注意θl与db[l]和dW[l]都是一一对应的),那么可能就是b[l]的求导过程出错。反过来,如果发现dθapprox[i]与dθ[i]的值相差较大,且全部来自dW,那么就有可能是W的求导过程出了错,但注意这里无法精准定为bug,但是可以帮我们定位bug的范围。

第三点就是在进行梯度检验的时候(调试神经网络的时候),如果训练神经网络时使用了L2正则化,那么代价函数的公式是 

,一定不要忘记了正则项,求dθ的时候也一定要记得L2正则项。

第四点不能同时使用Dropout正则化和梯度检验。因为Dropout在每次迭代的时候都会随机删除一些隐藏节点,那么就很难计算Dropout在梯度下降上的代价函数J。Dropout正则化可以优化代价函数J,但是J就被定义为所有指数极大节点子集求和,而且这些节点在每次迭代过程都可能被随机消除,因此要计算Dropout的代价函数还是比较困难的。因此是不建议Dropout正则化和梯度检验同时用,可以先用梯度检验对神经网络进行验证(设置keep.prop为1),检验完保证神经网络没有bug之后在开启Dropout正则化。

最后一点是现实情况中一般不会出现的一点,就是在随机初始化的过程中采用梯度检验,然后在训练网络,最后在执行梯度检验。因为随机化初始化的参数必须比较小才能保证后面的结果正确,如果一开始就比较大,就不能保证最后的结果正确,并且在迭代的过程中,参数的值会越变越大,如果初始化参数的值就比较大的化,就更不能保证结果,那么为了使初始化的参数的值接近于0,就在随机初始化的过程中就使用梯度检验。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐