文章

训练神经网络

训练神经网络

我们现在在哪

今天我们将学习有关如何训练神经网络的一些细节问题。

我们已经讨论过如何使用计算图来表达一个函数,其实任何函数我们都可以用计算图来表达,并且我们已经很明确地探讨过神经网络,就是一种(计算)图,它包含若干个线性层,而层与层之间通过非线性函数进行连接实现堆叠。我们在上节课中也介绍了卷积神经网络,这是一种特殊的网络,它使用卷积层,在贯穿整个网络的层次结构中保持(输入)的空间结构。

Desktop View 简单回顾:计算图(来自cs231n)

Desktop View 简单回顾:神经网络(来自cs231n)

Desktop View 简单回顾:卷积神经网络(来自cs231n)

这里我们看到一个卷积层,卷积层输出的每个激活图是通过使用一个权重滤波器在输入(矩阵)的空间位置上滑动而生成的。

Desktop View 简单回顾:卷积层(来自cs231n)

我们通常会看到,每个卷积层会有多个滤波器,每个滤波器会生成各自独立的激活图,所以我们可以从一个具有特定深度的输入得到一个激活图作为输出,其中一些空间中的维度被保留,而这一层的深度等于滤波器的个数。我们希望要实现的是,学习所有这些权重或参数的值。

Desktop View 简单回顾:卷积层深度,生成激活图(来自cs231n)

然后我们可以通过优化来更新网络参数,这点在之前提到过一点,因此我们希望在损失区域中找到一个点,使得产生损失较低。我们通过往负梯度的方向更新来实现这个想法。

Desktop View 简单回顾:学习优化,随机梯度下降(来自cs231n)

整个过程我们称为最小批量随机梯度下降,它的步骤是对数据进行连续的批量抽样,通过使用计算图或神经网络将数据进行正向传播,最终我们得到损失值通过整个网络的反向传播来计算梯度,然后使用这个梯度来更新网络中的参数或者权重。

Desktop View 简单回顾:最小批量随机梯度下降(来自cs231n)

从现在开始的后面几节课,我们将讨论一些网络训练过程中的细节。这包括我们在刚开始时要如何建立起神经网络,又要选择什么样的激活函数,以及怎么做数据预处理、权重初始化、正则化和梯度检查。我们还会讨论训练中的动态变化,那么我们该如何监控这个学习过程呢?我们如何选择参数的特定更新规则和怎样做超参数优化来选取最佳超参数。随后我们将介绍模型评估和模型集成。

Desktop View 接下来:训练神经网络(来自cs231n)

Desktop View Overview(来自cs231n)

第一部分:讲解激活函数、数据预处理、权重初始化、批量归一化、训练过程监控和超参数优化

激活函数

首先讲激活函数。我们之前看到了,任意特定层是如何产生输出的。我们输入数据,在全连接层或者卷积层将输入乘上权重值,然后将结果输入一个激活函数或者非线性单元。

Desktop View 激活函数(来自cs231n)

这里有一些激活函数,我们在之前的一些例子中使用sigmoid,也见过非线性ReLU,这节课将见到不同的非线性函数,并在它们之间进行权衡。

Desktop View 不同的激活函数(来自cs231n)

sigmod

首先是我们之前见过的sigmoid函数,表达式如上。每一个元素输入到sigmoid非线性函数中,每个元素被压缩到[0,1]范围内。如果有一个非常大的输入值,那么输出将会十分接近1;如果输入值是一个绝对值非常大的负值,将会接近0;在横坐标接近0的区域,我们可以将这部分看做是线性区域,它有点像线性函数。

Desktop View 激活函数: sigmod,以及存在的第一个问题(来自cs231n)

Sigmoid函数一度非常流行,因为sigmoid函数在某种意义上可以被看做是一种神经元的饱和放电率。如果它是一个介于01的值,你可以认为它是一个放电率。稍后我们将介绍其他非线性函数,例如ReLU从生物学上更合理。

如果我们更加深入地研究这个非线性函数,实际上它存在若干问题。

首先是饱和神经元将使得梯度消失,这意味着什么呢?

Desktop View 激活函数: sigmod门(来自cs231n)

这是一个sigmoid门,也就是计算图中的一个节点,我们将数据X作为输入,然后我们可以得到从sigmoid门传出的输出,那么它所返回的梯度流是什么样的呢?我们让Lσ求偏导,上游梯度往回传播,然后乘上σx的偏导这将是一个局部sigmoid函数的梯度。我们把它们链接起来作为传递回来的下游梯度。

那么当x=-10时会怎么样?这是一个绝对值很大的负值,梯度的取值会如何?梯度变为0,是因为这个负值太接近sigmoid函数的负饱和区域,这个区域本质上是平的,所以梯度会变为0。我们将返回的上游梯度乘以一个约等于0的数,所以会得到一个非常小的梯度。所以在某种意义上说,经过链式法则后会让梯度流消失,因此零梯度便会传递到下游的节点。

那么,当输入x=0时又会如何?在这个区域很好,这个区域在0附近,你将得到一个合理的梯度,并可以很好的进行反向传播。

x=10时会怎么样?梯度是0。当输入是一个很大的负值,或很大的正值时,它们位于sigmoid函数的平滑区域,这些区域会使梯度消失,从而无法得到梯度流的反馈。

第二个问题是,sigmoid是一个非零中心的函数,这会导致什么问题?

Desktop View 激活函数: sigmod,存在的第二个问题(来自cs231n)

考虑一下,当输入神经元的数值始终为正时,会发生什么?

Desktop View 激活函数: 当输入神经元的数值始终为正时,会发生什么?(来自cs231n)

在这个例子中,我们所有的输入都为正,它会乘以某个权重W,然后我们将通过激活函数来运行它,那么权重W的梯度会怎样呢?考虑一下这个线性层的局部梯度是多少?

我们用L对任何激活函数求偏导损失将减小,然后我们的局部梯度实际上就是x本身。如果所有的x值都是正的,这意味着结果要么全部是正的,要么全部是负的。我们把上游梯度传回来,就是对我们的损失L关于f进行求导,结果就是dL/df,它的值为正数或者为负数,我们任意传下来一些梯度,再用它乘以我们得到的局部梯度,如果我们想求关于w的梯度,也就是df/dw=x,如果x的值总是正数,那么对w的梯度就等于将dL/df乘以df/dw,这就相当于把上游梯度的符号传了回来,意味着所有关于w的梯度,因为他们或者全是正数或者全是负数,那么它们总是朝着同一个方向移动。当你在做参数更新时,你可以选择增加它们,你可以用同一个正数去增加所有w的值,或者用不同的正数去增加,或者用相似的想法去减小w的值,但这里会出现的问题是,这种方法对于梯度更新来说十分低效。

Desktop View 实际例子: 当输入神经元的数值始终为正时,会发生什么?(来自cs231n)

我们来看右边的这个实际的例子,我们假设w是二维的,所以可以用平面坐标轴来表示w,如果我们用全为正数或全为负数去迭代,我们得到两个象限,即在坐标轴上有两块区域,一块全部为正(一象限),一块全部为负(三象限),这些是根据梯度更新来得到的两个方向。在这种情况下,我们假设最佳的w实际上是图中的蓝色向量,我们从已知的某些点开始,或者从红色箭头的起始端的顶部开始,我们不能沿着w这个方向直接求梯度,因为这个方向并不是允许的两个梯度方向中的一个。所以只能这么做:在允许的方向进行一系列的梯度更新,例如沿着图中这些红色箭头的方向,它们是我们允许进行梯度更新的方向,目标是最终得到最佳的w。这也是为什么一般情况下我们使用均值为0的数据。我们想要我们的输入x的均值为0,这样我们就能得到正的和负的数值,这样就不会陷入梯度更新会出现的问题中,它们将会沿着同一个方向移动。

目前我们已经讨论了sigmoid函数的两个重要的问题。

一是如果我们输入的正数太大或负数太小,饱和神经元会造成梯度消失的问题;

二是它们也不是以0为中心的,这种情况下梯度更新的效率会非常低;

第三个问题是,这里使用了指数函数,计算代价稍微有点高,在你的网络整体框架中,这个问题通常并不是主要的问题,因为我们进行卷积和点乘的计算代价会更大,这只是需要注意的一小点。

Desktop View 激活函数: sigmod,存在的第三个问题(来自cs231n)

tan h

现在我们来看第二个激活函数——tan h

Desktop View 激活函数: tan h(来自cs231n)

它和sigmoid激活函数看起来十分相似,不同之处在于现在它被挤压到[-1,1]的范围内,所以主要的不同就是tanh函数是以0为中心的,所以不会有sigmoid函数的第二个问题。但是当它饱和时依然会出现梯度消失的问题,在这些区域内我们仍然会看到梯度基本上是扁平的,因此这将阻止梯度的传递,所以可以看到它比sigmoid函数要好些,但tanh仍然存在一些问题。

ReLU

下面看下ReLU函数。

Desktop View 激活函数: ReLU(来自cs231n)

上节课我们在讨论卷积神经网络时,在卷积层之间插入了非线性的ReLU函数,ReLU函数的形式为f(x)=max(0,x),根本来讲,它在你的输入上按元素进行操作,如果输入是负数,将会得到结果0,如果输入是正数,结果还是你输入的数。

跟前面的两种激活函数对比,在ReLU不会在正的区域产生饱和现象,因此在我们输入空间的一半都不会存在饱和,这是一个很大的优势。它的计算成本也不高,比其它低。前面sigmoid函数计算中有指数e,但是ReLU中只是简单的max操作,因此ReLU在计算速度上会非常快。在实际中我们使用ReLU,因为它比sigmoidtanh收敛的快得多,大约快6倍。同时也有证据表明,它比sigmoid更具备生物学上的合理性。如果你看一个神经元,看它的输入是什么样的,再看看它的输出是什么样的,你试图通过神经科学的实验来度量,你会看到相比于sigmoidReLU会对结果有更精确的估计。

所以在2012年左右,AlexNet出现之后,ReLU开始被大量使用。AlexNet是第一个在ImageNet和大规模数据上表现出色的重要的卷积神经网络。它在实验中使用了ReLU

然而,对于ReLU仍然存在一些问题,就是它不再是以0为中心了。还有一个问题,就是在输入是正半轴时没有产生饱和,但是负半轴却不一样。

Desktop View 激活函数: ReLU,以及存在的问题(来自cs231n)

进一步思考,
X=-10时会发生什么?此时梯度为0
X=10时会发生什么?此时不会出现问题,因为我们在线性区域内。
X=0时会发生什么?这时结果是不确定的,实践中可以取梯度为0,因此基本上在一半的区域中都会出现梯度消失的问题。

Desktop View 激活函数: ReLU门(来自cs231n)

我们可以把这种现象称为dead ReLU

当我们在负半轴的区域中时,在这里主要有几个可能的原因。如果我们观察我们的数据云,这个就是我们全部的训练数据。我们来看这些ReLU可能处于的位置,这些ReLU基本上在平面中的一半区域能够产生激活,在这个平面区域能激活的有相应的定义了这些ReLU,我们可以看到这些dead ReLU基本上不在数据云里。在这种情况下,它从来不会被激活和更新。相比激活的ReLU,一些数据可以是正数,那么就能传递,一些则不会。主要有以下几个原因。

第一个原因是,当你有一个不好的初始化时,如果你的权重设置很差,它恰巧不在数据云里,在这里就会出现dead ReLU的情况,这将导致我们不能得到一个能激活神经元的数据输入,同时也不会有一个合适的梯度传回来。它既不会更新,也不会被激活。

当你的学习率很大时,在这种情况下,你从一个ReLU函数开始,但因为你在进行大量的更新,权值不断波动,然后ReLU单元会被数据的多样性所淘汰,这些会在训练时发生,它在开始很正常,但在某个时间点之后开始变差最后挂掉。

所以实际上,你冻结了一个已经训练好的网络,然后你将数据传输进去,你可以看到实际上网络中多达10%20%的部分是这些挂了的ReLU单元。所以你知道这有个问题,但大多数使用ReLU的网络都有这类问题,它们中的部分会在实际中挂掉。在实际运用时,人们回去深入检查它,这是个研究性问题,但它还是能用作训练网络。

Desktop View 激活函数: ReLU数据云(来自cs231n)

注意,上面说的数据云只是你的训练数据。

Q:通过数据云如何判断一个ReLU是否会挂掉?

A:上面是一个简单的二维的例子。我们将输入内容传给ReLU,也就是非常基础的w1x1加上w2x2,然后我们应用它,所以这个定义了这里的分割超平面,然后我们从中取出一半,这些为正,而另一半将被抛弃。所以你知道无论权重为多少,这些数据的所在,也就是这些超平面所处的位置,因此经过训练之后,相对于数据云,有一些ReLU会处在不同的位置。

