跟着吴恩达学深度学习(二)

前言

第一门课的笔记见:跟着吴恩达学深度学习(一)

本文对应了吴恩达深度学习系列课程中的第二门课程《改善深层神经网络:超参数调试、正则化以及优化》

第二门课程授课大纲:

  • 深度学习的实用层面
  • 优化算法
  • 超参数调试、Batch正则化和程序框架

目录

1 深度学习的实用层面 

1.1  训练/开发/测试集

1.2  偏差/方差

1.3  机器学习基础

1.4  正则化

1.5  为什么正则化可以减少过拟合?

1.6  Dropout 正则化

1.7  理解 Dropout

1.8  其他正则化方法

1.9  归一化输入

1.10  梯度消失与梯度爆炸

1.11  神经网络的权重初始化

1.12  梯度的数值逼近

1.13  梯度检验

1.14  关于梯度检验实现的注记

2 优化算法

2.1  Mini-batch 梯度下降法

2.2  理解 mini-batch 梯度下降法

2.3  指数加权平均

2.4  理解指数加权平均

2.5  指数加权平均的偏差修正

2.6  动量梯度下降法

2.7  RMSprop

2.8  Adam 优化算法

2.9  学习率衰减

2.10  局部最优的问题

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

3.1  调试处理

3.2  为超参数选择合适的范围

3.3  超参数训练的实践:Pandas VS Caviar

3.4  正则化网络的激活函数

3.5  将 Batch Norm 拟合进神经网络

3.6  Batch Norm 为什么奏效?

3.7  测试时的 Batch Norm

3.8  Softmax 回归

3.9  训练一个 Softmax 分类器

3.10  深度学习框架


1 深度学习的实用层面 

1.1  训练/开发/测试集

 在配置训练(train)、验证(devlopment)和测试(test)数据集的过程中做出正确决策在很大程度上会帮助大家创建高效的神经网络。训练神经网络时我们需要做很多决策,包括:

  • 神经网络的层数
  • 每个隐藏层包含的隐藏单元
  • 学习因子(学习速率)
  • 各层激活函数的选择
  • ...

 应用型机器学习是一个高度迭代的过程。具体过程如下:

(1)项目启动时,先有一个初步想法(idea),比如构建一个含有特定层数、隐藏单元数量或者数据集个数等的神经网络。

(2)然后编码(code),并尝试运行这些代码,通过运行和测试得到该神经网络,或这些配置信息的运行结果。

(3)根据结果重新完善自己的想法,改变策略,不断试验(experiment),或者找到更好的神经网络不断更新迭代自己的方案。

同样,应用深度学习也是一个反复迭代的过程,需要通过反复多次的循环训练得到最优化参数。因此,循环该过程的效率是决定项目进展速度的一个关键因素。而创建高质量的训练数据集、验证集和测试集也有助于提高循环效率。

在实验中通常将所有的样本数据分成三个部分:Train/Dev/Test sets。训练集(Train sets):用来执行训练算法;验证集(Dev sets)简单交叉验证集:选择最好的模型,经过充分验证不同算法的表现情况,选定了最终模型; 测试集(Test sets):进行评估,用来测试最好算法的实际表现,作为该算法的无偏估计。

数据集比例划分:

  •  小数据量如数量为100、1000或10000一般三七分,即70%训练集30%测试集。如果有验证集,则设置比例依次为60%、20%、20%
  • 大数据量:对于百万级别的数据,训练集、验证集、测试集的比例通常可以设置为98%、1%、1%。对于超过百万级别的数据,比例可以为99.5%、0.25%、0.25或者99.5%、0.4%、0.1%。

因此,样本数据量越大,相应的验证集和测试集的比例可以设置的越低一些。 

数据没有测试集也没关系,测试集的目的是对所选定的神经网络系统做出无偏评估,如果不需要无偏评估,也可以不设置测试集,在验证集(test sets)上选择合适的模型。

1.2  偏差/方差

 如上图(图左)的数据集,拟合成一条直线,可能得到一个逻辑回归拟合,但它并不能很好地拟合该数据集,这是偏差高(high bias)的情况,。称为欠拟合(underfit the data)

如上图(图右)的数据集,拟合一个非常复杂的分类器,分类器方差较高(high variance),数据过度拟合(overfit the data)

这两者之间,如(图中)这种情况,复杂程度适中,数据拟合适度的分类器,这个看上起更为合理,称适度拟合(just right),是介于过拟合和欠拟合中间的一类。

以上是在二维的情况下,将偏差和方差可视化。而多维则无法通过绘制数据和可视化分割边界实现,但我们可以通过几个指标来研究偏差和方差。

理解偏差和方差的两个关键数据是训练集误差(train set error)验证集误差(development set error)

同样以识别小猫为例,可能出现以下情况。

情形1:Train set error为1%,Dev set error为11%。

根据误差情况可以看出训练集设置的非常好,而验证集设置的相对较差。分析原因,可能存在过拟合,某种程度上,验证集并没有充分利用交叉验证集的作用,模型泛化能力不强,导致验证集识别率低。这种情况,通常被称之为”高方差“(high variance)。 

情形2:Train set error为16%,Dev set error为16%。

根据误差情况可以看出训练集没有得到很好训练,而验证集设置的相对合理。如果训练集的拟合度不高,就是欠拟合,即偏差较高(high bias)。

情形3:Train set error为15%,Dev set error为30%。

根据误差情况,可以说明该模型既存在高偏差(high bias)也存在高方差(high variance),这是深度学习中最坏的情况。

情形4:Train set error为0.5%,Dev set error为1%。

根据误差情况,可以说明该模型不仅低偏差(low bias)还低方差(low variance),这是深度学习中最好的情况。

注意,上面的情形都是假设人眼辨别的错误率接近0%,即人类都能正确识别所有猫类图片。一般来说,最优误差(optimal error)也称为基本误差(base error),所以最优误差接近0。如果基本误差(base error)不同,例如图片很模糊,相应的train set error和dev set error会有所变化,那分析过程就要做出改变了。

一般来说,Train set error体现了是否出现bias,Dev set error体现了是否出现variance(正确地说,应该是Dev set error与Train set error的相对差值)。

上图中的模型既存在high bias也存在high variance,可以理解成某段区域是欠拟合的,某段区域是过拟合的。

1.3  机器学习基础

在训练神经网络时, 如果碰到高偏差(欠拟合),通常是增加神经网络的隐藏层层数、神经元个数,训练时间延长,选择其它更复杂的NN模型

如果碰到高方差(过拟合),通常是增加训练样本数据,但有时无法获得更多的数据,进行正则化(Regularization)来减少过拟合。

总之,通过不断地尝试找到一个低方差,低偏差的模型。

⚠️注意:

  • 处理高方差和高偏差的方法不一样,首先要通过验证集找到存在的问题,再对症下药,明确这一点有助于选出最有效的方法。
  • 在当前的深度学习和大数据时代,只需要持续训练一个更大的网络,只要准备了更多的数据,同时减少方差和偏差不是没有可能。

1.4  正则化

如果怀疑神经网络过度拟合了数据,即出现了高方差(high variance),那么最先想到的可能是正则化(regularization),另一个方法是准备更多的数据(more data)。但获取更多样本的成本可能很高但正则化通常有助于避免过度拟合或减少网络误差。

成本函数(cost function)J的最小值。它是定义的成本函数,参数包含一些训练数据和不同数据中个体预测的损失,w和b逻辑回归的两个参数,w是一个多维度参数矢量,b是一个实数。实现正则化只需要添加参数λ(正则化参数),这种方法称为L2 regularization。先给出两种常见的正则化表达式:

