码字不易,转载请注明出处~
tags: SGD;optimizer;
做了多个分类、检测的算法运用之后,发现对于深度学习中的优化方法仍然云里雾里。现在的开源框架可以非常方便的使用各种现成的库来实现,无论是SGD、Adagrad、Adam等,随便一行代码,传入几个参数就可以完成使用,但究竟这行代码背后是什么原理,反而知之甚少,也心虚不已。原因在于,虽然利用一个什么TF、Tourch等框架,随便down一段代码,整合一下便可以完成某个的任务,但是在理论层面的储备太少,也许将来自己注定是一个搬砖的人,但是也想要成为一个高级搬运工。
本人水平有限,但是却不想成为简简单单一个“调包侠”,本着学习的心态,本系列文章旨在通过学习,熟悉并掌握深度学习中常用的优化方法其背后所承载的初衷与原理,文章内容均来自以下博客,仅仅是处于自身学习爱好,进行翻译整理,并加入自己的调研理解,希望通过这些内容来让自己的理论水平提升起来,也从中获得学习的快乐。
https://medium.com/konvergen/gradient-descent-and-stochastic-gradient-descent-algorithms-for-neural-networks-e817f3c411ef
我们都知道,神经网路训练的过程就是不断减少损失、提升精度的一个过程。通常论文中所用的方法基本上都是基于梯度下降法的(gradient descent),包括后期带动量的梯度下降法(Momentum)或者一些其他的自动优化算法(Adagrad、Adam)等。那么理解这些算法是非常重要的,否则只知其然不知其所以然。
神经网络的训练过程实际上就是不断更新权重参数的过程,通过大量的样本和一定的优化方法,逐步更新寻找到最优的权重参数,而优化算法就是用来求取这组参数的,也是神经网络的最终所求,以后再进行其他相关的任务时,这组数据拿过来直接用就可以了。最典型的就是很多开源的在ImageNet或者其他数据集上预训练好的模型,对于tensorflow来讲,就是.ckpt文件或者pb文件。这个过程通常来讲,给定训练集 S ( x , y ) S(x,y) S(x,y),其中第 i i i个样本 x i x^i xi所对应的标签为 y i y^i yi,神经网络的做法是首先初始化各层的权重参数 θ \theta θ,然后在训练过程中,通过进行前向计算网络的输出值 y ^ \hat{y} y^,然后根据损失函数的定义,计算 L ( y i , y ^ ) L(y^i,\hat{y}) L(yi,y^),如果损失接近0,那么网络基本已经训练好了,不需要进一步权重更新;而如果损失不为0,那么需要进行反向传播进行权重参数的更新,而更新的方法就是梯度下降法。
梯度下降法有全局梯度下降和批量梯度下降两种。
全局梯度下降法,就是在更新权重的时候,使用的是全部的训练样本,梯度计算公式如下:
(1) g G D = 1 n ∑ i = 1 n ∇ θ L ( x ( i ) , y ( i ) , θ t ) g_{GD}=\frac{1}{n}\sum_{i=1}^{n}\nabla_{\theta} L(x^{(i)}, y^{(i)},\theta_t)\tag{1} gGD=n1i=1∑n∇θL(x(i),y(i),θt)(1)
式中的 t t t表示第 t t t次迭代, L ( ⋅ ) L(\cdot) L(⋅)表示损失函数, ∇ θ \nabla_{\theta} ∇θ表示损失函数 L L L相对于 θ t \theta_t θt的偏导数, n n n为总的样本数。
那么,在得到的梯度之后,需要通过反向传播进行权重参数的更新。这里不会讲反向传播是怎么一个过程,这部分是比较费时费力的过程。梯度更新的公式如下:
(2) θ t + 1 = θ t − η g G D \theta_{t+1}=\theta_t-\eta g_{GD}\tag{2} θt+1=θt−ηgGD(2)
式中的 η \eta η为学习率,它决定着在更新参数的时候,参数变化的幅度有多大。一个经典的例子就是“下山”过程,这个参数决定着沿着梯度的方向迈多大的步子。
但是,这里存在一个问题,就是当数据量小的时候,这样做是没有问题的;但是对于数据驱动的深度学习来讲,数据集的样本动辄上百万上千万,如果要进行全局的更新,那就意味着每次都需要计算所有样本之后才能进行一次参数更新,这样的效率简直是太慢了。虽然算法可能找到全局最优解,但是这种方式是不可取的。而且,神经网络训练所使用的GPU内存是有限的,每次能够送入网络的样本也因此受到了限制,实际操作中,基本上没有人这样做。
随机批量梯度下降是在实际中用的较多的方法,顾名思义,就是通过每次送入网络小批量数据,利用这小批量数据计算的梯度来更新参数,从而完成训练过程,公式如下:
(3) g S G D = 1 n ′ ∑ i = 1 n ′ ∇ θ L ( x ( i ) , y ( i ) , θ t ) g_{SGD}=\frac{1}{n^\prime}\sum_{i=1}^{n^\prime}\nabla_{\theta} L(x^{(i)}, y^{(i)},\theta_t)\tag{3} gSGD=n′1i=1∑n′∇θL(x(i),y(i),θt)(3)
可以从上式中看到,与 ( 1 ) (1) (1)相比, ( 3 ) (3) (3)中唯一改变了的就是所用的数据量 n ′ n^\prime n′,其大小为 n ′ = n M n^\prime=\frac{n}{M} n′=Mn,也就是说将原始数据分为 M M M个批次,每个批次为 n ′ n^\prime n′。
那么权重参数更新的过程就会变成:
(4) θ t + 1 = θ t − η g S G D \theta_{t+1}=\theta_t-\eta g_{SGD}\tag{4} θt+1=θt−ηgSGD(4)
通过这种小批量的梯度更新,一方面可以保证小批量数据能够一次送入GPU,不会受到GPU内存的限制;另一方面小批量计算过程较快,可以快速更新梯度。可以看到,当 n ′ n^\prime n′变为1时,就是随机梯度下降,有时也叫做在线训练。
当然,从实际操作出发,是利用批量随机梯度下降做的,但是为什么可以这样做?这样做的合理性在哪里?
实际上,这是有一个理论支撑的:
(6) E [ g S G D ] = ∑ i = 1 n 1 n g S G D ( x ( i ) ) = ∑ i = 1 n 1 n ∇ θ L ( x ( i ) , y ( i ) , θ t ) = 1 n ∑ i = 1 n ∇ θ L ( x ( i ) , y ( i ) , θ t ) = g G D \begin{aligned} E[g_{SGD}]&=\sum_{i=1}^{n}\frac{1}{n}g_{SGD}(x^{(i)})\\ &=\sum_{i=1}^{n}\frac{1}{n}\nabla_{\theta} L(x^{(i)}, y^{(i)},\theta_t)\\ &=\frac{1}{n}\sum_{i=1}^{n}\nabla_{\theta} L(x^{(i)}, y^{(i)},\theta_t) \\ &=g_{GD} \end{aligned}\tag{6} E[gSGD]=i=1∑nn1gSGD(x(i))=i=1∑nn1∇θL(x(i),y(i),θt)=n1i=1∑n∇θL(x(i),y(i),θt)=gGD(6)
也就是说,批量随机梯度下降法的数学期望是与全局梯度下降的数学期望一致的。因此可以通过批量梯度下降来逼近全局梯度下降的解,而不可能精确等于。
这也就解释了,为什么在随机梯度下降中进行参数更新时,训练过程有些波动。这是因为在随机梯度下降进行梯度估计时,它所采用的样本不是“纯净的”样本,而是在随机选取样本时,由于样本选择的随机性而加入了噪声,这种噪声不是真正意义上在样本中添加噪声,而是由于网络输入的样本的不全面而导致的。
同时,采用批量梯度下降所带来的噪声在一定程度上可以使网络摆脱训练样本本身所存在的问题。假设训练样本本身就不是非常均衡,如果使用全局梯度下降,那么在更新权重时计算的梯度势必会由于样本问题带来不均衡。而采用批量梯度下降则在一定程度上可以避免此类问题,因为网络在每次参数更新的时候,不会见到所有的样本,因此也不会存在偏向某个方向,可以说还能够起到正则化的效果。