关于sigmoid函数提到了两个问题,其中之一是神经元会饱和,而问题是这样的情况并非如此,你所有的输入均为正数的时候,它们都位于大于0的区间上,所以你仍然可以得到一个饱和的神经元,因为正区间在输入域为1时达到平顶。当你的正数很大时也将得到零梯度。

在实际运用时,人们也喜欢使用较小的正偏置来初始化ReLU,以增加它在初始化时被激活的可能性,并获得一些更新。这基本上只是让更多的ReLUs在一开始就能放电的偏置项,然而在实践中,有些人认为这一点有用,有些人说没用,通常人们并不总是使用这个,多数时候人们只是将偏置项初始化为0

Desktop View 激活函数: ReLU用较小的正偏置初始化神经元(来自cs231n)

Leaky ReLU

下面介绍Leaky ReLU,这是基于ReLU的改进函数。这和原始的ReLU函数看起来很相似,它们唯一的区别是,有别于ReLU函数在负区间中保持平直,而我们将在这里给出一个微小的负斜率。这解决了之前提出的很多问题,在这里我们没有任何的饱和机制,即使是在负空间,这计算仍然很高效,它仍然比sigmoidtanh收敛的快,与ReLU非常相似,并且没有挂掉的问题。

Desktop View 激活函数: Leaky ReLU(来自cs231n)

这里有另一个例子——参数整流器,简称PReLU。这种情况下它和Leaky ReLU很相似,我们再次在负区间有这个倾斜的区域,但是现在在负区间的斜率是通过alpha参数确定的,所以我们不需要指定它,不用硬编码它,而是把它当做一个可以反向传播和学习的参数,这给了它更多的灵活性。

还有一种叫做指数线性单元,简称ELU。我们已经有这些不同的线性单元(LUs),并且这一个它具有ReLU的所有优点,但是现在它的输出均值还接近为0,这实际上是一个优势。Leaky ReLUPReLU它们中许多允许你获得均值为零的输出,但与Leaky ReLU相比较,ELU没有在负区间倾斜,这里实际上是在建立一个负饱和机制,这里有一些具有争议的观点认为,这样使得模型对噪音具有更强的鲁棒性,然后你得到这些更健壮的反激活状态。某种意义上说,这是一种介于ReLUsLeaky ReLUs之间,具有Leaky ReLU所具有的曲线形状,使输出均值更接近零,但是它也有一些这样的比ReLUs更饱和的行为。

Desktop View 激活函数: ELU(来自cs231n)

以上这些是所有ReLU的变种,可以发现在实际运用中,每个都有特定的优点和缺点,人们会在实验中运行它们,然后依据结果来判断哪个效果更好,尝试不断修正它,并提出新的想法。

下面再看一个叫做最大输出神经元,这个看起来有点不一样,因为它和其他具有不相同的形式,这里并没有先做点积运算,然后把这个元素级的非线性特征放在它的前面。相反它看起来像这样,它所做的是在这两个函数中取最大值,所以它的作用是泛化ReLULeaky ReLU。因为你只是提取这两个线性函数的最大值,所以它给我们的是另一个线性机制的操作。这种方式不会饱和,也不会消亡,问题在于你会把每个神经元的参数数量翻倍。

Desktop View 最大输出神经元(来自cs231n)

所以说,若每个神经元原来有权重集W,但现在有了W1W2,相当于原来的两倍。所以在实际操作中,当我们在考察所有这些激活函数时,一般最好的经验法则是使用ReLU,这几乎是所有能用的方法中最为标准的一种。并且通常情况下,你会非常谨慎地调整学习速率,让我们来看一下应该怎么做。后面将会谈及更多关于调整学习速率的内容。

Desktop View TLDR 实践(来自cs231n)

数据预处理

Desktop View 数据预处理(来自cs231n)

下面介绍一点关于数据预处理的内容。我们设计的激活函数是我们网络中的一部分,现在我们想要训练这个网络,我们有用于开始训练的输入数据。一般来说,我们总是想要预处理数据,这些东西很可能已经见过。如果你们上过机器学习或者类似课程的话,一些预处理的标准类型是你拿到你的原始数据,想要零均值化数据,然后还可能想要归一化数据,通过标准差来归一化。

我们为什么想要这么干?

Desktop View 第一步,预处理数据(来自cs231n)

对于零中心化,你应该记得我们早些时候讲到,若所有输入数据都是正的,我们得到的权重上的所有梯度也全是正的,然后我们得到的基本是次最优的优化,并且一般来说,即使不是非全零或是全负,任意的偏差都会导致这类问题。

Desktop View 回顾:当输入神经元的数值始终为正时(来自cs231n)

接着说归一化数据的问题。基本上当你想归一化数据时,特别是在机器学习中,因此所有的特征都在相同值域内,并且这些特征贡献相同。实际上,因为对于我们本课程中正处理的图像,在大部分情况下,我们的确会做零中心化的处理,但实际上我们不会真的去过多地归一化像素值,因为一般对图像来说,就在每个位置你已经得到了相对可比较的范围与分布,所以没必要归一化太多,这是因为对比之下,一般的机器学习问题会有差别很大的特征,并且这些特征会处于的范围差别也很大。

还有在机器学习中,你可能还会看到更为复杂的东西,像是PCA或是白化,但又说回图像应用领域,我们向来坚持以零均值化而不做归一化,我们也不会做一些更为复杂的预处理。其中一个原因是,一般来说对于图像我们不会真的想要所有的输入,我们就用像素值来举例,将其投影到一个更为低维的空间,这个空间有我们正在处理的各种新的特征,我们就是想在空间上应用卷积网络,并且得到我们原图像的空间结构。

Q:我们在训练阶段做数据预处理,还会在测试阶段做同样的事吗?

A:一般来说,在训练阶段我们会决定我们的均值,然后我们会将一样的均值应用到测试数据中去,所以我们会用从训练数据中得到的相同的经验均值来归一化。

总结来说,一般对于图像处理,我们就是做零均值化的预处理,并且我们可以减去整张均值图像的值,从训练数据可计算均值图像,其尺寸和你的每张图像都是相同的。

举个例子,一张32*32*3的图像,你会得到一组数,然后对你要传到网络中的每张图,减去均值图像这组数的值,在测试时间,你会对训练时间中决定的数组做相同的事。实际上我们也通过减去单通道的均值代替用一整张均值图像来将图像集零中心化,我们只是取每个通道的均值,这么做是因为发现纵观整张图片来看,相似度足够减去均值图像,和只是减去单通道值相比没有太大的不同,并且更容易传送和处理,所以你也会在像是VGG网络中见到这种操作。VGG网是在Alex网之后出现的。

Q:刚才讲到的单通道,通道指的是RGB

A:我们的图像就是,本例中是32*32*3的,所以相当于宽高都是32,而我们的深度就是三通道的RGB,所以我们会有一个红色通道的均值、一个绿色通道的均值以及一个蓝色通道的均值。

Q:当我们减去均值图像的值时,均值是怎么得到的?

A:均值从你所有的训练图像中得到,所以你拿到所有的训练图像,然后计算它们的均值。

Q:数据预处理解决了sigmoid的问题了吗?

A:数据预处理做的是零均值化,我们也说过想让sigmoid得到零均值,那么这的确能在我们通过的第一层解决这个问题,所以现在我们对网络第一层的输入将是零均值化。但我们之后将会看到这个问题以更恶化和严重的形式出现了。当我们用的是深度网络时,你将会在后续遇到很多的非零均值的问题,所以在这种情形下,这种处理并不足够,这仅仅解决了第一层网络的问题。

Desktop View TLDR 实践(来自cs231n)

权重初始化

Desktop View 权重初始化(来自cs231n)

下面看一下初始化网络的权值。

Desktop View 权重初始化为0,会发生什么?(来自cs231n)

以标准的双层神经网络为例,我们看到所有这些用来学习的权值,但是我们需要给它们初始值,然后我们再用我们的梯度更新来更新它们。

第一个问题,当我们用零为权值,W的初始值会发生什么?

我们一下子把所有参数都设为0,会有什么问题?这会导致所有神经元做同样的事,但不会消亡,这取决于你们的输入值。你可能处于神经元的任一状态,所以它们可能并没有死掉,但关键的部分是它们将做相同的事情。由于你的权重都是零,给定一个输入,每个神经元将在你的输入数据上有相同的操作,接着它们将输出相同的数值,并得到相同的梯度,因为如此它们用相同的方式更新,你将得到完全相同的神经元,这并不是你想要的,你期望的是不同的神经元学习到不同的知识。所以问题的关键是,当你用相同的值去初始化所有参数时,这里基本没有打破参数对称问题。

Q:由于梯度同样依赖于代价函数,一个反向传播不会与其他不同吗?

A:梯度会针对不同的神经元得到不同的代价函数,依赖于它所连接的类,但如果你通过网络去观察所有的神经元,遍历整个网络,你基本上有很多神经元,用一样的方式连接在一起,有相同的更新,这将是这个问题。

权重初始化,第一件要做的是,可以尝试将其改变为所有权重是一个小的随机数,我们可以从一个概率分布中抽样。在这个例子中,我们从标准高斯分布中抽样。我们对它进行尺度变换,标准差是1e-2,然后乘以0.01,这样给定了很多小的随机数。这样的参数在小型网络中适用,我们打破了参数对称问题,但在结构深一点的网络可能会存在问题。

Desktop View 第一个想法:初始化小的随机数(来自cs231n)

我们来看看这是为什么。

Desktop View 实例:一个结构深一点的网络,初始化小的随机数(来自cs231n)

这里有一个试验,使用一个结构深一点的网络,在这个例子中,我们初始化一个十层神经网络,每层有500个神经元,使用tan h非线性激活函数,我们用小的随机数来初始化,就像在上面描述的,这里我们做数据初始化。使用随机数据作为输入,经过网络在每一层,观察一下产生的激活函数的数据的统计结果。

我们观察到这可能有点难观察到顶部数据,但如果我们计算每一层的均值和标准差,我们就可看到第一层的数据如图所示,均值总是在0附近。这个是有意义的,我们看上面的代码,这里去xw的点积,我们在线性函数后加上tan h激活函数,然后保存这些值。由于tan h是以0为中心的,这就变得很有意义。标准差会缩小,它会快速逼近0。如果画出来,图的第二列显示的是每层的均值和标准差。在底部是一系列的图展示了每一层激活值的概率分布。我们可以看到在第一层我们有一个合理的高斯分布。问题是当我们每一层乘以一个W,一些小随机数后,它随着我们一次次的乘法迅速地缩小,所有的数值消失了,最后得到一堆0,这并不是我们期望的,我们的所有激活值都变成了0

Desktop View 实例:所有激活值都变成了0(来自cs231n)

现在考虑反向传播。我们运行反向传播,假设这是我们的正向传播,我们计算梯度,首先这些参数的梯度会是什么样子?我们来考虑一下,我们每层有很小的输入值,因为他们都近似于0,每层我们上层的梯度下传下去,为了得到针对权重的梯度,需要用上层的梯度乘以本层的梯度,即W点乘X,它就是X,也就是输入,这和我们之前的问题相似,由于X很小,我们的权重得到了一个非常小的梯度,它们基本上没有更新。

这是一个方法,可以尝试和理解梯度流动在网络中的影响。你可考虑正在进行什么样的正向传播,然后考虑一下当梯度流下降时会发生什么?不同类型的输入实际上对我们的权重有什么影响?以及它们的梯度是什么?

如果我们考虑当我们把所有的梯度连接在一起,从每层回传的梯度是什么?这是一个反转的事情,我们有反向传播的梯度是上层梯度乘以本层的梯度,W乘以X,由于这是点积运算,反向传播所有层,我们基本上是在做上层梯度和权重的乘法来得到下层的梯度。由于这个原因我们不断的乘以W,基本是相同的现象。在正向传播中,所有的数据越来越小,梯度和上层梯度都趋于0

已经理解了刚才演示的,当权重很小时会出现问题。让我们想想,如果尝试增大权重来解决这个问题会发生什么?

Desktop View 实例:几乎所有神经元完全饱和,梯度全会趋于0(来自cs231n)

我们从标准高斯分布中采样,而不再用0.01,而是用1乘以标准差,会发生什么问题?

如果权重都很大,我们通过这样的网络用WX得到输出,通过tan h非线性激活函数,输入不同的值给tan h会产生什么结果?饱和

由于我们的权重变大了,我们的架构变得总是处于饱和状态,不论tan h的负方向还是正方向。在实际操作中你会看到,如果观察激活值在底部每一层的分布,它们会趋于-11。这将是一个问题,我们之前讨论过tan h函数,这会导致饱和,以致于所有的梯度趋于0,权重将得不到更新。

