深度学习DeepLearning.ai系列课程学习总结:9.深度学习基础实践理论

来源:互联网 发布:苏州迈科网络 编辑:程序博客网 时间:2024/06/04 20:03

转载过程中,图片丢失,代码显示错乱。

为了更好的学习内容,请访问原创版本:

http://www.missshi.cn/api/view/blog/5a1ff5cf9112b3493d000000

Ps:初次访问由于js文件较大,请耐心等候(8s左右)


在本文中,我们将了解一些深度学习中的实际理论内容。

主要包括:

1. 训练、验证、测试集的划分

2. 对偏差和方差的理解

3. 机器学习中解决高偏差/高方差的基本方法

4. 正则化基本理论

5. Dropout的原理

6. 其他防止过拟合的方法

7. 如何加快训练速度

8. 梯度消失和梯度爆炸的概念

9. 梯度校验方法

训练、验证、测试集的划分

在神经网络训练开始之前,我们需要对很多网络结构及超参数进行确定。 
例如网络有多少层;每层有多少个神经单元,学习速率,激活函数类型等等。 
机器学习通过是一个反复实践的过程,就像之前提到的这个图一样: 
enter image description here 
下面,我们首先来了解一下如何对训练集、验证集和测试集进行分配: 
对于整个数据集而言,我们需要将其分为训练集、验证集和测试集三个部分。 
首先,我们需要利用训练集进行模型训练,然后通过验证集来判断我们训练模型的好坏,并找出其中最好的模型最终在测试集上进行评估。 
在之前的小样本的机器学习应用中,一个常见的分配方式是进行三七分配:即70%的数据用于训练,而30%的数据用于进行验证。 
或者是6,2,2分配:即训练集、验证集和测试集分别占60%,20%,20%。 
这种方式在样本数量不大的时候(1W以下),是一种比较恰当的选择。 
然而,在现在的大数据时代,随着数据量的爆发时增长,这样方式已经不再适用了。 
我们的数据量可能很容易达到百万量级,此时,验证集和测试集并不需要那么大的比例。 
我们通常只需要1W条数据左右用于验证和训练就足够了。 
也就是说,当样本量是100W时,训练集、验证集和测试集分别占98%,1%,1%就足够了。 
同理,如果样本量是1000万时,那么我们可以将训练集、验证集和测试集分别占99.6%,0.2%,0.2%。 
此外,我们还需要考虑另外一种情况,就是训练集和测试集来源于不同的分布: 
以一个识别猫的App为例,用户可以上传一张图片来辨别是否是一张猫的照片。 
那么在实际应用中,用户通过手机上传的图片可能并不太清晰、甚至有些模糊。 
然而,我们在训练过程中,我们并没有大量的这类用户上传的图片,只有少量的此类图片,并不足以我们完成模型的训练。 
但是,我们可以利用爬虫,从网络上爬取大量的猫的照片,但是网上猫的照片通常是高清的。 
此时,就面临着一个问题:真实的应用数据与我们所能拥有的更为大量的数据并不来源于同一个分布,那么此时该如何解决呢? 
此时,我们首先需要保证验证集和测试集是来源于同一分布且与真实应用场景的分布相同,即验证集和测试集应该是全部来源于用户上传的图片。 
而对于训练集,可以包含一部分用户上传的图片,此时,如何数据量不足的话,我们可以将大量的网络爬虫得到的图片添加至训练集中用于训练。

偏差与方差

偏差和方差是机器学习中两个很重要的概念。 
接下来,我们将详细了解一下偏差和方差在机器学习中的意义。 
在通常的机器学习中偏差和方差通常是一个权衡的问题,而在深度学习应用中,我们则通常会将两者分开进行讨论,我们也将会讲解两者之间的这些关联。 
以下面的三张图为例: 
方差和偏差的讲解

  • 左图中,就是一个典型的高偏差的场景。以这种方式进行分类时,明显会表现出欠拟合的状态。此时,说明训练不够充分。
  • 右图中,我们可以看出是一个典型的高方差的场景。很明显,该模型对于训练样本过于拟合,对于真实场景不具备很好的扩展性,此时,一种常见的方法是扩大训练样本。
  • 在中间的图中,则很好的保证了偏差和方法。一方面,尽可能的符合了分布场景;另一方面也避免了分割函数过高的复杂度。

