Python深度学习:模型、方法与实现
上QQ阅读APP看书,第一时间看更新

1.3 训练神经网络

本节把训练一个神经网络定义为以最小化代价函数Jθ)的方式调整其参数(权重)的过程。代价函数是对由多个样本组成的训练集的性能度量,以向量表示。每个向量都有一个相关的标签(有监督学习)。最常见的是,代价函数度量网络输出和标签之间的差异。

这一节中,我们将简要回顾一下梯度下降优化算法。如果你已经熟悉它,可以跳过本节。

1.3.1 梯度下降

本节中,我们使用具有单一回归输出和均方误差(MSE)代价函数的神经网络,其定义如下:

000

此处,公式解释如下:

  • fθx(i))是神经网络的输出。
  • n是训练集中的样本总数。
  • x(i)为训练样本的向量,上标i表示数据集的第i个样本。用上标是因为x(i)是一个向量,下标是为每个向量分量保留的。000例如,是第i个训练样本的第j个分量。
  • t(i)是与样本x(i)相关的标签。

000 不应该将第i个训练样本的(i)上标指数与代表神经网络层索引的(l)上标混淆。我们只在梯度下降和代价函数部分使用(i)样本索引符号,其他部分使用(l)作为层索引符号。

首先,梯度下降计算Jθ)分别对所有网络权重的导数(梯度)。梯度给了我们一个提示,关于Jθ)是如何随着每一个权重变化的。然后,该算法使用这些信息更新权重,使将来出现相同输入/目标对时的Jθ)最小化。目标是逐步达到代价函数的全局最小值。下面是MSE和单权重神经网络的梯度下降可视化表示。

下面逐步执行梯度下降:

1)用随机值初始化网络权重。

2)重复操作,直到代价函数低于某个阈值:

(1)前向传递:使用前面的公式计算训练集所有样本的MSE的Jθ)代价函数。

(2)反向传递:用链式法则计算Jθ)对所有网络权重的导数:

000
000

MSE的梯度下降可视化

000 分析一下∂Jθ)/∂θj的导数。J作为网络输出的函数,是θj的一个函数。因此,它也是神经网络函数本身的函数,即Jfθ))。然后,通过链式法则,可以得到000

(3)使用这些导数来更新每个网络权重:

000

此处,η是学习率。

梯度下降法通过累积所有训练样本的误差来更新权重。实际上,可以使用其中两项进行修改:

  • 随机(在线)梯度下降(SGD)在每次训练样本后更新权重。
  • 小批量梯度下降对每n个样本(一个小批量)累积误差,并执行一次权重更新。

接下来,讨论一下SGD中可以使用的不同代价函数。

1.3.2 代价函数

除了MSE,还有一些其他的损失函数经常在回归问题中使用。以下是一份非详尽的列表:

  • 平均绝对误差(MAE)是网络输出和目标之间的绝对误差(而不是平方)的平均值。MAE的图和公式见下页。
  • 000

    相对于MSE,MAE的一个优点是它能更好地处理离群样本。对于MSE,如果样本的误差是fθx(i))-t(i)>1,它会指数下降(因为平方)。与其他样本相比,这个样本的权重过大,这可能会导致结果偏差。在MAE中,差异不是指数级的,这个问题不那么明显。

    另外,MAE梯度会有相同的值,直到达到最小值,它会立即变成0。这使得算法更难预测代价函数的最小值有多接近。与MSE相比,当接近代价最小值时,斜率逐渐减小。这使得MSE更容易优化。综上所述,除非训练数据被异常值破坏,否则通常建议在MAE基础上使用MSE。

  • Huber损失试图通过结合MAE和MSE的属性来解决两者的问题。简而言之,当输出数据和目标数据之间的绝对差低于一个固定参数的值δ时,Huber损失类似于MSE。相反,当差异大于δ时,它与MAE相似。这样,它对异常值(差值较大时)的敏感性较低,同时函数的最小值是适当可微的。以下是单一训练样本的三种不同的训练对象的Huber损失图及其公式,反映了它的二重性。
000

Huber损失

接下来,关注分类问题的代价函数。以下是一份非详尽的清单:

  • 交叉熵损失:这里省略了一些工作,因为1.1.2节已经定义了交叉熵。这种损失通常应用于softmax函数的输出。这两者配合得很好。首先,softmax将网络输出转换为概率分布。然后,交叉熵度量网络输出(Q)与真实分布(P)的差值,作为训练标签提供。它还有另一个好的性质,HPQsoftmax)的导数非常简单(虽然计算并不简单):
  • H′(P,Qsoftmax)=Qsoftmaxx(i))-Pt(i)

    此处,x(i)/t(i)是第i个输入/标签训练对。

  • KL散度损失:像交叉熵损失一样,1.1.2节已经对它进行了介绍,并得到KL散度和交叉熵损失之间的关系。其关系可以说明,如果使用两者中的一个作为损失函数,我们也会隐式地使用另一个。