Desktop View 实例:xavier initialization(来自cs231n)

由此可见,权重初始化是一个挺难的事情。权重太小,网络崩溃;权重太大,网络饱和。有研究尝试表明,初始化权重的合适方式,一个很好的经验是Xavier初始化。这是Glorot2010年发表的论文。

当你使用类似ReLU激活函数时,在ReLU激活函数的情况下,因为它会消除一半的神经元,也就是说每次都将有大约一半的神经元被设置为0。它实际上是把你得到的方差减半,如果你现在得到的推导和之前的一样,那么你就不会得到正确的方差,而是一个特别小的值。所以你将会看到单位高斯分布开始收缩,在这种情况下越来越多的峰值会趋于0,并且神经元会失活。

Desktop View 实例:使用类似ReLU激活函数时,神经元会失活(来自cs231n)

在一些论文中指出了一些方法来解决这个问题,可以试着除以2这个方法来解决这个问题。所以这些做法基本是在调整,因为有一半的神经元被置0。和之前的未用ReLU激活函数相比等效的输入,实际上只有一半的输入,所以你只需要除以2这个因子,这将很好的解决这个问题。并且你可以发现单位高斯分布在深度网络的每个层都很好。

Desktop View 实例:试着除以2,将很好的解决这个问题(来自cs231n)

在实践中,重视你的权重值对于训练这些小的东西很重要。举个例子,在一些论文中,实际上这就是深度网络间的不同,即使是训练也能表现的很好。

所以,适当初始化仍然是一个活跃的研究领域,一个很好的一般经验法则是从使用Xavier法开始的。

批量归一化

Desktop View 批量归一化(来自cs231n)

接下来我们要讲一个相关的想法,这个想法是在我们想要的高斯范围内保持激活,这就是我们所谓的批量归一化。我们想要单位高斯激活,那么让我们对它们进行批量归一化。

批量归一化是怎么工作的呢?

Desktop View 激活单位高斯函数(来自cs231n)

让我们考虑一下在某一层上的批量激活,那么现在我们的激活方法已经呼之欲出,实际上我们可以根据以往的经验去做。如果我们想要正确地激活单位高斯函数,我们可以取目前批量处理的均值,然后我们可以用均值和方差来进行归一化。所以基本上我们在训练开始时才设置这个值,而不是在权重初始化时,以便我们能在每一层都有很好的单位高斯分布,希望在训练的时候能够一直保持。现在我们要明确地让所有通过深度网络的转发都能在每一层有很好的高斯分布,我们要实现这个功能,基本上通过归一化每一个神经元的均值和方差。我们观察所有流入神经元的输入,并且计算这批数据的均值和方差,然后将其归一化。这是一个可微函数,如果我们的均值和方差是常数,那么这就是一步步的计算了。我们可以对它进行微分并返回。

Desktop View 激活单位高斯函数(来自cs231n)

就像我之前说的那样,如果我们观察一下输入的数据,我们就会得到,假设我们在当前的批处理中有N个训练样本,并且假设每批是D维的,我们将对每个维度独立计算经验均值和方差,所以基本上每个特征元素通过批量处理我们都进行计算过了,我们的小批处理对前面的批量处理补充说明,并且对其进行归一化。

Desktop View 对每个维度独立计算经验均值和方差(来自cs231n)

这通常是在完全连接或者卷积层之后插入的,我们不断地在这些层上乘以W,虽然会对每一层造成不好的尺度效应,但是这基本上可以消除这种影响。因为我们基本上是通过每个与神经元激活函数相连的输入来进行缩放,所以我们可以用相同的方法来完全连接卷积层,唯一的区别是,在卷积层的情况下,我们不仅想要归一化所有的特征维度的独立训练实例,而且在我们的激活映射图,包括我们的训练实例,我们想要归一化跨特征维度和空间位置的所有特性。并且我们这样做是因为我们想要服从卷积的性质,我们希望附近的位置能以同样的方式进行归一化,所以在卷积层的情况下,在每个激活图中会有一个均值和一个标准差,我们将在批处理的所有实例中进行归一化。

Desktop View 在完全连接或者卷积层之后插入的,在非线性层之前(来自cs231n)

接下来要指出的一件事是,我们并不清楚要在每个全连接层之后进行批量归一化操作,也并不清楚我们是否确实想要给这些tan h非线性函数输入一单位高斯数据,因为这里所做的是把输入限制在非线性函数的线性区域,虽然并不绝对,但基本来说,我们是想避免出现饱和,但有一点点饱和也不坏,你希望能控制饱和的程度。

Desktop View 批量归一化,是必需的吗?(来自cs231n)

所以我们强调的是,批量归一化就是在完成归一化操作之后,需要进行额外的缩放操作,所以我们先做了归一化,然后使用常量γ进行缩放,再用另一个因子β进行平移,并且这里实际在做的是这样允许你恢复恒等函数。

Desktop View 归一化,缩放,平移(来自cs231n)

如果你愿意的话,如果网络需要,它可以学习缩放因子γ,使之等于方差,它可以学习β使之等于均值。在这种情况下,你就可以恢复恒等映射,就像你没有进行批量归一化一样。所以,现在你拥有让网络为了达到比较好的训练效果,去学习控制让tan h具有更高或更低饱和度的能力。

总结下批量归一化的思想,

Desktop View 总结:批量归一化(来自cs231n)

我们提供输入,然后计算小批量均值,对每个输入的小批量都做这个操作,然后计算方差。通过均值和方差进行归一化,然后还有额外的缩放和平移因子,从而改进了整个网络的梯度流。它还具有更高的鲁棒性,它能在更广范围的学习率和不同初始值下工作。所以人们发现一旦使用批量归一化,训练会变得更加容易,这就是你为什么应该这样做。

另一个需要指出的是,你也可以把它看做正则化的一种方法。因为每层的输出每个激活值和这些输出都来源于输入X,以及批中被采样的其他样本,因为你将通过这一批样本的经验化均值对输入数据进行归一化,所以它不再对给定的训练样本提供确定性的值,而是将这些输入放入批中,因为它不再是确定值,就像往X中加入一些抖动,从而实现正则化的效果。

Q:上面的βγ是通过学习得到的参数?

A:为什么我们希望学习βγ从而恢复恒等函数,原因是我们希望它具有灵活性,批量归一化做的是将数据转换为单位高斯数据,我们的输入数据将成为单位高斯。虽然总的来说这是个好主意,但也不总是最好的选择,在tan h这样的特殊例子中,我们希望控制饱和的程度,所以这样做,是为了让你像进行单位高斯归一化一样更具灵活性。如果希望如此,你也需要知道在网络的特定部分可能并不是最好的选择,我们可能希望一些东西在这个总体思路上有一些小的不同,进行微小的缩放和平移。所以这些参数就可以为这个目的提供额外的灵活性。如果批量归一化是最好的选择,它将学到最合适的参数。

基本上,每个神经元输出全连接层的输出W乘以X,我们有它们的所有输出值,然后我们将对这些神经元分别进行批量归一化。

Q:像强化学习批量可能非常小,这该怎么处理?

A:在实际使用中,我认为批量归一化更多的是用在标准卷积神经网络,有一些论文讲了如何在不同递归网络中使用归一化,你知道这些网络可能用于强化学习,在这里你可能有不同的考虑,这仍然是研究的热门领域。但对一个典型的卷积神经网络,总的来说它效果非常好,如果你有一个更小的批量,准确率可能会变小,但它仍然会起相同的作用,可以用更多的样本来计算均值和方差,在实际使用时也可以得到不错的结果,你可能不常看见,但在出现问题时它可能会有所帮助。

Q:如果我们强制输入符合高斯分布会损失结构吗?

A:并不会,你可能会这样想,举个例子,如果你全部特征分布都是高斯分布,即便你只是进行数据预处理,高斯化并不会损失任何结构,它只是进行平移,将数据缩放到一个让你将要执行的操作能很好运行的区域。在卷积层确实有一些空间结构希望保留,如果你看看激活映射,你希望它们互相之间都有关联,这样你希望将它们考虑在内,所以我们将进行归一化对整个激活映射找到一个均值,所以我们只找到训练样本的经验均值和方差。

Q:我们是否将权重归一化,让它们变成高斯分布?

A:我们对每一层的输入进行归一化,在这个过程中我们并没有改变权重。

Q:减去均值再除以标准差,是否就是高斯化?

A:答案是正确的。所以如果想想正在做的操作,基本上就是位移均值的距离,移动到以0为中心,然后用标准差进行缩放,从而得到单位高斯分布。如果想了解更多,应该多看看很多机器学习的解释,它们做的正是将操作可视化,基本上讲就是获得你的数据,然后转换为高斯分布。

Q:如果我们要做平移变换和尺度变化,以及学习这些参数,这里也有批量归一化冗余,因为你能恢复恒等映射。在这种情况下,网络学习到的恒等映射是最好的,并且学习到了这些参数,这里应该没有数据需要去归一化,但是实践中这种情况没有发生。在现实中我们学习参数γ和参数β,这和恒等映射是不同的,在某种程度上它会产生一些平移和尺度变化,但不会达到真正的同一映射,这将给你一个恒等映射,所以批处理的数据归一化依然有效。

A:既然这里说到了恒等映射,我只能简单地来说,在极端情况下,它能学习到恒等映射,但是在实践中网络并不能学习到恒等映射。

我们做这些平移变换和尺度变换,我们能得到符合均值为0,单位高斯数据,这已经是很不错的情况了,但它不用必须是高斯分布。在理想情况下,如果我们去观察那些层间的输入,它们大约相近与高斯分布,我们希望它能拥有这种效果,但是在实践中它不用必须完全符合。

Desktop View 总结:批量归一化(来自cs231n)

最后一点需要注意的是,在测试阶段批量归一化层,我们现在减去了训练数据中的均值和方差,训练数据中的均值和方差,我们在测试阶段不用重新计算,我们只是把这当成了训练阶段,例如我们用到了平均平移,接下来我们在测试阶段也同样会用到,并用于尺度变换。

训练过程监控

下面开始下一节——观察学习过程。

我们已经定义了我们的网络结构,现在我们将要讨论如何监视训练,并在训练过程中调整这些超参数,以获得最好的学习结果。

一直以来,我们要做的第一步是数据预处理,我们要把数据进行零均值处理,

Desktop View 第一步,预处理数据(来自cs231n)

正如我们之前所说的,然后选择网络结构,例如这里是一个简单的神经网络,开始它具有一个含有50个神经元的隐藏层,但基本上我们可以选择任何我们想要的网络结构。

Desktop View 第二步,选择网络结构(来自cs231n)

接下来我们要做的第一件事就是,初始化我们的网络,网络进行前向传播,我们想确定最终的损失函数是合理的,这些内容我们在几节课之前就讲过。这里我们有一个归一化指数函数分类器,我们知道损失函数应该是什么样的。当我们的权重很小且分布很分散时,然后归一化指数函数分类器的损失将是负对数似然操作的结果。如果我们有10个类别的话,它将会是像一个1/10的负对数,大概是2.3左右。我们希望损失函数值是我们所期望的那样,所以这是一个很好的完整性检查。

Desktop View 禁用正则化项(来自cs231n)

现在一旦我们看到我们的初始损失还不错,接下来我们想做的是加入0正则化项。如果我们不加入它,那么损失值就是数据的损失值,在2.3左右。

Desktop View 启用正则化项(来自cs231n)

现在我们想要启动这个正则化,当我们启动时,我们希望看到我们的损失值上升了。因为我们添加了额外的正则化项,这是完整性检查过程中很好的一步。

现在我们开始训练。我们最好先从小数据集开始,因为如果你有一个小数据集,你能够把它们拟合的非常好,获得非常小的训练损失。这里我们要关闭正则化操作,只是为了观察我们是否能把训练损失降为0

Desktop View 尝试开始训练(来自cs231n)

我们看到训练损失正在降低,当我们训练这么多epoch时,我们在每一个epoch计算我们的损失,我们希望看到最终损失降为0,另一方面,我们也看到训练集的准确率上升为1,所以如果你有一个小数据集,你能完美地拟合这些数据。

Desktop View 训练损失正在降低,直到完美拟合(来自cs231n)

如果你现在全部完成了完整性检查的操作,你可以开始真正的训练了。现在你可以拿出你的所有训练数据,加上一个小的正则化项,让我们确定下什么才是最优的学习率。学习率是最重要的参数之一,它是你想要首先调整的参数,你可以试一些学习率的值,这里我用的是1e-6。可以看到损失基本不变化,损失不变化的原因通常是,你设置的学习率太低了。当学习率很小时,你的梯度更新就会很小,你的cost也一样。

Desktop View 加上一个小的正则化项,确定最优的学习率(来自cs231n)

