吴恩达CS230深度学习笔记(二)

2020-02-08

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

深度学习的实践层面

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

偏差、方差:

  • 低偏差,低方差:这是训练的理想模型,数据离散程度小,基本在合理范围内;
  • 低偏差,高方差:这是深度学习面临的最大问题,过拟合了。也就是模型太贴合训练数据了,导致其泛化(或通用)能力差,若遇到测试集,则准确度下降的厉害;
  • 高偏差,低方差:这往往是训练的初始阶段;
  • 高偏差,高方差:这是训练最糟糕的情况,准确度差,数据的离散程度也差。

减少偏差的方法,比如训练更大的神经网络,或者跑久一点梯度下降。减少方差的方法,正则化。

正则化

深度学习可能存在过拟合问题——高方差,有两个解决方法,一个是正则化,另一个是准备更多的数据,这是非常可靠的方法,但你可能无法时时刻刻准备足够多的训练数据或者获取更多数据的成本很高,但正则化通常有助于避免过拟合或减少你的网络误差。

$\frac{\lambda}{2m}​$乘以$w​$范数的平方,其中 $\left| w \right|*2^2​$ 是$w​$的欧几里德范数的平方,等于$w*{j}​$($j​$ 值从1到$n_{x}​$)平方的和,也可表示为$w^{T}w​$,也就是向量参数$w​$ 的欧几里德范数(2范数)的平方,此方法称为$L2​$正则化,因为这里用了欧几里德范数,被称为向量参数$w​$的$L2​$范数。

为什么只正则化参数$w$?为什么不再加上参数 $b$ 呢?你可以这么做,只是我习惯省略不写,因为$w$通常是一个高维参数矢量,已经可以表达高偏差问题,$w$可能包含有很多参数,我们不可能拟合所有参数,而$b$只是单个数字,所以$w$几乎涵盖所有参数,而不是$b$,如果加了参数$b$,其实也没太大影响,因为$b$只是众多参数中的一个,所以我通常省略不计,如果你想加上这个参数,完全没问题。

$L2$正则化是最常见的正则化类型,你们可能听说过$L1$正则化,$L1$正则化,加的不是$L2$范数,而是正则项$\frac{\lambda}{m}$乘以$\sum_{j= 1}^{n_{x}}{|w|}$,$\sum_{j =1}^{n_{x}}{|w|}$也被称为参数$w$向量的$L1$范数,无论分母是$m$还是$2m$,它都是一个比例常量。

如果用的是$L1$正则化,$w$最终会是稀疏的,也就是说$w$向量中有很多0,有人说这样有利于压缩模型,因为集合中参数均为0,存储模型所占用的内存更少。实际上,虽然$L1$正则化使模型变得稀疏,却没有降低太多存储内存。人们在训练网络时,越来越倾向于使用$L2$正则化。

如何在神经网络中实现$L2$正则化呢?


神经网络含有一个成本函数,该函数包含$W^{[1]}$,$b^{[1]}$到$W^{[l]}$,$b^{[l]}$所有参数,字母$L$是神经网络所含的层数,因此成本函数等于$m$个训练样本损失函数的总和乘以$\frac{1}{m}$,正则项为$\frac{\lambda }{2m}{{\sum\nolimits_{1}^{L}{| {{W}^{[l]}}|}}^{2}}$,我们称${||W^{\left[l\right]}||}^{2}$为范数平方,这个矩阵范数${||W^{\left[l\right]}||}^{2}$(即平方范数),被定义为矩阵中所有元素的平方求和(矩阵F范数)。

从而偏导数变成了下面这个表达式:

该正则项说明,不论$W^{[l]}$是什么,我们都试图让它变得更小,实际上,相当于我们给矩阵W乘以$(1 - \alpha\frac{\lambda}{m})$倍的权重,矩阵$W$减去$\alpha\frac{\lambda}{m}$倍的它,也就是用这个系数$(1-\alpha\frac{\lambda}{m})$乘以矩阵$W$,该系数小于1,因此$L2$范数正则化也被称为“权重衰减”。

