目录
- 第一门课:第二门课 改善深层神经网络:超参数调试、正 则 化 以 及 优 化 (Improving Deep Neural Networks:Hyperparameter tuning, Regularization and Optimization)
- 第一周:深度学习的 实践层面 (Practical aspects of Deep Learning)
- 1.6 dropout 正则化(Dropout Regularization)
- 1.7 理解 dropout(Understanding Dropout)
- 1.8 其他正则化方法(Other regularization methods)
第一门课:第二门课 改善深层神经网络:超参数调试、正 则 化 以 及 优 化 (Improving Deep Neural Networks:Hyperparameter tuning, Regularization and Optimization)
第一周:深度学习的 实践层面 (Practical aspects of Deep Learning)
1.6 dropout 正则化(Dropout Regularization)
除了𝐿2正则化,还有一个非常实用的正则化方法——“Dropout(随机失活)”,我们来看看它的工作原理。
假设你在训练上图这样的神经网络,它存在过拟合,这就是 dropout 所要处理的,我们复制这个神经网络,dropout 会遍历网络的每一层,并设置消除神经网络中节点的概率。假设网络中的每一层,每个节点都以抛硬币的方式设置概率,每个节点得以保留和消除的概率都是 0.5,设置完节点概率,我们会消除一些节点,然后删除掉从该节点进出的连线,最后得到一个节点更少,规模更小的网络,然后用 backprop 方法进行训练。
这是网络节点精简后的一个样本,对于其它样本,我们照旧以抛硬币的方式设置概率,保留一类节点集合,删除其它类型的节点集合。对于每个训练样本,我们都将采用一个精简后神经网络来训练它,这种方法似乎有点怪,单纯遍历节点,编码也是随机的,可它真的有效。不过可想而知,我们针对每个训练样本训练规模极小的网络,最后你可能会认识到为什么要正则化网络,因为我们在训练极小的网络。
如何实施 dropout 呢?方法有几种,接下来我要讲的是最常用的方法,即 inverted dropout(反向随机失活),出于完整性考虑,我们用一个三层(𝑙 = 3)网络来举例说明。编码中会有很多涉及到 3 的地方。我只举例说明如何在某一层中实施 dropout。首先要定义向量𝑑,𝑑[3]表示一个三层的 dropout 向量:
d3 = np.random.rand(a3.shape[0],a3.shape[1])
然后看它是否小于某数,我们称之为 keep-prob,keep-prob 是一个具体数字,上个示例中它是 0.5,而本例中它是 0.8,它表示保留某个隐藏单元的概率,此处 keep-prob 等于 0.8,它意味着消除任意一个隐藏单元的概率是 0.2,它的作用就是生成随机矩阵,如果对 a [ 3 ] a^{[3]} a[3]进行
因子分解,效果也是一样的。 d [ 3 ] d^{[3]} d[3]是一个矩阵,每个样本和每个隐藏单元,其中 d [ 3 ] d^{[3]} d[3]中的对应值为 1 的概率都是 0.8,对应为 0 的概率是 0.2,随机数字小于 0.8。它等于 1 的概率是 0.8,等于 0 的概率是 0.2。
接下来要做的就是从第三层中获取激活函数,这里我们叫它 a [ 3 ] a^{[3]} a[3], a [ 3 ] a^{[3]} a[3]含有要计算的激活函数, a [ 3 ] a^{[3]} a[3]等于上面的 a [ 3 ] a^{[3]} a[3]乘以 d [ 3 ] d^{[3]} d[3],a3 =np.multiply(a3,d3),这里是元素相乘,也可写为𝑎3 ∗= 𝑑3,它的作用就是让 d [ 3 ] d^{[3]} d[3]中所有等于 0 的元素(输出),而各个元素等于 0 的概率只有 20%,乘法运算最终把 d [ 3 ] d^{[3]} d[3]中相应元素输出,即让 d [ 3 ] d^{[3]} d[3]中 0 元素与 a [ 3 ] a^{[3]} a[3]中相对元素归零。
如果用 python 实现该算法的话, d [ 3 ] d^{[3]} d[3]则是一个布尔型数组,值为 true 和 false,而不是1 和 0,乘法运算依然有效,python 会把 true 和 false 翻译为 1 和 0,大家可以用 python 尝试一下。最后,我们向外扩展𝑎[3],用它除以 0.8,或者除以 keep-prob 参数。
𝑎3/= 𝑘𝑒𝑒𝑝 − 𝑝𝑟𝑜𝑏
下面我解释一下为什么要这么做,为方便起见,我们假设第三隐藏层上有 50 个单元或50 个神经元,在一维上 a [ 3 ] a^{[3]} a[3]是 50,我们通过因子分解将它拆分成50 × 𝑚维的,保留和删除它们的概率分别为 80%和 20%,这意味着最后被删除或归零的单元平均有 10(50×20%=10)个,现在我们看下 z [ 4 ] z^{[4]} z[4], z [ 4 ] = w [ 4 ] a [ 3 ] + b [ 4 ] z^{[4]} = w^{[4]}a^{[3]} + b^{[4]} z[4]=w[4]a[3]+b[4],我们的预期是,𝑎[3]减少 20%,也就是说 a [ 3 ] a^{[3]} a[3]中有 20%的元素被归零,为了不影响 z [ 4 ] z^{[4]} z[4]的期望值,我们需要用 w [ 4 ] a [ 3 ] / 0.8 w^{[4]}a^{[3]}/0.8 w[4]a[3]/0.8,它将会修正或弥补我们所需的那 20%, a [ 3 ] a^{[3]} a[3]的期望值不会变,划线部分就是所谓的 dropout 方法。
它的功能是,不论 keep-prop 的值是多少 0.8,0.9 甚至是 1,如果 keep-prop 设置为 1,那么就不存在 dropout,因为它会保留所有节点。反向随机失活(inverted dropout)方法通过除以 keep-prob,确保 a [ 3 ] a^{[3]} a[3]的期望值不变。
事实证明,在测试阶段,当我们评估一个神经网络时,也就是用绿线框标注的反向随机失活方法,使测试阶段变得更容易,因为它的数据扩展问题变少,我们将在下节课讨论。
据我了解,目前实施 dropout 最常用的方法就是 Inverted dropout,建议大家动手实践一下。Dropout 早期的迭代版本都没有除以 keep-prob,所以在测试阶段,平均值会变得越来越复杂,不过那些版本已经不再使用了。
现在你使用的是𝑑向量,你会发现,不同的训练样本,清除不同的隐藏单元也不同。实际上,如果你通过相同训练集多次传递数据,每次训练数据的梯度不同,则随机对不同隐藏单元归零,有时却并非如此。比如,需要将相同隐藏单元归零,第一次迭代梯度下降时,把一些隐藏单元归零,第二次迭代梯度下降时,也就是第二次遍历训练集时,对不同类型的隐藏层单元归零。向量d或 d [ 3 ] d^{[3]} d[3]用来决定第三层中哪些单元归零,无论用 foreprop 还是 backprop,这里我们只介绍了 foreprob。
如何在测试阶段训练算法,在测试阶段,我们已经给出了𝑥,或是想预测的变量,用的是标准计数法。我用 a [ 0 ] a^{[0]} a[0],第 0 层的激活函数标注为测试样本𝑥,我们在测试阶段不使用dropout 函数,尤其是像下列情况:
z [ 1 ] = w [ 1 ] a [ 0 ] + b [ 1 ] z^{[1]} = w^{[1]}a^{[0]} + b^{[1]} z[1]=w[1]a[0]+b[1]
a [ 1 ] = g [ 1 ] ( z [ 1 ] ) a^{[1]} = g^{[1]}(z^{[1]}) a[1]=g[1](z[1])
z [ 2 ] = w [ 2 ] a [ 1 ] + b [ 2 ] z^{[2]} = w^{[2]}a^{[1]} + b^{[2]} z[2]=w[2]a[1]+b[2]
a [ 2 ] = ⋯ a^{[2]} = ⋯ a[2]=⋯
以此类推直到最后一层,预测值为 y ^ \hat{y} y^。
显然在测试阶段,我们并未使用 dropout,自然也就不用抛硬币来决定失活概率,以及要消除哪些隐藏单元了,因为在测试阶段进行预测时,我们不期望输出结果是随机的,如果测试阶段应用 dropout 函数,预测会受到干扰。理论上,你只需要多次运行预测处理过程,每一次,不同的隐藏单元会被随机归零,预测处理遍历它们,但计算效率低,得出的结果也几乎相同,与这个不同程序产生的结果极为相似。
Inverted dropout 函数在除以 keep-prob 时可以记住上一步的操作,目的是确保即使在测试阶段不执行 dropout 来调整数值范围,激活函数的预期结果也不会发生变化,所以没必要在测试阶段额外添加尺度参数,这与训练阶段不同。
l = k e e p − p r o b l = keep − prob l=keep−prob
这就是 dropout,大家可以通过本周的编程练习来执行这个函数,亲身实践一下。
为什么 dropout 会起作用呢?下节课我们将更加直观地了解 dropout 的具体功能。
1.7 理解 dropout(Understanding Dropout)
Dropout 可以随机删除网络中的神经单元,他为什么可以通过正则化发挥如此大的作用呢?
直观上理解:不要依赖于任何一个特征,因为该单元的输入可能随时被清除,因此该单元通过这种方式传播下去,并为单元的四个输入增加一点权重,通过传播所有权重,dropout将产生收缩权重的平方范数的效果,和之前讲的L2正则化类似;实施 dropout 的结果实它会压缩权重,并完成一些预防过拟合的外层正则化;L2对不同权重的衰减是不同的,它取决于激活函数倍增的大小。
总结一下,dropout 的功能类似于L2正则化,与L2正则化不同的是应用方式不同会带来一点点小变化,甚至更适用于不同的输入范围。
第二个直观认识是,我们从单个神经元入手,如图,这个单元的工作就是输入并生成一些有意义的输出。通过 dropout,该单元的输入几乎被消除,有时这两个单元会被删除,有时会删除其它单元,就是说,我用紫色圈起来的这个单元,它不能依靠任何特征,因为特征都有可能被随机清除,或者说该单元的输入也都可能被随机清除。我不愿意把所有赌注都放在一个节点上,不愿意给任何一个输入加上太多权重,因为它可能会被删除,因此该单元将通过这种方式积极地传播开,并为单元的四个输入增加一点权重,通过传播所有权重,dropout 将产生收缩权重的平方范数的效果,和我们之前讲过的𝐿2正则化类似,实施 dropout的结果是它会压缩权重,并完成一些预防过拟合的外层正则化。
事实证明,dropout 被正式地作为一种正则化的替代形式,𝐿2对不同权重的衰减是不同的,它取决于倍增的激活函数的大小。
总结一下,dropout 的功能类似于𝐿2正则化,与𝐿2正则化不同的是,被应用的方式不同,dropout 也会有所不同,甚至更适用于不同的输入范围。
实施 dropout 的另一个细节是,这是一个拥有三个输入特征的网络,其中一个要选择的参数是 keep-prob,它代表每一层上保留单元的概率。所以不同层的 keep-prob 也可以变化。第一层,矩阵 W [ 1 ] W^{[1]} W[1]是 7×3,第二个权重矩阵 W [ 2 ] W^{[2]} W[2]是 7×7,第三个权重矩阵 W [ 3 ] W^{[3]} W[3]是 3×7,以此类推, W [ 2 ] W^{[2]} W[2]是最大的权重矩阵,因为 W [ 2 ] W^{[2]} W[2]拥有最大参数集,即 7×7,为了预防矩阵的过拟合,对于这一层,我认为这是第二层,它的 keep-prob 值应该相对较低,假设是 0.5。对于其它层,过拟合的程度可能没那么严重,它们的 keep-prob 值可能高一些,可能是 0.7,这里是0.7。如果在某一层,我们不必担心其过拟合的问题,那么 keep-prob 可以为 1,为了表达清除,我用紫色线笔把它们圈出来,每层 keep-prob 的值可能不同。
注意 keep-prob 的值是 1,意味着保留所有单元,并且不在这一层使用 dropout,对于有可能出现过拟合,且含有诸多参数的层,我们可以把 keep-prob 设置成比较小的值,以便应用更强大的 dropout,有点像在处理𝐿2正则化的正则化参数𝜆,我们尝试对某些层施行更多正则化,从技术上讲,我们也可以对输入层应用 dropout,我们有机会删除一个或多个输入特征,虽然现实中我们通常不这么做,keep-prob 的值为 1,是非常常用的输入值,也可以用更大的值,或许是 0.9。但是消除一半的输入特征是不太可能的,如果我们遵守这个准则,keep-prob 会接近于 1,即使你对输入层应用 dropout。
总结一下,如果你担心某些层比其它层更容易发生过拟合,可以把某些层的 keep-prob值设置得比其它层更低,缺点是为了使用交叉验证,你要搜索更多的超级参数,另一种方案是在一些层上应用 dropout,而有些层不用 dropout,应用 dropout 的层只含有一个超级参数,就是 keep-prob。
结束前分享两个实施过程中的技巧,实施 dropout,在计算机视觉领域有很多成功的第一次。计算视觉中的输入量非常大,输入太多像素,以至于没有足够的数据,所以 dropout在计算机视觉中应用得比较频繁,有些计算机视觉研究人员非常喜欢用它,几乎成了默认的选择,但要牢记一点,dropout 是一种正则化方法,它有助于预防过拟合,因此除非算法过拟合,不然我是不会使用 dropout 的,所以它在其它领域应用得比较少,主要存在于计算机视觉领域,因为我们通常没有足够的数据,所以一直存在过拟合,这就是有些计算机视觉研究人员如此钟情于 dropout 函数的原因。直观上我认为不能概括其它学科。
dropout 一大缺点就是代价函数𝐽不再被明确定义,每次迭代,都会随机移除一些节点,如果再三检查梯度下降的性能,实际上是很难进行复查的。定义明确的代价函数𝐽每次迭代后都会下降,因为我们所优化的代价函数 J 实际上并没有明确定义,或者说在某种程度上很难计算,所以我们失去了调试工具来绘制这样的图片。我通常会关闭 dropout 函数,将 keepprob 的值设为 1,运行代码,确保𝐽函数单调递减。然后打开 dropout 函数,希望在 dropout过程中,代码并未引入 bug。我觉得你也可以尝试其它方法,虽然我们并没有关于这些方法性能的数据统计,但你可以把它们与 dropout 方法一起使用。
1.8 其他正则化方法(Other regularization methods)
除了𝐿2正则化和随机失活(dropout)正则化,还有几种方法可以减少神经网络中的过拟合:
一.数据扩增
假设你正在拟合猫咪图片分类器,如果你想通过扩增训练数据来解决过拟合,但扩增数据代价高,而且有时候我们无法扩增数据,但我们可以通过添加这类图片来增加训练集。例如,水平翻转图片,并把它添加到训练集。所以现在训练集中有原图,还有翻转后的这张图片,所以通过水平翻转图片,训练集则可以增大一倍,因为训练集有冗余,这虽然不如我们额外收集一组新图片那么好,但这样做节省了获取更多猫咪图片的花费。
除了水平翻转图片,你也可以随意裁剪图片,这张图是把原图旋转并随意放大后裁剪的,仍能辨别出图片中的猫咪。
通过随意翻转和裁剪图片,我们可以增大数据集,额外生成假训练数据。和全新的,独立的猫咪图片数据相比,这些额外的假的数据无法包含像全新数据那么多的信息,但我们这么做基本没有花费,代价几乎为零,除了一些对抗性代价。以这种方式扩增算法数据,进而正则化数据集,减少过拟合比较廉价。
像这样人工合成数据的话,我们要通过算法验证,图片中的猫经过水平翻转之后依然是猫。大家注意,我并没有垂直翻转,因为我们不想上下颠倒图片,也可以随机选取放大后的部分图片,猫可能还在上面。
对于光学字符识别,我们还可以通过添加数字,随意旋转或扭曲数字来扩增数据,把这些数字添加到训练集,它们仍然是数字。为了方便说明,我对字符做了强变形处理,所以数字 4 看起来是波形的,其实不用对数字 4 做这么夸张的扭曲,只要轻微的变形就好,我做成这样是为了让大家看的更清楚。实际操作的时候,我们通常对字符做更轻微的变形处理。因为这几个 4 看起来有点扭曲。所以,数据扩增可作为正则化方法使用,实际功能上也与正则化相似。
二.early stopping
还有另外一种常用的方法叫作 early stopping,运行梯度下降时,我们可以绘制训练误差,或只绘制代价函数𝐽的优化过程,在训练集上用 0-1 记录分类误差次数。呈单调下降趋势,如图。
因为在训练过程中,我们希望训练误差,代价函数𝐽都在下降,通过 early stopping,我们不但可以绘制上面这些内容,还可以绘制验证集误差,它可以是验证集上的分类误差,或验证集上的代价函数,逻辑损失和对数损失等,你会发现,验证集误差通常会先呈下降趋势,然后在某个节点处开始上升,early stopping 的作用是,你会说,神经网络已经在这个迭代过程中表现得很好了,我们在此停止训练吧,得到验证集误差,它是怎么发挥作用的?
当你还未在神经网络上运行太多迭代过程的时候,参数𝑤接近 0,因为随机初始化𝑤值时,它的值可能都是较小的随机值,所以在你长期训练神经网络之前𝑤依然很小,在迭代过程和训练过程中𝑤的值会变得越来越大,比如在这儿,神经网络中参数𝑤的值已经非常大了,所以 early stopping 要做就是在中间点停止迭代过程,我们得到一个𝑤值中等大小的弗罗贝尼乌斯范数,与𝐿2正则化相似,选择参数𝑤范数较小的神经网络,但愿你的神经网络过度拟合不严重。
术语 early stopping 代表提早停止训练神经网络,训练神经网络时,我有时会用到 early stopping,但是它也有一个缺点,我们来了解一下。
我认为机器学习过程包括几个步骤,其中一步是选择一个算法来优化代价函数𝐽,我们有很多种工具来解决这个问题,如梯度下降,后面我会介绍其它算法,例如 Momentum,RMSprop 和 Adam 等等,但是优化代价函数𝐽之后,我也不想发生过拟合,也有一些工具可以解决该问题,比如正则化,扩增数据等等。
在机器学习中,超级参数激增,选出可行的算法也变得越来越复杂。我发现,如果我们用一组工具优化代价函数𝐽,机器学习就会变得更简单,在重点优化代价函数𝐽时,你只需要留意𝑤和𝑏,𝐽(𝑤, 𝑏)的值越小越好,你只需要想办法减小这个值,其它的不用关注。然后,预防过拟合还有其他任务,换句话说就是减少方差,这一步我们用另外一套工具来实现,这个原理有时被称为“正交化”。思路就是在一个时间做一个任务,后面课上我会具体介绍正交化,如果你还不了解这个概念,不用担心。
但对我来说 early stopping 的主要缺点就是你不能独立地处理这两个问题,因为提早停止梯度下降,也就是停止了优化代价函数𝐽,因为现在你不再尝试降低代价函数𝐽,所以代价函数𝐽的值可能不够小,同时你又希望不出现过拟合,你没有采取不同的方式来解决这两个问题,而是用一种方法同时解决两个问题,这样做的结果是我要考虑的东西变得更复杂。
如果不用 early stopping,另一种方法就是𝐿2正则化,训练神经网络的时间就可能很长。我发现,这导致超级参数搜索空间更容易分解,也更容易搜索,但是缺点在于,你必须尝试很多正则化参数𝜆的值,这也导致搜索大量𝜆值的计算代价太高。
Early stopping 的优点是,只运行一次梯度下降,你可以找出𝑤的较小值,中间值和较大值,而无需尝试𝐿2正则化超级参数𝜆的很多值。
如果你还不能完全理解这个概念,没关系,下节课我们会详细讲解正交化,这样会更好理解。
虽然𝐿2正则化有缺点,可还是有很多人愿意用它。吴恩达老师个人更倾向于使用𝐿2正则化,尝试许多不同的𝜆值,假设你可以负担大量计算的代价。而使用 early stopping 也能得到相似结果,还不用尝试这么多𝜆值。
这节课我们讲了如何使用数据扩增,以及如何使用 early stopping 降低神经网络中的方差或预防过拟合。