L2 regularization的表达式为:

L1 regularization的表达式为:

具体解释看下图:

🙋为什么只对w进行正则化而不加上b呢?

其实也可以对b进行正则化。因为w一般是高维参数矢量,已经可以表达高方差问题。w可能含有很多参数,我们不可能拟合所有参数,而b只是单个数字。所以w几乎涵盖所有参数,当然,加上之后也可以。

当然你可能听过L1 regularization,它得到的w更加稀疏,因为w向量中很多零值。有人说这样有利于压缩模型。实际上,虽然L1得到的结果更稀疏,却没有降低太多存储空间,所以L1的主要目的不是为了压缩模型。在训练模型时,人们越来越倾向于使用L2。

λ是正则化参数(超参数的一种),通过验证集或者交叉验证来配置这个参数,尝试各种各样的数据,选择最好的λ。要考虑训练集中的权衡,把参数设置为较小值,这样可以避免过拟合。在python中,lambda是保留字,所以编码时,使用lambd来表示lambda,以避免冲突。

首先写出成本函数J的表达式,这里加了惩罚项(如上图)。注意矩阵L2范数听起来更自然,但鉴于一些大家无须知道的特殊原因,按照惯例,我们称之为“弗罗贝尼乌斯范数”,它表示一个矩阵中所有元素的平方和。如何使用该范数实现梯度下降呢?用反向传播计算dW的值,反向传播会给出J对W的偏导数,实际上是W^{[l]},因为我们额外增加的正则化项,所以给dW加上(\frac{\lambda}{m})*W^{[l]}这一项,然后定义这个更新项,使用新定义的dW^{[l]},它的定义含有相关参数代价函数导数和,以及最后添加的额外正则项,这也是L2正则化有时被称为“权重衰减”(weight decay)的原因。

1.5  为什么正则化可以减少过拟合?

还是 1.2中的那张图,从左至右分别表示欠拟合(underfit the data)刚好拟合(just right)过拟合(overfit the data)三种情况。

从成本函数J来看,直观上理解就是如果正则化λ设置得足够大,权重矩阵W被设置为接近于0的值。直观理解,就是把很多隐层单元的权重设置的太接近0了,以至于这些隐藏单元的影响被消除。这种情况下,复杂神经网络就被简化成很小的网络,同时深度很大,它会使这个网络从过拟合的状态更接近于左图的高偏差状态。但是λ存在中间值,使得接近“just right”的中间状态。因此,选择合适大小的λ值,就能够同时避免高偏差(high bias)和高方差(high variance),得到最佳模型。

直觉上会认为大量隐藏单元被完全消除了,其实不然,实际上是该神经网络的所有隐藏单元依然存在,但是它们的影响变得更小了。

1.6  Dropout 正则化

除了L2 regularization之外,还有另外一种非常实用的正则化方法:Dropout(随机失活)。下面看工作原理。 

假设你在训练左图这样的神经网络,存在过拟合,我们复制这个神经网络,dropout会遍历网络的每一层,并设置消除神经网络中节点的概率。假设网络中的每一层,每个节点都以抛硬币的方式设置概率,即每个节点得以保留和消除的概率都是0.5,这样我们会随机消除一些节点,然后删除掉从该节点进出的连线,最后得到一个节点更少,规模更小的网络,然后用反向传播进行训练。(如上图右侧)

在实现Dropout 正则化时,最常采用inverted dropout(反向随机失活),出于完整性考虑,我们用一个三层网络来举例说明。

(1)首先要定义向量d(如上图),d^[3]表示一个三层的dropout向量。

d3 = np.random.rand(a3.shape[0],a3.shape[1]) < keep-prob

其中keep-prob是一个具体数字,它表示保留某个隐藏单元的概率,在本例中被赋值为0.8,这意味着消除任意一个隐藏单元的概率是0.2,它的作用就是生成随机矩阵,如果对进行因子分解,效果也是一样的。d^{[3]}是一个矩阵,每个样本和每个隐藏单元,其中对应值为1的概率都是0.8(对应为0的概率是0.2)。

(2)接着要做的是从第三层中获取激活函数a^{[3]}a^{[3]}等于上面的a^[3]乘以d^{[3]}

a3 = np.multiply(a3,d3) #a3 *= d3

上述d3是一个布尔型数组,值为true和false,而不是1和0,乘法运算依然有效,python算法会把true和false翻译为1和0。

(3)最后,向外拓展a^{[3]},用它除以keep-prob参数(0.8)。

a3 /= keep-prob

🙋为什么要这么做呢?

假设第三隐藏层上有50个单元(神经元),在一维上a^{[3]}是50,通过因子分解拆分为50*m维的,保留和删除它们的概率分别为80%和20%,这意味着最后被删除或归零的单元平均有10(50×20%=10)个。z^{[4]}=w^{[4]}*a^{[3]}+b^{[4]}a^{[3]}减少了20%,为了不影响z^{[4]}的期望值,将w^{[4]}*a^{[3]}除以0.8,它将会修正或弥补我们所需的那20%,a^{[3]}的期望值不变,a3 /= keep-prob就是所谓的dropout方法,这样使测试阶段变得更容易,因为它的数据扩展问题变少。不论keep-prop的值是多少0.8,0.9甚至是1,如果keep-prop设置为1,那么就不存在dropout,因为它会保留所有节点。反向随机失活(inverted dropout)方法通过除以keep-prob,保证a^{[3]}的期望值不变。

Dropout早期的迭代版本都没有除以keep-prob,所以在测试阶段,平均值会变得越来越复杂,不过那些版本已经不再使用了。

在测试阶段,我们并未使用dropout,自然也就不用抛硬币来决定失活概率,以及要消除哪些隐藏单元了,因为在测试阶段进行预测时,不期望输出结果是随机的,所有神经元都在工作,如果测试阶段应用dropout函数,预测会受到干扰。Inverted dropout函数在除以keep-prob时可以记住上一步的操作,目的是确保即使在测试阶段不执行dropout来调整数值范围,激活函数的预期结果也不会发生变化,所以没必要在测试阶段额外添加尺度参数,这与训练阶段不同。

1.7  理解 Dropout

1.6小节中,对dropout随机删除网络中的神经单元有了一个直观了解,不依赖于任何一个特征,因为该单元的输入可能随时被清除,因此该单元通过这种方式传播下去,并为单元的四个输入增加一点权重,通过传播所有权重好像每次迭代之后,神经网络都变得比以前更小,因此采用一个较小神经网络好像和使用正则化的效果是一样的。

从单个神经元入手,这个单元的任务就是利用输入生成一些有意义的输出。而使用了dropout,这些输入会被随机丢弃,有时候可能1个输入,有时候会是2个输入。也就是说,这个神经单元不能依靠任何特征,因为特征都有可能被随机清除。因此,在特定的时候不愿将所有赌注都放在一个节点上,因为它可能会被删除。因此该单元将通过这种方式积极地传播开,并为单元的四个输入增加一点权重,通过传播所有权重,dropout将产生收缩权重的平方范数的效果。和之前的L2正则化类似,实施dropout的结果是它会压缩权重,并完成一些预防过拟合的外层正则化。

总结一下,如果你担心某些层比其它层更容易发生过拟合,可以把某些层的keep-prob值设置得比其它层更低,缺点是为了使用交叉验证,你要搜索更多的超级参数,另一种方案是在一些层上应用dropout,而有些层不用dropout,应用dropout的层只含有一个超级参数,就是keep-prob。