直观理解就是$\lambda$增加到足够大,$W$会接近于0,实际上是不会发生这种情况的,我们尝试消除或至少减少许多隐藏单元的影响,最终这个网络会变得更简单,这个神经网络越来越接近逻辑回归,我们直觉上认为大量隐藏单元被完全消除了,其实不然,实际上是该神经网络的所有隐藏单元依然存在,但是它们的影响变得更小了。拿tanh(z)函数举例,如果正则化参数变得很大,参数$W$很小,$z$也会相对变小,此时忽略$b$的影响,$z$会相对变小,实际上,$z$的取值范围很小,这个激活函数,也就是曲线函数$tanh$会相对呈线性,整个神经网络会计算离线性函数近的值,这个线性函数非常简单,并不是一个极复杂的高度非线性函数,不会发生过拟合。

drop正则化

假设你在训练下图这样的神经网络,它存在过拟合,这就是dropout所要处理的,我们复制这个神经网络,dropout会遍历网络的每一层,并设置消除神经网络中节点的概率。假设网络中的每一层,每个节点都以抛硬币的方式设置概率,每个节点得以保留和消除的概率都是0.5,设置完节点概率,我们会消除一些节点,然后删除掉从该节点进出的连线,最后得到一个节点更少,规模更小的网络,然后用backprop方法进行训练。这种方法似乎有点怪,单纯遍历节点,编码也是随机的,可它真的有效。

如何实施dropout呢?方法有几种,接下来我要讲的是最常用的方法,即inverted dropout(反向随机失活),出于完整性考虑,我们用一个三层($l=3$)网络来举例说明。编码中会有很多涉及到3的地方。我只举例说明如何在某一层中实施dropout

定义向量$d$,$d^{[3]}$表示网络第三层的dropout向量。然后看它是否小于某数,我们称之为keep-prob,它表示保留某个隐藏单元的概率。然后用激活函数a3乘以d3,a3 =np.multiply(a3,d3),这里是元素相乘,也可写为$a3=d3$,相当于a3中乘以0的部分清零。然后再除以*keep-prob(这样做的目的是为了确保a3的期望值不变)。

据我了解,目前实施dropout最常用的方法就是Inverted dropout,建议大家动手实践一下。Dropout早期的迭代版本都没有除以keep-prob,所以在测试阶段,平均值会变得越来越复杂,不过那些版本已经不再使用了。Inverted dropout函数在除以keep-prob时可以记住上一步的操作,目的是确保即使在测试阶段不执行dropout来调整数值范围,激活函数的预期结果也不会发生变化,所以没必要在测试阶段额外添加尺度参数,这与训练阶段不同。(也就是说dropout用在训练阶段,不用在测试阶段)

Dropout可以随机删除网络中的神经单元,他为什么可以通过正则化发挥如此大的作用呢?

直观上理解:不要依赖于任何一个特征,因为该单元的输入可能随时被清除。我不愿意把所有赌注都放在一个节点上。因此该单元通过这种方式传播下去,并为单元的四个输入增加一点权重,通过传播所有权重,dropout将产生收缩权重的平方范数的效果,和之前讲的$L2$正则化类似;实施dropout的结果实它会压缩权重,并完成一些预防过拟合的外层正则化;$L2$对不同权重的衰减是不同的,它取决于激活函数倍增的大小。因此,dropout的功能类似于$L2$正则化,与$L2$正则化不同的是应用方式不同会带来一点点小变化,甚至更适用于不同的输入范围。

如果你担心某些层比其它层更容易发生过拟合,可以把某些层的keep-prob值设置得比其它层更低,缺点是为了使用交叉验证,你要搜索更多的超级参数,另一种方案是在一些层上应用dropout,而有些层不用dropout,应用dropout的层只含有一个超级参数,就是keep-prob。要牢记一点,dropout是一种正则化方法,它有助于预防过拟合,因此除非算法过拟合,不然我是不会使用dropout的。

