【DL-CV】正则化,Dropout<前篇---后篇>【DL-CV】浅谈GoogLeNet(咕咕net)segmentfault
原味版的SGD(如下称SGD)是经过死跟负梯度方向来对参数进行更新的,也就是走一步、停下肯定方向、再走一步,如此循环。很是简单,很是老实的走法不是麽?可是SGD这个相对简单的算法在实际使用中仍是会产生很多的问题,下面咱们来看看最主要的几个数组
1️⃣SGD 一旦陷入损失的局部最小值或鞍点(既不是最大值也不是最小值的临界点)训练将变得缓慢:网络
做为问题的引入咱们先考虑参数只有一个的状况下损失关于该参数的图(如上,x轴是该参数,y轴是损失)函数
这是一维的状况,在高维的状况下(上千上万个参数),局部最小值意味着全部参数不管往哪一个方向走损失都会增大,这实际上是很是罕见的事情。而高维状况下的鞍点能够这样理解,即在该点下某些方向会使损失增大,某些方向会使损失减小;因此在高维状况下遇到鞍点将会是很是常见的事情,在鞍点附近训练速度将会变得缓慢。学习
2️⃣SGD 对全部参数更新时应用一样的学习率:
梯度由许多偏导数组成,对应着各个参数的更新。对于偏导数大的,咱们但愿配个小的学习率给他;对于偏导数小的,咱们但愿配个大的学习率给他,这样各个参数都能得到大体相同的更新幅度,提升网络的健壮性。惋惜SGD固定死的学习率不能知足咱们的要求优化
3️⃣SGD 在遇到噪声时,训练将变得缓慢:
SGD 是经过随机抽取小批量样本进行训练的,是对每次损失和梯度的估计(不像批量学习同样所有训练样本全往里塞得到真实的损失和估计),当估计中存在噪音,参数更新的方向会受到影响而偏离,致使训练时间延长spa
综上,使用原版的SGD进行参数更新带来的体验并非那么好(须要更新换代啦)。还好的是,伟大的研究人员,或是对原版SGD进行了各类各样的魔改,或是灵光一闪提出新的更新算法,如今已经有多种更高级的参数更新的方法啦,下面就来看一下3d
该方法的关键是引入一个速度的概念。速度这个量将对历次求得的梯度进行累加,在每次累加时会有一参数$\gamma$对原速度进行衰减处理后再进行累加。参数更新时,咱们不在根据当前的负梯度方向进行更新,而是根据当前速度方向更新。这样引入速度以后 SGD卡在局部最小值和鞍点的问题 会获得有效解决,从而加速训练。blog
上面的说明不懂不要紧,看了下面的比喻你便会豁然开朗
懂了下面就放公式,顺便比较SGD和魔改后的SGD,公式中的t表明第t次迭代更新
SGD | SGD + 动量 | |
---|---|---|
公式(x是待更新参数) | $$x_{t+1}=x_t-\alpha\nabla_xL(x_t)$$ | $$v_{t+1}=\gamma v_t+\alpha\nabla_xL(x_t)$$$$x_{t+1}=x_t-v_{t+1}$$ |
代码实现 | ![]() |
![]() |
至于参数$\gamma$,其存在至关于摩擦力,可以使使速度衰减。若是没有$\gamma$,小球到达最后的“U型”山底就不会停下来,训练时参数可能就收敛不了。$\gamma$常为[0.5,0.9,0.95,0.99]中的一个,而且一般为0.9。$\gamma$也能够随着训练的进行而逐渐上升,如刚开始将$\gamma$设为0.5而在后面的多个epoch中慢慢提高到0.99
Nesterov动量与普通动量原理上有些许不一样(Nesterov动量能够说是普通动量的小升级)。在理论上对于凸函数Nesterov动量能获得更好的收敛,在实践中也确实比普通动量表现更好一些。
使用Nesterov动量时,不会计算当前点的梯度,而是先往速度方向步进到下一点,计算这“下一点”的梯度,而后回到原点把这“下一点”的梯度加到速度上,再用累加后的速度在原点上进行步进。这个看似画蛇添足的操做并非无心义的,计算“下一点”的梯度能够看做是对将来位置梯度的预测从而提早对速度方向进行修正,从而进一步加快训练速度。下面来对比一下普通动量和Nesterov动量:
普通动量 | Nesterov动量 | |
---|---|---|
图示 | ![]() |
![]() |
公式 | $$v_{t+1}=\gamma v_t+\alpha\nabla_xL(x_t)$$$$x_{t+1}=x_t-v_{t+1}$$ | $$v_{t+1}=\gamma v_t + \alpha \nabla_xL(x_t - \gamma v_t)$$$$x_{t+1} = x_t-v_{t+1}$$ |
这两个动量更新方法都有效解决了SGD的问题1️⃣