dropout一大缺点就是成本函数J不再被明确定义。每次迭代,都会随机移除一些节点,这样会加大检查算法的性能的难度。定义明确的成本函数J每次迭代后都会下降,因为我们所优化的代价函数J实际上并没有明确定义,或者说在某种程度上很难计算,所以失去了调试工具来绘制这样的图片。

为了解决这一问题,通常可以关闭dropout函数,将keep-prob的值设为1。运行代码,确保函数J单调递减。然后再打开dropout函数。

1.8  其他正则化方法

除了L2正则化dropout正则化,还有几种方法可以减少神经网络中的过拟合。

一种方法是数据扩增(data augmentation)。以拟合猫咪图片分类器为例,如果想通过扩增训练数据来解决过拟合,这时候会发现扩增数据代价高,而且有时候我们无法扩增数据,但可以通过添加这类图片来增加训练集。例如,水平翻转图片,并把它添加到训练集。所以现在训练集中有原图,还有翻转后的这张图片,所以通过水平翻转图片,训练集则可以增大一倍,因为训练集有冗余,这虽然不如额外收集一组新图片那么好,但这样做节省了获取更多猫咪图片的花费。

除了水平翻转图片,也可以随意裁剪图片。通过随意翻转和裁剪图片,可以增大数据集,额外生成假训练数据。无法包含像全新数据那么多的信息,但这么做基本没有花费,代价几乎为零,除了一些对抗性代价。

对于光学字符识别,还可以通过添加数字,随意旋转或扭曲数字来扩增数据,把这些数字添加到训练集,它们仍然是数字。

另一种方法是早终止法(early stopping)。在运行梯度下降时,可以绘制训练误差,或只绘制成本函数J的优化过程,在训练集上用0-1记录分类误差次数。呈单调下降趋势。还可以绘制验证集误差,它可以是验证集上的分类误差,或验证集上的代价函数,逻辑损失和对数损失等,可以发现,验证集误差通常会先呈下降趋势,然后在某个节点处开始上升。

当还未在神经网络上运行太多迭代过程的时候,参数w接近0,因为随机初始化w值时,它的值可能都是较小的随机值,所以在你长期训练神经网络之前w依然很小,在迭代过程和训练过程中w的值会变得越来越大,所以early stopping要做就是在中间点停止迭代过程,得到一个不大不小w值,与L2正则化相似,选择参数w范数较小的神经网络,理想状态下就能少点过度拟合。

early stopping有一个缺点。机器学习的其中一步是选择一个算法来优化代价函数J,解决方法有梯度下降,Momentum,RMSprop和Adam等。同时,也不想出现过拟合,解决方法如正则化,扩增数据等。

在重点优化成本函数J时,只需要留意w和b,J(w,b)的值越小越好,需要想办法减小这个值,其它的不用关注。然后,预防过拟合还有其他任务,换句话说就是减少方差,这一步用另外一套工具来实现,这个原理有时被称为“正交化”(Orthogonalization)。而early stopping的主要缺点就是不能独立地处理这两个问题(优化J和预防过拟合)

如果不用early stopping,另一种方法就是L2正则化,训练神经网络的时间就可能很长。这导致超级参数搜索空间更容易分解,也更容易搜索,但是缺点在于,必须尝试很多正则化参数的值,这也导致搜索大量值的计算代价太高。

Early stopping的优点是,只运行一次梯度下降,可以找出w的较小值,中间值和较大值,而无需尝试L2正则化超级参数的很多值。

1.9  归一化输入

训练神经网络,其中一个加速训练的方法就是归一化输入(normalizing inputs)。对输入进行归一化包括两个步骤。

第一步,零均值化,根据\mu =\frac{1}{m}\sum_{i=1}^{m}x^{(i)}得到向量\mu后,再用训练集样本x-\mu然后赋值给x。即,将训练集进行平移使得它的均值为0。

第二步,归一化方差,根据\sigma^{2} =\frac{1}{m}\sum_{i=1}^{m}(x^{(i)})^2得到\sigma^{2}后,将所有数据均除以\sigma^{2}。归一化结果如下图(最右)。

🙋为什么要进行归一化呢?

如果你使用非归一化的输入特征,成本函数J会像左上方,是一个非常细长狭窄的成本函数。假如特征x1取值范围从1到1000,x2的取值范围从0到1,结果是参数w1和w2值的范围或比率将会非常不同,这些数据轴应该是w1和w2。但直观理解,这里标记为w和b,成本函数就有点像狭长的碗一样,如果你能画出该函数的部分轮廓,它会是这样一个狭长的函数(左下方)。在这样的成本函数J上运行梯度下降法,必须使用一个非常小的学习率。而且梯度下降法可能需要多次迭代过程,直到最后找到最小值。但如果进行归一化处理,函数是一个更圆的球形轮廓(右上方),那么不论从哪个位置开始,梯度下降法都能够更直接地找到最小值,也可以使用较大步长,而不需要像在左图中那样反复执行。

当然,实际上是w一个高维向量,因此用二维绘制w并不能正确地传达并直观理解,但总的理解是成本函数会更圆一些,而且更容易优化,前提是特征都在相似范围内,而不是从1到1000,0到1的范围,而是在-1到1范围内或相似偏差,这使得成本函数优化起来更简单快速。所以如果输入特征处于不同范围内,那么归一化特征值就非常重要了。如果特征值处于相似范围内,那么归一化就不是很重要了。

1.10  梯度消失与梯度爆炸

训练神经网络,尤其是深度神经面临的一个问题就是梯度消失或梯度爆炸(vanishing gradients and exploding gradients),也就是你训练神经网络的时候,导数或坡度有时会变得非常大,或者非常小,甚至于以指数方式变小,这加大了训练的难度。

假设正在训练这样一个极深的神经网络,神经网络每层只有两个隐藏单元: 

输出为:\hat{y}=W^{[l]}W^{[l-1]}W^{[l-2]}...W^{[2]}W^{[1]}x

假设每个权重矩阵为

w=\begin{bmatrix} 1.5 & 0\\ 0 & 1.5 \end{bmatrix}

权重矩阵每个元素都大于1,则

\hat{y}=W^{[l]}\begin{bmatrix} 1.5 & 0\\ 0 & 1.5 \end{bmatrix}^{(l-1)}x

L越大,\hat{y}越大,呈指数型增长,也称爆炸式增长。相反,如果权重矩阵W的元素都小于1,如0.5,L越大,\hat{y}越小,呈指数型减小,称为数值消失。当各层权重W都大于1或者小于1,当层数很大时,出现数值爆炸或消失。

1.11  神经网络的权重初始化

 针对1.10小节提出梯度消失和梯度爆炸问题,本小节提出一个不完整的解决方案。

先看只有一个神经元的情况,单个神经元可能有4个输入特征,从x1到x4,如下图:

经过a=g(z)处理,最终得到\hat{y}

因为b=0,先忽略b,为了预防z值过大或者过小,可以看到n越大,w_i越小,最合理的方法是设置为w_i = 1/n,n表示神经元的输入特征数量。设置第l层权重矩阵为:

w[l] = np.random.randn(n[l],n[l-1])*np.sqrt(1/n[l-1])

其中,n^{[l-1]}是第l-1层神经元的数量。

如果你是用的是Relu激活函数,方差设置为2/n,效果会更好。

w[l] = np.random.randn(n[l],n[l-1])*np.sqrt(2/n[l-1])

📜总结

激活函数是tanh,初始化令方差为1/n。如果激活函数是ReLU,权重w的初始化一般令其方差为2/n。