000 有时,会遇到损失函数和代价函数互换使用的情况。人们通常认为它们稍有不同。我们把损失函数称作训练集的单个样本的网络输出和目标数据之间的差值。代价函数是相同的,但是对训练集的多个样本(批量)进行平均(或求和)。

学习不同的代价函数后,把重点放在通过网络反向传播的误差梯度上。

1.3.3 反向传播

这一节将讨论如何更新网络权重以使代价函数最小化。正如在1.3.1节所论证的,这意味着找到代价函数Jθ)对每个网络权重的导数。在链式法则的帮助下我们已经朝这个方向迈出了一步:

000

此处,fθ)是网络输出,而θj是第j个网络权重。在这一节中,我们学习如何推导所有网络权重的神经网络函数本身(提示:链式法则),通过网络反向传播误差梯度来实现这一点。下面是几个假设:

  • 为了简单起见,使用顺序前馈神经网络。顺序意味着每一层从前一层获取输入并将输出发送到下一层。
  • 我们定义wij为第l层的第i个神经元和第l+1层的第j个神经元之间的权重。也就是说,使用下标ij,其中有下标i的元素属于包含下标j的元素的层的前面一层。在多层网络中,ll+1可以是任意两个连续的层,包括输入层、隐藏层和输出层。
  • yi(l)表示第l层的第i个单元的输出,用yj(l+1)表示第l+1层的第j个单元的输出。
  • aj(l)表示第l层的第j个单元的激活函数的输入(即激活前输入的加权和)。

下面的图展示了前面介绍的所有符号。

000

l层表示输入,第l+1层表示输出,wi,j连接第l层中的000对第l+1层中的第j个神经元的激活

有了这些知识铺垫后,让我们进入正题:

1)首先,假设ll+1分别是倒数第二个和最后的(输出)网络层。知道了这个,Jwi,j的导数如下:

000

2)让我们关注∂aj(l+1)/∂wi,j。此处,计算第l层的输出对其中一个权重wi,j的加权和的偏导数。正如在1.1.3节中讨论的,在偏导数中,将考虑除wi,j常量之外的所有函数参数。当导出aj(l+1)时,它们都变成0,只剩下yi(l)wi,j)/∂wi,j=yi(l)。因此,可以得到:

000

3)对于网络的任意两个连续的隐藏层(ll+1),由第1)步得出的公式都成立。我们了解yi(l)wi,j)/∂wi,j=yi(l),也知道∂yj(l+1)/∂aj(l+1)是我们可以计算的激活函数的导数(参见1.2.4节)。需要做的就是计算导数∂J/∂yj(l+1)(回想一下,此处,第l+1层是某个隐藏层)。我们注意到这是误差对第l+1层的激活函数的导数。现在可以因为以下应用,从最后一层开始反向移动,计算所有的导数:

  • 可以计算最后一层的导数。
  • 假设可以计算下一层的导数,有一个公式允许我们计算某一层的导数。

4)记住这些步骤,用链式法则得到如下方程:

000

j的和反映了,在网络的前馈部分,输出000被反馈给在第l+1层的神经元。因此,当误差反向传播的时候它们全都提供给了yi(l)

我们可以再一次计算∂yj(l+1)/∂aj(l+1)。跟着与第3)步相同的逻辑,可以计算∂aj(l+1)/∂yi(l)=wi,j。因此,一旦知道了∂J/∂yj(l+1),可以计算∂J/∂yj(l)。因为可以计算最后一层的∂J/∂yj(l+1),并且能够反向计算任意一层的∂J/∂yj(l),所以我们能计算任意一层的∂J/∂wi,j

5)总结一下,假设有一系列层,它适用于以下内容:

yiyjyk

此处,有以下基本方程:

000

通过使用这两个方程,可以计算代价对每一层的导数。

6)如果设000,然后000表示代价对激活值的变化,可以把000看作在神经元yj(l+1)处的误差。可以将这些方程改写为:

000

由此,可以得到:

000

这两个方程为我们提供了反向传播的另一种观点,因为代价随激活值的变化而变化。一旦知道了下一层(l+1)的变化,它们就为我们提供了计算任意层l的变化的方法。

7)将这些方程组合起来,可以看出:

000

8)各层权重的更新规则如下:

000

现在熟悉了反向传播,下面讨论训练过程的另一个组成部分:权重初始化。

1.3.4 权重初始化

深度网络训练的一个关键组成部分是随机权重初始化。这很重要,因为有些激活函数,如sigmoid和ReLU,只有它们的输入在一定范围内,才会产生有意义的输出和梯度。

一个著名的例子就是梯度消失问题。为了理解它,考虑一个具有sigmoid激活的FC层(这个示例对tanh也有效)。1.2.4节展示了sigmoid函数及其导数。如果输入的加权和落在(-5,5)范围之外,sigmoid激活实际上变为0或1。本质上,它是饱和的。这在推导sigmoid的反向传递过程中是可见的(公式是σ′=σ(1-σ))。可以看到,在相同的(-5,5)输入范围内,导数大于0。因此,无论试图传播回之前的层的误差是什么,如果激活不在这个范围内,它就会消失(这就是该名称的来源)。