dropout一大缺点就是代价函数$J$不再被明确定义,每次迭代,都会随机移除一些节点,如果再三检查梯度下降的性能,实际上是很难进行复查的。定义明确的代价函数$J$每次迭代后都会下降,因为我们所优化的代价函数$J$实际上并没有明确定义,或者说在某种程度上很难计算,所以我们失去了调试工具来绘制这样的图片。我通常会关闭dropout函数,将keep-prob的值设为1,运行代码,确保J函数单调递减。然后打开dropout函数,希望在dropout过程中,代码并未引入bug。我觉得你也可以尝试其它方法,虽然我们并没有关于这些方法性能的数据统计,但你可以把它们与dropout方法一起使用。

其他正则化方法

  • 数据扩增。对已有图片再加工。
  • early stoppingearly stopping的作用是,你会说,神经网络已经在这个迭代过程中表现得很好了,我们在此停止训练吧,要做就是在中间点停止迭代过程。early stopping的主要缺点就是你不能独立地处理代价函数尽可能小防止过拟合这两个问题,因为提早停止梯度下降,也就是停止了优化代价函数$J$,因为现在你不再尝试降低代价函数$J$,所以代价函数$J$的值可能不够小,同时你又希望不出现过拟合,你没有采取不同的方式来解决这两个问题,而是用一种方法同时解决两个问题,这样做的结果是我要考虑的东西变得更复杂。Early stopping的优点是,只运行一次梯度下降,你可以找出$w$的较小值,中间值和较大值,而无需尝试$L2$正则化超级参数$\lambda$的很多值。如果不用early stopping,另一种方法就是$L2$正则化,训练神经网络的时间就可能很长。

虽然$L2$正则化有缺点,可还是有很多人愿意用它。吴恩达老师个人更倾向于使用$L2$正则化,尝试许多不同的$\lambda$值,假设你可以负担大量计算的代价。而使用early stopping也能得到相似结果,还不用尝试这么多$\lambda$值。

归一化输入

训练神经网络,其中一个加速训练的方法就是归一化输入。假设一个训练集有两个特征,输入特征为2维,归一化需要两个步骤:

  1. 零均值

  2. 归一化方差;

    我们希望无论是训练集和测试集都是通过相同的$μ$和$σ^2$定义的数据转换,这两个是由训练集得出来的。

第一步是零均值化,$\mu = \frac{1}{m}\sum_{i =1}^{m}x^{(i)}$,它是一个向量,$x$等于每个训练数据 $x$减去$\mu$,意思是移动训练集,直到它完成零均值化。

第二步是归一化方差,注意特征$x_{1}$的方差比特征$x_{2}$的方差要大得多,我们要做的是给$\sigma$赋值,$\sigma^{2}= \frac{1}{m}\sum_{i =1}^{m}{({x^{(i)})}^{2}}$,这是节点$y$ 的平方,$\sigma^{2}$是一个向量,它的每个特征都有方差,注意,我们已经完成零值均化,$({x^{(i)})}^{2}$元素$y^{2}$就是方差,我们把所有数据除以向量$\sigma^{2}$。

提示一下,如果你用它来调整训练数据,那么用相同的 $μ$ 和 $\sigma^{2}$来归一化测试集。尤其是,你不希望训练集和测试集的归一化有所不同,不论$μ$的值是什么,也不论$\sigma^{2}$的值是什么,这两个公式中都会用到它们。所以你要用同样的方法调整测试集,而不是在训练集和测试集上分别预估$μ$ 和 $\sigma^{2}$。

为什么要进行归一化呢?原因是因为非归一化特征的代价函数可能是非常狭长的,非常不均匀,假如$x_{1}$取值范围从1到1000,特征$x_{2}$的取值范围从0到1,结果是参数$w_{1}$和$w_{2}$值的范围或比率将会非常不同。然而如果你归一化特征,代价函数平均起来看更对称,如果你在上图这样的代价函数上运行梯度下降法,你必须使用一个非常小的学习率。因为如果是在这个位置,梯度下降法可能需要多次迭代过程,直到最后找到最小值。但如果函数是一个更圆的球形轮廓,那么不论从哪个位置开始,梯度下降法都能够更直接地找到最小值,你可以在梯度下降法中使用较大步长,而不需要像在左图中那样反复执行。归一化可以清洗数据分布