1.12  梯度的数值逼近

在实施反向传播时,有一个测试叫做梯度检验(gradient checking),它的作用是确保反向传播正确实施。为了逐渐实现梯度检验,下图将介绍如何计算梯度的数值逼近。

1.13  梯度检验

梯度检验(gradient checking)节省了很多时间,也可以帮我们发现反向传播实施过程中的bug。本小节,介绍了如何利用它来调试或检验反向传播的实施是否正确。

假设神经网络中有下列参数,梯度检验首先要做的是W^[1],b^[1],...,W^[L],b^[L]这些矩阵构造成一维向量,然后将这些一维向量组合起来构成一个更大的一维向量θ。这样cost function J(W^[1],b^[1],...,W^[L],b^[L])就可以表示成J(θ)。然后将反向传播过程通过梯度下降算法得到的dW^[1],db^[1],...,dW^[L],db^[L] 按照一样的顺序构造成一个一维向量 dθ。 dθ的维度与 θ一致。具体分析如下图:

在实施梯度检验算法时,首先写一个循环,对每个i也就是对每个θ组成元素计算的值,这里使用双边误差,也就是:

d\theta_{approx} =\frac{J(\theta_1,\theta_2,...,\theta_i+\varepsilon,...)-J(\theta_1,\theta_2,...,\theta_i-\varepsilon,...)}{2\varepsilon }

从上一小节中了解到这个值应该逼近dθ[i]=偏J/偏θ_i(成本函数J的偏导数),你需要对i的每个值都执行这个运算,得到两个向量,验证这些向量是否彼此接近。

如何定义两个向量是否真的接近彼此?计算两个向量的欧式距离

\frac{\left \| d\theta_{approx}-d\theta \right \|_2}{\left \| d\theta_{approx}\right \|_2+\left \| d\theta \right \|_2}

除以这个分母只是为了防止这两个向量太小或太大,分母使其变成了比值。

执行这个方程式,ε可能为10^{-7}。会得到以下三种情况:

  • 如果发现上面方程式得到的值为10^{-7}或更小,就意味着导数逼近很有可能是正确的,它的值非常小。
  • 如果这个值在10^{-5}范围内,表明梯度计算可能有问题,需要再次检查这个向量的所有项,确保没有一项误差过大,可能这里有bug。
  • 如果这个值为10^{-3},或者更大,这时应该仔细检查所有θ项,经过一些调试,最终结果会是非常小的值。

1.14  关于梯度检验实现的注记

本小节分享一些关于如何在神经网络实施梯度检验的实用技巧和注意事项。

(1)不要在训练中使用梯度检验,它只用于调试。为了实施梯度下降,你必须使用W和b反向传播来计算dθ,只有调试的时候才会计算它。

(2)如果算法的梯度检验失败,要检查所有项,检查每一项,并试着找出bug。注意θ的各项与b和w的各项都是一一对应的。

(3)在实施梯度检验时,如果使用正则化,请注意正则项。

(4)梯度检验不能与dropout同时使用,因为每次迭代过程中,dropout会随机消除隐藏层单元的不同子集,难以计算dropout在梯度下降上的代价函数J。

(5)最后也是比较微妙的一点,现实中几乎不会出现这种情况,随机初始化时运行梯度检查。

2 优化算法

2.1  Mini-batch 梯度下降法

 深度学习往往在大数据领域才表现得最好,而训练大量数据的过程很慢。因此,快速的优化算法可以大大提高团队的效率。本小节主要介绍了mini-batch梯度下降法(Mini-batch Gradient Descent,MBGD)。

向量化能够有效地对所有m个样本进行计算,允许处理整个训练集,而无需某个明确的公式。把训练样本放到矩阵X中,有X=[x^{(1)} x^{(2)} x^{(3)} ... x^{(m)}]

所以X的维度是(n_x,m),Y的维度是(1,m),向量化可以相对较快地处理所有m个样本,但是如果m很大时(500万或者5000万或者更大),处理速度仍然缓慢。在对整个训练集执行梯度下降法时,必须处理整个训练集,然后才能进行一步梯度下降法,然后你需要再重新处理500万个训练样本,才能进行下一步梯度下降法。所以如果在处理完整个500万个样本的训练集之前,先让梯度下降法处理一部分,算法速度会更快。

因此,可以把训练集分割为小一点的子集训练,这些子集被取名为mini-batch。如果总训练样本个数m=5000000,维度是(n_x,m),假设每一个子集中只有1000个样本,把其中的x^{(1)}x^{(1000)}到取出来,将其称为第一个子训练集,也叫做mini-batch,然后再取出接下来的1000个样本,从x^{(1001)}x^{(2000)},然后再取1000个样本,以此类推。将每一个mini-batch记为X^{t},维度是(n_x,1000),对应每个mini-batch的输出记为Y^{t},维度是(1,1000),此时t=1,2,...,5000。

🙋那么mini-batch梯度下降法的原理是什么?

在训练集上运行mini-batch梯度下降法,运行for t=1……5000,因为有5000个子集,每个子集有1000个样本,在for循环里要做得就是对X^{t}Y^{t}执行一步梯度下降法。首先输入是X^{t},执行正向传播:

z^{[1]}=W^{[1]}X^{[t]}+b^{[1]}

然后执行

A^{[1]k}=g^{[1]}(Z^{[1]})

以此类推直至

A^{[L]}=g^{[L]}(Z^{[L]})

这里需要用到一个向量化的执行命令,这个向量化的执行命令,一次性处理1000个而不是500万个样本。接下来你要计算损失成本函数J,因为子集规模是1000,所以:

J=\frac{1}{1000}\sum_{i=1}^{l}L(\hat{y}^{(i)},y^{(i)})

如果使用到了正则化。

J^{\{t\}}=\frac{1}{1000}\sum_{i=1}^{l}L(\hat{y}^{(i)},y^{(i)})+\frac{\lambda}{2\ast 1000}\sum_l\left \| w^{[l]} \right \|^2_F

因为这是一个mini-batch的损失,J加入上角标t

接下来,执行反向传播来计算J^{\{t\}}的梯度,然后更新权重:

W^{[l]}:=W^{[l]}-adW^{[l]}

b^{[l]}:=b^{[l]}-adb^{[l]}

这是使用mini-batch梯度下降法训练样本的一步,代码中被称为进行“一代”(1 epoch)的训练。一代这个词意味着只是一次遍历了训练集。

使用batch梯度下降法,一次遍历训练集只能做一个梯度下降,使用mini-batch梯度下降法,一次遍历训练集,能做5000个梯度下降。当然正常来说想要多次遍历训练集,还需要为另一个while循环设置另一个for循环。所以可以一直处理遍历训练集,直到最后能收敛到一个合适的精度。如果有一个丢失的训练集,mini-batch梯度下降法比batch梯度下降法运行地更快。

2.2  理解 mini-batch 梯度下降法

如上图(图左),使用batch梯度下降法时,每次迭代都需要历遍整个训练集,可以预期每次迭代成本都会下降,所以如果成本函数J是迭代次数的一个函数,它应该会随着每次迭代而减少,如果J在某次迭代中增加了,那肯定出了问题,也许你的学习率太大 。

如上图(图右),使用mini-batch梯度下降法,其成本函数图,并不是每次迭代都是下降的,很可能会看到这样的结果,走向朝下,但有更多的噪声。之所以出现细微振荡的原因是不同的mini-batch之间是有差异的。例如可能第一个子集是好的子集,而第二个子集包含了一些噪声,出现细微摆动是正常的。

 在mini-batch梯度下降法中,需要决定的变量之一是mini-batch的大小,m就是训练集的大小,极端情况下:如果为m,即为batch梯度下降法(BGD),只包含一个子集;如果为1,即为随机梯度下降法(SGD),每次只处理一个训练样本,每个样本就是一个子集,共有m个子集。

