改进深度神经网络:超参数调整,正则化和优化
Week 1
第一课:训练/开发/测试集
本周我们将学习如何在实际应用中让你的神经网络高效工作?
这些方法包括超参数调整、数据准备,再到如何确保你的优化算法运行的足够快以及使得你的学习算法能在合理的时间内完成学习任务。
机器学习问题+正则化
确保你能正确实现你的神经网络的小技巧。
在你考虑如何设置你的训练集/开发集/测试集时,如何能够做出一个好的选择将会帮助你快速的建立一个高性能的神经网络。
层数?
隐藏单元数?
学习速率?
激活函数?
因此在实际应用中,机器学习是一个高度迭代的过程。你只需要将你的想法转换为代码,然后运行你的代码并尝试实现它,在你运行并且实验之后,你会得到一个结果。这个结果会告诉你这个特定的网络或者说这个特定的配置执行的好不好。然后根据结果,改进想法改变选择。也许你也会为了尝试找出一个越来越好的神经网络而不端迭代。
超参数最好的选择可能取决于你所拥有的数据量,输入数据的特征个数,也取决于你的计算机配置(GPU或者CPU以及配置)。
想一次就猜中最优参数是不可能的,所以目前机器学习的应用是相当反复的迭代的过程。
所以有一件事能决定你能多快的取得进展,那就是你进行迭代时的效率。而恰当的将你的数据集分为训练集、开发集(交叉验证集)和测试集能让你的迭代效率更高。
整个工作流程是首先你不停地用训练集来训练你的算法,然后用你的开发集或者说交叉验证集来测试许多不同的模型里哪一个在开发集上效果最好,当这个过程进行的时间够长之后,你可能想评估一下你最终的训练结果,你可以用测试集对结果中最好的模型进行评估。这样以使得评估算法性能时不引入偏差。
数年之前,数据比较小,广范认为的最佳比例是60% 20% 20%
大数据时代,趋势变化了,假如你有1000000个数据,交叉测试集和测试集就变小了。因为开发集存在的意义是用来测试不同的算法并确定哪种最好,所以开发集只要大到能够用来评估两种不同的算法,或是十种不同的算法时快速选出较好的一种,达到这个目标可能不需要多达20%的数据。
测试集的主要功能是对训练好的分类器的性能给出可信度较高的评估,不需要20%就足够评估单个分类器的性能。
1百万 1万 1万 98% 1% 1%
95.5% 0.25% 0.25%
...
当前深度学习中还有一个趋势是:
有越来越多的人的训练集与测试集的数据分布不匹配。
网页上分析的猫的图片都是高清的,但是用户上传的分辨率各方面都比较随意,这就可能造成两种不同的分布,在这种情况下我的建议就是:
确保开发集和测试集中的数据分布相同。
因为你需要用开发集对许多不同的模型进行评估,费尽权利改善模型在开发集上的性能,如果开发集和测试集的数据分布相同就很方便,但是因为深度学习算法对训练数据量需求巨大,我能看到的一种趋势是用各种有创意的办法,比如爬去网页来获得比其他途径大得多的训练集,即使这会带来一些代价,也就是训练集的数据分布与开发集和测试集的数据分布不同。但你只需要遵守这个经验法则,你的算法进步速度就会更快。
最后,即使没有测试集也许也是可以的,回想一下,测试集的目的是给你一个无偏估计来评价你最终所选取的网络的性能。但是如果你不需要无偏估计的话,没有测试集也许也没有问题。所以当你只有开发集而没有测试集的时候,你所做的就是用训练集尝试不同的模型结构,然后用开发集去评估它们,根据结果进一步迭代,并尝试得到一个好的模型,因为你的模型拟合了开发集中的数据,所以开发集不能给你无偏的估计,但是如果你不需要无偏估计的话也许完全无妨。
如果没有训练集,大多数人会把开发集成为测试集,即把开发集当做测试集,但是在测试集上会发生过拟合现象,所以如果一个团队告诉你说只有训练集和测试集,我会小心,向他们是否其实只有训练集和开发集,因为它们的模型实在测试集上过拟合,但是也是可以接受的。
建立好训练、开发和测试集,你会迭代的更快。而且你还能更高效地测量算法存在的偏差和方差,然后就能更高效的选用适当的方法来改进算法。
第二课:偏差/差异
偏差(偏离度)和方差(集中度)处理?
在深度学习领域,另一个现象是有关偏差-方差困境(偏差-方差权衡)的讨论很少。深度学习也讨论方差,也讨论偏差,但对困境讨论的比较少。
低维度问题:逻辑回归:
高维度问题:尝试去理解偏差和方差的含义:
模型在某种程度上对于交叉验证集泛化性不够好(高方差)
通过观察训练集误差和开发集误差,你将能判断出你的模型算法是否有高方差问题。
并未将训练集数据处理的很好,欠拟合(高偏差)
相比之下,这个算法应用在开发集时还处于一个可接受的水准
人工识别的误差默认是0%(贝叶斯误差)
如果理想误差或贝叶斯误差比较高,那么高偏差可能就需要重新判断了。
如果没有好的分类器存在,如何来分析偏差和方差呢?假设你的图像中真的很模糊,哪怕是真人或者任何系统都不能够进行分类,那么贝叶斯误差非常高,会有一些不同的手段来改变分析方法?
总之,通过观察训练集的误差,至少可以知道你的算法是否可以很好的拟合训练数据,然后总结出是否属于高偏差问题。然后通过观察同一个算法,在开发集上的误差,判断算法是否属于高方差问题。上述结果都基于贝叶斯误差非常低,并且你的训练集和开发集都来自于同一个分布,如果不满足这个假设,你就需要做更复杂的分析。
高偏差:你需要的可能是一个曲线函数或者二次函数
高方差
总而言之,您已经看到了如何通过查看算法在训练集上的错误以及您可以尝试诊断的开发集上的算法错误,是否存在高偏差或高差异的问题,或者两者兼而有之,或者可能都不是。
如何根据是否存在高偏差或高差异问题更系统地尝试改进算法?
第三课:机器学习的基本原则
当训练好了最初的神经网络时,我会首先问,这个算法是否有高偏差?
根据在训练集上的表现。
果它确实有很高的偏差,那么你可以尝试:
选择一个新的网络(更多隐藏层)
延长训练时间
让梯度下降法运行更长时间
换用一些更高级的优化算法
或者(可能无效)找到一种更加适合当前问题的神经网络结构
尝试这些算法,直到偏差问题消除。
如果贝叶斯偏差比较小(人类可以完成),那只要训练一个足够大的网络,就应当能够在训练集上取得良好表现。
当把偏差减小到可以接受的范围之后,就再问:这个算法是否有高方差?
看模型在开发集上表现(泛化能力)。
如果存在高方差问题,解决高方差问题的:
最好方法就是取得更多数据
如果不能取得更多数据,尝试着正则化(减少过拟合)
或者(可能无效)找到一种更加适合当前问题的神经网络结构
但是如何找到一种更加适合当前问题的神经网络结构?
不太容易总结出完全系统性的规律。
直到找到一种低偏差、低方差的网络。
这里有几点需要注意:
1.首先,根据你是否有高偏见或高差异,你应该尝试的事情可能会有很大不同。所以我通常会使用训练开发集来尝试诊断您是否存在偏差或方差问题,然后使用结果来选择要尝试的方法。
2.偏差和方差取舍问题。
因为对于很多你能尝试的方法,你只能在增大偏差的同时减小方差,或减小偏差的同时增大方差。机器学习时代,我们没有的太多那种能够单独减小偏差或单独减小方差,而不顾此失彼的工具。
但在当前这个深度学习和大数据的时代,只要你能不断扩大所训练的网络的规模,只要你能不断地获得更多的数据,那扩大网络总是能够减小偏差而不增大方差。恰当的正则化总是能够减小方差而不增大偏差。
因此我们就具备了单独减少方/偏差而不过多影响另一指标的能力。这也就解释为啥深度学习在监督学习中如此有用?
以及为何在深度学习中,偏差和方差的权衡要不明显的多?
训练神经网络太大的主要代价只是计算时间(采取正则化的前提下)。
因此,我希望这能让您了解如何组织机器学习问题以诊断偏差和差异的基本结构,然后尝试选择正确的操作来帮助您在问题上取得进展。
正则化,是一种非常有用的减少方差的技术。 使用正则化时,存在一点偏差方差权衡。它可能会稍微增加偏差,但如果你有足够大的网络,往往不会太多。深入细节?
第四课:正则化
如果你怀疑神经网络过度拟合了你的数据,表示有一个很大的方差问题,你应该尝试的第一件事就是正规化。
解决高差方差的另一种方法是获得更多可靠的训练数据。但是你不能总是获得更多的训练数据,或者获得更多数据可能代价很大。但添加正规化通常有助于防止过度拟合或减少网络中的误差。那么让我们看看正规化是如何运作的:
以逻辑回归为例:
参数矢量w的欧几里得范数的平方,这成为L2正则化,因为这里使用的是参数矢量w的欧几里得范数,也称为L2范数。
为什么只对参数w进行正则化呢?为什么不把b的相关项加进去?
b有关项省略掉了,因为w往往是一个非常高维的参数矢量,尤其是发生在高方差问题的情况下。
L2正则化是最常见的正则化方法,此外还有L1正则化。即不使用L2范数,而是使用lambada/m乘以这一项的和(参数矢量的L1范数)。
如果你使用L1正则化,w最终会变得稀疏,这意味着w矢量中有很多0(有助于压缩模型,只需较少的内存来存储模型)。然而我在实践中发现,通过L1正则化让模型变得稀疏,带来的收效甚微。
这里的λ称为正则化参数。通常使用开发集验证来配置这个参数,通过尝试一系列的值,找到最好的那个。即在训练集上得到较好的效果和保持参数L2范数较小以避免过拟合之间取舍。
在Python中使用lambd表示lambda。
在神经网络中呢:
W矩阵的范数,称为矩阵的弗罗贝尼乌斯范数,使用角标F标记。习惯上不把它称为L2范数,而是称为弗罗贝尼乌斯范数,它表示矩阵中元素的平方和。
如何实现梯度下降呢?
dw[l]后需要加上一个正则化等式。
L2正则化有时也被称为权重衰减(它就像普通的梯度下降,更新w为w减去α乘以从反向传播得到的原梯度)。
为什么正则化能防止过拟合?
第五课:为什么正规化会减少过度拟合?
为什么正则化能防止过拟合?
为什么它有助于减少方差问题?
为什么通过压缩L2范式或者弗罗贝尼乌斯范数或者参数项就能减轻过拟合情况呢?
直观理解:如果你把λ设置的很大,群众矩阵W就会被设置为非常接近于0的值,把很多隐藏单元的比重设置为接近0,这些隐藏单元的影响就被消除了。就会将复杂神经网络转换为类似逻辑回归单元的浅神经网络。就会把过拟合网络带到更加接近左边高偏差的状态。但是λ存在一个中间值,能够得到一个更加接近中间这个刚刚好的状态。
另一个例子:
对于tanh函数,如果λ被设置的很大的话,那么激活函数的参数实际上会变小,即w[l]变得很小,此时忽略b的影响,那么z也会非常小,如果z值也相对比较小的话,g(z)函数就会接近于线性函数,每一层都很小(类似于线性网络),就会接近逻辑回归。
因此,即使是一个非常深的神经网络,但如果使用线性激活函数,最终也只能计算线性的函数,就不能拟合那些很复杂的决策函数,因此就不大容易出现过拟合的情况了。
小建议:
在程序中增加正则项的时候,对于代价函数,我们加入额外的惩罚项来防止权重过大,如果你使用梯度下降,调试程序的一个步骤就是画出代价函数J关于梯度下降的迭代次数的图像。可以看到的是每次迭代后代价函数J都会单调递减。
如果你实现了正则化的部分,更新J的公式,否则J不会再每次迭代后都单调递减。
另一个正则化技巧dropout正则化?
第六课:dropout正则化
除了L2正则化之外,另一种非常强大的正则化技术称为“dropout随机失活正则化”(丢弃法 dropout)。让我们看看它是如何工作的?
使用随机失活技术,我们要遍历神经网络的每一层,并且为丢弃(drop)网络中的某个节点设置一个概率值。即对于神经网络中的每一层,我们将对每一个结点做一次公平投币,使这个节点有50%的几率被保留,50%的几率被丢弃,抛完这些硬币,我们会决定消除哪些节点,然后清除那些节点上所有正在进行的计算,得到一个小的多的被简化了很多的网络。
然后再做反向传播训练,这是一个被简化的神经网络的例子。
疯狂?它们只是按照随机的编码决定这些节点的去留?
但确实有效,因为对于每一个训练样例你都在训练一个小得多的网络,这样或许能让你理解为什么你能正则化整个网络。
随机失活正则化有好几种方法,最常用的个是反向随机失活(inverted dropout):
d3表示层3的失活向量。keep-prob是一个数值,可以赋值为0.5,,0.8...(这是给定隐藏单元将被保留的概率值)
选择0.8,意味着这个隐藏单元有0.2的几率被丢弃,任意一个训练样例及隐藏单元的组合,其对应的d3中的元素都有0.8的几率取值为1,0.2的几率取值为0。如果你有50个样本做矢量化的运算,这意味着平均去来有10个单元失活或者被清零。a[3]也会减少20%,为了不减少z[4],我们需要除以0.8,因为真呢过提供你所需要的大约20%的校正值。
这就是所谓的反向随机失活技术。它的作用在于你可以将keep.prob设为任意值,0.8,0.9或者1,同时反向随机失活技术通过除以keep.prob确保a3的期望值不变。
而且你会发现在测试阶段,也就是你要评估一个网络时,他简化了神经网路的测试部分,因为他减少了可能引入的缩放问题?(下一集)
但目前为止随机失活正则化最普遍的实现,据我所致就是反向随机失活。
我们使用矢量d,而且你会注意到不同的训练样例的训练,实际上对不同的个隐藏单元实施了清零,实际上,如果用同一个训练集进行迭代,在不同的训练轮次中,你应该随机地将不同的隐藏单元清零,因此这并不意味着同一个训练样例的训练应该保证一致丢弃相同的隐藏单元。
即矢量d或者说层3对应的d3来决定哪些被清零,无论是在正向传播还是在反向传播中。
测试阶段的算法:
我们要做的是不在测试阶段使用随机失活算法。因为测试阶段在做测试的时候,你并不想让你的输出也是随机的。在测试阶段也是用随机失活算法,只会为预测增加噪声。
理论上来说可以做的一件事就是用不同的随机失活的神经网络进行多次预测并平均值。所以也不用在测试过程中加入额外的缩放参数。
随机失活的原理?
第七课:了解随机失活的原理
随机失活这种从网络中随机敲除神经元的做法看起来有些疯狂,但为什么它表现的很好呢?
之前的解释:dropout会让神经元随机失活,好像每一次迭代都会在一个更小的神经网络中计算,而使用更小的神经网络就像具有了正则化的效果。
另一个解释:一个神经单元的作用是利用它的输入单元生成一个有意义的输出,而如果使用dropout,这些输入会被随机的丢弃,这就意味着这个神经单元不能依赖于任何一个特征,因为每个都可能被随机丢弃,或者说它的每一个输入都可能随机失活。所以在特定的时候,就不愿把所有的赌注只放在这一个输入神经元上,因为任何一个输入都可能失活,所以我们也不愿把太多的权重放在某一个上,因此这个神经元将会更积极地使用这种方式对于每个输入都给一个比较小的权重。而泛化这些权值将有利于压缩这些权重的平方泛数(平方和)。与使用L2正则化相同,使用dropout有助于收缩权值以及防止过拟合,只是针对不同的情况,L2正则可以有少许变化,所以适用面更广。
使用dropout额外需要注意的细节:
我们必须要确定的一个参数是留存率(keep prob),他表示一层中一个神经元不失活的概率,因此,可以对每一层设定不同的留存率,对于不同的层(权值矩阵大小不同),可以选用不同的留存率。
在实践中通常不会对输入特征进行随机失活,即使用,也是一个接近1的数。
总结一下:如果你觉得某一层比其它层更容易发生过拟合,就可以给这一层设置更低的留存率,这样的缺点是在交叉验证(网格)搜索时,会有更多的超参数(运行更费时),另一个选择就是对一些层使用dropout(留存率相同),而另一些不使用,就只有一个超参数了。
最早使用dropout技术的多是在计算机视觉领域,在这个领域中,它的输入层向量温度非常大,因为要包含每个像素点的值,几乎不可能有足够的数据,因此dropout在计算机领域使用频繁,有些研究人员总是使用它。但需要记住dropout是一种正则化技术,目的是防止过拟合,所以除了我的算法已经过拟合了,否则不会考虑使用dropout,相对计算机视觉领域,它在其他领域的使用会比较少。
dropout的另一个缺点是让代价函数J变得不那么明确,因为每一次迭代,都有一些神经元随机失活,所以当你去检验梯度下降算法表现的时候,你会发现很难确定代价函数是否已经定义的足够好。因此就不能用绘图的方法来调试错误了。所以通常这个时候,我会关闭dropout,把留存率设为1,然后再运行代码并确保代价函数J是单调递减的。最后再打开dropout并期待使用dropput的时候没有引入别的错误。
第八课:其他正则化方法
除了L2正则化和dropout正则化,还有一些其他方法来减少神经网络的过拟合?
当出现过拟合现象,可以增大数据量,但是增大数据量有时代价很大,所以你可以水平翻转、扭曲...把训练集的数据量翻倍。
你的训练集现在有些冗杂,所以相比之下,不如另外收集独立的数据,但是这可以让你减少拍更多猫照片的成本。
再次强调,这些额外的伪训练样本,能增加的信息量不如独立数据多,但是这么做几乎不需要任何开销。(廉价的方法为你的算法获得更多的数据)
因此可以算为正则化,而且减少了过拟合。(相当于告诉你的算法,即使你旋转扭曲...它还是一只猫)
所以数据集扩增也可以作为一种正则化技术。
还有另外一种常用的方法:早终止法
你要做的是在运行梯度下降时,画一张训练误差的图,可以用训练集的0-1分类误差,或者说把代价函数J画出来,他应该要单调递减(随着训练次数的增加,代价函数J的值下降)。同时也画出开发集误差的曲线(一样的,可以使开发集的分类误差或者代价函数)。
通常开发集误差会先下将一段,然后接着开始增大,所以早终止法要做的就是在那次迭代附近(神经网络表现最好的地方),把神经网络的训练过程停住,并且选取这个(最小)开发集误差所对应的值。
为什么这个方法有用?
当你开始在神经网络上迭代时,通过随机初始化,很可能你给w的初始值是一个较小的值,在训练足够长的时间前,w依然很小,随着继续迭代,训练w越来越大,而早终止法通过停在半路,使你得到一个不大不小的w值。这和L2正则化有点像,通过选一个参数w范数较小的神经网络,理想状况下就能少点过拟合。而early stopping这个词就是指你会提前终止神经网络的训练。我有时候会用,但是它有个缺点:
机器学习步骤:
一.你需要一个算法,能够最优化代价函数J
工具:梯度下降
Momentum算法
RMSProp算法
Adam算法等
然而即便优化了代价函数J,你还是希望不要过拟合
工具:正则化
获取更多数据等
现在机器学习中已经激增了很多的超参数,在诸多的算法选择已经相当复杂了,但我认为机器学习可以变得简单,如果你有一套工具来优化代价函数J,而当你专注于优化代价函数J时,你在乎的是找到合适的w和b,使得J(w,b)尽可能地小,只要减少它就可以。
然后为了避免过拟合,或者说减小方差,就是另一项完全不同的任务,当你做这件事的时候,又有一套完全不同的工具来实现。
以上就是:正交化!即同一时间只考虑一个任务。
对我而言,early stopping的主要缺点是:它把两个任务结合了,所以你无法分开解决这两个问题,因为提早停止了梯度下降,意味着打断了优化代价函数J的过程。因为现在在降低代价函数J这件事上,你做的就不够好了,同时你又想做的是避免过拟合,所以你没有用不同的工具,你用一个工具解决两个问题,这就意味着你要做的十考虑起来更复杂了???
没懂。
如果不用early stopping可以替代的选择是L2正则化,那么你可以尽可能久的训练神经网络,这样可以让超参数的搜索空间更容易分解,也因此更容易搜索。但这么做的缺点是你不得不尝试大量的正则化参数λ的值,这使得搜索这么多λ值的计算代价很高。而early stopping的优势是只要运行一次梯度下降过程,你需要尝试小w值,中等w值,大w值,而不用尝试L2正则化中的一大堆λ值。
我个人偏爱L2正则化,并尝试不同的λ值,这预设你的计算能力是足够的,但早终止法确实能够实现相近的效果,而不用一个一个尝试不同的λ值,所以很多人使用它。
优化问题的配置方法?如何加速训练过程?