DL中常用的优化算法的分析与比较

DL中常用的优化算法的分析与比较

梯度下降(Gradient dencent)

梯度下降的想法来源于函数的梯度的方向是函数增长最快的方向,所以负梯度方向就是函数下降最快的方向,由于在DL中训练的目的是降低训练误差,(先不考虑终级目的—降低泛化误差),所以更新参数的时候就想到了朝着负梯度的方向来更新。

公式如下

一般这里的$\nabla f(x)$ 是损失函数对所有样本的梯度之后再平均,即

但是从上面的公式中明显可以发现,如果要更新一次参数的话,需要计算损失函数对于所有训练集上的梯度,而且这个梯度一旦算出来之后是个常值(这句话的意思是假设训练的batchsize为10, 训练集的总数是10000,那么在每个epoch中的1000个iters里面的每个iter中梯度都是一样的。)但是代价是需要先计算所有的样本上的loss,这当数据量非常大的时候是不可行的。

解决方案是下面的SGD

SGD (Stochastic Gradient Descent)

假设损失函数为 $L(x) = \frac{1}{n} \sum_{i=1}^{n} L_i(x)$, 此果损失函数(或者叫目标函数)在$x$处的梯度是

这仍是上面的公式,而在随机梯度下降中,每次迭代的时候用的是

其中$i\in {1,…,n}$ 里面是随机的,$\eta$是学习率,会发现如果这样弄的话,计算量会立即下降,从O(n)到O(1).并且非常重要的理论支撑是随机梯度$\nabla L_i(x)$ 是对梯度$\nabla L(x)$的无偏估计,即

这是很容易看出来的,这说明随机梯度是对梯度的一个良好的估计。

小批量随机梯度下降

在实际训练中因为数据量往往会很大,我们会分batch进行训练,实际中每个batch更新所用的梯度是这一batch中损失关于batch中每个样本的梯度平均,即 用的更新的梯度是

其中batch的量是一个超参数,而且当batch等于1的时候就是真正的SGD了,还有所谓的随机梯度下降并不是每次更新参数的时候随机的选一个样本的梯度进行计算,而是用的就是当前批次的样本上的梯度或者当前样本的梯度,其随机体现在梯度在不停地变化,所以SGD有一个好处是可以防止进入局部最小,这在梯度下降里面是做不到的,比如在梯度下降里面某一个epoch里面的梯度算完之后为0了,那么参数将不会更新,而且是在整个epoch里面参数将不再更新,不光如此,因为参数没有更新,在以后的epoch中参数也将不再更新。所以如果在GD中如果进入了局部极小的话,就会出不来了。而在SGD中并不是这样的,因为这个梯度在不停地变化,即使是在同一个epoch里面的不同的batch上面,梯度都是不一样的,那么,当这次梯度为0,参数没有更新的时候,下一次参数还是有可能进行更新的。这样就会跳出局部极小。

实际在使用SGD或者是mini-batch的SGD的时候,通常会让学习率在迭代中decay.

这样操作的原因是”随机采样得到的梯度的方差在迭代过程中无法减小” (比如用梯度下降的时候,梯度是真实的梯度,在每个epoch中是不变的,相当于梯度的方差是0,因为一直没有变化,而随机梯度每次更新参数的时候用的梯度一下处于变化中,并不稳定,所以梯度的方差也没有变,但是这时候梯度的方差大于0),但是如果学习率在减少的话,那么”学习率与梯度的乘积”的方差就会在迭代的过程中减小,从而有让训练过程稳定的作用,这里的稳定并不是没有学习,而是处于稳定的学习状态中的意思。

动量法

前面提到的GD或者SGD,根据自变量当前的位置,沿着当前位置的梯度更新自变量,但是如果自变量的迭代方向仅仅取决于自变量当前的位置,可能会有其他的麻烦,特别是在高维的时候。

比如 $L(x) = 0.1 x_1^2+10x_2^2$, 那么梯度分别是

所以参数更新的辐度在两个方向是不一样的,但是这样有可能变化大的那个方向可能会影响到变化慢的那个方向的收敛,比如下面这种情况

avator

这样的话,自然的想法是降低学习率让在变化快的方向上面变化的慢一些,但是这样会影响本来就变化慢的方向变的更慢,这样会增加训练时间,而且这时候如果调大学习率的话,很可能在变化快的方向上发散而不再收敛。

  • 动量法的提出就是为了解决梯度下降的上述问题

设$g_t$是小批量sgd随机梯度的定义,动量法的迭代是下面的公式

其中超参数$0\leq \gamma < 1$, 显然动量法是小批量随机梯度的扩展版本,因为当$\gamam=0$的时候就是小批量随机梯度下降。

  • 动量法背后的理论

  • 指数加权移动平均 设 $y_t = \gamma y_{t-1} + (1-\gamma)x_t$. 通过不断的迭代可以写出下面的式子,

因为第二项当N比较大的时候接近为0,那么去掉第二项的话,上式即

$y_t ~ (1-\gamma)(\sum_{i=0}^{N-1}\gamma^i x_{t-i})$, 比如可以取$N = \frac{1}{1-\gamma}$, 这也符合上面的推理,因为

这个数是比较小了。比如当$\gamma=0.95$时,

$y_t = 0.05 \sum_{i=0}^{19}0.95^ix_{t-i}$, 这相当于是将$y_t$看作是对最近$\frac{1}{1-\gamma}$个时间步的$x_t$的值进行的加权平均,从数学的角度来看其实就像是一个卷积,因为本身卷积就具有“麿光”的作用。