实际上选择的mini-batch大小在1和m之间。batch梯度下降法的主要弊端在于特别是训练样本数量巨大时,单次迭代耗时太长,如果训练样本不大,该算法运行地很好。而使用随机梯度下降法,如果你只要处理一个样本,那这个方法没有问题,通过减小学习率,噪声会被改善或有所减小,但随机梯度下降法的一大缺点是,会失去所有向量化带给你的加速,因为一次性只处理了一个训练样本,这样效率过于低下。因此,实践中最好选择不大不小的mini-batch尺寸,实际上学习率达到最快。其优势如下:

(1)得到了大量向量化,上个视频中我们用过的例子中,如果mini-batch大小为1000个样本,你就可以对1000个样本向量化,比你一次性处理多个样本快得多。

(2)不需要等待整个训练集被处理完就可以开始进行后续工作,再用一下上个视频的数字,每次训练集允许我们采取5000个梯度下降步骤,所以实际上一些位于中间的mini-batch大小效果最好。

🙋如果mini-batch大小既不是1也不是m,应该取中间值,那应该怎么选择呢?

首先,如果总体样本数量m不太大时,例如m<2000,建议直接使用Batch gradient descent。然后,如果总体样本数量m很大时,建议将样本分成许多mini-batches。推荐常用的mini-batch size为64,128,256,512。这些都是2的幂。之所以这样设置的原因是考虑到电脑内存设置和使用的方式,计算机存储数据一般是2的幂,这样设置可以提高运算速度。最后,注意确保X^{t}Y^{t}符合CPU/GPU内存。事实上mini-batch大小是另一个重要的变量,你需要做一个快速尝试,才能找到能够最有效地减少成本函数的那个,一般需要尝试几个不同的值,几个不同的2次方,然后看能否找到一个让梯度下降优化算法最高效的大小。

2.3  指数加权平均

指数加权平均(exponentially weighted averages),在统计中也叫做指数加权移动平均(exponentially weighted moving averages) 

 

上图为某年伦敦的每日温度,用数据作图,得到上面的散点图,起始日在1月份,结束日在12月末,这样的数据看起来有点杂乱,如果要计算趋势的话,即温度的局部平均值(移动平均值)。可以设V_0 = 0,当成第0天的气温值,每天需要使用0.9的加权前一天的数值,再加上当天温度的0.1倍(公式如上图)。现在将0.9记为β,0.1记为(1-β),则

v_t=\beta v_{t-1}+(1-\beta)\theta_t

可以看成v_t是1/(1-β)天的平均值。当β=0.9时,是这10天的平均值,就是上图的红线部分。当β=0.98时,是这50天的平均值,就是上图的绿线部分。

β值越大,你得到的曲线要平坦一些,原因在于多平均了几天的温度,所以这个曲线,波动更小,更加平坦,缺点是曲线进一步右移,因为现在平均的温度值更多,要平均更多的值,指数加权平均公式在温度变化时,适应地更缓慢一些,所以会出现一定延迟。如果β=0.5,则平均了2天的温度,得到黄线如下:

由于仅平均了两天的温度,平均的数据太少,所以得到的曲线有更多的噪声,有可能出现异常值(outliers),但是这个曲线能够更快适应温度变化。

指数加权平均数通过调整这个参数β,或者说后面的算法学习,会发现这是一个很重要的参数,可以取得稍微不同的效果,往往中间有某个值效果最好,β为中间值时得到的红色曲线,比起绿线和黄线更好地平均了温度。

2.4  理解指数加权平均

进一步分析2.3小节的例子,来理解如何计算出每日温度的平均值。

当β=0.9时,t从0到1到2到3,t的值在不断增加,为了更好的分析,使t从大到小开始写:

v_{100}=0.9v_{99}+0.1\theta_{100}

v_{99}=0.9v_{98}+0.1\theta_{99}

v_{98}=0.9v_{97}+0.1\theta_{98}

...

将所有括号展开:

V_{100}=0.1\theta_{100}+0.1\times 0.9\theta_{99}+0.1\times (0.9)^2\theta_{98}+0.1\times (0.9)^3\theta_{97}+...

这是一个加和并平均,100号数据,就是当日温度。计算v_100是通过把两个函数(数日的温度离散点,指数衰减函数)对应的元素相乘,然后求和。所有的这些系数0.1+0.1*0.9+0.1*0.9*0.9*+0.1*0.9*0.9*0.9+...相加起来和为1或者逼近1,称之为偏差修正(bias correction)

指数加权平均数公式的好处之一在于,它占用极少内存,电脑内存中只占用一行数字而已,然后把最新数据代入公式,不断覆盖就可以了,正因为这个原因,其效率,它基本上只占用一行代码,计算指数加权平均数也只占用单行数字的存储和内存,当然它并不是最好的,也不是最精准的计算平均数的方法。

2.5  指数加权平均的偏差修正

偏差修正(biascorrection)可以让平均数运算更加准确,本小节介绍它是怎么运行的。

在上图中,红色曲线对应β=0.9,绿色曲线对应β=0.98,如果执行这里的公式,当β=0.98时,得到的不是绿色曲线,而是紫色曲线。可以注意到紫色曲线的起点较低,来看看怎么处理。因为v_0=0,实际上计算的v_1值会小很多,所以第一天温度的估测不准。同样的v_2也不能很好的估测前两天的温度。

有个方法修正这种问题,是进行偏移校正(bias correction)。在估测初期,不用v_t,而是用\frac{v_t}{1-\beta^t}

其中,t是现在的天数。刚开始时t比较小,这样修正的就更大一些,紫色曲线上移;随着t增大,紫色曲线和绿色曲线基本重合。

在机器学习中,在计算指数加权平均数的大部分时候,大家不在乎执行偏差修正,因为大部分人宁愿熬过初始时期,拿到具有偏差的估测,然后继续计算下去。不过,在刚开始计算指数加权移动平均数的时候就考虑偏差,偏差修正能帮助你在早期获取更好的估测。

2.6  动量梯度下降法

有一种算法叫做Momentum,或者叫做动量梯度下降法,运行速度几乎总是快于标准的梯度下降算法,简而言之,算法的主要思想就是计算梯度的指数加权平均数,并利用该梯度更新权重。 

如上图,假设从这里(蓝色点)开始梯度下降法(gradient descent),如果进行梯度下降法的一次迭代,无论是batch或mini-batch下降法,也许会指向这里,现在在椭圆的另一边,计算下一步梯度下降,结果或许如此,然后再计算一步,再一步,计算下去,慢慢摆动(oscillate)到最小值(minimum),这种上下波动减慢了梯度下降法(gradient descent)的速度,就无法使用更大的学习率,如果要用较大的学习率(紫色箭头),结果可能会偏离函数的范围,为了避免摆动过大,你要用一个较小的学习率。

在纵轴上,希望学习慢一点,因为不想要这些摆动,但是在横轴上,希望加快学习,快速从左向右移,移向最小值,移向红点。所以使用动量梯度下降法,需要做的是,在每次迭代中,确切来说在第t次迭代的过程中,计算微分dW和db,要做的是计算公式:

v=\beta v+(1-\beta)\theta_t

v_{dW}=\beta v_{dW}+(1-\beta)dW

v_{db}=\beta v_{db}+(1-\beta)db

