深入浅出PyTorch:从模型到源码
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

1.7 损失函数和优化器

1.7.1 常用的损失函数

前面介绍了一些简单的损失函数。本节首先归纳总结一下深度学习中一些常用的损失函数。神经网络中常用的损失函数同样可以划分为用于分类的损失函数和用于回归的损失函数。对用于分类的损失函数来说,主要分为两类,一类是二分类的神经网络,其输出的激活函数是Sigmoid函数,对应的损失函数是二分类的交叉熵(Binary Cross Entropy),具体如式(1.42)所示。由于激活函数是Sigmoid函数,输出的是对应正分类的概率p,如果训练数据是正分类,那么l=1,否则l=0。由于深度学习的优化器一般是使损失函数最小,所以这里对应正分类和负分类的对数概率前面有个负号。于是当l=1时,因为对数函数是单调递增函数,优化损失函数使之最小就意味着优化神经网络Sigmoid激活函数输出值p,使得这个输出值尽可能大,即尽可能让神经网络预测正样本,反之,如果l=0,可以看到神经网络的优化会使(1-p)尽可能大,即p尽可能小,让神经网络偏向于预测负样本。对于多分类的问题来说,如前所述,使用的是概率分布,由Softmax函数产生,对应的损失函数是(多分类)交叉熵(Cross Entropy)。交叉熵的计算如式(1.43)所示,其中lii号标签的独热编码,如果训练数据给出的目标标签为i(0,…,N-1),其中N是标签的个数,则li=1,其他的标签都等于0。于是在给定优化器的情况下,优化器会使模型向着pi变大的方向优化。如果需要预测连续的值,即回归问题,那么需要的则是平方损失函数(Square Loss,或称为Mean Square Error,MSE),其公式如式(1.44)所示。其中yp是神经网络预测的值,yt是训练数据集的值。可以看到,这个损失函数设计的目标是让神经网络预测的值尽可能和训练数据的值接近。

1.7.2 基于梯度的优化器

前面介绍了神经网络中梯度的求解,正如前面提到的一样,之所以要计算神经网络中的权重梯度,其目的是为了在后续的优化算法中使用梯度下降(Gradient Descent)算法来进行优化(Optimization)。这里首先从最简单的例子来说明一下梯度下降算法是如何工作的。根据微积分的知识可以知道,在函数的极小值点附近,如果沿着函数梯度的方向行走,函数的值是下降的(见式(1.45))。在式(1.45)中可以看到有个参数α,这个参数控制着权重沿着梯度下降的大小,称为学习率(Learning Rate),当学习率比较小(比如10-3)的时候,权重梯度优化的速度比较慢,但由于每次变化的步长比较小。因此,函数的变化相对连续,优化过程也是连续的,能够逐渐收敛到函数的极小值点。当学习率比较大的时候(比如10-1),每次变化步长大,梯度优化的速度相对较快,但是优化的行为可能不连续,这就导致了函数可能在某些值附近振荡收敛,甚至可能会导致整个优化行为的数值不稳定,即函数在优化过程中不断振荡无法收敛,甚至会不收敛(函数的值越来越大,直到无穷大)。图1.18显示了不同的学习率情况下梯度下降算法进行优化的行为,可以看到,在小学习率的情况下,整体参数(横坐标)的变化趋势是使目标函数不断变小的。但是在大学习率的情况下,参数会在正负之间发生振荡,虽然目标函数还是不断变小,而且变小的速率也相对于小学习率情况下比较快,但是参数在正负之间不断地变化导致了优化行为的不稳定。

图1.18 不同学习率的损失函数下降情况

在深度学习的过程中,经常会遇到训练数据的数据量很大的情况。由于训练过程是相对于所有的训练数据来进行的,理论上,需要一次性计算给定所有数据的情况下的权重梯度。显然,这在训练数据很大的情况下是不可能的。为了能够在这个情况下对深度学习模型进行优化,一般采用的方式是使用基于迷你批次(Mini-batch)的数据来估算神经网络的权重梯度,并且使用估算得到的权重梯度来对模型进行梯度下降优化。其一般的流程为,对所有的数据进行随机排列(Shuffle),然后从这些随机排列的数据中抽取一定数目的数据,这个数目称为批次大小(Batch Size),计算出每个数据的损失函数,并对损失函数求平均(或者求和,视模型的架构而定),接着用平均后的损失函数进行反向传播,计算对应的权重梯度。因为求导计算是线性的,因此损失函数平均求得的权重梯度和数据梯度等于迷你批次中给所有数据中单个损失函数对应权重梯度和数据梯度的平均,如式(1.46)所示,其中i代表该迷你批次中的第i个数据,N是迷你批次的数目。我们称包含了所有数据的迷你批次的集合为迭代期(Epoch),称训练完一次训练数据包含的所有的迷你批次为一个迭代期。实际的训练往往需要经过几十甚至几百个迭代期。显然,因为迷你批次的大小在实际计算过程中远小于训练数据的数目,一个迷你批次计算出来的梯度并不能代表所有数据的梯度,只能代表所有数据梯度的一个近似。因此,在梯度下降的过程中带有一定的随机性。因此,这种优化算法被称为随机梯度下降算法(Stochastic Gradient Descent,SGD)。在实际的优化行为的表现上,随机梯度下降算法有一定的不稳定性,需要调节学习率使得损失函数处于下降速率比较快的状态。