现在回过头来对动量中的公式作一个变形 $v_t = \gamma v_{t-1} +(1-\gamma)(\frac{\eta_t}{1-\gamma}g_t)$, 从而由上面的分析可知,$v_t$实际上是对sequence$\frac{\eta_{t-i}g_{t-i}}{1-\gamma}$ 做了指数加权移动平均。这说明在这种算法中当前的更新量需要依赖于之前离的最近的$\frac{1}{1-\gamma}$个时间步的更新量做了指数加权平均之后,再乘以一个系数,这说明在动量法中自变量在各个方向上的移动幅度不仅取决于当前的梯度,而且取决于过去的各个梯度在各个方向上是否一致。

训练的时候如何设置

在torch 中用SGD的时候有几个参数。

torch.optim.SGD(params, lr=<required parameter>, momentum=0, dampening=0, weight_decay=0, nesterov=False)

这里的momentum 就是这里的动量法,一般设置为0.9。而这里的weight_decay是正则化项,之所以正则化项是因为

一般为了防止模型过拟合,会在代价函数后面加一个L2的正则化项 $C=C_0 +\frac{\lambda}{2n} \sum_{w} w^2$, 这里的$\lambda$就是”weight_decay系数”,

求导可以发现$\frac{\partial C}{\partial w} = \frac{\partial C_0}{\partial w} + \frac{\lambda}{n}w$, $\frac{\partial C}{\partial b} = \frac{\partial C_0}{\partial b}$, 说明L2的正则化对于参数b并没有什么影响,但是对w会有影响

从而会发现,它的效果是减小 w,这就是为了防止过拟合。

  • 为什么让一些参数尽可能地为0会防止过拟合

这个我自己想了一个理解的例子,拿拟合曲线来说,假设有N个样本点,那么根据多项式逼近定理,一定可以找到一个N阶的多项式完全通过这N个点,这时候的参数是N+1个,假设其真实的曲线是(N-1)阶的, 那么我们这时候就是过拟合了,就是我们的参数太多了,多的把那些噪声点也以为是正确的分布了,相当于模型不是学会了,而是记住了。而N-1个参数的话,就相当于其他的参数为0,所以在用N个参数的时候如果能减少参数的数量(让一些为0)就会降低拟合的风险。

AdaGrad算法

关键词: AdaGrad在迭代过程中不断调整学习率,让每个元素都有自己的学习率。

仍然回到最初的梯度下降算法中,当目标函数对不同的方向上的梯度值有较大的差别时,自然的想法是要选择足够小的学习率在幅度比较大的方向上,从而使其不发散,但是这样也会使得变化小的方向上学习的慢。动量法提供了一种解决办法,即用指数加权移动平均使得自变量的更新方向更加一致,或者说理角成让合方向更加朝着最优解的方向,从而降低了发散的可能。 其实也还有另外的办法,前面是因为学习率无论在变化快的方向上还是在变化慢的方向上都一致而导致的,那么也可以让学习率不一致,这样大家就不会相互影响,或者影响的小一些。

算法如下 $g_t$仍是小批量随机梯度。

上面的式子最好按向量的分量来理解,这样每个方向上的学习率就会与这个方向上的梯度有关系,如果梯度很大,那么随着$s_t$的累加,梯度前面的这一项也会慢慢地变小,可以看到,自变量中每个元素的学习率在迭代的过程中一直在降低或者不变,所以AdaGrad明显有一个drawback,即如果当学习率在早期降得比较快但是没有学到好模型时,会在后期因为学习率过小而可能较难找到一个比较有用的解。

RMSProp算法

上面的最后提到了AdaGrad算法的一个不好的地方,那么针对此RMSProp算法对AdaGrad做了一些简单的修改。

首先,RMSProp将这些梯度按元素平方做指数加权移动平均,给定$0\leq \gamma < 1$,

和AdaGrad算法一样,RMSProp算法将目标函数自变量中的每个元素的学习率重新调

这样的话,因为$s_t$是对$g_t\cdot g_t$的指数加权移动平均,所以自变量每个元素的学习率在迭代过程中就不再是一直降低(或不变)了。

所以RMSProp算法就相当于在AdaGrad的基础上做了小批量的指数加权移动平均来调整学习率。

在RMSProp的基础上,研究者又加上了动量的想法,这样就成了Adam 算法

Adam算法

其实算法不断地改进过程就是不断地把之前的变成子集。

然后是

最后是

所以很容易看出来Adam算法是RMSProp算法与动量法的结合。并且其中带$\hat{.}$的两项是偏差修正。

因为$v_t = (1-\beta_1)\sum_{i=1}^t\beta_1^{t-i}g_i$, 所以权值的和是$(1-\beta_1)\sum_{i=1}^t\beta_1^{t-i}= 1-\beta_1^t.$从而除以了这个$1-\beta_1^t$, $s_t$的也是类似,除的是$1-\beta_2^t.$

接下来是实验的验证。

结果如下图所次,其中baseline是用sgd加了动量和weight_decay做的。只不过一个是固定学习率的,一个是step 的学习率变化的。

baseline2 avator baseline1 avator adagrad avator rmsprop avator adam avator

其中用RMSProp训练的时候,刚开始初始学习率设的是0.01,结果发散了,然后设置到0.001但是仍然没有收敛。 可以看出来adam的测试的loss稳定性比adagrad要好一些,当然这只是在这个小实验上体现出来的。

打赏,谢谢~~

取消

感谢您的支持,我会继续努力的!

扫码支持
扫码打赏,多谢支持~

打开微信扫一扫,即可进行扫码打赏哦