然后重新赋值权重:

W:= W-av_{dW}b:=b - av_{db}

这样做可以让梯度下降的每一步变得平滑。

最后,介绍如何用算法实现。

现在有两个超参数,学习率α和参数β(控制指数加权平均数),β常用0.9,在天气例子中是平均过去十天的温度,现在是平均10次迭代的梯度。在实践过程中一般效果不错,当然也可以尝试不同的值,不过0.9是很棒的鲁棒数(robust value)。关于偏差修正,在实际操作中一般不用,因为10次迭代之后,移动平均已经过了初始阶段。实际中,在使用梯度下降法或动量梯度下降法时,人们不会受到偏差修正的困扰。

2.7  RMSprop

还有一个叫做RMSprop(root mean square prop)算法,也可以加速梯度下降,本小节将介绍它是如何运作的。

 假设纵轴代表参数b,横轴代表参数W,可能有W1,W2等重要的参数,为了便于理解,使用b和W。具体操作如下:

如果想减缓b方向的学习(纵轴方向),同时加快,至少不减慢横轴方向的学习,RMSprop算法可以实现这一点。在t次迭代中,其权重W和常数项b的更新表达式为:

S_{dW}=\beta S_{dW}+(1-\beta)dW^2

S_{db}=\beta S_{db}+(1-\beta)db^2

W:=W-a\frac{dW}{\sqrt{S_{dW}}},b:=b-a\frac{db}{\sqrt{S_{db}}}

下面解释一下原理。我们希望在横轴(W方向)学习速度快,而在纵轴(b方向)减缓摆动,所以有了S_{dW}S_{db},所以我们W要除以一个较小的数(S_{dW}会相对较小),同理b要除以较大的数(S_{db}较大),这样就可以减缓纵轴上的变化。这些微分中,db较大,dW较小,也可观察函数的倾斜程度,纵轴要大于在横轴,公式处理的结果就是纵轴上的更新要被一个较大的数相除,就能消除摆动,而水平方向的更新则被较小的数相除。

RMSprop的影响就是更新最后会变成上图绿色线,纵轴方向上摆动较小,而横轴方向继续推进。还有个影响就是,可以用一个更大学习率,然后加快学习,而无须在纵轴上垂直方向偏离

再说明一点,实际中dW和db是一个高维的参数向量,因为对微分进行平方,然后使用平方根,这就是RMSprop(root mean square prop)。操作规程中,可以在S_{dW}S_{db}的分母加上很小很小的值(10^{-8}),可以确保数值稳定。RMSprop跟Momentum有很相似的一点,可以消除梯度下降中的摆动,包括mini-batch梯度下降,并允许你使用一个更大的学习率,从而加快你的算法学习速度。

2.8  Adam 优化算法

在深度学习的研究中 ,RMSprop以及Adam优化算法就是少有的经受住人们考验的两种算法,已被证明适用于不同的深度学习结构,这个算法值得推荐,因为很多人都试过,并且用它很好地解决了许多问题。 Adam优化算法基本上就是将Momentum和RMSprop结合在一起,本小节将介绍如何使用Adam算法 。

(1)初始化(initialize):

v_{dW}=0,s_{dW}=0,v_{db}=0,v_{db}=0,

在t次迭代中,需要计算微分,一般使用mini-batch梯度下降法。

(2)计算Momentum指数加权平均数

v_{dW}=\beta_{1}v_{dW}+(1-\beta_{1})dW

v_{db}=\beta_{1}v_{db}+(1-\beta_{1})db

(3)使用RMSprop进行更新

S_{dW}=\beta_{2}S_{dW}+(1-\beta_{2})(dW)^2

S_{db}=\beta_{2}S_{db}+(1-\beta_{2})(db)^2

(4)使用Adam算法,一般要计算偏差修正

V_{dW}^{corrected}=\frac{v_{dW}}{1-\beta_1^t},V_{db}^{corrected}=\frac{v_{db}}{1-\beta_1^t}

S_{dW}^{corrected}=\frac{S_{dW}}{1-\beta_2^t},S_{db}^{corrected}=\frac{S_{db}}{1-\beta_2^t}

(5)最后,更新权重

W:=W-\frac{av_{dW}^{corrected}}{\sqrt{S_{dW}^{corrected}}+\varepsilon },b:=b-\frac{av_{db}^{corrected}}{\sqrt{S_{db}^{corrected}}+\varepsilon }

所以Adam算法结合了Momentum和RMSprop梯度下降法,并且是一种极其常用的学习算法,被证明能有效适用于不同神经网络,适用于广泛的结构。

在使用Adam算法的时候,一般使用默认值。

  • \beta_1=0.9
  • \beta_2=0.999(Adam论文的作者,也就是Adam算法的发明者推荐)
  • \varepsilon =10^-8(Adam论文的作者推荐)。

然后尝试不同的学习率,看看哪个效果最好。

2.9  学习率衰减

加快学习算法的一个办法就是随时间慢慢减少学习率,我们将之称为学习率衰减(learning rate decay)。 本小节将介绍学习率衰减,先来通过一个例子来看看“为什么计算学习率衰减”

假设使用mini-batch梯度下降法,mini-batch数量不大,64或者128个,在迭代过程中会有噪音(蓝色线),下降朝向这里的最小值,但是不会精确地收敛,所以算法最后在附近摆动,并不会真正收敛,因为α是固定值,不同的mini-batch中有噪音。但要慢慢减少学习率α的话,在初期的时候,α学习率还较大,学习还是相对较快,但随着α变小,步伐也会变慢变小,所以最后曲线(绿色线)会在最小值附近的一小块区域里摆动,而不是在训练过程中,大幅度在最小值附近摆动。所以慢慢减少α的本质在于,在学习初期,能承受较大的步伐,但当开始收敛的时候,小一些的学习率能让步伐小一些。

🙋那怎么实现学习率衰减呢?

学习率设为:

\alpha=\frac{1}{1+decayrate*epoch-num}\alpha_0

其中decayrate为衰减率,epoch-num为训练的代数,\alpha_0为初始学习率。在这里衰减率是一个需要调整的超参数。

除了这个公式,还有其他学习率衰减的公式:

\alpha=0.95^{epoch-num}\alpha_0

\alpha=\frac{k}{\sqrt{epoch-num}}\alpha_0

\alpha=\frac{k}{\sqrt{t}}\alpha_0

其中,k为可调参数,t为mini-bach number。有时候也可以进行手动衰减。

2.10  局部最优的问题

 在深度学习研究早期,人们总是担心优化算法会困在极差的局部最优,不过随着深度学习理论不断发展,人们对局部最优的理解也发生了改变。本小节将介绍现在人们怎么看待局部最优的问题及深度学习中的优化问题。

梯度下降法或者某个算法可能困在一个局部最优(local optima)中,而不会抵达全局最优(global optima)。如果要作图计算一个数字,比如说这两个维度,就容易出现有多个不同局部最优的图,而这些低维的图曾经影响了我们的理解,但是这些理解并不正确。事实上,如果你要创建一个神经网络,通常梯度为零的点并不是这个图中的局部最优点,实际上成本函数的零梯度点,通常是鞍点(saddle point)。在高维空间的函数,如果梯度为0,它可能是凸函数也可能是凹函数。设想在2万维的空间中,要得到局部最优,所有2万个方向都是凸函数,这发生的几率也许很小(2^{-20000})。因此在高维空间,更可能碰到鞍点,而不是局部最优点,图像如上图右。

