美文网首页
PyTorch学习笔记6 - 理解更多神经网络优化方法

PyTorch学习笔记6 - 理解更多神经网络优化方法

作者: 小新_XX | 来源:发表于2019-04-16 21:18 被阅读0次

    本篇笔记的完整代码:https://github.com/ChenWentai/PyTorch/blob/master/task6.py

    1. 优化器(Optimizer)简介

    在backprop更新权重时,一般采用SGD(Stochastic Gradient Descent)作为优化器。其实除了SGD以外,还有很多其他的优化器可供选择。

    1.1 BGD (Batch Gradient Descent)和 SGD

    在每一轮的训练过程中,BDG算法用整个训练集的数据计算cost fuction的梯度,并用该梯度对模型参数进行更新。这样在cost function为凸函数的情况下,可以收敛到全局最优。但是由于每轮迭代都需要在整个数据集上计算一次,所以批量梯度下降可能非常慢解。
    W = W -\lambda \frac{dE(W;x,y)}{dW} \tag{1.1.1}
    相比之下,SGD每次只采用一个样本来计算cost function,大大节省了计算空间,提升了效率。
    W = W -\lambda \frac{dE(W;x_{i},y_{i})}{dW} \tag{1.1.2}
    在BGD和SGD之间还有一种折中的方法:Mini-batch Gradient Descent,即每次只是用一部分样本来计算cost function对W的梯度。
    W = W -\lambda \frac{dE(W;x_{i:i+n},y_{i:i+n})}{dW} \tag{1.1.3}

    1.2 SGD with momentum

    SGD方法的一个缺点是其更新方向完全依赖于当前batch计算出的梯度,若batch选取不具有代表性,则结果会十分不稳定。而Momentum机制会观察历史梯度v_{t-1},若当前梯度的方向与历史梯度一致(表明当前样本不太可能为异常点),则会增强这个方向的梯度,若当前梯度与历史梯方向不一致,则梯度会衰减,如公式(1.2)和图1所示
    \begin{equation}\begin{split} v_{t} = \gamma v_{t-1} + \lambda \frac{dE(W)}{dW}\\ W = W - v_{t}\:\:\:\:\:\:\:\:\:\:\:\\ \end{split}\end{equation} \tag{1.2}

    图1 Momentum

    1.3 Nesterov Momentum

    Nesterov Momentum通过将一部分梯度更新加入到现有的求梯度公式中,在Momentum的基础上减小了误差,如式(1.3)和图2所示:
    \begin{equation}\begin{split} v_{t} = \gamma v_{t-1} + \lambda \frac{dE(W-\gamma v_{t-1})}{dW}\\ W = W - v_{t}\:\:\:\:\:\:\:\:\:\:\:\\ \end{split}\end{equation} \tag{1.2}

    图2 Nesterov Momentum

    1.4 Adagrad

    Adagrad的思想是根据现有的学习率(learning rate)来更新权重,能够在学习的过程中自适应地对学习率进行调整,可以看做是learning rate schedular的一个类型。
    \begin{equation}\begin{split} G_{i}^{t-1} = \sum_{T=0}^{t-1}(\frac{dE(W_{i}^{T} )}{dW})^{2}\:\:\:\:\:\\ W_{i}^{t} = W_{i}^{t} - \frac{\lambda}{\sqrt{G_{i}^{t-1} + \epsilon}} \frac{dE(W_{i}^{t-1} )}{dW} \end{split}\end{equation} \tag{1.4}
    Adagrad的缺点是在训练的中后期,分母上梯度平方的累加将会越来越大,从而梯度趋近于0,使得训练提前结束。

    1.5 RMSprop

    RMSprop也是一种学习率调整的算法。Adagrad会累加之前所有的梯度平方,而RMSprop仅仅是计算对应的平均值,因此可缓解Adagrad算法学习率下降较快的问题。RMSprop其更新公式如下:
    \begin{equation}\begin{split} G_{i}^{t} = \gamma G_{i}^{t-1} + (1-\gamma)(\frac{dE(W_{i}^{t-1} )}{dW})^{2}\\ W_{i}^{t} = W_{i}^{t} - \frac{\lambda}{\sqrt{G_{i}^{t-1} + \epsilon}} \frac{dE(W_{i}^{t-1} )}{dW}\: \end{split}\end{equation} \tag{1.5}

    1.6 Adam

    Adam的思想是在RMSprop的基础上加入Momentum, 利用梯度的一阶矩估计和二阶矩估计动态调整每个参数的学习率。
    \begin{equation}\begin{split} m_{i}^{t} = \beta_{1} m_{i}^{t-1} + (1-\beta_{1})\frac{dE(W_i^{t-1})}{dW}\:\: \\ v_{i}^{t} = \beta_{2} v_{i}^{t-1} + (1-\beta_{2})(\frac{dE(W_i^{t-1})}{dW})^2\\ W_{i}^{t} = W_{i}^{t} - \frac{\lambda}{\sqrt{v_{i}^{t} + \epsilon}}m_{i}^t\:\:\:\:\:\:\:\:\:\:\ \end{split}\end{equation} \tag{1.6}

    2 使用numpy模拟不同的优化器

    #构造数据:J为损失函数,J_prime为损失函数的导数
    import numpy as np
    import matplotlib.pyplot as plot
    J = lambda w1, w2: w1**2+10*w2**2
    J_prime1 = lambda w1: 2*w1
    J_prime2 = lambda w2: 2*w2
    w1 = 1
    w2 = -1
    epoch = 200
    lr = 0.1
    
    #SGD
    Loss_sgd = []
    W1_sgd = []
    W2_sgd = []
    for i in range(epoch):
        w1 -= lr*J_prime1(w1)
        w2 -= lr*J_prime2(w2)
        W1_sgd.append(w1)
        W2_sgd.append(w2)
        Loss_sgd.append(J(w1, w2))
    
    # Momentum
    gamma = 0.5
    v1 = 0
    v2 = 0
    s = 0
    Loss_moment = []
    W1_moment = []
    W2_moment = []
    for i in range(epoch):
        v1 = gamma*v1 + lr*J_prime1(w1)
        w1 -= v1
        v2 = gamma*v2 + lr*J_prime2(w2)
        w2 -= v2    
        W1_moment.append(w1)
        W2_moment.append(w2)
        Loss_moment.append(J(w1, w2))
    
    #Adagrad
    v = 0
    s = 0
    Loss_ada = []
    W1_ada = []
    W2_ada = []
    s1=s2=0
    for i in range(epoch):
        s1 += J_prime1(w1)**2
        w1 -= lr*(J_prime1(w1)/np.sqrt(s1))
        s2 += J_prime2(w2)**2
        w2 -= lr*(J_prime2(w2)/np.sqrt(s2))
        W1_ada.append(w1)
        W2_ada.append(w2)
        Loss_ada.append(J(w1, w2))
    
        #RMSprop
    epoch = 200
    lambda0 = 0.01
    gamma = 0.5
    v = 0
    s = 0
    Loss_RMS = []
    W1_RMS = []
    W2_RMS = []
    s1=s2=0
    for i in range(epoch):
        s1 = gamma*s1 + (1-gamma)*(J_prime1(w1)**2)
        w1 -= lambda0*(J_prime1(w1)/np.sqrt(s1))
        s2 = gamma*s2 + (1-gamma)*(J_prime2(w2)**2)
        w2 -= lambda0*(J_prime2(w2)/np.sqrt(s2))
        W1_RMS.append(w1)
        W2_RMS.append(w2)
        Loss_RMS.append(J(w1, w2))
    
    #画出loss和weight的曲线
    LOSS = [Loss_sgd, Loss_moment, Loss_ada, Loss_RMS]
    labels = ['SGD', 'Momentum','Adagrad','RMSprop']
    for i, loss in enumerate(LOSS):
        plt.plot(loss, label=labels[i])
    plt.legend()
    plt.xlabel('epoch')
    plt.ylabel('loss')
    plt.title('Loss')
    plt.savefig('./task6/Loss.jpg', dpi=500)
    plt.show()
    
    W1 = [W1_sgd, W1_moment, W1_ada, W1_RMS]
    for i, w1 in enumerate(W1):
        plt.plot(w1, label=labels[i])
    plt.legend()
    plt.xlabel('epoch')
    plt.ylabel('W1')
    plt.title('W1')
    plt.savefig('./task6/W1.jpg', dpi=500)
    plt.show()
    
    W2 = [W2_sgd, W2_moment, W2_ada, W2_RMS]
    for i, w2 in enumerate(W2):
        plt.plot(w2, label=labels[i])
    plt.legend()
    plt.xlabel('epoch')
    plt.ylabel('W2')
    plt.title('W2')
    plt.savefig('./task6/W2.jpg', dpi=500)
    plt.show()
    

    作出不同优化器loss和权值更新曲线:

    图3. 不同优化器的Loss
    图4. 不同优化器的W1更新
    图5. 不同优化器的W2更新
    由图3-图5可以看到,对于loss函数,SGD有着最快的收敛速度和最陡峭的下降曲线;排在后面的依次是Adagrad, Momentum 和RMSprop[1].

    [1] 按照优化器理论,SGD不应该是收敛最快的。可能是因为模拟的损失函数J_{0}过于简单,其他优化器方法减慢收敛速度。具体原因有待进一步探究。

    相关文章

      网友评论

          本文标题:PyTorch学习笔记6 - 理解更多神经网络优化方法

          本文链接:https://www.haomeiwen.com/subject/tqeiwqtx.html