权重初始化

训练神经网络,尤其是深度神经所面临的一个问题就是梯度消失或梯度爆炸,也就是你训练神经网络的时候,导数或坡度有时会变得非常大,或者非常小,甚至于以指数方式变小,这加大了训练的难度。如果激活函数的输入特征被零均值和标准方差化,方差是1,$z$也会调整到相似范围,这就没解决问题(梯度消失和爆炸问题)。但它确实降低了梯度消失和爆炸问题,因为它给权重矩阵$w$设置了合理值,你也知道,它不能比1大很多,也不能比1小很多,所以梯度没有爆炸或消失过快。

梯度检验

首先,不要在训练中使用梯度检验,它只用于调试。我的意思是,计算所有$i$值的$d\theta_{\text{approx}}\left[i\right]$是一个非常漫长的计算过程。 第二点,如果算法的梯度检验失败,要检查所有项,检查每一项,并试着找出bug,也就是说,如果$d\theta_{\text{approx}}\left[i\right]$与dθ[i]的值相差很大,我们要做的就是查找不同的i值,看看是哪个导致$d\theta_{\text{approx}}\left[i\right]$与$d\theta\left[i\right]$的值相差这么多。可能帮你定位bug的位置,虽然未必能够帮你准确定位bug的位置,但它可以帮助你估测需要在哪些地方追踪bug。

第三点,在实施梯度检验时,如果使用正则化,请注意正则项。如果代价函数$J(\theta) = \frac{1}{m}\sum_{}^{}{L(\hat y^{(i)},y^{(i)})} + \frac{\lambda}{2m}\sum_{}^{}{||W^{[l]}||}^{2}$,这就是代价函数$J$的定义,$d\theta$等于与$\theta$相关的$J$函数的梯度,包括这个正则项,记住一定要包括这个正则项。

第四点,梯度检验不能与dropout同时使用,因为每次迭代过程中,dropout会随机消除隐藏层单元的不同子集,难以计算dropout在梯度下降上的代价函数$J$。因此dropout可作为优化代价函数$J$的一种方法,但是代价函数J被定义为对所有指数极大的节点子集求和。而在任何迭代过程中,这些节点都有可能被消除,所以很难计算代价函数$J$。

俺没用过梯度检验…这里只是看看罢了

优化算法

mini-batch梯度下降

使用batch梯度下降法,一次遍历训练集只能让你做一个梯度下降,使用mini-batch梯度下降法,一次遍历训练集,能让你做更多的梯度下降。当然正常来说你想要多次遍历训练集,还需要为另一个while循环设置另一个for循环。所以你可以一直处理遍历训练集,直到最后你能收敛到一个合适的精度。如果你有一个丢失的训练集,mini-batch梯度下降法比batch梯度下降法运行地更快,所以几乎每个研习深度学习的人在训练巨大的数据集时都会用到。

当mini-batch大小为1,就有了新的算法,叫做随机梯度下降法,每个样本都是独立的mini-batch,当你看第一个mini-batch,也就是$X^{{1}}$和$Y^{{1}}$,如果mini-batch大小为1,它就是你的第一个训练样本,这就是你的第一个训练样本。接着再看第二个mini-batch,也就是第二个训练样本,采取梯度下降步骤,然后是第三个训练样本,以此类推,一次只处理一个。随机梯度下降法的一大缺点是,你会失去所有向量化带给你的加速,因为一次性只处理了一个训练样本,这样效率过于低下,所以实践中最好选择不大不小的mini-batch尺寸,实际上学习率达到最快。

你会发现两个好处,一方面,你得到了大量向量化,比你一次性处理多个样本快得多。另一方面,你不需要等待整个训练集被处理完就可以开始进行后续工作。