接下来,我们沿用之前经常提到的对猫咪进行分类的场景进行说明。 
猫咪 
我们可以用训练集样本的误差和验证集样本的误差来对应到偏差与方差中。 
假设我们训练出的模型在训练集中,错误率为1%,而在验证集中,错误率为11%。 
那么说明对于训练集,表现的性能很好,但是在验证集中性能并不好。 
通常而言,这种场景中是由于对训练集过度拟合造成的。此时,我们称之为高方差。 
如果当模型在训练集中,错误率为15%,而在验证集中,错误率为16%。而该任务对于人类而言,错误率通常不足1%。 
那么也就说明了该模型还没有很好的拟合,即属于欠拟合状态,此时,我们称之为高偏差。 
再举一个例子:当模型在训练集中,错误率为15%,而在验证集中,错误率为30%。 
那么这种情况下,说明该模型既有高方差,又有很高的偏差。 
而当当模型在训练集中,错误率为0.5%,而在验证集中,错误率为1%时,说明该模型既较小的方差,又有很小的偏差。 
总结一下:

训练误差验证误差方差偏差1%11%大小15%16%小大15%30%大大0.5%1%小小

上述的所有分析都是基于我们对识别猫这个任务的复杂度预测的。 
我们认为该任务对于人类而言,错误率接近于0。 
我们通常称最优误差为贝叶斯误差。也就是说,在该场景中,贝叶斯误差接近于0。 
如果在某个复杂的任务中,贝叶斯误差本身就相对较高,那么,我们在判断偏差时,也需要根据根据相应的标准进行判断。

机器学习中解决高偏差/高方差的基本方法

在解决问题之前,我们首先需要判断当前情况下是否存在高偏差。 
如果的确偏差很大,对训练集都不能很好的拟合时,我们通常可以考虑以下思路:

  • 采用更大的网络
  • 训练更长的时间
  • 换一个新的网络结构

通过这些方面的反复试验,首先要保证其偏差降低到一定程度后,再继续后续过程。 
当偏差降低到一个可接受的值时,我们将继续考虑其方差的问题。 
此时,我们要查看模型在验证集上的性能,如果模型在验证集上表现出的性能并不足够好,我们通常可以考虑一下思路:

  • 使用更多的数据进行训练
  • 正则化
  • 换一个新的网络结构

最终,我们的目标就是通过不断的调试、试验来找出一个具有较低偏差、较低方差的模型。 
总结一下,高偏差和高方差是两种完全不同的问题,解决方法有完全不同,我们需要根据特定的问题来选择对应的解决方法。 
在早期的机器学习中,我们通常很难做到仅仅减少方差/偏差时,不影响另外一个指标。因此,通常需要考虑两者的权衡问题。 
但是在深度学习和大数据时代,理论上,我们只需要扩大网络结构并用更多的数据进行训练,就可以保证较小的方差和偏差。

正则化基本理论

当你发现你的网络发生了过拟合的问题时(存在高偏差),那么首先想到的一种方法应该就是正则化了。此外,收集更多的样本数据也是避免过拟合的一个好的方法。但是,当收集样本很大时,我们首选的方法就是正则化了。 
接下来,我们将首先讲解正则化的相关基本理论。 
逻辑回归模型为例: 
我们需要最小化的表达式如下:minw,bJ(w,b)minw,bJ(w,b) 
其中,J(w,b)J(w,b)的计算公式如下: 

J(w,b)=1mi=1mL(y^(i),y(i))+λm||w||22J(w,b)=1m∑i=1mL(y^(i),y(i))+λm||w||22