这里想指出来的一点是,即使我们的损失基本不变,但是训练集和验证集的准确率都非常快地提升到了20%,这个现象说明了什么?

为什么呢?记得我们有一个归一化指数函数,损失基本不变,但准确率提升了很多。这里的原因是,虽然这里的分布依然很分散,因此我们的损失项很接近,但我们把这些所有的分布都朝着正确的方向在轻微的移动,我们的权重参数在朝着正确的方向改。现在准确率可能发生突变,因为我们正选取最大的准确率,所以准确率会得到一个很大的提升,虽然损失还是相当分散。

Desktop View 学习率太小,损失几乎没有改变(来自cs231n)

现在我们尝试另一个学习率,这里选择了另一个极端,选择了一个非常大的学习率1e-6

Desktop View 选择了一个非常大的学习率1e-6(来自cs231n)

现在发生的是代价值是NaNs,当你得到这种结果时,这往往意味着Cost已经很大了,原因就是你设置的学习率太大了。

Desktop View NaN(来自cs231n)

现在你可以调低你的学习率,这里尝试3e-3范围的学习率,这依然很大。所以,一般情况下,我们的学习率设置于1e-31e-5之间,这是一个我们想要交叉验证的粗略范围。你在这个范围里尝试不同的学习率,取决于你的损失变化太小或者太大,在这个原则上调整它。

Desktop View 现在调低学习率,比如尝试3e-3范围的学习率(来自cs231n)

超参数优化

Desktop View 超参数优化(来自cs231n)

我们到底应该怎么选择这些超参数?是执行超参数优化选取最佳的超参数吗?

Desktop View 交叉验证(来自cs231n)

我们用的策略是,对任何超参数,比如说学习率执行交叉验证,交叉验证是在训练集上训练,然后在验证集验证。观察这些超参数的实验效果,一些工作已经在作业里做过了。这就是我们这节想做的事。第一件我们要做的是选择相当分散的数值,然后用几个epoch的迭代去学习,经过几个epochs,你可以知道哪些超参数有效,哪些值好或者不好。你也可以很快发现它是一个NaN,或者什么也没有,然后做出相应的调整。通常只要这么做了,就可以发现一个较好地参数区间,有了这个区间就可以进一步搜索更精确的值,这就是第二步,或许会花很长的时间来运行,并在得到的区间进行进一步精确搜索。在训练循环中,有一个找到像NaN这样激增的技巧开始训练一些参数,在每一个迭代或epoch观察你的代价。如果出现一个远远大于初始代价的值,比如说超过了3倍,你就可以知道这不是一个正确的方向。它会迅速的变得非常大,跳出你的循环,停止这个参数的训练。

选择其他的,看一下这个例子。我们想用5epochs来进行过程搜索,这和我们之前提的一个网络很相似。我们可以做的就是去观察,这些得到的验证准确度,我已经用红框标注了较好的结果。这些区间是我们准备进一步细化的区域,要注意的一点是,通常采用对数来优化,效果会更好,与在1e的-0.001次方到10100次方之间均匀采样相比,我们更愿意在一些区间用10的幂次进行采样,这是因为学习率是乘以梯度更新具有乘法效应。所以考虑学习率时,用一些值的乘或除的值更加合理,而不是用均匀采样。

Desktop View 用5个epochs来进行过程搜索(来自cs231n)

接下来,想要解决幅值阶数的问题。

只要发现了,就可以调整变化范围。例子中我们有一个范围,10-4次方到100次方,这是我们可以进一步精确的好区间,重复之前的步骤,就可以找到一个相对较好的准确率53%,这就表示我们调整对了方向。

Desktop View 更好地搜索(来自cs231n)

需要指出的一点是,这其实有一个问题,这里是我们精度最高的地方。我们都知道,所有的较好地学习率的范围在e-4次方以上,因为我们把学习率区间精确到10-4次方到100次方,这就使得所有好的学习率都在采样边缘,这并不好,因为我们不能在我们的空间上充分寻找,实际上,我们是想找到10-5次方或10-6次方这样,如果继续下移,这可能是更好的区间,于是我们想要确定这些好的值是你区间的一部分,还是在整个区间当中。

另外一点就是,使用网格搜索,对不同超参数的采样,我们可以对每个超参数的一组固定值采样。实际上对这些值网格的方式采样,不如用一种随机排列的方式,对每一个超参数在一定范围内进行随机值采样。在这里有两个超参数需要采样,你可以得到像右边这样的采样点,随机采样是考虑到对一个超过一个变量的函数而言,随机更加真实,通常我们会对我们真正有的维度进行稍微的有效的降维。接着就可以得到更多已有的重要变量的样本,看一下图上方我画出的绿色的函数,表明了较好值的位置。如果采用网格分布就只能采样到3个值,而且错过了很多好的局域,有了重要变量的不同值的更多样本,才能得到更多有用信息。

Desktop View 随机搜索 .vs. 网格搜索(来自cs231n)

接下来要讲的,就是和超参数相关的学习率、不同类型的衰减表、更新类型、正则化以及网络结构、隐藏层数量、深度,这些都可以优化超参数。我们会学到其中的一些,在下节课中详细了解。可以想象一下,如果你控制着所有旋钮的开关,你也是个神经网络工作者,你可以将音乐作为你想要的损失函数,你可以近似调整,近似得到你想要的输出,也可以是你从事的艺术工作。

Desktop View 和超参数相关的学习率、不同类型的衰减表、更新类型、正则化以及网络结构、隐藏层数量、深度(来自cs231n)

实际运用时,有大量的超参优化、大量的交叉验证。为了获取数据,大家通常会对一大堆超参数进行交叉验证,来看一下哪些效果更好,哪些效果更差。这是所有的损失曲线,选择一些重新调整。

Desktop View 所有的损失曲线,选择一些重新调整(来自cs231n)

经过之前提到的同样的过程,在观察每条损失曲线时,学习率是重要的一个因素。你可以很好地感受到哪些学习率是好的,哪些是不好的。如果有一个很高的激增线,这就是损失爆炸,表明学习率过高,如果过于线性和平缓,它就变化的非常缓慢,如果进行一些突变,还是在平滑区,这也可能是学习率过高,因为当你步长过大时,可能不能落入局部最优。好的学习率就像这样,一个相对陡峭的曲线,然后又连续下降,然后可以从这里调节学习率。在实际运用情形下你们可以仔细观察。

Desktop View 一个相对陡峭的曲线(来自cs231n)

最后想要说明一点,当你观察学习率损失曲线时,如果它在一定时间内很平滑,然后突然开始训练,它可能是初始值没有设好。从图上可以看出,开始时梯度变化并不太好,什么也没学到,到达某一点之后突然开始调节,正确地调节就像才开始训练一样,看这些需要一些经验,不断累积经验就可以发现哪里出错了,你就可以观察并可视化精度。如果在训练精度和验证精度间有一个很大的差值意味着有可能产生了过拟合,可以试着增加正则项权重。如果没有差值就没有过拟合,可以增加模型容量,这样也会提高精度。

Desktop View 在一定时间内很平滑,然后突然开始训练(来自cs231n)

Desktop View 产生了过拟合(来自cs231n)

整体来说,我们可以用已有参数的范数来记录更新值,权重更新和权重幅值的比率,从而知道它们有多大,什么时候更新尺寸,也可以采用范数描述它的大小,让比率在0.001附近区间内很多变化,不用得到具体的某个值,但需要知道哪些值和你要的值相比过大或过小,就不需要训练了。这样就避免无用功,这个也可以帮助解决一些问题的漏洞。

Desktop View 用已有参数的范数来记录更新值,权重更新和权重幅值的比率(来自cs231n)

小结

总结一下,我们今天学习了激活函数、数据预处理、权值初始化、批量归一化、监测学习过程以及参数优化,每一个都需要好好理解。

  • ReLUs用来减去均值
  • Xavier用来初始化
  • 批归一化和超参数随机采样

Desktop View 第一部分:小结(来自cs231n)

回顾上面讨论的各种激活函数和一些它们不同的属性,在10年前,sigmoid函数曾经在训练神经网络方面很受欢迎,但在sigmoid激活函数的两端存在梯度消失的问题,tan h函数也存在类似的问题。对于这样的问题,一般的建议是,大多数这样的情况下,把ReLU激活函数作为默认的选择,因为它在很多不同的框架下都能运行的很好。

我们也讨论了权重初始化。首先要记住的是,当你们在开始训练时,初始化你们的权重值(即参数W),如果那些权重的初始值太小,那么当你在学习深度网络时会发现激活值消失。因为当不断的乘以这些很小的数,它们就会衰减到0,那么所有的都会变成0,学习也就无从谈起。从另一个角度来说,如果你的权重初始值过大,那么这些初始值不断地乘以你的权值矩阵,会呈现爆炸式增长,最终一发不可收拾,这样也无法学习。但是如果正确的初始化权重参数,举个例子,使用Xavier初始化法,或者MSRA初始化法,那么在你学习深度网络时,在深度网络的每一层激活值有很好的分布,记住在你们的深度网络越来越深层的时候,权值的初始化会变得至关重要。因为随着你的深度网络变深,你将不断地乘以那些权值矩阵。

Desktop View 回顾:权重初始化(来自cs231n)

我们也讨论过关于数据预处理方面的问题。我们讲过在卷积神经网络中,中心化和归一化是非常常用的手段,它会使数据分布均值为0,方差为1

Desktop View 回顾:数据预处理,举个例子(来自cs231n)

这里再直接讲一下这样做的原因。举一个简单的例子,我们要通过二元分类的方法分离这些红色的点和蓝色的点。从左边图来看,如果这些数据点没有被归一化,没有被中心化,而且距离坐标系原点很远,那么虽然我们仍然可以用一条直线分离它们,但现在如果这条直线稍微转动一点,那么我们的分类器将会被完全的破坏。这意味着左边的损失函数对我们的权重矩阵中的线性分类器中的小扰动非常敏感。我们仍然可以表示相同的函数,但这会使深度学习变得异常艰难,再一次强调因为它们的损失对我们的参数向量非常敏感,而在右边的情况下,如果你在使用数据集时将那些数据点移动到原点附近并且缩小它们的单位方差,再一次强调,我们仍然可以很好的对这些数据进行分类,但是现在当我们稍微的转动直线,我们的损失函数对参数值中的小扰动就不那么敏感了。这可能会优化变得更容易一些的同时能够看到一些进展。顺便提一下,这种情况不仅仅在线性分类中遇到,记住在神经网络里我们需要交叉地使用线性矩阵相乘或者卷积,还有非线性激活函数。如果神经网络中某一层的输入均值不为0,或者方差不为1,该层网络权值矩阵的微小摄动,就会造成该层输出的巨大摄动,从而造成学习困难。所以这就直观解释了为什么归一化那么重要。

还要记住,因为我们了解了归一化的重要性,所以引入了批量归一化的概念,即在神经网络中加入额外一层,以使得中间的激活值均值为0、方差为1

在这里,我通过几个更直观的形式重新总结了批量归一化的方程。

Desktop View 回顾:批量归一化(来自cs231n)

在批量归一化中,正向传递时,我们用小批量的统计数据计算平均值和标准差,并用这个估计值对数据进行归一化,之后我们还介绍了缩放参数和平移参数来增加一层的可表达性。

上面我们还讲了一小块跟踪学习过程的内容,比如训练过程中如何观察损失函数曲线。以下是一些神经网络的例子,左侧是一些随时间变化的损失函数曲线,你会发现曲线多多少少是下降的,说明神经网络的损失函数在降低,这是个好的信号;右侧曲线x轴同样是训练时间或者迭代次数,y轴表示模型在训练集和验证集上的效果,你会发现随着时间的增加,训练集上效果会随着损失函数下降越来越好。但验证集上的表现,却在某一点之后不再上升,这表明模型进入了过拟合的状态。这时候我可能就需要加入其他的正则化手段。

Desktop View 回顾:训练过程中,观察损失函数曲线(来自cs231n)

上面我们还简要涉及了超参数搜索,所有这些神经网络涉及到大量超参数,找到正确的参数十分重要。我们讲到网格搜索,以及随机搜索在理论上的优越性在哪里。因为当你的模型性能对某一个超参数比对其他超参数更敏感时,随机搜索可以对超参数空间覆盖的更好。我们还介绍了粗细粒交叉搜索,当你做超参数优化时,一开始可能会处理很大的搜索范围,几次迭代之后,就可以缩小范围圈定合适的超参数所在的区域,然后再对这个小范围重复这个过程,你可以多次迭代进行上述的步骤,以获得超参数的右区域。另外,很重要的一点是,一开始你得确定粗略的范围,这个范围要非常宽,覆盖你所有的超参数。理想情况下,范围应该足够宽到你的网络不会超过范围的任一边,这样你就知道自己的搜索范围足够大。

