torch.optim.SGD参数详解(除nesterov)

torch.optim.SGD

torch.optim.SGD(params, lr=, momentum=0, dampening=0, weight_decay=0, nesterov=False):随机梯度下降

  • 【我的理解】虽然叫做“随机梯度下降”,但是本质上还是还是实现的批量梯度下降,即用全部样本梯度的均值更新可学习参数。

    这里所说的全部样本可以是全部数据集,也可以是一个batch,为什么这么说?因为计算梯度是调用backward函数计算的,而backward函数又是通过损失值张量调用的,损失值的计算和样本集的选取息息相关。如果每次都使用全部样本计算损失值,那么很显然调用SGD时也就是用全部的样本梯度的均值去更新可学习参数,如果每次使用一个batch的样本计算损失值,再调用backward,那么调用SGD时就会用这个batch的梯度均值更新可学习参数。

  • params:要训练的参数,一般我们传入的都是model.parameters()

  • lr:learning_rate学习率,会梯度下降的应该都知道学习率吧,也就是步长。

  • weight_decay(权重衰退)和learning_rate(学习率)的区别

    learning_rate就是我们熟知的更新权重的方式,假设可学习参数为 θ \theta θ ,学习率由 γ \gamma γ表示,梯度均值为 g g g;计算出这批样本对应的梯度为 g t g_t gt,迭代完第 t − 1 t-1 t1次可学习参数的值为 θ t − 1 \theta_{t-1} θt1,当前第 t t t次迭代的更新方式为 θ t = θ t − 1 − γ g t \theta_t = \theta_{t-1}-\gamma g_t θt=θt1γgt,即梯度下降法。

    weight_decay是在L2正则化理论中出现的概念。

    什么是L2正则化?
    L2范数也被称为“权重衰减”和“岭回归”,L2的主要作用是解决过拟合,L2范数是所有权重的平方开方和,最小化L2范数,使得权重趋近于0,但是不会为0。那么为什么参数的值小,就能避免过拟合。模型的参数比较小,说明模型简单,泛化能力强。参数比较小,说明某些多项式分支的作用很小,降低了模型的复杂程度。其次参数很大,一个小的变动,都会产生很大的不同。所以采用L2正则化可以很好地解决过拟合的问题。

    在损失函数中加入L2正则化项后(大致)变为
    J ( θ ) = 1 2 m [ ∑ i = 1 m ( h θ ( x ( i ) ) − y ( i ) ) 2 + λ ∑ j = 1 n θ j 2 ] J(\theta)=\frac{1}{2m}[\sum_{i=1}^{m} (h_{\theta}(x^{(i)})-y^{(i)})^2+\lambda\sum_{j=1}^{n}\theta^2_j] J(θ)=2m1[i=1m(hθ(x(i))y(i))2+λj=1nθj2]
    其中, λ ∑ j = 1 n θ j 2 \lambda\sum_{j=1}^{n}\theta^2_j λj=1nθj2就是正则化项, λ \lambda λ就是weight_decay。

    根据上面的加入正则项的损失函数我们可以不严谨地推导一下,加入的正则项是权重的平方和,为了保证损失函数尽可能小,就要让权重和尽量小,所以权重会倾向于训练出较小的值。

    可是梯度不是由backward计算的吗,而backward中可未指明存在正则化项,那不成还要由优化器算法再算一遍新梯度?

    先看看官方文档是如何操作的:

    torch.optim.SGD参数详解(除nesterov)_第1张图片
    只看权重衰退和学习率,描述一下过程,计算使用第 t − 1 t-1 t1次训练到的可学习参数的模型在这一批次中的梯度,赋值给 g t g_t gt,再用权重衰退乘上第 t − 1 t-1 t1次训练到的可学习参数,重新赋值给 g t g_t gt,再用这个 g t g_t gt和学习率更新权重,从而实现训练参数的目的。

    也就是说正则化的实现是在原梯度的基础上加上一个系数乘以可学习参数值作为新梯度,用新梯度更新可学习参数值。并没有重新计算加入正则项后的损失函数的梯度。

    本质上是对 ∂ J ( θ i ) ∂ θ i \frac{\partial{J(\theta_i)}}{\partial\theta_i} θiJ(θi)进行一下变形的结果。

    为了推导的方便,假设加入了正则化项后的损失函数为
    R e g u l a r i z e d C o s t = C o s t + λ ∑ i θ i 2 RegularizedCost=Cost+\lambda\sum_i\theta_i^2 RegularizedCost=Cost+λiθi2

    加入正则化项后的梯度为:
    ∂ R e g u l a r i z e d C o s t ∂ θ i = ∂ C o s t ∂ θ i + 2 λ θ i \frac{\partial{RegularizedCost}}{\partial{\theta_i}} = \frac{\partial Cost}{\partial \theta_i} + 2\lambda\theta_i θiRegularizedCost=θiCost+2λθi

    可以看出 ∂ C o s t ∂ θ i \frac{\partial Cost}{\partial \theta_i} θiCost的值就是未加正则化项求出的梯度,即backward计算出的梯度。

    ∂ R e g u l a r i z e d C o s t ∂ θ i \frac{\partial{RegularizedCost}}{\partial{\theta_i}} θiRegularizedCost将会作为新的梯度值用于更新权重,即
    U p d a t e d    θ i = θ i − γ ∂ R e g u l a r i z e d C o s t ∂ θ i   ⇒ U p d a t e d    θ i = γ ( ∂ C o s t ∂ θ i + 2 λ θ i ) Updated\space\space\theta_i = \theta_i - \gamma \frac{\partial{RegularizedCost}}{\partial{\theta_i}} \\ \space \\ \Rightarrow Updated\space\space\theta_i = \gamma (\frac{\partial Cost}{\partial \theta_i}+2\lambda\theta_i) Updated  θi=θiγθiRegularizedCost Updated  θi=γ(θiCost+2λθi)
    求导数得到的系数2可以扔掉,所以就得到了官方文档中的过程。

  • SGD方法的一个缺点是,其更新方向完全依赖于当前的batch,因而其更新十分不稳定。解决这一问题的一个简单的做法便是引入momentum。

    momentum即动量,它模拟的是物体运动时的惯性,即更新的时候在一定程度上保留之前更新的方向,同时利用当前batch的梯度微调最终的更新方向。这样一来,可以在一定程度上增加稳定性,从而学习地更快,并且还有一定摆脱局部最优的能力。

    torch.optim.SGD参数详解(除nesterov)_第2张图片
    从中可以看出,如果我们设置momentum参数不为0,则判断一下是否是第一次迭代,如果是第一次迭代,那么直接将 g t g_t gt 赋值给 b t b_t bt (将 b t b_t bt理解为临时变量吧),如果不是第一次迭代,那么就用前一次的 b t − 1 b_{t-1} bt1 更新 b t b_t bt,更新方程为 b t = μ b t − 1 + ( 1 − τ ) g t b_t=\mu b_{t-1} + (1-\tau)g_t bt=μbt1+(1τ)gt(反正就是个公式,其中 τ \tau τ 为抑制参数)。由于不考虑nesterov参数,即为False,所以直接将计算得到的 b t b_t bt 赋值回 g t g_t gt

  • 如果对Nesterov Momentum(牛顿动量)感兴趣可以看看Hinton这篇论文


参考

[1] How does SGD weight_decay work? - autograd - PyTorch Forums

[2] L1、L2正则化知识详解 - 简书

[3] L1,L2正则化的原理与区别 - CSDN博客

[4] 各种优化方法总结比较(sgd/momentum/Nesterov/adagrad/adadelta) - 博客园

你可能感兴趣的:(【Pytorch学习】,机器学习,深度学习,pytorch)