其中,||w||22=nxj=1w2j=wTw||w||22=∑j=1nxwj2=wTw 
该方法我们也称之为L2正则化方法。 
L2是最常见的正则化方法之一。 
除了L2正则化外,可能你还听说过L1正则化。 
此时,用||w||1||w||1来代替||w||22||w||22,且||w||1=nxj=1|wj|||w||1=∑j=1nx|wj| 
需要注意的是,在添加正则化的过程中,有一个重要的超参数λλ。 
我们在实际应用中,通常需要通过验证集和多次试验来选择合适的λλ。 
下面,我们来看下在神经网络中,正则表达式是如何计算的吧: 
J(w[1],b[1],,w[l],b[l])||w||2Famp;=amp;=amp;1mi=1mL(y^(i),y(i))+λml=1L||w||2Famp;i=1n[l1]j=1n[l](w[l]ij)2(1)(2)(1)J(w[1],b[1],⋯,w[l],b[l])amp;=amp;1m∑i=1mL(y^(i),y(i))+λm∑l=1L||w||F2(2)其中:||w||F2amp;=amp;∑i=1n[l−1]∑j=1n[l](wij[l])2

我们称||w||2F||w||F2为frobenius范数。 
那么,我们来看一下添加了正则化后的反向传播是什么样子的吧: 
dw[l]w[l]w[l]amp;=amp;:=amp;:=amp;:=amp;()+λmw[l]amp;w[l]αdw[l]amp;w[l]α[()+λmw[l]]amp;(1λαm)w[l]α()(3)(4)(5)(6)(3)dw[l]amp;=amp;(原始内容)+λmw[l](4)w[l]amp;:=amp;w[l]−αdw[l](5)w[l]amp;:=amp;w[l]−α[(原始内容)+λmw[l]](6)amp;:=amp;(1−λαm)w[l]−α(原始内容)

此时,我们可以发现ww的权重变小,这一现象我们也称之为权重衰减。 
那么,为什么通过正则化可以很好的防止过拟合问题呢? 
我们从公式的角度进行一下直观的理解: 
由于在代价函数中,我们添加了一个正则项n[l1]i=1n[l]j=1(w[l]ij)2∑i=1n[l−1]∑j=1n[l](wij[l])2。 
λλ足够大时,如果矩阵ww中有很多不为0的数值,那么将会造成很大的代价函数值。 
因此,在降低代价函数值得同时,将会对网络的结构进行简化,此时则可以有效的防止过拟合的问题。 
但是,需要注意的时,如果λλ值很大的时候,会导致模型从过拟合状态直接变化为欠拟合状态,因此,选择一个合适的λλ的值非常重要。

Dropout的原理与实现

除了L2正则化以外,Dropout方法也是一个十分常用的正则化手段。 
enter image description here 
假设我们在上图的神经网络结构中进行训练,验证时发现了较严重的过拟合现象。 
那么Dropout的基本原理如下: 
遍历每层的神经元节点,并设置每层节点随机消失的概率。 
例如,我们设置所有节点都是有0.5的概率会消失。 
那么,在完成这个过程后,我们会发现有一些节点现在已经被失效: 
enter image description here 
然后,我们删除掉与这些节点关联的连线: 
enter image description here 
此时,我们将会得到一个节点更少,网络更加简单的模型结构。 
对于该样本,以同样的结构进行前向传播和反向传播。 
而当下一样本输入时,我们需要重新随机选择节点置为失效并进行前向传播和反向传播。 
上面,我们讲解了Dropout的基本原理,那么具体应该如何去实现呢? 
接下来,我们将以一个三层的神经网络结构来进行说明。

  1. keep_prob = 0.8 # 设置消除的概率为0.2
  2. d3 = np.random.rand(a3.shape[0], a3.shape[1]) < keep_prob # 随机的0,1矩阵
  3. a3 = np.multiply(a3, d3) # 部分元素清零
  4. a3 /= keep_prob # 复原尺度

需要注意的是,在使用Dropout进行训练得到的模型,在进行验证,测试或应用时,应该不再适用Dropout函数进行随机失效处理。 
主要原因是因为在测试或验证阶段,我们不希望输出的结果是随机的。 
那么为什么Dropout函数会对避免过拟合有很好的效果呢?我们来继续分析一下: 
从直观上理解,添加了Dropout函数后,我们无法对某些神经元或特征进行强依赖,而是更多地会依赖于整个的权重分配。 
Ps:

  • 在使用Dropout函数时,不同层的keep_prob可以变化。例如,对于某些神经元较多的层,我们可以设置更低的keep_prob来避免过拟合,因此理论上,在参数越多的层,造成过拟合的可能性通常更大。
  • Dropout函数在计算机视觉领域应用相对较多。
  • 只有在模型过拟合的时候才考虑使用Dropout函数。
  • 缺点是在使用了Dropout函数后,代价函数JJ不再能够被明确的定义。每次迭代中会随机删除一些节点。此时,我们很难进行调试验证。因此,我们通常建议先将keep_prob设置为1,进行调试,保证代价函数单调递减时,再开启Dropout层进行训练。