Desktop View 回顾:超参数搜索(来自cs231n)

下一次,我们将从不同的主题继续学习神经网络的训练。

Q:通常一次搜索几个超参数?

A:例子中给的是两个,可能会超过两个。通常情况下,这主要取决于你的模型和架构,可能性的数量是超参数的指数,所以其实一次没法处理太多超参数,这还取决于多少可用设备能为你所用,所以这是因人而异,因实验而异。通常我每次不会处理超过2个、3个,甚至4个超参数,因为这会让指数搜索失控,通常学习速率是最重要的,你需要首先确定它,之后其他诸如规则化、学习判据、模型大小等参数不那么敏感,与学习速率相比,所以一般你需要来回迭代,找出最佳学习速率,然后返回去寻找不同的模型大小,这能帮你减少实验搜索的时间。这个问题实际上取决于你需要以什么顺序搜索数据。

Q:当你改变一个超参数时,其他超参数的最佳值有多大可能性会改变?

A:这确实会发生。虽然对于学习速率来说,这方面一般不是问题,学习速率方面通常你要找到一个好的范围,然后设定的比最佳值稍微低一点就可以运行很长时间,采用这种方法再配上更加优化策略(今天要学的),你会发现很多模型在你找到好的范围之后,对学习速率就不那么敏感了。

Q:降低学习速率,增加时间点的数量有什么问题么?

A:直观上看,如果你把学习速率设的很低,让它持续很长时间,这在理论上是行得通的,但实际上参数是10还是100,对训练来说非常重要。如果你采用正确的学习速度,可能6小时、12小时或一天完成训练,如果你只是为了保险,把一个参数10换成了100,那么1天的训练就变成了100天的训练,那可是3个月,就没那么好了。这就像你去上计算机课,课上通常会忽略常量,但你考虑训练这方面时,常量就非常重要了。

Q:对于低学习速率会不会出现卡在局部最优解的情况?

A:这个直觉上是可能的,但实际上并不是,今天稍后会讨论。

第二部分:更好的优化

上次课上,我们讨论了很多在训练神经网络的具体细节方面相关的方法和技巧,今天会继续讨论更多的关于训练神经网络方面的细节。

Desktop View 第二部分(来自cs231n)

现在讨论在训练神经网络时更有效的优化方法。这里会涉及到之前提到过的正则化,这些你用来构造网络的概念,可以减小训练和测试误差之间的鸿沟。谈到神经网络,我想讲更多的人们实际使用的正则化策略,最后我想讲一下迁移学习。当你拥有的数据比预期少时,你可以通过它将一个问题转换为另一个问题。

优化算法

回顾一下之前的课程,训练神经网络的核心策略是一个优化问题。我们写下损失函数,定义网络权重的每一个值,损失函数告诉我们,这些权重值在解决我们问题时表现是好是坏。我们设想在当前权重下,损失函数给了我们漂亮的等高线图,在右边是一个二维的问题,XY轴表示两个权重值,图上的颜色表示损失值,在这个二维问题的卡通图中,我们只优化两个值W1W2,目标是找到红色最深的区域,在这种情况下对应了损失最小的权重值。我们已经使用过最简单的优化算法——随机梯度下降,非常简单,只有三行代码。我们首先评估一下,一些小批数据中损失的梯度,再进一步向梯度为负的方向更新参数向量,因为它给出了损失函数下降最快的方向,然后我们重复这个过程,幸运的话,它在红色区域收敛,我们如愿得到很小的误差值,但不幸的是,这个相对简单的优化算法在实际使用中会产生很多问题。

Desktop View 优化:等高线图(来自cs231n)

随机梯度下降算法的问题之一,想象一下,我们的目标函数发生了什么?就像这样,我们画两个值W1W2,当我们改变其中之一时,损失函数变化非常慢。当我们在水平方向上改变值,损失函数变化非常慢。当我们在等高线图上下运动,损失值则对竖直方向的变化非常敏感。顺便提一句,对损失值来说,在这一点上是很坏的情况。在这一点,它是汉森矩阵中最大奇异值和最小奇异值之比。但是直观来看,损失值等高线图看起来像一个玉米卷饼,它在一个方向上非常敏感,而在其他方向则并不敏感。问题是对于一个像这样的函数,SGD会做什么?

Desktop View 优化:SGD存在的问题(来自cs231n)

如果你在这类函数上运行随机梯度下降算法,你会得到这样特有的之字形图形,其原因正是因为这类目标函数梯度的方向并不是与最小值成一条线,当你计算梯度并沿着前进时,你可能一遍遍跨过这些等高线,之字形地前进或后退,所以你在水平维度上前进速度非常慢,在这个方向上的敏感度较低,但却在非常敏感的垂直维度上做之字形运动,这并不是我们所希望的。而且事实上,这个问题在高维空间变得更加普遍,在这种卡通图中,我们只展示了两维优化等高线图。但在实际中,我们的神经网络可能包含百万、千万甚至上亿个参数,它将会沿着上亿个方向进行运动,在这上亿个不同的运动方向上,介于最大值和最小值的方向上的比例可能很高,SGD表现并不好。你可以想象如果我们有一亿个参数,在它们两者之间的最大比例可能很大,我认为在很多多维问题中,这是一个大问题。

SGD的另一个问题是,局部极小值或鞍点。这里我把图形做小小的改动,X轴显示一个参数的值,Y轴显示损失值。在上面的例子中,我们有这类曲线的目标函数,在曲线中间有一段凹陷。在这种情况下,SGD会发生什么?

Desktop View 优化:SGD存在的另一个问题,局部极小值或鞍点(来自cs231n)

在这种情况下,SGD会卡在中间,因为那里是局部极小值,梯度为0,因为那一段是平的。还记得SGD,我们计算梯度,向梯度相反的方向前进,在目前的点上,相反的梯度值为0,我们不会做任何动作,我们就被卡在了这个位置。

关于鞍点还有另外一个问题,相比于局部极小值,你可以设想在一点上,往一个方向是向上,往另一个方向是向下,在当前的位置梯度为0。在这种情况下,函数将被卡在鞍点,因为这里梯度为0

Desktop View 优化:SGD存在的另一个问题,函数将被卡在鞍点(来自cs231n)

虽然我希望指出一点,在一维问题上,局部极小值看起来是个大问题,鞍点看起来并不需要担心,但事实上,一旦涉及到高维问题,恰恰相反,如果你想想一亿个参数的空间,鞍点意味着什么?它意味着在当前点上,某些方向上损失会增加,某些方向上损失会减小,如果你有一亿个维度,它发生得会更加频繁,基本上几乎在任何点上都会发生,然而在局部极小值点上,向一亿个方向中任何一个方向前进损失都会变大。事实上当你考虑这种很高维的问题时,看起来似乎这种情况非常稀少,这个问题是最近几年在训练非常大的神经网络过程中才显露出来,问题多出现在鞍点,局部极小值问题少一点。不过有时问题并非恰好在鞍点上,也可能在鞍点附近。如果你看看下面的例子,可以看见鞍点附近梯度并不是0,但是斜率非常小。这意味着如果我们向梯度方向前进,而梯度非常小,任何时候当当前参数值在目标等高线图上接近鞍点时,我们前进都会非常缓慢。这是一个大问题。

随机性是SGD的另一个问题。记住SGD是随机梯度下降。回忆一下,损失函数是通过多次重复计算不同的实例的损失来定义的。

Desktop View 优化:在每一点的梯度上加入了随机均匀噪声,搞乱梯度(来自cs231n)

在上面这个例子中,如果N是你的整个训练集,可能有一百万个,每次计算损失都会耗费很大的计算量。事实上,我们经常通过小批的实例对损失和梯度进行估计。这意味着,我们并不会每一步都去计算真实的梯度,而是在当前点对梯度进行噪声估计。在右边对图做了一点修改,我只是在每一点的梯度上加入了随机均匀噪声,搞乱梯度。在这样的噪声条件下运行SGD,这可能并不完全是SGD过程发生的事情,但这仍然给你了一种感觉,如果在你的梯度估计中存在噪音,那么常规的SGD这种周围曲折的空间可能实际上需要花费很长时间才能得到极小值。

Desktop View 聚焦:随机均匀噪声(来自cs231n)

现在我们已经讨论了很多这样的问题。

Q:如果我们使用正常的梯度下降,这些问题都会消失吗?

A:刚才讨论的问题,在高条件数下,仍然是一个全量梯度下降问题。噪声像我们看到的一样,我们有时可能会在网络中引入额外的噪声,不仅仅是因为小批量采样,还因为网络中具有明确的随机性,我们在后面会看到这仍然会是一个问题。鞍点对于全量梯度下降来说仍然是一个问题。因为在目标函数的全等高轮廓线中,也仍然会存在鞍点。基本上,即使我们使用全量梯度下降,也不能真正解决这些问题。我们需要思考一个稍微有点花哨的优化算法来尝试解决这些问题。

SGD + Momentum

有一个非常简单的策略,在这些简单的问题上都能表现地很好。这个想法就是,在我们的随机梯度下降中加入一个动量项。

Desktop View SGD + Momentum(来自cs231n)

在左侧,是我们经典的SGD,我们通常只是在梯度的方向上步进。但在右侧,有一个非常小的方差,称为带动量的SGD,它包含两个等式和五行代码,所以变得加倍复杂。但它真的很简单,这个思想就是,保持一个不随时间变化的速度,并且我们将梯度估计添加到这个速度上,然后在这个速度的方向上步进,而不是在梯度的方向上步进。我们也有这个和摩擦有关的超参数ρ。现在在每一步,我们采用当前的速度,然后用摩擦系数ρ来对其衰减,摩擦系数有时取值比较大,例如0.9,就是一个通常的选择。我们使用当前的速度,然后用摩擦系数ρ进行衰减,之后加到梯度上,现在我们在速度向量的方向上步进,而不是在原始梯度向量的方向上步进。

这个超级简单的策略,实际上帮我们解决了所有我们刚才讨论过的问题,如果你去思考在局部极小值点或者鞍点发生了什么,可以想象这个系统中的速度,你可能可以理解为一个球滚下山,它会在下降的时候速度变快,现在一旦加上了速度,那么甚至当通过局部极小值的时候,在这个点仍然有速度,即使这里没有梯度,那么我们就能越过这个局部极小值点,然后继续下降,在鞍点附近也是类似。虽然鞍点附近的梯度非常小,但是我们还有在下山时就建立起来的速度向量,这能帮助我们通过鞍点,并且让我们继续滚动下去。如果你去思考在不好的情况下会发生什么?现在如果我们有这种曲曲折折的近似梯度,那么一旦我们使用动量,这些之字形的曲折就会很快相互抵消,这会很有效地减少我们朝敏感方向步进的数量,而在水平方向上我们的速度只会不断增加,实际上还会加速在不那么敏感的维度上下降,在这里添加动量,实际上也会帮助我们处理高条件数的问题。最后我们在右侧重现了同样的带有噪声的梯度下降可视化过程,这条黑色的曲线是常规SGD,它会曲曲折折地经过所有地方,这条蓝色曲线展示的是带有动量的SGD,你能够看到这个,是因为我们添加了动量,我们随着时间变化得到了速度,而噪音在估计梯度时,被抵消掉了一下。现在相比于因为噪声而路径有些曲折的SGD,带动量的SGD最终更加平稳地接近最小值点。

Desktop View SGD + Momentum效果图(来自cs231n)

Q:SGD动量如何处理条件很差的坐标?

A:回顾一下速度估计以及速度计算,会发现我们在每一步都增加了梯度。这一定程度上取决于你摩擦系数ρ的设置,但你仍然可以想象,如果梯度相对较小,并且这种情况下ρ表现地很好,那么我们的速度可以单调递增到一个速度会比实际梯度更大的点,然后我们可能会更快速地处理条件差的维度。

SGD + Nesterov Momentum

当我们在使用带有动量的SGD时,你可以想象这样一幅画面,这个红色的点是我们当前的位置,在当前位置有一些红色的向量表示梯度,或者我们对当前位置梯度估计的方向;绿色向量是速度向量的方向,当我们做动量更新时,实际上我们是在根据这两者的平均权重进行步进,这有助于克服梯度估计中的一些噪声。

Desktop View Nesterov Momentum(来自cs231n)

