课程视频
第一周PPT汇总
吴恩达深度学习专项课程共分为五个部分,本篇博客将介绍第二部分改善深层神经网络专项的第一周课程:深度学习的实用层面。本周主要讲解如何配置训练/验证/测试集,如何分析方差和偏差,如何处理高偏差或高方差或高偏差和高方差共存的问题,以及如何在神经网络中应用不同形式的正则化,如L2正则化、dropout等,加快神经网络训练的技巧,最后介绍了梯度检验。
目录
1.训练/开发/测试集
2.偏差/方差
3.机器学习基础
4.正则化
5.为什么正则化可以减少过拟合?
6.Dropout正则化
7.理解Dropout
8.其他正则化方法
9.正则化输入
10.梯度消失和梯度爆炸
11.神经网络的权重初始化
12.梯度的数值逼近
13.梯度检验
14.关于梯度检验实现的标记
在配置训练、验证和测试数据集的过程中做出正确决策,会在很大程度上帮助我们创建高效的神经网络。
需要设置的超参数有很多,即使是深度学习专家也不可能一下子设置好这些超参数,得到一个最优的神经网络。所以,应用机器学习是一个迭代的过程:
首先,有一个初始的想法,如隐层数量,隐层单元数量的设置等;然后,编码实现这个初始想法,并通过实验验证它的效果,根据实验效果有针对性的改进想法。以此不断循环迭代,直到构建出一个性能优良的神经网络。
应用机器学习是一个循环的过程,而循环的效率是影响整个项目进展的关键因素。而创建高质量的训练集 、验证集和测试集有助于提高循环效率。
一般把数据集分为如下三部分:
训练集、验证集(交叉验证集)和测试集。训练集用于训练算法或模型;验证集用于模型选择,选择一个最优的模型(对应一组最优的超参数配置);最后用测试集无偏评估算法的运行状况。
在机器学习的小数据时代(100、1000、10000条),传统的数据集划分一般把70%的数据作为训练集,30%作为测试集或者60%训练集、20%验证集、20%测试集。
在大数据时代,数据集的规模一般比较大(百万级以上),传统的划分方式不再适用。验证集是用来进行模型选择,要求尽可能在短时间内,在若干模型中选出效果最好一两个模型,假设有100w条数据,此时不再把其中的20%作为验证集,而是取其中的1w条数据即可。测试集主要用于最后无偏评估模型的性能,和验证集一样,1w条数据就足够了。那么此时的划分就是把98%的数据作为训练集,1%验证,1%测试。
当然也有其他的划分方式,可能把99.5%数据作为训练集,验证集和测试集各占0.25%或验证集0.4%、测试集0.1%。
比如现有一个识别猫咪图片的应用程序,训练集采用的是从网页抓取的图片,测试集是用户通过应用程序上传的图片。显然这两部分图片来自不同的分布,一般来说网页上的图片分辨率高、制作精良;而用户上传的图片,可能是用手机随意拍摄的,质量不是很高。
实际上,在许多深度学习问题中,都会面临训练集和测试集数据分布不同的问题。因为训练需要大量的数据,此时一般在网页上抓取,而测试集一般是实际应用中的数据,如上例猫咪图片识别所示。
此时,需要遵循一条法则:确保验证集和测试集数据来自同一分布。因为要用验证集来评估不同的模型,尽可能的优化性能,遵循这条法则可以使机器学习算法变得很快(具体原因之后探讨)。
最后,没有测试集也是可以的,测试集主要提供对最后选定模型的无偏评估。如果不需要无偏评估,那么可以不需要设置测试集。
如果只有验证集,我们需要做的是,在训练集上训练,尝试不同的模型框架(不同的超参数),然后在验证集上评估这些模型,迭代并选出适用的模型。此时验证集已经涵盖了测试集,只是不再提供无偏性能估计。
如果在机器学习问题中,只有训练集和验证集,此时的验证集也会被叫做测试集。所以,当有人告诉你,只设置了训练集和测试集时,一定要确保验证集的存在(即,此时的测试集应该是验证集)。
偏差和方差是两个易学难精的两个概念,很多机器学习研究者都想很深刻的理解这两个概念。即使你知道了这两个概念,但在实际的应用中也会遇到各种各样的问题。
在深度学习中一般分别考虑方差和偏差,不会考虑2者的权衡问题。
如下所示是一个待分类数据集的可视化效果:
图1可能采用逻辑回归算法进行分类,决策边界是一条直线,非常简单,此时数据拟合不佳,称为高偏差或欠拟合;图3可能采用一个复杂模型如多隐层的神经网络,决策边界非常复杂,把异常样本也考虑在内,在训练集上分类非常精准,此时称为高方差或过拟合;图2的数据拟合程度介于图1和图3之间,比较适度,称为“just right”。
对于上图所示的包含两个特征的数据集,可以采用可视化的数据集和决策边界的方法,来判断偏差和方差;但对于高维数据,可视化非常困难,此时可以用其他指标来判断偏差和方差。
接下来还是以猫咪图片分类的例子进行说明:
对于这种高维数据,一般通过训练误差和验证误差这两个指标来判断偏差/方差:
假设训练集和验证集来自同一分布,且最优误差(人眼或其他高性能系统分类误差)非常小接近于0%(当不满足该前提假设时,以下的分析过程将更加复杂):
当训练误差和最优误差相差比较大时,则出现了高偏差(欠拟合);当训练误差和验证误差相差比较大时,则出现了高方差(过拟合)。
如上图中的紫色决策边界所示。之所以说它是高偏差和高方差,是因为整体来看该决策边界大部分是直线,数据拟合程度比较低;而从局部来看,该决策边界又有很高的灵活度,拟合了两个异常样本,数据拟合程度又比较高。可能用2维数据来说明有些勉强,但在高维数据中,确实会出现有些区域高偏差,有些区域高方差的情况,整体来说,我们称其既是高偏差又是高方差。
首先通过模型在训练集上的表现(训练误差与最优误差的差距大小)来判断是否出现了高偏差。若是可以采用更“大”的神经网络(增加隐层数和隐层单元数)、训练更长时间以及采用不同的网络结构(不一定有效)来解决高偏差的问题(这是最基本的要求);
若没有出现高偏差问题,则进一步通过模型在验证集上的表现(训练误差和验证误差的差距大小)来判断是否出现了高方差。若是增加更多的数据(如果可能)、采用正则化的方法以及采用不同的网络结构(不一定有效)来解决高方差问题。
然后进行循环迭代,再依次判断高偏差/方差问题,直到得到一个低偏差和低方差的模型。
注意:
1)高偏差和高方差是两个独立的问题,对应的解决方案不一样。一般通过模型在训练集和验证集上的表现判断是否出现高偏差或高方差问题,然后根据结果选择尝试部分方法。比如,模型如果出现了高偏差问题,那么增加更多数据其实没什么用。我们要明确模型到底出现了什么问题,高偏差/高方差/还是都有,从而选择一个最有效的解决方法。
2)在机器学习初期,关于方差和偏差权衡问题的讨论屡见不鲜,原因是你可以采取很多方法来增加偏差降低方差或降低偏差增加方差,所以要考虑2者的权衡;在深度学习初期,有很少工具可以做到只减少偏差或方差而不影响另外一个,但在现在的深度学习和大数据时代,只要正则化适度,增大网络结构可以在不影响方差的情况下降低偏差;获取更多数据可以在不影响偏差的情况下降低方差。我们现在有很多工具可以做到在减少偏差或方差的同时不对另外一方产生过多的不良影响。这是深度学习不需要考虑偏差/方差权衡的重要原因,也是其对监督学习问题大有裨益的重要原因。
正则化有助于帮助模型避免/缓解过拟合问题。
w是一个高维向量,b是一个实数,一般只对w(权重)进行正则化,因为其涵盖了绝大多数参数,当然也可以加上参数b,不过一般不加。
是正则化系数,使用Python编程时,避免和保留字冲突,一般使用lambd表示。
上式中采用的是L2正则化,这也是使用最广泛的,还有一种是L1正则化:
如果使用L1正则化,最终得到的w向量将会比较稀疏(向量中0比较多)。有人说这样有利于模型压缩,模型参数中0比较多可以减少存储内存,但实际上使用L1正则化会使模型参数比较稀疏,但并没有起到减少存储内存的效果。在实际应用中,越来越倾向于使用L2正则化。
与逻辑回归类似,也是在原始代价函数的基础上加上L2正则化项,不过我们称为弗洛贝尼乌斯范数不再是L2范数,它等于权重矩阵中每个元素的平方和:
代价函数:
依旧是只惩罚权重参数,不惩罚偏置参数。因为已经涵盖了绝大多数参数。
此时,反向传播计算的梯度将包含原始反向传播梯度(不包含正则化,记做B1)和正则化梯度两部分:
L2正则化也被叫做权重衰减:
加入L2正则化项后,在进行梯度下降时,相当于把先乘以,再减去之前不加正则化时反向传播计算的梯度。会使权重参数相对变得更小,所以叫做权重衰减。
带L2正则化的代价函数:
下图是之前高偏差/方差的可视化效果:
假设现有一个非常复杂的神经网络结构(如下所示),它会产生如上图3所示的复杂决策边界(过拟合):
当正则化系数非常大时,要使代价函数最小,那么,此时各隐层的隐藏单元将具有高度的对称性(计算的值都是一样的),相当于只有一个隐藏单元起作用,如上图红色部分所示,神经网络的结构就会大大简化,虽然深度还在,但会无限接近于逻辑回归算法。可能会产生上图1中的高偏差现象(实际上,这种消除隐藏单元影响的情况是不会出现的,隐藏单元还会在,只不过影响较小了,简化网络结构)。
而当非常小时,如时,此时相当于不做正则化,神经网络结构非常复杂,那么就会产生上图3中的高方差现象。
那么,此时就会存在一个适中的取值,使得分类效果恰好为上图2中的“just right”。
所以直观上讲,使用L2正则化,可以简化网络结构,降低高方差(过拟合)。如果设置一个合适的将会达到一个理想的分类效果。
假设每个隐藏使用tanh作为激活函数:
当增大正则化系数时,模型参数会变小(因为要使得代价函数变小),那么也会相对变小:
通过tanh的图像,会发现当输入比较小时,tanh的图像呈现线性。此时,相当于每个隐层都使用线性激活函数,之前曾讨论过,如果使用线性激活函数,那么再多的隐层也是没有意义的,相当于一个线性函数的效果。此时将不会出现复杂的决策边界,所以正则化会简化我们的模型,减少过拟合,如果值适中的话,就会达到“just right”的状态。
如果代价函数中包含正则化项,绘制代价关于梯度下降迭代次数的曲线,会发现代价在每次迭代后都会减小,呈现单调递减的趋势;如果不包含的话,可能不会在每次迭代后都减小:
假设左图中的复杂神经网络结构存在过拟合,dropout正则化做的工作是,在一次前向传播中,通过遍历神经网络中的每个节点,并且每个节点通过“掷硬币”的方式(设置一定的概率,上图中是0.5)决定去留,去掉的节点连同它的所有连线(参数)都从原结构中“消失”,以此来简化网络结构,反向传播时也只更新简化后结构中的参数。每次前向/反向传播失活的单元都是随机的,每次都面临不同的网络结构,不同样本面临的网络结构也是不同的。
下面举例说明如何在某一层运用dropout:
对于单个输入样本:
dl = np.random.rand(al.shape) < keep_prob
al = al*d1 #对应位置元素相乘
al /= keep_prob
dl是一个向量,与al同维,即大小是第l层的单元数。dl相当于一个开关,把它的元素随机初始为0-1之间的浮点数,设置一个保留概率keep_prob,若keep_prob=0.8,就是说dl中有20%的元素为0.
然后将第l层的激活输出al与dl对应位置元素相乘,同样会使al中20%的元素为0,相当于使20%的单元失活,简化网络结构,无论在前向还是反向传播中只考虑保留下的节点以及与其连接的参数。
为了保证第l层的激活输出al的期望(平均值)不发生太大变化,al要除以keep_prob.比如第l层有50个单元,keep_prob=0.8,则平均有10个神经元失活,也就是说al要减少20%的元素(20%的元素为0),此时为保证al的期望大体不变,弥补那20%的损失,所以让al除以keep_prob.早期版本的dropout没有除以keep_prob,导致测试阶段平均值的计算非常复杂。
对于m个输入样本:
Dl = np.random.rand(Al.shape) < keep_prob
Al = Al*D1 #对应位置元素相乘
Al /= keep_prob
Dl是一个矩阵,与Al同维,即大小是第l层的单元数乘以样本数m。Dl相当于一个开关,把它的元素随机初始为0-1之间的浮点数,设置一个保留概率keep_prob,若keep_prob=0.8,就是说Dl中有20%的元素为0.
然后将第l层的激活输出Al与Dl对应位置元素相乘,同样会使Al中20%的元素为0,相当于使20%的单元失活,简化网络结构,无论在前向还是反向传播中只考虑保留下的节点以及与其连接的参数。对于m个样本来说,每个样本随机失活的单元不尽相同,面临的网络结构是不同的。
为了保证第l层的激活输出Al的期望(平均值)不发生太大变化,Al要除以keep_prob.比如第l层有50*m个单元,keep_prob=0.8,则平均有10*m个神经元失活,也就是说Al要减少20%的元素(20%的元素为0),此时为保证Al的期望大体不变,弥补那20%的损失,所以让Al除以keep_prob.早期版本的dropout没有除以keep_prob,导致测试阶段平均值的计算非常复杂。
测试阶段不使用dropout(keep_prob=1),在训练阶段,al /= keep_prob保证了在测试阶段不必执行dropout来调整数值范围,激活函数输出期望大体不变,没必要在测试阶段额外添加尺度参数:
对于一个单神经元的网络结构:
实施dropout后,它会使某些输入单元失活,那么此时对于上图中紫色的单元来说,它不能完全依靠其中的任何一个特征,因为每个特征都有可能失活,所以不会把某一个输入单元的权重设置的很大,相反而是把权重值分散,每个权重都比较小。
dropout的功能与L2正则化类似,简化网络结构,使每个参数不至于过大,都比较小,防止过拟合。
对于一个复杂的神经网络结构:
1)如果使用dropout,需要设置一个超参数keep_prob
2) 一般输入层不使用dropout,即keep_prob=1,如果在输入层使用dropout,keep_prob应该接近于1,如0.90
3)每一层的keep_prob可以不相同。一种做法是,如果觉得某一层更容易出现过拟合,比如参数比较多,可以把该层的keep_prob设置的小一些,其他层大一些,不过这样在交叉验证时,搜索超参数会更复杂;也可以在某些层使用dropout,且这些层的keep_prob相同,某些层不使用。
dropout在计算机视觉中使用非常频繁,几乎成了必选,因为没有足够的数据,模型又比较复杂,容易出现过拟合。
dropout是一种正则化方法,用于解决过拟合,如果算法没有出现过拟合,不要使用。
dropout的缺点,每次迭代都会随机使一些单元失活,导致我们所优化的代价函数J没有明确的定义,所以不能应用梯度检验或代价函数随迭代次数的变化曲线进行调试。一般,先关闭dropout(keep_prob=1),再进行梯度检验或绘制曲线进行调试,如果梯度检验没有问题或代价函数J随迭代次数单调递减,再打开dropout,期望dropout的过程不会引入bug。
可以通过增加训练数据的方式,解决过拟合问题。不过额外增加训练数据有时候成本会很高,也很耗时,可以通过数据增强的方式来增加训练数据。
比如,你在做猫咪图片分类的任务,可以对原始训练集中的猫咪图片采取如下操作:
1)对猫咪图片进行水平翻转
2)对猫咪图片进行任意裁剪,如旋转某个角度,放大后再裁减。
当然还有很多类似的人工增加数据的操作,虽然这样可能会使训练集出现冗余,不如额外收集的猫咪图片好;但是这样做几乎没有任何代价,且容易操作,扩展数据集的速度快,比额外收集数据成本低得多。
但要注意对于人工增加的图片要进行检验,确保这些图片仍然是猫咪。
对于光学字符识别,可以对训练集中的字符进行适当的旋转扭曲,并把处理过的字符添加到训练集中,以此来扩展数据集:
数据增强可以作为正则化方法来使用,实际上功能上也与正则化相似。
Early stopping:提前终止训练神经网络
在使用梯度下降法时,绘制出训练误差(模型在训练集上的代价函数值)和验证误差(模型在验证集上的代价函数值)随迭代次数的变化曲线。通常会发现验证误差先呈现下降趋势,然后在某个节点后上升。Early stopping就是在验证集误差开始变大的节点处提前停止训练。
原理:
当梯度下降刚开始迭代时,参数W接近于0,因为随机初始化参数W的值很小;在经过多次迭代和训练后,参数W会越来越大。所以Early stopping要做的就是在中间点停止迭代过程,选择一个F范数较小的参数W,与L2正则化类似选择参数W范数较小的神经网络,减少过拟合。
缺点:
机器学习问题一般包括两个步骤:一是利用优化算法(梯度下降或高级优化算法)最小化代价函数J;二是减少模型的过拟合,可以采用正则化、数据增强等方法。但随着机器学习超参数的激增,选出可行的算法越来越困难。但是,如果用一组工具来优化代价函数,此时只考虑参数W、b,使得代价函数越小越好,其他不关注;使用另一组工具来减少过拟合,会使问题变得简单。这个也被称作正交化原理,在一个时间做一个任务。
Early stopping的主要缺点使你不能独立考虑这两个问题,因为提早终止梯度下降,也就停止了优化代价函数J,这就造成代价函数的值不够小,同时又希望不要出现过拟合。所以你没有采取不同的方式来解决两个问题,而是用一种方法同时解决两个问题。这样会使要考虑的东西变得复杂。
如果不使用Early stopping,可以采用L2正则化。训练神经网络的时间就可能更长,这会使超参数空间更容易分解和搜索,缺点是必须尝试大量的取值,导致搜索大量值的计算代价太高。而Early stopping的优点是仅运行一次梯度下降过程,就可以找出W的较小值中间值和较大值,不用像L2正则化那样尝试许多值。
个人更倾向于L2正则化,当然要负担搜索大量值的计算代价;Early stopping也可以获得类似效果,不必搜索值。
正则化输入就是对输入进行归一化,可以加快神经网络的训练速度。
假设我们的训练集有两个输入特征,即输入特征向量x是2维的。
归一化输入包括两步:
1)零均值化:对特征向量每一维求均值,每一维减去各自均值。
如上图所示,x1和x2的均值都为0,不过x1的方差明显比x2大。
2)归一化方差:对特征向量每一维求方差,和是同维向量。再把所有数据都除以。
最后处理完如上图所示,x1和x2的均值为0,方差为1.
注意:如果对训练集进行规范化,测试集也要进行同样的规范化操作。对测试集进行规范化时,使用的均值和方差为规范化训练集时计算出来的。
代价函数:
假设w,b都是实数,如果不规范化输入,输入特征向量x各个维度的取值范围可能相差很大,可视化代价函数会如下图所示:
轮廓图:
如果不规范化输入,代价函数的轮廓图会比较狭长。如果使用梯度下降法优化代价函数,需要一个较小的学习率,经过多次迭代才能达到最小值。
如果规范化输入,代价函数图像会比较对称,此时会得到一个更圆的球形轮廓:
使用梯度下降法优化代价函数时,无论从哪个位置开始,都能直接找到最小值,可以在梯度下降法中设置一个较大的步长,不用像之前那样反复迭代。
实际上w是一个高维向量,用上述2维图来直观理解可能不太准确。但是规范化输入,使输入特征的取值范围相近,会使代价函数优化起来更简单快速。
如果各个输入特征的取值范围相差很大,归一化输入非常重要;如果各个输入特征本身取值范围就比较接近,归一化输入也没什么危害。总之,需要对输入进行归一化,加快学习算法的训练速度。
当你在训练深层网络时,梯度有时会非常大或非常小甚至以指数方式减小,这加大了训练神经网络的难度。可以通过更明智的随机初始化参数,来减缓这个问题。
假设你正在训练一个很深的神经网络,如下所示,假设每个隐层有两个单元(当然也可以有多个单元):
为了简单起见,假设每个隐层使用线性激活函数,且不考虑偏置参数:
那么前向传播最终得到的输出等于:
忽略最后一个维度不同的权重矩阵,初始化其他权重矩阵比单位矩阵I(对角矩阵)大一些:
如果层数L非常大,那么此时神经网络的输出将呈指数级增长(忽略):
相反地,如果初始化其他权重矩阵比单位矩阵I小一些:
如果层数L非常大,那么此时神经网络的输出将呈指数级减小(忽略):
如果输入特征x1=x2=1,那么经过每层的激活函数输出后会变成1/2,1/4,1/8...,,呈现指数减小.
之前论述了前向传播中激活函数输出将呈现指数增加或减小,对于反向传播中的梯度来说一样,也会呈现指数增加或减小,从而导致训练非常困难。尤其是梯度和L相差指数级,梯度下降法的步长会非常小,梯度下降法将花费很长时间来学习。
对于梯度爆炸/消失问题有一个不完整的解决方案,合理的初始化权重参数矩阵,使其既不比单位矩阵I过大也不比单位矩阵I过小,从而减缓二者的产生。
对于逻辑回归来说:
如果输入特征数量n很大,为了使z不过大也不过小,一种做法是使变小。
逻辑回归可以看作是一个简单的神经网络,对于复杂神经网络结构也是如此,需要合理的初始化权重参数矩阵。
1)如果使用ReLU激活函数,权重参数矩阵一般如下初始化:
首先初始化为的矩阵,矩阵中的值服从标准高斯分布,然后再乘以(为输入单元数量)。
2)如果使用tanh激活函数,权重矩阵初始化一般最后乘以(为输入单元数量)
对梯度进行数值逼近,把梯度近似值和实际计算的梯度进行比较,从而检查反向传播计算的梯度是否正确。
图像:
单边公差就是把在处的导数近似为,图中绿色三角形的高宽比,即:
假设:
误差:
双边公差就是把在处的导数近似为,图中大绿色三角形的高宽比,即:
假设:
误差:
由上可见,双边公差比单边公差的误差小,而且二者的计算代价几乎一样,所以实际进行梯度检验时使用的是双边公差作为梯度的近似值。
导数的数学定义,就是趋于无穷小时的单边或双边公差:
1)把神经网络的所有参数拉伸为一个大的向量
2)把神经网络的所有参数的梯度拉伸为一个大的向量
3)用双边公差计算参数的近似梯度,得到一个与同维的向量:
4)比较和:
1) 不要在训练中使用梯度检验,它只用于调试。计算参数的近似梯度非常慢,用反向传播计算参数的梯度比较快,所以训练过程中不要使用梯度检验来计算参数的梯度,它只用来调试。
2)如果梯度检查失败,要查看和中的每一项来定位bug,比较和.看看哪几项导致二者的差距很大,然后这几项来自哪一层的参数()计算的梯度存在bug。
3)执行梯度检验时,如果使用了正则化,不要忘记正则化项
4)不要同时使用dropout和梯度检验。dropout会使一些节点随机失活,此时代价函数J没有明确的定义。一般先关闭dropout(keep_prob=1),然后进行梯度检验,无误后再打开dropout。
5)在随机初始化参数后,运行一次梯度检验;然后在神经网络训练一段时间后(经过很多次迭代后),再运行一次梯度检验。因为在参数w,b接近于0时(刚随机初始化后),梯度下降的实施是正确的,即反向传播的实施是正确的;运行梯度下降一段时间内,参数w,b会增大,反向传播计算的结果可能越来越不准确;反复训练迭代后,参数w,b可能又会减小,反向传播的实施可能又比较准确了。