如果局部最优不是问题,那么问题是什么?结果是平稳段会减缓学习,平稳段是一块区域,其中导数长时间接近于0,如果在此处,梯度会从曲面从从上向下下降,因为梯度等于或接近0,曲面很平坦,得花上很长时间慢慢抵达平稳段的这个点,因为左边或右边的随机扰动,可以沿着这段长坡走,直到这里,然后走出平稳段(红色笔)。如下图所示:

首先,你不太可能困在极差的局部最优中,当你在训练较大的神经网络,存在大量参数,并且成本函数J被定义在较高的维度空间。

其次,停滞区是一个问题,这样使得学习十分缓慢,这也是像Momentum或是RMSprop,Adam这样的算法,能够加速学习算法的地方。在这些情况下,更成熟的优化算法,如Adam算法,能够加快速度,尽早往下走出平稳段。

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

3.1  调试处理

 在训练神经网络时,超参数的调试十分重要,下面分享一些指导原则。超参数重要性如下:

学习因子α是最重要的超参数,也是需要重点调试的超参数。

动量梯度下降因子β、各隐藏层神经元个数hidden unitsmini-batch size的重要性仅次于α。

③然后就是神经网络层数layers学习因子下降参数learning rate decay

④最后,Adam算法的三个参数\beta_1,\beta_2,\sigma一般常设置为0.9,0.999和10^{-8},不需要反复调试。

⚠️注意:这里超参数重要性的排名并不是绝对的,具体情况,具体分析

🙋如果尝试调整一些超参数,该如何选择调试值?

一种常见的做法是在网格中取样(均匀间隔)。如果你有两个超参数,这里放置的是5*5的网络(见下图左),你可以尝试所有的25个点,然后选择效果最好的参数。当参数的数量相对较少时,这个方法很实用。

如果超参数不止两个,你搜索的是一个立方体,在三维立方体中取值,三个超参数都可以试验大量的更多的值。(见上图右)

而在实践中,要寻找的可能不仅仅是三个超参数,有时候最难判断的是哪一个超参数对于模型更重要。这种情况下,使用随机取值(sampling at random)而不是网格取值(sampling in the grid)表明,可以帮助探究更多重要超参数,无论结果如何。

另一种做法是采用由粗糙到精细的策略(coarse to fine sampling scheme)。具体做法就是放大表现较好的区域(见下图小蓝色方框内),再对此区域做更密集的随机采样。

通过试验超参数的不同取值,你可以选择对训练集目标而言的最优值,或对于开发集而言的最优值,或在超参搜索过程中你最想优化的东西。

3.2  为超参数选择合适的范围

 随机取值(sampling at random)并不是在有效范围内的随机均匀取值,而是选择合适的标尺,用于探究这些超参数,这很重要。对于某些超参数(隐藏单元的数量或者神经网络的层数)是可以进行尺度均匀采样的,它们都是正整数,是可以进行均匀随机采样的(即超参数每次变化的尺度都是一致的),这是合理的。

但是某些超参数需要选择不同的合适尺度进行随机采样。以搜索超参数α(学习速率)为例,假设对α怀疑的下限是0.0001,上限是1。如果画一条从0.0001到1的数轴,沿其随机均匀取值,那90%的数值将会落在0.1到1之间,结果就是,在0.1到1之间,应用了90%的资源,而在0.0001到0.1之间,只有10%的搜索资源,这看上去不太对。反而,用对数标尺搜索超参数的方式会更合理,因此这里不使用线性轴,分别依次取0.0001,0.001,0.01,0.1,1,在对数轴上均匀随机取点,这样,在0.0001到0.001之间,就会有更多的搜索资源可用,还有在0.001到0.01之间等等。

线性区间为[a, b],令m=log(a),n=log(b),则对应的log区间为[m,n]。对log区间的[m,n]进行随机均匀采样,然后得到的采样值r,最后反推到线性区间,即10^r10^r就是最终采样的超参数。

m = np.log10(a)
 
n = np.log10(b)
 
r = np.random.rand()
 
r = m + (n-m)*r
 
r = np.power(10,r)

除了α之外,动量梯度因子β(用于计算指数的加权平均数)的取值也是一样,在超参数调试的时候也需要进行非均匀采样,因为当β越接近1时,所得结果的灵敏度(sensitivity)会变化,即使只有微小的变化,所以需要更加密集地取值。一般β的取值范围在[0.9, 0.999]之间,那么1−β的取值范围就在[0.001, 0.1]之间。那么直接对1−β在[0.001, 0.1]区间内进行log变换即可,如下图:

3.3  超参数训练的实践:Pandas VS Caviar

深度学习领域中,发展很好的一点是,不同应用领域的人们会阅读越来越多其它研究领域的文章,跨领域(cross-fertilization)去寻找灵感。在超参数搜索中,人们通常采用的两种重要但不同的方式(two major different ways)。

其中一种方式是精心照料某个单一的模型(babysit one model)。通常需要处理一个庞大的数据集,但没有充足的计算资源,只能一次训练一个或者少量模型,然后每天花时间观察它,不断调整参数。

另一个方式就是并行训练多个模型(training many models in parallel)。通常已经设置了一些超参数,让它自己运行,或者是一天甚至多天,然后会获得像这样的学习曲线(learning curve),这可以是损失函数J(cost function )或训练误差的损失(cost of your training error)或数据误差的损失(cost of your data set error),但都是曲线轨迹的度量(metric)。同时可以开始一个有着不同超参数设定的不同模型,所以,第二个模型会生成一个不同的学习曲线,也许是像这样的一条(紫色曲线)。与此同时,还可以试验第三种模型,其可能产生一条像这样的学习曲线(红色曲线),还有另一条(绿色曲线),等等。或者可以同时平行试验许多不同的模型,橙色的线就是不同的模型。用这种方式可以试验许多不同的参数设定,然后只是最后快速选择工作效果最好的那个。

上图中左边的方法称为熊猫方式(panda approach);右边称之为鱼子酱方式(caviar strategy) 。

这两种方式的选择,是由拥有的计算资源决定的,如果拥有足够的计算机去平行试验许多模型,那绝对采用鱼子酱方式,尝试许多不同的超参数,看效果怎么样。

3.4  正则化网络的激活函数

 在深度学习兴起后,最重要的一个思想是它的一种算法,叫做Batch归一化(Batch Normalization),由Sergey loffe和Christian Szegedy两位研究者创造。Batch归一化会使参数搜索问题变得很容易,使神经网络对超参数的选择更加稳定,超参数的范围会更加庞大,工作效果也很好,也会是你的训练更加容易,甚至是深层网络。

在训练之前的logistic回归模型时(如上图:左上),归一化输入特征可以加快学习过程,可以把问题的轮廓从很长的东西变成更圆的东西,易于算法优化。在训练深层网络时(如上图:左下)不仅有输入特征还有激活值a。既然归一化特征输入可以更有效的训练w和b,那么能否归一化a值,以更快的速度训练w和b?这就是Batch归一化的作用。实践中,通常是归一化z。

接下来,介绍Batch归一化的使用方法:
 

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

3.5  将 Batch Norm 拟合进神经网络

 3.4小节介绍了在单一隐藏层进行Batch归一化,本小节将介绍怎样在深度网络训练中拟合。

 实践中,Batch归一化通常和训练集的mini-batch一起使用,如下图:

最后介绍用Batch归一化来应用梯度下降法。

如上图,运行for循环,

(1)在mini-batch上应用正向传播,每个隐藏层都应用正向传播,使用Batch归一化。

(2)然后反向传播计算梯度(这部分去掉b)。