有时你会看到动量的一个轻微变化,叫做Nesteroy加速梯度,有时也称之为Nesteroy动量,它把这个顺序改变了一下,在普通的SGD动量中,我们估算当前位置的梯度,然后取速度和梯度的混合,在Nesteroy加速梯度中,你要做的事情有一点不同,你从红色点开始,然后在取得的速度的方向上进行步进,之后你评估这个位置的梯度,随后回到初始位置,将这两者混合起来,这是一个有趣的解释。但是你可以想象,你正把信息一点点混合在一起,如果速度的方向实际上有一点错误,那它可以让你在目标函数的等高轮廓图更大一点的部分中加入梯度信息。在凸优化问题上,这也有一些很好的理论性质,但一旦涉及到诸如神经网络的非凸优化问题,就会有一些问题了。

把它写成等式Nesteroy动量,看起来是这样的,现在来更新速度,我们根据之前的速度来前进一步,然后计算此处的梯度。现在当我们前进下一步时,我们实际上是在速度的方向上步进,这就是从多个点合并信息。

Desktop View Nesterov Momentum公式(来自cs231n)

Q:一个速度应该初始化成什么样才算好?

A:基本上都是初始化到0,它甚至不是一个超参数,把它设为0,不用担心。

直觉上,速度是你所看到的梯度的一个加权和,随着最近的梯度权重越来越大,在每一步我们都采用旧的速度,通过摩擦系数衰减,然后再加上当前的梯度,你可以把它看做是一个最近梯度平均的平滑移动并且在梯度上有一个能够及时回来的指数衰减权重。

Nesteroy公式可能会让人感到不便,因为当你在使用SGD法优化神经网络时,你通常会希望能同时计算损失函数和梯度,而Nesteroy的动量优化形式会对此造成破坏,从而造成应用上的麻烦,好在我们有办法使用换元法改进Nesteroy公式。像这样替换变量,并且化简公式之后,Nesteroy动量公式就可以有另一种形式,那么你就可以同时计算损失函数和梯度,一旦你做了这样的换元,Nesteroy公式就会变得优美起来,特别是第一步的式子。看起来就和在常规SGD动量优化法中,更新速度向量是一样的,我们得到当前点的速度,计算梯度,并且将两者用相减的方式混合,在第二步中,我们实际上在更新我们的参数向量,我们看第二个等式,我们用当前点参数向量加上当前速度,再加上一个权重化的这一次的速度和上一次速度的差,这里我们可以说Nesteroy动量包含了当前速度向量和先前速度向量的误差修正。

Desktop View Nesterov Momentum公式,换元法(来自cs231n)

SGD .vs. 带动量的SGD .vs. SGD + Nesterov Momentum

如果你看这个简单的例子,比较SGD、带动量的SGDSGD+Nesteroy动量优化,我们可以发现代表SGD的黑线在训练中会逐渐被困在这个局部极小点,而代表动量优化法和Nesteroy动量法的蓝线和绿线会借着它们构建的速度从而越过局部极小点,因此这两种方法能自我修正,从而到达真正的极小值点。另一方面可以看到,带动量的SGDNesteroy动量的一个不同,就是由于Nesteroy有校正因子的存在,与常规方法相比,它不会那么剧烈的越过局部极小值点。

Desktop View Nesterov Momentum效果图(来自cs231n)

Q:这个图看起来是不错,但实际情况是如果你的局部极小点在一个非常窄的盆地里呢?上面两种优化方法带来的速度能否让你越过这个局部极小点?

A:最近也有针对这方面的理论工作,但事实上这些非常极端的局部极值是所谓的坏点,我们的算法甚至不会经过这些点。因此实际上如果你遇到了一个非常极端的极值点,那么事实上你的训练很有可能已经过拟合了。如果我们能扩大我们的训练数据集到两倍,那么整个优化函数的形状都会改变,以致于这个非常极端的极值点会消失。前提是如果我们收集更多的训练数据的话,我们可以得到的一个直觉判断就是,我们愿意去靠近一个相对平缓的极值点,因为这样的极值点往往随着训练数据的变化有更好的鲁棒性,这样的平缓极值点往往针对测试数据有更好的泛化能力。这里又提到最近的理论工作,但实际上你提了一个非常好的问题,某种意义上来说,跳过这些非常尖锐的极值点,这实际上是带动量的SGD的一个特性,而不是一个Bug

AdaGrad

另一种常见到的优化策略是,斯坦福的John Duchi教授提出的AdaGrad算法。AdaGrad算法的核心思想是,在优化的过程中需要保持一个在训练过程中的每一步的梯度的平方和的持续估计。与速度项不同的是,现在我们有了一个梯度平方项,在训练时我们会一直累加当前梯度的平方到这个梯度平方项,当我们在更新我们的参数向量时,我们会除以这个梯度平方项。

Desktop View 优化:AdaGrad(来自cs231n)

那么现在的问题是,这样的缩放对于矩阵中条件数很大的情形有什么改进呢?

Desktop View 优化:AdaGrad存在的问题(来自cs231n)

这个思想就是,如果我们有两个坐标轴,沿其中一个轴我们有很高的梯度,而另一个轴方向却有很小的梯度,那么随着我们累加小梯度的平方,我们会在最后更新参数向量时,除以一个很小的数字,从而加速了在小梯度维度上的学习速度。一个轴的情况是这样,然后在另一个维度方向上,由于梯度变得特别大,我们会除以一个非常大的数,所以我们会降低这个维度方向上的训练进度。

不过这里有一个问题,也就是当t(时间)越来越大时,在训练的过程中使用AdaGrad会发生什么?使用了AdaGrad,步长会变得越来越小,因为我们一直在随时间更新,梯度平方的估计值,所以这个估计值在训练过程中一直随时间单调递增,这会导致我们的步长随时间越来越小。在学习目标是一个凸函数的情况下,有理论证明这个特征效果很好,因为当你接近极值点时,你会逐渐的慢下来最后到达收敛。这点是AdaGrad在凸函数情况下的一个很好的特性,但是在非凸函数的情况下,事情会变得复杂。因为当你到达一个局部极值点时,使用AdaGrad会让你在这里被困住,从而使得训练过程无法再进行下去。

Desktop View 优化:AdaGrad存在的问题(来自cs231n)

因此我们对AdaGrad有一个变体叫做RMSProp,它实际上就考虑到了这个问题。

RMSProp

Desktop View 优化:RMSProp(来自cs231n)

RMSProp中,我们仍然计算梯度的平方,但是我们并不是仅仅简单的在训练中累加梯度平方,而是我们会让平方梯度按照一定比率下降,它看起来就和动量优化法很像,除了我们是给梯度的平方加上动量,而不是给梯度本身。有了RMSProp,在我们计算完梯度之后,我们取出当前的梯度平方,将其乘以一个衰减率,通常是0.90.99,然后用1减去衰减率乘以梯度平方,加上之前的结果。

现在随着训练的进行,你们可以想象得到的是,我们的步长和AdaGrad一样,在被除了平方梯度之后,步长会有一个良好的性质,在一个维度上(梯度下降很慢的)训练会加快,而在另一个维度方向上训练减慢,而现在有了RMSProp,由于梯度平方估计被衰减了,这有可能会造成训练总是一直在变慢,而这可能不是我们想要的。

在这里,我们又一次在图中用黑线表示SGD,蓝线表示带动量的SGD,红线表示RMSProp。大家可以看到RMSProp和带动量的SGD效果都比单纯的SGD要好,但是它们在轨迹上有一点不同。带动量的SGD会先绕过最小值然后又拐回来,但是使用RMSProp的话,它就在一直调整自己的路线,这样我们就是在每个维度上做出了大致相同的优化。

SGD .vs. 带动量的SGD .vs. RMSProp

Desktop View RMSProp效果图(来自cs231n)

顺便说一句,你们可能看不出来,这个图也展现了使用相同的学习率下的AdaGrad算法(用绿线来画),但是它走着走着因为不断减小的学习率就卡住了。在实际应用中,AdaGrad可能不太会出现这种问题。这个比较对AdaGrad不太公平,也许你需要在使用AdaGrad时提升学习率,然后它就可以表现的像RMSProp那样,但是通常来说,我们在做神经网络训练时,倾向于不使用AdaGrad

Adam

在动量中,我们有关于速度的概念,我们通过梯度的叠加得到速度,然后顺着速度的方向走。在AdaGradRMSProp中,我们有另一套方法,先求梯度平方的估计值,然后除以梯度的平方的实际值,这两种方法单独来看都是不错的方法。那么我们为什么不把它们结合到一起呢?也许那样效果会更好,这就引入了Adam算法,或者说接近Adam的算法。

Desktop View 优化:Adam(来自cs231n)

使用Adam算法,我们更新第一动量和第二动量的估计值,在红框里我们让第一动量的估计值等于我们梯度的加权和。我们有一个第二动量的动态估计值,像AdaGradRMSProp一样,就是一个梯度平方的动态近似值,现在我们来看看怎么更新它们,我们使用第一动量,有点类似于速度,并且除以第二动量,或者说第二动量的平方恨,就是这个梯度平方项。这样的话,Adam最后看起来有点像RMSProp加上动量,或者看起来像动量加上第二个梯度平方,就像是合并了两者各自好的性质,但是这里也存在一点问题,这个问题就是,在最初的第一步会发生什么?在最初的第一步,可以看到,在开始时,我们已经将第二动量初始化为0,第二动量经过一步更新后,通常beta2,也就是第二动量的衰减率,大概是0.90.99,非常接近于1的一个数,经过一次更新,第二动量仍然非常非常接近于0。现在我们在这作出更新步骤,除以第二动量,也就是一个非常小的数,那么我们在一开始就会得到一个很大的步长。这个在开始时非常非常大的步长,并不是因为这一步的梯度太大,只是因为我们人为地将第二动量初始化成了0

Q:当你的第一动量也非常小时,那么你就是乘以一个小的数,并且除以一个很小的数的平方根,那么这样会发生什么呢?它们也许会相互抵消,也就不会有问题。

A:确实是有时候它们相互抵消,并且不影响结果,但是有时候就会使开始时的步长变得很大,这样就糟糕了。也许你的初始化的值本来就有些不合理,然后你用了一个很大的步长,那么你的初始化工作就彻底被搞砸了。然后你到了一个并不合适的区域中,最终很难收敛。

Q:关于在最后一个式子中1e-7是干嘛的?

A:实际上,在AdaGradRMSProp以及Adam中这一项会出现,之所以有这一项,是因为我们除以一个什么东西,而且想保证我们不是除以0,所以我们就在分母中加上一个很小很小的正的常数来确保我们不会除以0。理论上讲是一个超参数,但是它对最后结果不能有太大影响,因此就设成10-7次方、10-8次方类似这样大小的数就能起到效果。

使用Adam,我们刚刚说过,在前几步更新时,我们可能得出很大很大的步长,然后把事情搞砸了。Adam算法也增加了偏置校正项,来避免出现开始时得到很大步长的问题出现,在我们更新了第一和第二动量之后,你们会看到我们构造了第一和第二动量的无偏估计。通过使用当前时间步t,现在我们实际上在使用无偏估计来做每一步更新,而不是初始的第一和第二动量的估计值,这样我们就得到了Adam算法的完整形式。

Desktop View 优化:Adam(完整形式)(来自cs231n)

顺便说一下,Adam确实是一个非常好的最优化算法,并且对于不同的问题使用Adam算法都能得到非常不错的结果,因此Adam差不多是我的一个用来解决任何新问题的默认算法。特别是如果你将beta1设置为0.9beta2设置为0.999,学习率为1e-3,或者5e-4。我无论使用什么网络架构都会从这个设定开始。大家可以试试,Adam算法一般情况下真的是首选。

SGD .vs. 带动量的SGD .vs. RMSProp .vs. Adam

如果我们真的把这些都画出来,

Desktop View Adam效果图(来自cs231n)

来看SGD、带动量的SGDRMSPropAdam在解决同一问题中的表现,你们可以看到Adam算法,紫色的这条线像是结合了带动量的SGDRMSPropAdam也是像带动量的SGD一样有一点,但是没有像SGD动量一样绕过太多,Adam也有类似RMSProp的行为,尝试做出在所有维度上都相同的改进,也许在这个小的二维例子中,Adam收敛起来和其他算法相似。但是你们可以看到实质上它结合了SGD动量和RMSProp的特征。

Q:还有哪些问题是Adam解决不了的?

A:神经网络仍然很大,仍然花费很长的时间来训练,这仍然是个问题。这张图上是损失函数的等高线图,看起来像个椭圆,想象着我们是在沿着每个维度独立地去做估计,这允许我们沿着不同的坐标轴加速或者减速。如果这个墨西哥卷饼状的等高线图不是沿着坐标轴对齐的,然而我们还是沿着每个轴独立地去做估计,这就相当于把这个卷饼沿着水平和竖直的方向压缩,但是你无法扭正它,在这种倾斜的等高线图的糟糕情况下,Adam或者上面其他算法都是没法解决的。

学习率