其他防止过拟合的方法

除了L2正则化和Dropout函数外,还有一些方法可以用于正则化来防止过拟合。 
方法1:数据扩充 
当我们无法获得更多的数据时,我们可以根据已有的数据来生成一些新的数据。 
以图像领域为例,我们可以对原始图像进行任意翻转、裁剪等来生成一些新的样本。 
enter image description here 
这种方式虽然不像全新的样本那么有价值,但是好在获取成本极低,也可以对我们训练起到一定的帮助作用。 
方法2:Early stop 
在迭代的过程中,代价函数的变化曲线通常如下: 
enter image description here 
如果我们将验证集的误差也绘制在同一张图上时,通常而言得到的结果如下: 
enter image description here 
从图中,我们可以发现,验证集的误差通常会首先呈现出下降的趋势,达到最低点后则会开始上升。 
Early stop指的就是当验证集误差达到最低点后,就停止继续训练。 
但是这样一来,将会有一个缺点:就是无法单独来保证偏差和方差两个指标。因为提前终止训练会导致偏差无法继续下降。

加快训练速度

在模型训练的过程中,一个加快训练速度的好的方法是归一化输入。 
假设我们的输入是二维特征,以下图为例: 
enter image description here 
那么归一化可以分为两个步骤:

  1. 零均值化 
    μ=1mi=1mx(i)x:=xμμ=1m∑i=1mx(i)x:=x−μ

    enter image description here
  2. 归一化方差 
    σ2=1mi=1m[x(i)]2x:=x/σ2σ2=1m∑i=1m[x(i)]2x:=x/σ2

    enter image description here

Ps:如果我们在训练过程中对训练数据进行了零均值化和方差归一化,那么我们要记录在零均值化和方差归一化时的参数μμσσ,同样在验证,测试和应用过程中,使用相同的参数对输入进行处理。 
那么为什么通过对输入数据进行归一化会是有效的呢? 
我们来看一个没有进行归一化的场景: 
enter image description here 
这是一个非常窄小的代价函数。 
此时,其轮廓线的下降曲线通常如下: 
enter image description here 
即当起始位置位于狭长区域时,可能需要经过较多次迭代才能找到一个较优的值。 
此外,在这个过程中,我们还需要保持较小的步长才能保证迭代寻优的过程稳定。 
而如果我们对数据进行归一化后,其代价函如下: 
enter image description here 
而其轮廓线的下降曲线通常如下: 
enter image description here 
而在此时,无论初始位置位于哪儿,都会更容易的找到较优的值。 
同时,即使选择相对较大的步长,通常也不会影响模型的收敛稳定性。

梯度消失和梯度爆炸

在神经网络的训练过程中,我们常常需要考虑梯度消失/梯度爆炸的问题。 
那么梯度消失或梯度爆炸的含义是什么呢?通常是指我们在训练一个较大的神经网络时,随着网络层数的增加、导数的值可能会变得非常大或者非常小,而这将会导致训练的难度加大。 
那么为什么会出现这种现象呢? 
以下面一个神经网络为例: 
enter image description here
假设我们使用线性激活函数g(z)=zg(z)=z以及设置偏置为0,即b=0b=0。 
此时, 

y^=w[l]w[l1]w[l2]w[3]w[2]w[1]xy^=w[l]w[l−1]w[l−2]⋯w[3]w[2]w[1]x

如果: 
w=[1.50amp;0amp;1.5]y^=w[l][1.5l10amp;0amp;1.5l1]xw=[1.5amp;00amp;1.5]y^=w[l][1.5l−1amp;00amp;1.5l−1]x

那么,可以发现如果网络很深时,即l很大时,y^y^将会呈指数式的变大。 
反之,如果w的大小为0.5,那么如果网络很深时,即l很大时,y^y^将会呈指数式的减少。 
这就是我们所说的梯度爆炸和梯度衰减现象。