000 除了sigmoid导数的严格意义范围外,我们注意到,即使在最佳条件下,其最大值也只有0.25。当把梯度传播到sigmoid导数,一旦通过,最小会变为其1/4。因此,即使没有落在期望的范围之外,梯度也可能会在几个层中消失。这是sigmoid相对于*LU函数族的主要缺点之一,在大多数情况下,梯度为1。

解决这个问题的一种方法是使用*LU激活。但即便如此,使用更好的权重初始化还是有意义的,因为它可以加速训练过程。一种流行的技术是使用Xavier/Glorot初始化器(http://proceedings.mlr.press/v9/glorot10a/glorot10a.pdf)。简而言之,这种技术考虑了单元的输入和输出连接的数量。它有两种变体:

  • Xavier均匀初始化,它从范围[-a,a]的均匀分布中抽取样本。参数a的定义如下:
  • 000

    此处ninnout分别表示输入和输出的数量(即输出到当前单元的单元数和当前单元输出的单元数)。

  • Xavier正态初始化,从均值为0、方差为0的正态分布(见1.1.2节)中抽取样本如下:
  • 000

建议对sigmoid或tanh激活函数进行Xavier/Glorot初始化。论文“Delving Deep into Rectifiers: Surpassing Human-Level Performance on ImageNet Classification”(https://arxiv.org/abs/1502.01852)中,提出了一种类似的更适合ReLU激活的技术。同样,它有两种变体:

  • He均匀初始化,它从范围[-a,a]的均匀分布中抽取样本。参数a的定义如下:
  • 000
  • He正态初始化,从均值为0、方差为0的正态分布中抽取样本如下:
  • 000

    当输入为负时,ReLU输出总是0。如果假设ReLU的初始输入集中在0附近,那么半数的ReLU输出为0。与Xavier初始化相比,He初始化通过增加两次方差来弥补这一点。

    下一节将讨论对标准SGD的权重更新规则的一些改进。

1.3.5 SGD改进

我们将从动量开始,它通过使用先前权重更新的值来调整当前权重更新,并扩展SGD。也就是说,如果第t-1步的权重更新较大,会增加第t步的权重更新。可以用类比来解释动量。把损失函数的表面想象成小山的表面。现在,假设拿着一个球在山顶(最大值)。如果把球扔下去,由于地球的重力,它会开始滚向山底(最小值)。它滚动的距离越远,速度就越快。换句话说,它将获得动量。

现在,讨论如何在权重更新规则中实现动量。回顾我们在1.3.1节中介绍的更新规则,θjθj-η∂Jθ)/∂θj。假设在训练过程的第t步:

1)首先,通过包含之前更新的速度vt-1,计算当前权重更新值vt

vtμvt-1-η∂Jθ)/∂θj

此处,μ是一个在[0:1]范围内的超参数,称为动量率。在第一次迭代中,vt被初始化为0。

2)然后,执行实际的权重更新:

θjθj+vt

对基本动量的改进是Nesterov动量。它依赖于观察到第t-1步的动量可能不能反映第t步的条件。例如,在第t-1步的梯度很陡,因此动量很高。然而,在第t-1步的权重更新后,实际上达到了代价函数的最小值,只需要在t时刻进行较小的权重更新。尽管如此,仍然会从t-1得到较大的动量,这可能会导致调整后的权重跳过最小值。Nesterov动量提出了改变计算权重更新速度的方式——根据代价函数的梯度来计算vt,它是由权重θj的潜在未来值来计算的。更新后的速度公式如下:

vtμvt-1-η∂Jθ;θj+μvt-1)/∂θj

如果t-1处的动量相对于t处的不正确,则改进的梯度会在相同的更新步骤中补偿该误差。

接下来,讨论一下Adam自适应学习率算法(“Adam: A Method for Stochastic Optimization”,https://arxiv.org/abs/1412.6980)。它根据以前的权重更新(动量)计算每个权重的个体和自适应学习率。让我们讨论它是如何工作的:

1)首先,需要计算梯度的一阶矩(或均值)和二阶矩(或方差):

000

此处,β1β2是超参数,默认值分别为0.9和0.999。mtvt作为梯度的移动平均值,类似于动量。它们在第一次迭代中被初始化为0。

2)因为mv从0开始,所以在训练的初始阶段,它们会偏向于0。例如,在t-1处,β1=0.9并且∂Jθ)/∂θj=10。此处,m1=0.9*0+(1-0.9)*10=1,这比实际为10的梯度小了很多。为了补偿这个偏差,计算mtvt的偏差修正版本:

000

3)最后,需要使用以下公式进行权重更新:

000

此处,η是学习率,000是防止被0整除的一些小值。