因为随机梯度下降算法使用的梯度是迷你批次的近似梯度,而不是所有数据的准确梯度。因此,在使用随机梯下降算法的情况下会导致优化行为有一定的不稳定性,而且有可能会导致算法收敛速度降低。这时就需要一系列的其他优化算法来加快算法的收敛速度并提高算法的稳定性。一般来说,深度学习的优化算法大多数是仅基于损失函数对权重的一阶导数,这些算法统称为一阶优化算法,对于基于权重的二阶导数的优化算法,因限于篇幅,这里不再介绍,有兴趣的读者可以参考相关资料21。对于一阶优化算法而言,一个重要的关注点是如何校正当前的梯度,让当前迷你批次的梯度尽可能正确地反映真正的梯度方向。这里首先需要介绍的一个概念是动量(Momentum)。我们知道,之所以梯度随机下降算法的梯度不正确,是因为没有所有批次的梯度的信息。为了引入其他迷你批次梯度的信息,一般需要在当前梯度中引入历史的信息。式(1.47)展示了动量的计算方法,令初始时刻的动量为0,t时刻的动量等于t-1时刻的动量加上权重梯度乘以学习率。γ的值代表优化器中包含过去历史信息的多少,一般取值是0.9左右。在整合了动量以后,随机梯度下降算法可以应用动量来进行优化,如式(1.48)所示。相比于原始的随机梯度下降算法,基于动量的梯度下降算法能够更接近真实的梯度,从而增加了算法的稳定性。

除引入动量外,还可以引入权重梯度的平方来对权重进行优化。下面描述的算法称为AdaGrad算法,其基本思想是把历史的权重梯度的分量平方求和,并用这个平方和更新权重,如式(1.49)和式(1.50)所示。从这个公式可以看出,如果梯度的平方在过去的历史中某个分量很大,对应的Gt在这个分量上也会很大。式(1.50)描述了AdaGrad算法如何更新权重,其中是一个很小的常数,主要目的是为了避免无穷大,可以看到,如果Gt在某个分量上很大,对应的权重分量的更新会减小。相比于随机梯度下降算法,AdaGrad算法对于学习率α比较不敏感,一般取值0.01能够得到很好的效果。同样,由于AdaGrad引入了历史的梯度数据,同时减小了过大的分量,其数值稳定性相对于随机梯度下降算法有了很大的提高。

AdaGrad算法由于引入了梯度平方的历史求和,随着训练的进行,权重下降会越来越小。为了解决这个问题,需要引入过去历史的平均,而不是求和。为此,在RMSProp算法中,引入了梯度平方(又称为速度,Velocity)的指数移动平均(Exponential Moving Average),用于求梯度在一定时长内的平均,具体如式(1.51)所示。其中γ的值决定了记忆历史的长度,我们可以看到γ越大,记忆的历史越长,一般取γ在0.9左右。有了梯度平方的指数移动平均后,可以通过指数移动平均来对权重进行更新,如式(1.52)所示。

Adam算法则整合了动量和速度,并且把它们整合进一个优化框架中。可以看到,式(1.53)和式(1.54)分别计算了动量和速度的指数移动平均,然后式(1.55)把动量和速度整合进了优化过程中。一般来说,β1默认取值为0.9,β2默认取值为0.999。

1.7.3 学习率衰减和权重衰减

在深度学习模型的优化过程中,另一个比较重要的技巧是学习率衰减。一般来说,在优化的时候,可以观察到损失函数逐渐下降,然后停滞到一个平台不再下降。这时候可以尝试着降低学习率,然后观察损失函数是否继续下降。学习率可以降低为初始学习率的0.5倍或0.1倍,正常情况下,可以尝试降低学习率2~3次,然后发现损失函数有一定的下降。这个过程就是最简单的学习率衰减的过程,其主要原理就是可能当前的学习率对于当前的权重和损失函数来说偏大,调低学习率后能够让损失函数继续沿着梯度的方向做更多的下降。

以上是最简单的学习率衰减过程,在实践中,还有更复杂的梯度下降算法,比如,指数学习率衰减(Exponential Decay)和余弦退火(Cosine Annealing)等算法,具体的实现算法可以参考深度学习框架(如PyTorch等)的文档。

优化器除了能够用来优化神经网络,还能对神经网络做一定的正则化,具体使用的方法是权重衰减(Weight Decay)。让我们从前面介绍的L2正则化出发。假设神经网络的损失函数是L,如果需要对权重进行正则化,则需要加上相关的正则化的项(其中α是正则化系数),得到新的损失函数L′,如式(1.56)所示。如果对新的损失函数求导,可以得到新的梯度项,如式(1.57)所示。这样,相当于在优化过程中引入了跟权重相关的项,这一项和权重的值成正比。在存在正则化项的情况下,优化器的优化相当于沿着梯度的方向进行优化,然后把权重替换为1倍原来的权重。整个过程就相当于对权重进行了衰减。一般来说,权重衰减的值可以取得比较小,一般在10-4到10-5之间。

以上算法就是现代深度学习中使用的主流优化算法。事实上,由于神经网络的复杂性,损失函数存在复数个局部极小值点(即损失函数是非凸的,Non-convex),而并不像一般的机器学习算法一样只有一个全局的极小值点(凸函数,Convex)。所以,优化器最终收敛到的点可能不是所有的极小值点,甚至可能不是极小值点,而是鞍点(Saddle Point)。鉴于一般情况下神经网络优化得到的模型在准确率方面还是相对比较高的,所以这个问题在实践中对大多数的情况不造成影响。关于神经网络损失函数的特性,可以参考相关资料22