这种现象又该怎么去解决呢? 
首先,在初始化神经网络的权重时,要有一定的技巧。 
以一个神经元为例: 
enter image description here 
其中:z=w1x1+w2x2++wnxnz=w1x1+w2x2+…+wnxn 
此处,我们先忽略b。 
nn很大时,即输入的神经元很多时,我们希望对应的wiwi较小,即每个输入的权重值较小。 
这样才能保证输出的大小一致。 
例如,我们可以初始化时候,设置所有的wiwi权重初始为1n1n。(Ps:实验证明,当选择ReLU激活函数时,的wiwi权重系数设置为2n2n可以得到较好的结果。) 
示例: 

w[l]=np.random.rand(shape)np.sqrt(2/n[l1])w[l]=np.random.rand(shape)∗np.sqrt(2/n[l−1])

此外,在使用tanh作为激活函数时,更好的初始化系数如下: 
w[l]=np.random.rand(shape)np.sqrt(1/n[l1])w[l]=np.random.rand(shape)∗np.sqrt(1/n[l−1])

同理,对于Xavier初始化方法(后续讲解)而言,则更好的初始化系数如下: 
w[l]=np.random.rand(shape)np.sqrt(2/(n[l1]+n[l]))w[l]=np.random.rand(shape)∗np.sqrt(2/(n[l−1]+n[l]))

通过对网络权重进行合理的初始化,可以在一定程度上缓解梯度爆炸/消失问题。

梯度校验方法

当我们在实现了一个反向传播的代码时,我们通常需要去验证我们实现的内容是否正确,然后再进行长时间的训练。 
首先,我们来学习如何从数值计算来逼近梯度值: 
以下图为例: 
enter image description here 
表示了y=x3y=x3函数。 
θθ点的梯度的数值计算公式如下: 

g(θ)f(θ+ε)f(θε)2εg(θ)≈f(θ+ε)−f(θ−ε)2ε

当我们选择θ=1θ=1ε=0.01ε=0.01时: 
可以得到g(θ)=3g(θ)=3,逼近算法计算得到的值为3.0001,误差为0.0001。

接下来,我们将学习如何进行梯度校验,梯度校验可以帮我们提前发现反向传播实现中的一些问题。

  1. W[1],b[1],,W[L],b[L]W[1],b[1],…,W[L],b[L]重新Reshape后组成了一个一维的向量θθ
  2. dW[1],db[1],,dW[L],db[L]dW[1],db[1],…,dW[L],db[L]重新Reshape后组成了一个一维的向量dθ
  3. 依次计算θθ中的每一个元素的dθapproxdθapprox 
    dθapprox[i]=J(θ1,θ2,,θi+ε,+)J(θ1,θ2,,θiε,+)2εdθapprox[i]=J(θ1,θ2,…,θi+ε,+…)−J(θ1,θ2,…,θi−ε,+…)2ε
  4. 使用我们之前编写的反向传播算法计算dθ
  5. 计算当ε=107ε=10−7时二者的差异: 
    error=||dθapproxdθ||2||dθapprox||2+||dθ||2error=||dθapprox−dθ||2||dθapprox||2+||dθ||2

    Ps:当error属于10710−7量级时,计算结果通常是正确的。 
    当error属于10510−5量级时,计算结果有可能会是错误的,尽量重新进行验证。 
    当error属于10310−3量级时,计算结果通常是错误的,需要重新计算。

下面是在使用梯度校验的一些说明和注意事项:

  • 仅仅在调试阶段使用,而不是在训练阶段使用。(训练阶段使用会极大的影响训练速度)
  • 如果验证有问题,需要逐个项进行检查。
  • 在梯度检查时,需要注意正则化的影响。
  • 在梯度检查时,需要禁用Dropout函数。(设置keep_prob为1)
  • 使用随机方法进行初始化,可以先进行一定程序的训练后再进行梯度校验(有时当W和b很小时,梯度校验无法发现问题)。




更多更详细的内容,请访问原创网站:

http://www.missshi.cn/api/view/blog/5a1ff5cf9112b3493d000000

Ps:初次访问由于js文件较大,请耐心等候(8s左右)


阅读全文
0 0
原创粉丝点击