另一个事情是学习率,这些优化算法都有这个超参数。下面这个图,我们前面见过好几次了。当你使用不同的学习率时,有时候太高了,就会导致损失函数爆炸,如黄色的曲线。如果学习率很小,如蓝色的曲线,可能要花很长的时间才收敛。挑选正确的学习率确实需要点技巧。这个技巧是我们不必在整个训练的过程中都一直固定使用同一个学习率。

Desktop View 以上,全有学习率作为超参(来自cs231n)

有时,人们会把学习率沿着时间衰减,有点像是结合了左图中不同的曲线的效果,而且是每个图里好的性质,比如在训练开始时用较大的一些学习率,然后在训练的过程中逐渐衰减地越来越小。一个衰减的策略是步长衰减,比如在第10万次迭代时,可以衰减一个因子,然后继续训练,还有指数衰减,这种是训练时持续衰减。你可以看到训练时学习率连续衰减的不同做法。

Desktop View 学习率沿着时间衰减(来自cs231n)

如果你读论文,特别是残差网络那篇论文,你会经常看到像这样的曲线,可以看到损失先一直下降,然后骤降,在然后平坦,接着又骤降,这些曲线背后其实是它们在用步长衰减的学习率,那些曲线中出现骤降的地方,是在迭代时把学习率乘上了一个因子。降低学习率的想法是说,假设模型已经接近一个比较不错的取值区域,但是此时的梯度已经很小了,保持原有学习速率只能在最优点附近来回徘徊,如果我们降低了学习率,目标函数仍然能进一步降低,即在损失函数上进一步取得进步,这个有时候在实际中很有用。不过值得指出的一点是,带动量SGD的学习率衰减很常见,但是像Adam的优化算法就很少用。

Desktop View 用步长衰减的学习率,曲线中出现骤降的地方,是在迭代时把学习率乘上了一个因子(来自cs231n)

学习率的衰减,另一点要指出的是,学习率衰减是一种二阶的超参数,你通常不应该一开始就用上学习率衰减这样的事情,通常你想要让神经网络开始工作,你想要挑选一个不带学习率衰减的不错的学习率来作为开始,尝试在交叉验证中同时调学习率衰减和初始学习率等等其他的事情,你会一头雾水的。设置学习率衰减的方法是,先尝试不用衰减,看看会发生什么,然后仔细观察损失曲线,看看你希望在哪个地方开始衰减。

一阶优化、二阶优化

另一个我要简单提一下的是,我们之前谈过的,所有这些算法都是一阶优化算法。在下面这张图中,这个一维的图中,我们有一个目标函数曲线,当前点是这个红色的点,我们在这个点上求一个梯度,我们用梯度信息来计算这个函数的线性逼近,这个相当于是对我们的函数进行的一阶泰勒逼近。现在假设我们的一阶逼近就是实际的函数,然后我们想要迈出一步来找到逼近的最小值,但是这个逼近在稍大的区间内并不成立,所以我们不能朝那个方向一下走太多。事实上这里梯度的想法用上了函数的一阶偏导,你可以多做一些工作,其实是有二阶逼近。

Desktop View 一阶泰勒逼近(来自cs231n)

这里,我们同时考虑一阶和二阶偏导信息,现在我们对函数做一个二阶泰勒逼近,就是用一个二次函数来局部逼近我们的函数。因为是二次函数,可以直接跳到最小值点,这就很开心了。这个就是二阶优化的思想。

Desktop View 二阶泰勒逼近(来自cs231n)

当把这个思想推广到多维的情况,就会得到一个叫做牛顿步长的东西,计算这个海森矩阵,即二阶偏导矩阵,接着求这个海森矩阵的逆,以便直接走到对你的函数用二次逼近后的最小值的地方。有人发现和我们之前的方法相比,这里的更新规则有什么不一样的地方吗?这里没有学习率,我们是做了个二次逼近,直接走到这个二次函数的最小值点,至少在这个牛顿法的原始版本中不需要学习率,每次只需要直接走到最小的点就可以了。然而实际中你可能也会用上一个学习率,因为这个二次逼近也不是完美的,你可能只是想要沿着二次函数最小值的方向前进,而不是走到最小值的位置。不过在这个原始版本中是没有学习率的。

Desktop View 二阶泰勒逼近公式(来自cs231n)

不幸的是,这个算法对深度学习来说有点不切实际,因为海森矩阵的维数是N*N的,其中N表示网络中参数的数量,如果N是一亿,那么一亿的平方非常大,内存肯定存不下,也没办法求这个矩阵的逆。

事实上,人们经常用这个拟牛顿法来替代牛顿法,不是直接地去求完整的Hessian矩阵的逆,而是去逼近这个矩阵的逆,常见的是低阶逼近。

Desktop View 二阶泰勒逼近公式,使用拟牛顿法做低阶逼近(来自cs231n)

某些问题的求解,你可能会看到L-BFGS就是一个二阶优化器,它是二阶逼近,用Hessian矩阵来逼近。实际中,你可能会看到很多深度学习的问题并不适应这个算法,因为这些逼近,这些二阶逼近的方法对随机的情况处理的不是很多,不是很好,而且在非凸问题上表现不是很好,这个我们就不再深入探讨了。

Desktop View L-BFGS(来自cs231n)

实践建议

在实践中,你可以考虑的是,对很多不同的神经网络问题,Adam已经是一个很好的选择了。然而,如果是这种情况,如果能承受整个批次的更新,而且你知道你的问题没有很多随机性,那么L-BFGS是一个很好的选择。L-BFGS在训练神经网络时不是很有用,但是我们会看到后面几节课中,比如风格迁移,问题中会用的上,这个问题有很少的随机性,参数也很少,但仍然是一个优化问题。

Desktop View 优化:实践建议(来自cs231n)

目前,我们讲过的所有策略都是在减少训练误差,这些优化算法都是在降低训练误差和最小化目标函数,但是我们并不在意训练误差,我们更在意在没见过的数据上的表现。我们很在意减少训练误差和测试误差之前的差距。现在的问题是,如果我们已经很擅长优化目标函数,要怎么做来减少训练和测试之间的误差差距,以使得我们的模型在没见过的数据上表现的更好呢?

Desktop View 更在意在没见过的数据上的表现(来自cs231n)

一个快速简单的方法是模型集成,模型集成在机器学习的很多领域点子其实很简单,比起使用一个模型,我们选择从不同的随机初始值上训练10个不同的模型,到了测试时,我们就会在10个模型上运行测试数据,然后平均10个模型的预测结果,把这些多个模型加到一起,能缓解一点过拟合,从而提高一些性能,通常会提高几个百分点。这不是很大的提升,但却是很固定的提升。可以在ImageNet竞赛或者其他类似的比赛中使用集成技术很常见,这样能得到最大的性能。

Desktop View 模型集成(来自cs231n)

再发挥一下创造力,有时可以不用独立地训练不同的模型,可以在训练过程中保留多个模型的快照,然后用这些模型来做集成学习。然后在测试阶段,你仍然需要把这些多个快照的预测结果做平均。但你可以在训练的过程中收集这些快照。

下面是这周ICLR会议上面一篇非常好的论文,讲的是上面的想法的一个高级一点的版本,这里我们用了一个疯狂的学习率计划,学习率开始时很慢,然后非常快,接着又很慢,再然后又很快。这个疯狂的学习率计划的想法是说,这样的学习率会使训练过程中模型会收敛到目标函数不同的区域,但是结果仍然还不错,如果你对这些不同的快照做集成以后,就能大幅提高最后的性能,虽然你只训练一次。

Desktop View 模型集成:Tips and Tricks(来自cs231n)

Q:既然知道训练和测试错误率相差不大是个不好的现象,这意味着过拟合,但如果它们相差不大,就一定好吗?我们真正想要的,是不是在两者之间找到一个足够小有最佳的差距呢?

A:我们其实不在乎这个差别,我们真正在乎的是在验证集上得到最优的结果。一般情况下,如果你看不到这个差别,你应该还有很多种方法提升效果来达到一点过拟合,在验证集上的测试效果和刚才提到的差距大小就是有这种怪异的关联性,但我们只关注模型在验证集上的效果。

Q:集成学习中的超参数是否是一样的?

A:有时候它们并不一样,你可能会尝试不同尺寸的模型,不同的学习率,不同的正则化策略,然后把它们放到一起集成学习,有时候就是这样做。另外一个小技巧就是,在训练模型时,对不同时刻的每个模型参数求指数衰减平均值,从而得到网络训练中一个比较平滑的集成模型,之后使用这些平滑衰减的平均后的模型参数,而不是截至在某一时刻的模型参数,这个方法叫做Polyak平均,有时有一点效果,这是你们可能会用到的。

Desktop View 模型集成:Tips and Tricks(来自cs231n)

正则化

Desktop View 正则化(来自cs231n)

还可能想问一个问题是,我们如何能提高单一模型的效果?使用集成学习时,我们测试时还是要跑,比如10个模型,这并不是很好。我们想要找到一些方法可以提高单一模型的效果,正则化正是这样一种方法。我们在模型中加入一些成分来防止训练集上的过拟合,从而使训练集上的效果得到提升。我们已经看过几种正则化的方法了,这里面我们在损失函数上加入额外的一项,我们有一项是让模型拟合数据,另一项则是正则项。之前作业中见过L2正则化,L2正则化在神经网络中可能意义并不是很明确,有时候我们会在神经网络中选择其他方案。

Desktop View 在损失函数上加入额外的一项,有一项是让模型拟合数据,另一项则是正则项(来自cs231n)

Dropout

一个在神经网络中非常非常常用的方法就是DropoutDropout非常简单,每次在网络中正向传递时,我们在每一层随机将一部分神经元置零,每次正向传递时,随机被置零的神经元都不是完全相同的,每次处理网络中的一层,我们经过一层网络算出这一层的值,随机将其中一些置成零,然后继续在网络中前进。如果我们把左边这个全连接网络和右边经过dropout的版本进行对比,会发现dropout后的网络像是同样的网络变小了一号。我们只用到了其中一部分神经元,而且每次遍历,每次正向传递都是不同的部分。

Desktop View 正则化 Dropout(来自cs231n)

Q:我们把什么置零了?

A:是激活函数,每一层都是在计算上一个激活函数的结果乘以权重矩阵,得到下一个激活函数前的结果,然后我们计算激活函数,将其中一部分置零,那么下一层拿到的激活函数结果就有一部分是零,乘以另一个权重矩阵得到下一个激活函数的输入。

Q:在哪些层里使用Dropout

A:一般是在全连接层,但有时你在卷积层也能看到,当你使用卷积层时,有时你可能并不是随机把某个神经元上激活函数的结果置零,而是随机把整个特征映射置零。在卷积神经网络里有一个维度表示通道,你有可能要把某几条通道整个置零,而不是某几个元素。

Dropout的实现非常简单,只需要两行代码,每行dropout一次,这是一个三层的神经网络,我们加上了dropout,唯一需要我们做的就是加上这行,随机将一部分神经元置零,这个实现起来非常简单。

Desktop View 正则化 Dropout 实现(来自cs231n)

但问题是这个想法为什么可取?我们认真地在训练时将一部分神经元置成零,看看这样做是否有意义。一个比较勉强的解释是,人们觉得dropout避免了特征间的相互适应,假设我们要分类判断是不是猫,可能在网络里一个神经元学到了有一只耳朵,一个学到了尾巴,一个学到了输入图像有毛,然后这些特征被组合到一起来判断是否是猫。但现在加入Dropout后,判断是不是猫时,网络就不能依赖这些特征组合在一起,而是要通过不同的零散的特征来判断,这也许某种程度上抑制了过拟合。

Desktop View 正则化 Dropout,为什么有效?(一种解释)(来自cs231n)

另一种最近出现的关于dropout的解释是,这是在单一模型中进行集成学习。如果你们观察左图,在dropout之后,我们是在一个子网络中用所有神经元的子集进行运算,每一种可能的dropout方式都可以产生一个不同的子网络,所以dropout像是同时对一群共享参数的网络进行集成学习。顺便说一下,因为dropout的可能性,随神经元个数呈指数倍增长,不可能穷举每种情况,这可以看做是一个超级无比巨大的网络集合在同时被训练。

Desktop View 正则化 Dropout,为什么有效?(另一种解释)(来自cs231n)

那么测试时又是什么样呢?当我们使用了dropout,我们把神经网络基本的运算都改变了,之前我们的神经网络里有权重w的函数f,输入x得到输出y。现在我们有了一个额外的输入z,表示dropout中被置零的项,z的值是随机的,测试时引入一些随机性可能不是一个好主意,比如传入一张图片,今天测试是猫,明天测试是狗,这样确实会很奇怪。你可能希望一旦这个网络训练好了,在测试时能消除这种随机性,然后我们就会想要平均这个随机性。如果把它写出来,可以想象通过一些积分来边缘化随机性,但在实践中这个积分是完全难以处理的,我们还不知道怎样对这进行求解。你可能想做的一件事是,通过采样来逼近这个积分,在这你可以对z多次取样,然后在测试时把它们平均化,但这仍然会引入一些随机性,这有点不好。