(3)最后更新这些参数。除了传统的梯度下降算法之外,还可以使用我们之前介绍过的动量梯度下降、RMSprop或者Adam等优化算法。

3.6  Batch Norm 为什么奏效?

Batch归一化会起作用,一个原因是,看到经归一化的输入特征,它们的均值为0,方差为1,这将大大加速训练过程。第二个原因是 ,它可以使权重比网络更滞后或更深层。

如上图,假如用一个浅层神经网络(类似逻辑回归)来训练识别猫的模型。假设在所有黑猫的图像上训练了数据集,现在对有色猫进行测试,正样本(positive examples)不只是左边的黑猫,还有右边其他颜色的猫,这样测试的效果可能不好。所以数据分布改变叫做协变量转移(covariate shift),如果x的分布改变了,那么可能需要重新训练学习算法。

如上图,试想一个神经网络,从第三层来看学习过程。此网络已经学习了参数w^{[3]}b^{[3]},从前层取得一些值,接下来需要做什么,使输出\hat{y}接近真实值y。

如上图,先遮住左边的部分,从第三隐藏层来看,它输入一些值a_1^{[2]},a_2^{[2]},a_3^{[2]},a_4^{[2]}。第三层隐藏层的任务是找到一种方式,使这些值映射到\hat{y}

然后将蓝色方块揭开,注意到这个网络的参数还有w^{[2]}b^{[2]}w^{[1]}b^{[1]},如果这些参数改变,则a^{[2]}的值也会改变。所以从第三层隐藏层的角度来看,这些隐藏单元的值在不断地改变,就有了“Covariate shift”的问题,之前讲过的。Batch归一化做的是减少了这些隐藏值(hidden unit values)分布变化的数量。它限制了在前层的参数更新,会影响数值分布的程度,第三层看到的这种情况,因此得到学习。

Batch归一化还有一个作用,它有轻微的正则化效果,具体表现在:

  • 每个mini-batch都进行均值为0,方差为1的归一化操作。
  • 每个mini-batch中,对各个隐藏层的Z^{[l]}添加了随机噪声,效果类似于Dropout,有轻微的正则化效果。
  • mini-batch越小,正则化效果越明显。

但是,不要将Batch归一化当做正则化,它的正则化效果比较微弱,把它当做归一化隐藏单元激活值并加速学习的方式。

⚠️注意:Batch归一化一次只能处理一个mini-batch数据,它在mini-batch上计算均值和方差。

3.7  测试时的 Batch Norm

Batch归一化将你的数据以mini-batch的形式逐一处理,但在测试时,可能需要对每个样本逐一处理。本小节将介绍如何修改神经网络来实现这一功能。

3.8  Softmax 回归

有一种更普遍的逻辑回归方法叫Softmax回归(Softmax regression),能在试图识别某一分类时做出预测,或者说是多种分类中的一个,不只是识别两个分类。 

如上图,我们想识别猫(cats),狗(dogs)和小鸡(baby chicks),我把猫加做类1,狗为类2,小鸡是类3,如果不属于以上任何一类,就分到“其它”或者说“以上均不符合”这一类,我把它叫做类0。用大写C来表示类别总个数,这里C=4。想要输出层单元的数字告诉这4种类型中每个的概率有多大,这里从上到下分别为:其他、猫、狗、小鸡。因此输出\hat{y}是一个4*1维向量,输出必须是数字,而且加起来等于1。

让神经网络做到这一点的标准模型要用到Softmax层,以及输出层来生成输出。

在神经网络的最后一层,和往常一样,计算

z^{[l]}=W^{[l]}a^{[l-1]}+b^{l} 

算出z之后,应用Softmax激活函数,这个激活函数对于Softmax层而言有些不同。首先,要计算一个临时变量(temporary variable),记为t,公式如下:

t=e^{z^{[l]}}

上式对所有元素求幂,t也是一个4*1维向量,下面计算输出:

a^{[l]}=\frac{e^{z^{[l]}}}{\sum_{i=1}^4t_i}

z^{[l]}a^{[l]}的计算步骤,整个计算过程,从计算幂到得出临时变量,再归一化,可以将此概括为一个Softmax激活函数。

这一激活函数的与众不同之处在于,需要输入一个4×1维向量,然后输出一个4×1维向量。之前,激活函数都是接受单行数值输入(single row value input),例如Sigmoid和ReLu激活函数,输入一个实数(real number),输出一个实数。Softmax激活函数的特殊之处在于,因为需要将所有可能的输出归一化,就需要输入一个向量,最后输出一个向量。

如上图,假设有两个输入x1,x2,它们直接输入到Softmax层,它有三四个或者更多的输出节点,输出\hat{y}。输出分类的Softmax层能够代表这种类型的决策边界(decision boundaries),请注意这是几条线性决策边界。

3.9  训练一个 Softmax 分类器

本小节将更深入地了解Softmax分类(classification),并学习如何训练一个使用了Softmax层的模型。

注意到向量z中,最大的元素是5,而最大的概率也就是第一种概率。

Softmax回归或Softmax激活函数将logistic激活函数推广到类,而不仅仅是两类,结果就是如果,那么的Softmax实际上变回了logistic回归。

接下来,来看怎样训练带有Softmax输出层的神经网络,具体来说,需要先定义训练神经网络使会用到的损失函数

训练集中某个样本的真实标签是[0 1 0 0],上个视频中这表示猫,目标输出\hat{y}=[0.3 0.2 0.1 0.4],这里只分配20%是猫的概率,所以这个神经网络在本例中表现不佳在Softmax分类中,一般用到的损失函数是:

L(\hat{y},y)=-4\sum_{j=1}^4y_j\log{\hat{y_j}}

对上面的样本而言,y1=y3=y4=0,y2=1,于是有:

L(\hat{y},y)=-4\sum_{j=1}^4y_j\log{\hat{y_j}}=-y_2\log\hat{y_2}=-\log\hat{y_2}

这就意味着,如果学习算法试图将损失函数L变小,因为梯度下降法是用来减少训练集的损失的,要使它变小的唯一方式就是使\hat{y_2}尽可能大,因为这些是概率,所以不可能比1大,但这的确也讲得通,因为在这个例子中是猫的图片,你就需要这项输出的概率尽可能地大。

概括来讲,损失函数所做的就是它找到你的训练集中的真实类别,然后试图使该类别相应的概率尽可能地高,如果熟悉统计学中最大似然估计(maximum likelihood estimation statistics)就会知道,这其实就是最大似然估计的一种形式。

最后,来看一下,在有Softmax输出层时如何实现梯度下降法。

这个输出层会计算z^{[l]},它是C*1维的,这个例子中是4*1,然后你用softmax激活函数得到y帽,又能由此计算出损失(loss)。

有了Softmax分类,你就可以运用学习算法将输入分成不止两类,而是C个不同类别。

3.10  深度学习框架

常见的深度学习框架:

  • Caffe/Caffe2
  • CNTK
  • DL4J
  • Keras
  • Lasagne
  • mxnet
  • PaddlePaddle
  • TensorFlow
  • Theano
  • Torch

选择深度学习框架的标准:

①一个重要的标准就是便于编程(Ease of programming),这既包括神经网络的开发和迭代,还包括为产品进行配置,为了成千上百万,甚至上亿用户的实际使用,取决于你想要做什么。

②第二个重要的标准是运行速度(Running speed),特别是训练大数据集时,一些框架能让你更高效地运行和训练神经网络。

③这个框架是否真的开放(Truly open),要是一个框架真的开放,它不仅需要开源(open source ),而且需要良好的管理(good governance)。

  • 16
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值