如果训练集较小,直接使用batch梯度下降法,样本集较小就没必要使用mini-batch梯度下降法,你可以快速处理整个训练集,所以使用batch梯度下降法也很好,这里的少是说小于2000个样本,这样比较适合使用batch梯度下降法。不然,样本数目较大的话,一般的mini-batch大小为64到512,考虑到电脑内存设置和使用的方式,如果mini-batch大小是2的次方,代码会运行地快一些。

其他优化算法

动量梯度下降(Momentum

均方根传播(RMSprop

Adam优化算法,基本上就是将MomentumRMSprop结合在一起,被证明能有效适用于不同神经网络,适用于广泛的结构。

本算法中有很多超参数,超参数学习率$a$很重要,也经常需要调试,你可以尝试一系列值,然后看哪个有效。$\beta_{1}$常用的缺省值为0.9,这是dW的移动平均数,也就是$dW$的加权平均数,这是Momentum涉及的项。至于超参数$\beta_{2}$,Adam论文作者,也就是Adam算法的发明者,推荐使用0.999,这是在计算${(dW)}^{2}$以及${(db)}^{2}$的移动加权平均值,关于$\varepsilon$的选择其实没那么重要,Adam论文的作者建议$\varepsilon$为$10^{-8}$,但你并不需要设置它,因为它并不会影响算法表现。但是在使用Adam的时候,人们往往使用缺省值即可,$\beta_{1}$,$\beta_{2}$和$\varepsilon$都是如此,我觉得没人会去调整$\varepsilon$,然后尝试不同的$a$值,看看哪个效果最好。你也可以调整$\beta_{1}$和$\beta_{2}$,但我认识的业内人士很少这么干。

学习率衰减

加快学习算法的一个办法就是随时间慢慢减少学习率,我们将之称为学习率衰减。

要慢慢减少学习率$a$的话,在初期的时候,$a$学习率还较大,你的学习还是相对较快,但随着$a$变小,你的步伐也会变慢变小,所以最后你的曲线(绿色线)会在最小值附近的一小块区域里摆动,而不是在训练过程中,大幅度在最小值附近摆动。

一代要遍历一次数据,你可以拆分成不同的mini-batch,第一次遍历训练集叫做第一代。第二次就是第二代,依此类推,你可以将$a$学习率设为$a= \frac{1}{1 + decayrate * \text{epoch}\text{-num}}a_{0}$(decay-rate称为衰减率,epoch-num为代数,$\alpha_{0}$为初始学习率),注意这个衰减率是另一个你需要调整的超参数。还有其他的一些公式,比如,这个叫做指数衰减,其中$a$相当于一个小于1的值,如$a ={0.95}^{\text{epoch-num}} a_{0}$,所以你的学习率呈指数下降。 人们用到的其它公式有$a =\frac{k}{\sqrt{\text{epoch-num}}}a_{0}$或者$a =\frac{k}{\sqrt{t}}a_{0}$($t$为mini-batch的数字)。

有时人们也会用一个离散下降的学习率,也就是某个步骤有某个学习率,一会之后,学习率减少了一半,一会儿减少一半,一会儿又一半,这就是离散下降(discrete stair cease)的意思。

超参数调试、Batch正则化和程序框架

调参经验

结果证实一些超参数比其它的更为重要,我认为,最为广泛的学习应用是$a$,学习速率是需要调试的最重要的超参数。第二个是mini-batch的大小,以确保最优算法运行有效。我还会经常调试隐藏单元个数,我用橙色圈住的这些,这三个是我觉得其次比较重要的,相对而言。层数有时会产生很大的影响,学习率衰减也是如此。

希望你粗略了解到哪些超参数较为重要,无疑是最重要的,接下来是我用橙色圈住的那些,然后是我用紫色圈住的那些。

对于调参,在深度学习领域,我们常做的,我推荐你采用下面的做法,随机选择点,不是取每个值,而是取一些有特点的值。另一个惯例是采用由粗糙到精细的策略,先确定一个大致的范围,然后细致搜索。

如何调参,主要分两种方式:这是由你拥有的计算资源决定的。如果你拥有足够的计算机去平行试验许多模型,那绝对采用右边这种同时训练好几个模型的方式,尝试许多不同的超参数,看效果怎么样。但在一些应用领域,比如在线广告设置和计算机视觉应用领域,那里的数据太多了,你需要试验大量的模型,所以同时试验大量的模型是很困难的,所以这个时候你就只能逮住一个模型死调。

Batch Norm归一化

当训练一个模型,比如logistic回归时,你也许会记得,归一化输入特征可以加快学习过程。你计算了平均值,从训练集中减去平均值,计算了方差,接着根据方差归一化你的数据集,在之前的视频中我们看到,这是如何把学习问题的轮廓,从很长的东西,变成更圆的东西,更易于算法优化。所以这是有效的,对logistic回归和神经网络的归一化输入特征值而言。但是对于更深的网络,每层的输入特征不是原始特征了,而是一个个激活值 $a$,那该怎么办呢能不能归一呢?。实践中,经常做的是归一化$z^{[i]}$。Batch归一化的做法是将$z^{[i]}$值进行Batch归一化,简称BN,此过程将由${\beta}^{[i]}$和$\gamma^{[i]}$两参数控制,这一操作(计算均值和方差,减去均值,再除以方差)会给你一个新的规范化的$z^{[i]}$值(${\tilde{z}}^{[i]}$),然后将其输入激活函数中得到$a^{[i]}$,即$a^{[i]} = g^{[i]}({\tilde{z}}^{[ 1]})$。也就是把另一些参数加入到此新网络中${\beta}^{[1]}$,${\beta}^{[2]}$,$\gamma^{[1]}$,$\gamma^{[2]}$等等。

实践中,通常可以直接调用框架的封装函数来实现BN。batch归一化通常和训练集的mini-batch一起使用。

Batch归一化的作用是它适用的归一化过程,不只是输入层,甚至同样适用于神经网络中的深度隐藏层。你应用Batch归一化了一些隐藏单元值中的平均值和方差,不过训练输入和这些隐藏单元值的一个区别是,你也许不想隐藏单元值必须是平均值0和方差1。

也许另一个轻微非直观的效果是,BN可以带了一些轻微的正则化作用。如果你应用了较大的mini-batch,对,比如说,你用了512而不是64,通过应用较大的min-batch,你减少了噪音,因此减少了正则化效果,这是dropout的一个奇怪的性质,就是应用较大的mini-batch可以减少正则化效果。但是不要把Batch归一化当作正则化,把它当作将你归一化隐藏单元激活值并加速学习的方式,我认为正则化几乎是一个意想不到的副作用。另外,Batch归一化一次只能处理一个mini-batch数据,它在mini-batch上计算均值和方差。

BatchNorm就是在深度神经网络训练过程中使得每一层神经网络的输入保持相同分布;若对神经网络每一层做归一化,会使每一层输出为标准正太分布,会使神经网络完全学习不到特征。

在测试时咋整呢?你可能需要逐一处理样本,方法是根据你的训练集估算$\mu$和$\sigma^{2}$,估算的方式有很多种,理论上你可以在最终的网络中运行整个训练集来得到$\mu$和$\sigma^{2}$,但在实际操作中,我们通常运用指数加权平均来追踪在训练过程中你看到的$\mu$和$\sigma^{2}$的值。还可以用指数加权平均,有时也叫做流动平均来粗略估算$\mu$和$\sigma^{2}$,然后在测试中使用$\mu$和$\sigma^{2}$的值来进行你所需要的隐藏单元$z$值的调整。在实践中,不管你用什么方式估算$\mu$和$\sigma^{2}$,这套过程都是比较稳健的。而且如果你使用的是某种深度学习框架,通常会有默认的估算$\mu$和$\sigma^{2}$的方式,应该一样会起到比较好的效果。但在实践中,任何合理的估算你的隐藏单元$z$值的均值和方差的方式,在测试中应该都会有效。

【原文出自黄海广博士 deeplearning_ai_books

支付宝打赏 微信打赏

如果文章对你有帮助,欢迎点击上方按钮打赏作者,更多文章请访问想飞的小菜鸡