Desktop View 正则化 Dropout 测试(来自cs231n)

幸运的是,在dropout的情况下,实际上我们可以用一种省事的方式局部逼近这个积分。如果我们考虑单个神经元,输出是a,输入是xy以及两个权重,然后在测试时我们得到a的值是***。现在想象一下,我们训练了这个网络,在训练期间,我们使用了dropout,丢弃神经网络单元的概率是0.5,现在这个例子中,训练期间的期望值可以算出解析解,有四个可能的dropout的掩码集合,我们将通过这四个掩码得到的值进行平均,我们可以看到a的期望在训练期间为0.5*(w1*x+w2*y)。这里有一点不统一,测试时平均值是w1*x+w2*y,训练时则只有一半,我们能做的一件省事的事情就是,在测试时我们没有任何随机性,而是用dropout的概率乘以这个输出,现在这些期望值是一样的。这有点像简易地局部逼近这个复杂的积分,这就是人们对于dropout在实践中非常普遍的做法。

Desktop View 正则化 Dropout 测试(来自cs231n)

dropout时,对于预测函数,我们用dropout的概率乘以我们输出层的输出。

Desktop View 正则化 Dropout 测试(来自cs231n)

总结起来,dropout在正向传播中非常简单,你只需要添加两行到你的实现中,随机对一些节点置零,然后在测试时的预测函数内,你仅仅增加了一点点乘法乘以你的概率,Dropout超级简单,它有时对于正则化神经网络有很大帮助。

Desktop View 正则化 Dropout 总结(来自cs231n)

顺便说一下,有一个常见的技巧——反转dropout,也许在测试时你更关心效率,所以你想在测试时,消除乘以概率p的这一额外的乘法,那么你可以做的是,在测试时,你使用整个权重矩阵,但是在训练时除以p,因为训练可能发生在GPU上,你真的不在乎在训练时做一个额外的乘法,然而在测试时,你想要这个过程尽可能高效。

Desktop View 反转 Dropout(来自cs231n)

Q:训练中dropout对梯度有什么影响?

A:我们只在未被丢弃的节点上传递梯度,当你用dropout训练时,会有一个现象是训练通常需要更长的时间,因为在每一步你只是更新网络的一些子部分。当你使用dropout时,它通常需要更长的时间去训练。但是在收敛后,模型的鲁棒性更好,dropout在我们看来是这样一个具体实例。

Batch normalization

这里有一个更通用的正则化策略,在训练期间,我们给网络添加一些随机性,以防止它过拟合训练数据。一定程度上扰乱它,防止它完美地拟合训练数据。现在在测试时,我们要抵消掉所有随机性,希望能提高我们的泛化能力。Dropout可能是最常见的使用这种策略的例子,但实际上Batch normalization也符合这个想法。

Desktop View 正则化 Batch Normalization(来自cs231n)

记得在batch normalization中,在训练时一个数据点可能和其它不同的数据点出现在不同的小批量中,对于单个数据点来说,在训练过程中该点会如何被正则化具有一定的随机性,但是在测试过程中,我们通过使用一些基于全局估计的正则化来抵消掉这个随机性,而不是采用每一小批量估算,实际上batch normalization倾向于具有和dropout类似的正则化效果,因为它们在训练时,都随机引入某种随机性或噪声,但在测试时抵消掉它们。实际上当你使用batch normalization来训练神经网络时,有时你一点都不会使用dropout,仅仅是batch normalization给你的网络增加了足够的正则化效果,dropout某种程度上更好,因为你实际上可以通过改变参数p调整正则化的力度,但是在batch normalization中并没有这种控制机制。

数据增强

另一种符合这种范式的策略,就是这种数据增强的想法。在训练时有一个最初的版本,我们有自己的数据,也有自己的标签,在每一次迭代中,我们使用它去更新我们的卷积神经网络。

Desktop View 正则化:数据增强(来自cs231n)

但是我们可以做的是在训练过程中以某种方式随机地转换图像,使得标签可以保留不变。现在我们用这些随机转换的图像进行训练,而不是原始的图像。

Desktop View 以某种方式随机地转换图像(来自cs231n)

有时你可能看到随机的水平翻转,假如你才用了一张猫的图像,并水平翻转了它,它依旧是一只猫。

Desktop View 随机的水平翻转(来自cs231n)

你可以从图像中随机抽取不同尺度大小的裁剪图像,因为猫随机地裁剪图像依旧是一只猫,然后在测试过程中,通过评估一些固定的裁剪图像来抵消这种随机性,通常是四个角落和中间,以及它们的翻转。比较常见的就是,当你阅读ImageNet上的论文时,他们会总结他们模型的单个裁剪图像效果,这个就像整个图像一样,和它们模型的10种裁剪方式的效果,包括这5种标准裁剪,加上它们的翻转。

Desktop View 随机裁剪(来自cs231n)

同样,在数据增强中,有时会使用色彩抖动,你可能会在训练时随机改变图像的对比度和亮度,你也可以通过色彩抖动来得到一些更复杂的结果,当你试图在你的数据空间的主成分分析方向上产生色彩抖动,或者其他什么的时候,你会以某种与数据相关的方式进行色彩抖动,但这不太常见。

Desktop View 色彩抖动(来自cs231n)

一般来说,数据增强是非常普遍的事情,你可以将其应用于任何问题。不管你想要解决什么问题,可以考虑在不更改标签的前提下对数据进行转换。现在在训练时,你只需将这些随机转换应用于你的输入数据,这种方式对网络有正则化效果,因为在训练时,你又增加了某种随机性,然后在测试时将它们淡化。

Desktop View 数据增强:其他方法(来自cs231n)

Dropconnect

现在我们已经看到了这种模式的三个例子,dropoutbatch normalization和数据增强,但是还有很多其他的例子,一旦你学会这个模式,当你阅读其他论文时,你可能会认出它们。还有一种与dropout相关的算法——DropconnectDropconnect是同样的想法,但不是在每次正向传播中将激活函数置零,而是随机将权重矩阵的一些值置零,它们有一样的效果。

Desktop View 正则化 Dropconnect(来自cs231n)

部分最大池化

另一个我认为非常好的想法是,部分最大池化。一般地,当你进行2*2的最大池化时,以前我们会把固定的2*2的区域在前向传播的前面进行池化,但现在通过部分最大化池化,每次我们在池化层操作时,我们将随机池化我们正在池化的区域,如右图所示,展示了3个不同的在训练时可能遇到的随机池化区域。在测试的时候有很多方法可以得到抵消随机性,要么使用一些固定的池化区域,要么选取很多样本对它们取平均,这是一个很好的想法,虽然并不常用。

Desktop View 正则化:部分最大池化(来自cs231n)

随机深度

另一个令人眼前一亮的想法是随机深度。如图左边所示,我们有一个很深的网络,在训练时,我们随机的从网络中丢弃部分层,在训练时我们消除一些层,只用部分层。在测试时,我们用全部的网络,有点不可思议,这是一个神奇的研究,效果有点趋向dropout的正则化效果,和其他类似的研究。这是非常前沿的研究,在实际操作时并不常用,但想法不错。

Desktop View 正则化:随机深度(来自cs231n)

Q:你经常使用超过一个的正则化方法吗?

A:通常使用batch normalization,因为它是一个现在大多数网络使用的方法,因为它的确帮助收敛,特别是非常深的网络。大多数情况下,单独使用一个batch normalization方法就够了,有时当你发现网络过拟合,如果batch normalization单独使用不太够,你可以增加dropout或一些其他的东西。一般不要盲目地交叉验证这些方法,而且有的放矢,在网络过拟合时把它们加进去。

迁移学习

一个简单的介绍,迁移学习的思想。

Desktop View 迁移学习(来自cs231n)

我们看到使用正则化,加入不同正则策略可以帮助减小训练误差和测试误差的间隙。过拟合的一个问题是,有时过拟合是由于数据不够,你希望得到一个大的、功能大的模型,一个大的、功能强大的网络在你使用小数据集合时很容易过拟合,正则化是一种处理它的方法。另一种方法是使用迁移学习,迁移学习能打破这种神话,你不需要超大的样本集也能训练卷积神经网络。

它的思想很简单,你首先找到一些卷积神经网络,这是VGG架构的网络。你首先使用你的卷积神经网络在一个非常大的数据集训练,例如ImageNet,这里你有足够的数据去训练整个网络。

现在你想尝试的想法是把从这个数据集中训练出的提取特征的能力用到你更感兴趣的小的数据集上,可能不需要像ImageNet1000类,你可能只关注10个狗的品种分类,或者类似的分类。你只需要一个小的数据集。下图这里我们的数据集只有C个类别,接着你一般的做法是修改从最后一层的特征到最后的分类输出之间的全连接层。你需要重新随机初始化这部分矩阵,对于ImageNet,它是4096*1000维的矩阵,对于你新的分类,矩阵大小变为4096*C,例如10,或者任何一个数,重新随机初始化最后的矩阵,冻结前面层的权重,现在只需要一个线性分类器,只需要训练最后这层,让它在你的数据上收敛。当你只处理一个小的数据集时这会让你的工作很完美。

如果你的数据稍微充裕一点,另一件可以尝试的事情是微调整个网络,在最后一层收敛,在数据集上充分训练之后,你可以试着更新整个网络的权值。如果你有更多的数据,可以更新网络的更大一部分。一个通用的策略是,你更新网络时将学习率调低。因为最初的网络参数可能是在ImageNet上收敛的,泛化能力已经很强了,你只是希望让它们有微小的调整来适应你的数据集。

Desktop View 迁移学习,用在CNN上(来自cs231n)

当你使用迁移学习时,你可以想成是一个2*2的情景网络。在一侧的,你可能会有很小的数据集或者很大的数据集,可能你的数据和一些大数据集的图片很相似,例如ImageNet有很多动物、植物之类的图片,如果你只是想分类动物植物或者其他类别的图片就比较好办了。接下来要做的就是,如果你的数据和ImageNet很像,但你的数据量很少,你可以在ImageNet预训练模型的基础上,只训练最后一层线性分类器。如果你有更多的数据,你可以精调你的模型,但如果你的数据和ImageNet不相似,情况就不乐观了。例如你可能处理的是,例如处理X光或者CT图像,或者一些和ImageNet的图片大相径庭的图片时,可能需要一些创造力。这种方法有时可能还行得通,但最后一层提取的特征可能没有太多信息量,你可以考虑是重新初始化大部分的网络,多做一些实验。在你的数据集数据量比较大时,这种情况会多少得到缓解,因为你可以微调大部分网络。

Desktop View 迁移学习,数据规模大小和相似性(来自cs231n)

另一点我想说的是,迁移学习的思想是很普遍的。如果你读机器视觉的论文,你经常看类似这样的系统图,针对不同的任务,左边是目标检测,右边是图像加标,所以的模型都有一个卷积神经网络处理图像的模块。目前无论是计算机视觉的哪方面应用,大多数人都不会从头训练这些东西。大多数卷积神经网络任务在ImageNet上预训练,然后根据任务精调。同样地,在图像加标的环境下,有时可以预先训练一些和语言相关的词向量,你可以在ImageNet上预训练卷积神经网络,在一些文本词典上预训练一些词向量,然后针对你的网络精调。在加标任务中预训练词向量的方法不太常见,也不是很关键。

Desktop View 迁移学习,CNN在ImageNet数据集上预训练(来自cs231n)

要记住的是,不管是课程的项目还是以后遇到的各种模型,对于你要处理的问题,你们有大数据集,你应该做的是下载一些相关的预训练的模型,然后要么重新初始化部分模型,或者在你的数据上精调模型。即使你的训练数据有限,这种方法也能行的通。因为这是一个普遍的策略,在所有不同的深度学习软件包上都提供了一个模型库,你可以下载不同模型的预训练版本。

Desktop View 迁移学习,不同框架的model zoo(来自cs231n)

小结

总结今天的内容,我们讲了最优化,它是用来改进训练效果的。还讲了正则化,改变在测试集上的性能,集成模型也算是其中一种。我们还讲了迁移学习,它可以让你在小样本的时候训练的一样好。这些都是非常有用的策略,应该在你的工程中应用它们。下一节我们更具体的讲解一些不同的深度学习软件包。

Desktop View 第二部分:小结(来自cs231n)

本文由作者按照 CC BY 4.0 进行授权

© ManShouyuan. 保留部分权利。

本站总访问量 本站访客数人次

🚩🚩🚩🚩🚩🚩