(十)再探反向传播和神经网络优化

news2024/12/24 8:41:00

文章目录

    • 1.背景介绍
    • 2.神经网络的模型
    • 3.神经网络中的参数更新初探
      • 3.1随机查找取最优
      • 3.2局部随机查找参数更新
    • 3.3 沿着梯度反方向更新
    • 4.链式法则与反向传播
    • 5.梯度方向的参数更新策略
    • 6.学习率退火
      • 6.1学习率衰减策略基础
      • 6.2 二阶优化方法
      • 6.3自适应学习率方法
    • 参考资料


欢迎访问个人网络日志🌹🌹知行空间🌹🌹


1.背景介绍

反向传播算法是深度学习的基石,在2017年有一段时间总会跟着CS231N的课程讲义反复推导神经网络中
反向传播,时至今日再重新回顾一下神经网络中的优化方法。

2.神经网络的模型

对于线性分类器: s = W X s=WX s=WX,其中,x.shape(N,D),N表示的是样本数目,DX的特征维度,s.shape(N,K),K表示类别的数目,s即是每个样本对应每个类别的评分。

全连接神经网络是有很多个线性分类器组成的,如带一个隐层的2层全连接网络 s = W 2 m a x ( 0 , W 1 X ) s=W_2max(0, W_1X) s=W2max(0,W1X),W1.shape(D,H),其中H表示的 W 1 X W_1X W1X生成的中间数据的维度,W2.shape(H,K)max函数是引入的作用在向量中每个元素上的非线性单元函数,如果没有max,则 s = W 2 W 1 X s=W_2W_1X s=W2W1X就等同于s=WX,其中 W = W 2 W 1 W=W_2W_1 W=W2W1,这样若没有非线性激活函数,则无论添加多少层都依然是一个线性分类器。网络图示如下:

在这里插入图片描述

单个神经元来分析,类比与生物学中神经细胞,有树突,轴突等组成,神经网络神经元由参数W和非线性激活函数组成,如下图:

在这里插入图片描述

上图中右侧神经元表示,输入的数据为3维 X = [ x 0 , x 1 , x 2 ] X=[x_0,x_1,x_2] X=[x0,x1,x2],对应的权值为: W = [ W 0 , W 1 , W 2 ] W=[W_0,W_1,W_2] W=[W0,W1,W2],经线性分类器后又输入到非线性激活函数 f f f中得到神经元输出结果。选择合适的激活函数如sigmoid,带1个隐含层的全连接神经网络就具备了拟合任何连续函数的能力。

3.神经网络中的参数更新初探

一个机器学习分类算法的核心由三大块组成,一个是评分函数score function将原始像素图像映射成类别评分;一个是损失函数loss function,用来评估预测结果与标签的一致程度,预测结果越准确loss越低,预测结果越不准,loss越高。另外一部分就是score function的参数更新即机器学习算法的优化问题,找到一组最优的参数 W W W使得loss的值尽量低。如最简单的线性分类器 Y = W X + b Y=WX+b Y=WX+b,损失函数使用SOFTMAX+CrossEntropy

p k = e f k ∑ j e f j L i = − l o g ( p y i ) \begin{matrix} p_k = \frac{e^{f_k}}{\sum_je^{f_j}}\\ L_i = -log(p_{y_i}) \end{matrix} pk=jefjefkLi=log(pyi)

理解了神经网络后,要完成神经网络的训练还需要知道神经网络的参数是如何更新的。

先生成实验样本数据:

from matplotlib import pyplot as plt
import numpy as np
N = 100 # number of points per class
D = 2 # dimensionality
K = 3 # number of classes
X = np.zeros((N*K,D)) # data matrix (each row = single example)
y = np.zeros(N*K, dtype='uint8') # class labels
for j in range(K):
  ix = range(N*j,N*(j+1))
  r = np.linspace(0.0,1,N) # radius
  t = np.linspace(j*4,(j+1)*4,N) + np.random.randn(N)*0.2 # theta
  X[ix] = np.c_[r*np.sin(t), r*np.cos(t)]
  y[ix] = j
# lets visualize the data:
plt.scatter(X[:, 0], X[:, 1], c=y, marker='o', s=20)
plt.show()

在这里插入图片描述

3.1随机查找取最优

一个头脑风暴想法是随机选取W/b多次,然后取结果最好的那次权重:

last_loss = float('inf')
W = None
b = None
for i in range(10000):
    Wt = 0.015 * np.random.randn(D,K)
    bt = np.random.randn(1,K)

    scores = np.dot(X, Wt) + bt
    
    num_examples = X.shape[0]
    # get unnormalized probabilities
    exp_scores = np.exp(scores)
    # normalize them for each example
    probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True)
    correct_logprobs = -np.log(probs[range(num_examples),y])

    # compute the loss: average cross-entropy loss and regularization
    data_loss = np.sum(correct_logprobs)/num_examples
    reg_loss = 0.5*reg*np.sum(Wt*Wt)
    loss = data_loss + reg_loss
    if loss < last_loss:
        last_loss = loss
        W = Wt.copy()
        b = bt.copy()
    print(f"step: {i} loss: {loss} min loss: {last_loss}")

# evaluate training set accuracy
scores = np.dot(X, W) + b
predicted_class = np.argmax(scores, axis=1)
print('training accuracy: %.2f' % (np.mean(predicted_class == y)))
## training accuracy: 0.46

随机查找最优结果参数可以得到的分类准确率是46%,可以知道等样本3分类的随机概率是33%,因此随机查找最优得到了比盲猜要好的结果,虽然其准确率依然不高。其分类结果如下图:
在这里插入图片描述

3.2局部随机查找参数更新

类比于一个人在山顶迷路了要下山,其每往前走一下其是能够知道这一步是在往下走还是在往上升,只有当迈出的一步可以使得位置下降时才前进一步到达一个新位置,再重新开始寻找下一步。回到参数更新,也可以通过这种Iterative Refinement一步步找寻最优的参数:

reg = 0.001
step_size = 1.

last_loss = float('inf')
W = 0.015 * np.random.randn(D,K)
b = 0.015 * np.random.randn(1,K)
for i in range(10000):
    Wt = W + np.random.randn(D,K) * step_size
    bt = b + np.random.randn(1,K) * step_size

    scores = np.dot(X, Wt) + bt
    
    num_examples = X.shape[0]
    # get unnormalized probabilities
    exp_scores = np.exp(scores)
    # normalize them for each example
    probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True)
    correct_logprobs = -np.log(probs[range(num_examples),y])

    # compute the loss: average cross-entropy loss and regularization
    data_loss = np.sum(correct_logprobs)/num_examples
    reg_loss = 0.5*reg*np.sum(Wt*Wt)
    loss = data_loss + reg_loss
    if loss < last_loss:
        last_loss = loss
        W = Wt.copy()
        b = bt.copy()
    print(f"step: {i} loss: {loss} min loss: {last_loss}")
# evaluate training set accuracy
scores = np.dot(X, W) + b
predicted_class = np.argmax(scores, axis=1)
print('training accuracy: %.2f' % (np.mean(predicted_class == y)))
# training accuracy: 0.56

在这里插入图片描述

可以看到使用逐步迭代更新参数的方法,分类的效果得到了一定的提升。

3.3 沿着梯度反方向更新

梯度方向是函数值增长最快的方向,那么梯度的反方向就是函数值下降最快的方向。损失函数对参数求导数即可找到损失函数值下降最快的方向,很自然的想到沿着梯度反方向更新参数。

梯度的求解有两种方法:

  • 数值解: [ f ( x + h ) − f ( x − h ) ] 2 h \frac{[f(x+h)-f(x-h)]}{2h} 2h[f(x+h)f(xh)]
  • 解析解:即求得函数的导数
  • 数值解求解方便,但只是近似结果,有可能有比较大的累积误差,且计算量比较大,而解析解计算较为复杂,推导容易出错。因此通常比较两种梯度计算的结果,以验证梯度计算的是否准确。
reg = 0.0001
step_size = 2

W = np.random.randn(D,K) * 0.001
b = np.random.randn(1,K) * 0.001
for i in range(1000):
    scores = np.dot(X, W) + b
    num_examples = X.shape[0]
    # get unnormalized probabilities
    exp_scores = np.exp(scores)
    # normalize them for each example
    probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True)
    correct_logprobs = -np.log(probs[range(num_examples),y])

    # compute the loss: average cross-entropy loss and regularization
    data_loss = np.sum(correct_logprobs)/num_examples
    reg_loss = 0.5*reg*np.sum(Wt*Wt)
    loss = data_loss + reg_loss
    print(f"{i} loss: {loss}")
    dscores = probs
    dscores[range(num_examples),y] -= 1
    dscores /= num_examples

    dW = np.dot(X.T, dscores)
    db = np.sum(dscores, axis=0, keepdims=True)
    dW += reg*W # don't forget the regularization gradient
    
    W += -dW * step_size
    b += -db * step_size
    
# evaluate training set accuracy
scores = np.dot(X, W) + b
predicted_class = np.argmax(scores, axis=1)
print('training accuracy: %.2f' % (np.mean(predicted_class == y)))
# training accuracy: 0.57

受限于线性分类器的能力,使用梯度下降方法对准确率的提升已经不大,但模型学习速度要快的多,经过几十步迭代后,loss就逐渐稳定了,可见梯度反方向的确应该是参数更新的方向,程序中step_size是每一步更新权值大小,也被称为学习率,这也是机器学习中比较关键的参数,后续章节介绍。

step 0 loss: 1.0987081944507475
step 1 loss: 1.035012790265596
step 2 loss: 0.9847131322096168
step 3 loss: 0.9446124386650137
step 4 loss: 0.9122820461040575
step 5 loss: 0.8859083863629116
step 6 loss: 0.8641456746130115
step 7 loss: 0.8459924630117203
step 8 loss: 0.8306977637849042
step 9 loss: 0.8176927238746483
step 10 loss: 0.8065417050365444
step 11 loss: 0.7969073565356761
step 12 loss: 0.7885255785711087
step 13 loss: 0.7811874419263808
step 14 loss: 0.7747260116459382
step 15 loss: 0.7690066494799522
step 16 loss: 0.7639198043202386
step 17 loss: 0.7593755983752785
step 18 loss: 0.7552997218726981
step 19 loss: 0.7516302904789174
step 20 loss: 0.74831541777118
step 21 loss: 0.7453113237504392
step 22 loss: 0.7425808488143958
step 23 loss: 0.7400922770753964
step 24 loss: 0.7378183976522287
step 25 loss: 0.7357357504859927
step 26 loss: 0.7338240163214523
step 27 loss: 0.7320655201385343
step 28 loss: 0.730444824479833
step 29 loss: 0.728948394479751
step 30 loss: 0.7275643204427976
step 31 loss: 0.7262820868888554
step 32 loss: 0.7250923793318111
step 33 loss: 0.7239869218665891
step 34 loss: 0.722958340041463
step 35 loss: 0.7220000445858086
step 36 loss: 0.7211061324211997
step 37 loss: 0.7202713020605488

这里使用的梯度下降算法直接拿所有训练样本参与训练,但对于大规模训练数据,一次性加载计算是不可能实现的,像ILSVRC有120万张图像。此时可以使用mini batch gradient descent算法,每次选择打散数据中batch_size数目的数据参与训练用于更新参数,mini batch gradient descent只所以能够工作,是因为训练数据之间是相关的,考虑极端情况,ILSVRC120万张图像有1000个类,每个类的1200张图像都是相同的,则此时选一部分数据更新参数与利用所有数据同时参与训练,效果是等效的,实际中虽然相同类别的不同图像不可能完全相同,但其之间是有一定共同特征的,因此使用mini batch gradient descent算法训练模型依然能够收敛。

4.链式法则与反向传播

通过#3中的介绍,以线性分类器 Y = W X + b Y=WX+b Y=WX+b为例,寻找最优参数 W W W以得到最优分类结果的方法依赖损失对参数 W W W的导数,上述例子中使用的是线性分类器,评分映射函数还比较简单梯度也比较容易求解,但对于神经网络来说,其往往包含很多层,且层与层之间还有非线性激活函数,直接对其求导几乎是不可能的,这时可以使用链式法则和反向传播算法。

以函数: f = ( x + y ) z f=(x+y)z f=(x+y)z为例,虽然这个函数还是很简单的,能够直接对 x / y / z x/y/z x/y/z求偏导数,但为了说明复合函数求导和链式法则,将其写成 f = q z , q = x + y f=qz,q=x+y f=qz,q=x+y的形式,这时:

f = q z , q = x + y ∂ f ∂ z = q ∂ f ∂ q = z ∂ f ∂ x = ∂ f ∂ q ∂ q ∂ x = z ∂ f ∂ y = ∂ f ∂ q ∂ q ∂ y = z \begin{matrix} f=qz,q=x+y\\ \frac{\partial{f}}{\partial{z}}=q \\ \frac{\partial{f}}{\partial{q}}=z\\ \frac{\partial{f}}{\partial{x}}=\frac{\partial{f}}{\partial{q}}\frac{\partial{q}}{\partial{x}}=z\\ \frac{\partial{f}}{\partial{y}}=\frac{\partial{f}}{\partial{q}}\frac{\partial{q}}{\partial{y}}=z\\ \end{matrix} f=qz,q=x+yzf=qqf=zxf=qfxq=zyf=qfyq=z

梯度求解过程:

x=-2; y=5; z=-4

q = x + y # q=3
f = q * z # f=-12

## 反向传播求梯度
dfdz = q # dfdz=3
dfdq = z # dfdq=-4
dqdx = 1
dqdy = 1

dfdx = dfdq * dqdx # dfdx = -4, 此处就是链式法则的应用
dfdy = dfdq * dqdy # dfdx = -4

链式法则的形象化图示如下:

在这里插入图片描述

5.梯度方向的参数更新策略

  • 1.普通梯度下降策略

这里先实现如图1所示的带一层隐含层的全连接神经网络,X是二维特征的数据 X = [ x 1 , x 2 ] X=[x_1,x_2] X=[x1,x2],隐含层有4个神经元,则 W 1 . s h a p e = ( 2 , 4 ) , b 1 . s h a p e = ( 1 , 4 ) W_1.shape=(2,4),b_1.shape=(1,4) W1.shape=(2,4),b1.shape=(1,4),共有3个类别,则 W 2 . s h a p e = ( 4 , 3 ) , b 2 . s h a p e = ( 1 , 3 ) W_2.shape=(4,3),b_2.shape=(1,3) W2.shape=(4,3),b2.shape=(1,3):
Y 1 = W 1 X + b 1 Y 1 = m a x ( 0 , Y 1 ) s c o r e = W 2 Y 1 + b 2 \begin{matrix} Y_1 = W_1X+b_1\\ Y1 = max(0, Y_1)\\ score = W_2Y1+b_2\\ \end{matrix} Y1=W1X+b1Y1=max(0,Y1)score=W2Y1+b2

写成矩阵形式:

[ y 11 , y 12 , y 13 , y 14 ] = [ x 11 , x 12 ] [ w 1 11 w 1 12 w 1 13 w 1 14 w 1 21 w 1 22 w 1 23 w 1 24 ] + [ b 1 1 , b 1 2 , b 1 3 , b 1 4 ] [ y 11 , y 12 , y 13 , y 14 ] = m a x _ e l e m e n t w i s e ( [ y 11 , y 12 , y 13 , y 14 ] , 0 ) [ f 11 , f 12 , f 13 ] = [ y 11 , y 12 , y 13 , y 14 ] [ w 2 11 w 2 12 w 2 13 w 2 21 w 2 22 w 2 23 w 2 31 w 2 32 w 2 33 w 2 41 w 2 42 w 2 43 ] + [ b 2 1 , b 2 2 , b 2 3 ] \begin{matrix} [y_{11},y_{12},y_{13},y_{14}] = [x_{11}, x_{12}]\begin{bmatrix} w1_{11}& w1_{12} & w1_{13} & w1_{14}\\ w1_{21}& w1_{22} & w1_{23} & w1_{24} \end{bmatrix}+[b1_1, b1_2, b1_3, b1_4] \\ [y_{11},y_{12},y_{13},y_{14}] = max\_elementwise([y_{11},y_{12},y_{13},y_{14}], 0)\\ [f_{11},f_{12},f_{13}]=[y_{11},y_{12},y_{13},y_{14}]\begin{bmatrix} w2_{11}& w2_{12} & w2_{13}\\ w2_{21}& w2_{22} & w2_{23}\\ w2_{31}& w2_{32} & w2_{33}\\ w2_{41}& w2_{42} & w2_{43} \end{bmatrix}+[b2_1, b2_2, b2_3] \end{matrix} [y11,y12,y13,y14]=[x11,x12][w111w121w112w122w113w123w114w124]+[b11,b12,b13,b14][y11,y12,y13,y14]=max_elementwise([y11,y12,y13,y14],0)[f11,f12,f13]=[y11,y12,y13,y14]w211w221w231w241w212w222w232w242w213w223w233w243+[b21,b22,b23]

损失函数使用CrossEntropy,i表示第i个样本,同上述举证形式中的每行,k表示当前样本所属类别:

p k = e f k ∑ j e f j L i = − l o g ( p y i ) \begin{matrix} p_k = \frac{e^{f_k}}{\sum_je^{f_j}}\\ L_i = -log(p_{y_i}) \end{matrix} pk=jefjefkLi=log(pyi)
补充一下,多分类交叉熵损失函数的形式一般写为:

L i = − ∑ i = 1 K y t r u e l o g ( y p r e ) L_{i}=-\sum_{i=1}^{K}y_{true}log(y_{pre}) Li=i=1Kytruelog(ypre)
其中, y p r e y_{pre} ypre是经SOFTMAX后输出的在该类别上的评分, y t r u e y_{true} ytrue是经过One-Hot编码后的向量,如3个类时[0,0,1]。可以看到在不是当前类别的标签上都是0,因此 L i L_i Li可以写成 L i = − l o g ( y k p r e d ) L_i=-log({y_k}_{pred}) Li=log(ykpred)的形式。

先对上述全连接神经网络和损失函数应用链式法则求导:

∂ L i ∂ p y i = − 1 p y i y i = k , p y i = p k , p k = e f k c + e f k 其 中 c = ∑ j e f j , j ≠ k ∂ p k ∂ f k = c e f k ( c + e f k ) 2 ∂ L i ∂ f k = ∂ L i ∂ p k ∂ p k ∂ f k = − c + e f k e f k c e f k ( c + e f k ) 2 = − c c + e f k = e f k − ( c + e f k ) c + e f k = ( p k − 1 ) , y i = k ∂ L i ∂ f y i = ∂ L i ∂ p k ∂ p k ∂ f y i = p y i , y i ≠ k ∂ f ∂ w = Y 1 ∂ f ∂ b = 1 ∂ Y 1 ∂ Y 1 = 1 , Y 1 > 0 \begin{matrix} \frac{\partial L_i}{\partial p_{y_i}} = -\frac{1}{p_{y_i}} \\ y_i=k,p_{y_i}=p_k,p_k=\frac{e^{f_k}}{c+e^{f_k}}其中c=\sum_je^{f_j},j\neq k\\ \frac{\partial p_{k}}{\partial f_{k}} = \frac{ce^{f_k}}{(c+e^{f_k})^2}\\ \frac{\partial L_i}{\partial f_{k}}=\frac{\partial L_i}{\partial p_{k}}\frac{\partial p_k}{\partial f_{k}}=-\frac{c+e^{f_k}}{e^{f_k}}\frac{ce^{f_k}}{(c+e^{f_k})^2}= -\frac{c}{c+e^{f_k}} = \frac{e^{f_k}-(c+e^{f_k})}{c+e^{f_k}}= (p_k-1),y_i=k\\ \frac{\partial L_i}{\partial f_{y_i}}=\frac{\partial L_i}{\partial p_{k}}\frac{\partial p_{k}}{\partial f_{y_i}}=p_{y_i},y_i\neq k \\ \frac{\partial f}{\partial w} = Y1 \\ \frac{\partial f}{\partial b} = 1 \\ \frac{\partial Y1}{\partial Y_1} = 1, Y_1 \gt 0 \end{matrix} pyiLi=pyi1yi=k,pyi=pk,pk=c+efkefkc=jefj,j=kfkpk=(c+efk)2cefkfkLi=pkLifkpk=efkc+efk(c+efk)2cefk=c+efkc=c+efkefk(c+efk)=(pk1),yi=kfyiLi=pkLifyipk=pyi,yi=kwf=Y1bf=1Y1Y1=1,Y1>0

以上就根据链式法则求得了两层全连接神经网络的梯度,对于N个样本输入时,使用矩阵表示可同样推导。根据以上推导实现的两层全连接神经网络为:

H = 100
W = 0.015 * np.random.randn(D,H)
b = np.zeros((1,H))

W1 = 0.015 * np.random.randn(H,K)
b1 = np.zeros((1,K))

reg = 0.001
step_size = 1.5

def sigmoid(x):
    return 1/(1+np.exp(-x))

mu = 0.9
v1 = 0
v2 = 0
v3 = 0
v4 = 0
for i in range(5000):
    Y = np.dot(X, W) + b
    sY = np.maximum(Y, 0)
    scores = np.dot(sY, W1) + b1
    
    num_examples = X.shape[0]
    # get unnormalized probabilities
    exp_scores = np.exp(scores)
    # normalize them for each example
    probs = exp_scores / np.sum(exp_scores, axis=1, keepdims=True)
    correct_logprobs = -np.log(probs[range(num_examples),y])

    # compute the loss: average cross-entropy loss and regularization
    data_loss = np.sum(correct_logprobs)/num_examples
    reg_loss = 0.5*reg*np.sum(W*W) + 0.5*reg*np.sum(W1*W1)
    loss = data_loss + reg_loss
    print(f"step: {i} loss: {loss}")
    dscores = probs
    dscores[range(num_examples),y] -= 1
    dscores /= num_examples

    print(dscores.shape)
    dW1 = np.dot(sY.T, dscores)
    db1 = np.sum(dscores, axis=0, keepdims=True)
    dW1 += reg*W1

    dsY = np.dot(dscores, W1.T)
    dsY = dsY * sigmoid(Y)*(1-sigmoid(Y))
    dsY[sY<=0] = 0

    dW = np.dot(X.T, dsY)
    db = np.sum(dsY, axis=0, keepdims=True)
    dW += reg*W # don't forget the regularization gradient
    
    # perform a parameter update
    # Vanilla update
    # step: 5403 loss: 0.30999144570577264
    W += - step_size * dW
    b += - step_size * db
    W1 += - step_size * dW1
    b1 += - step_size * db1

使用普通梯度的更新,模型的分类准确率轻松达到了97%

在这里插入图片描述

  • 2.动量更新策略

将损失函数类比为丘陵,则势能U=mgh, U ∝ h U\propto h Uh,使用随机数字初始化参数,等同于将一个物体在某个位置的初速度设置为0,如此,可将优化过程类比为将物体从山顶往下滚。由势能的定义,物体所受的力可通过势能 U U U对位置 x x x求导得到,即 F = ▽ U = d U d h F=\bigtriangledown U=\frac{dU}{dh} F=U=dhdU F = m a F=ma F=ma,因此 U U U h h h的梯度与物体的加速度成正比,对于优化问题, l o s s loss loss即是 U U U, h h h即是参数 W W W,设初始速度为 v = 0 v=0 v=0,则参数更新不是直接作用在位置 x x x上,而是作用在速度 v v v上,这是与普通梯度更新的区别,其中超参数mu表示为物体的动量,通常取0.9

# Momentum update
v = mu * v - learning_rate * dx # integrate velocity
x += v # integrate position

## 作用在前述全连接神经网络实现上
v1 = mu * v1 - step_size * dW
W += v1
v2 = mu * v2 - step_size * db
b += v2
v3 = mu * v3 - step_size * dW1
W1 += v3
v4 = mu * v4 - step_size * db1
b1 += v4
  • 3.Nesterov动量更新策略

Nesterov Momentum是由俄国数学家Yurii Nesterov提出的凸优化方法,Nesterov Momentum的核心是对于位置x处的参数向量,只看动量部分,其动量对参数向量的贡献部分为mv,当更新梯度时,可以将x+mu*v当做远眺,求x+mu*v处的梯度用以更新参数x,而非使用滞后的位置x处的梯度。

x_ahead = x + mu * v
# evaluate dx_ahead (the gradient at x_ahead instead of at x)
v = mu * v - learning_rate * dx_ahead
x += v

## 通常利用`x_ahead = x + mu * v`将上述表示写为:

v_prev = v # back this up
v = mu * v - learning_rate * dx # velocity update stays the same
x += -mu * v_prev + (1 + mu) * v # position update changes form


## 应用到前述的两层全连接神经网络中
mu = 0.9
v1 = 0
v2 = 0
v3 = 0
v4 = 0

v1p = v1
v1 = mu * v1 - step_size * dW
W += -mu * v1p + (1+mu)*v1

v2p = v2
v2 = mu * v2 - step_size * db
b += -mu * v2p + (1+mu)*v2

v3p = v3
v3 = mu * v3 - step_size * dW1
W1 += -mu * v3p + (1+mu)*v3

v4p = v4
v4 = mu * v4 - step_size * db1
b1 += -mu * v4p + (1+mu)*v4
  • 4.三种方法对比

将以上三种参数更新方法的优化过程中loss下降情况绘制如下图,可以看到MomentumVanilla收敛的要快,Nesterov Momentum收敛过程比较振荡,但收敛的loss更低,收敛的效果的更好。

在这里插入图片描述

6.学习率退火

6.1学习率衰减策略基础

参考如上参数更新过程,学习率过大则每一步更新的参数量过大,会导致模型振荡且收敛于loss较大的状态,学习效果较差;而学习率过小,会导致模型参数更新速度过慢,花费过多训练时间。自然而然的会想到,随着训练step的变大逐步减小学习率,这就是学习率退火的原理。通常有三种学习率的衰减策略:

  • 1.按步衰减Step decay,如每5epoches学习率衰减一半,或者20 epochs衰减0.1,一个启发式的办法是使用固定学习率,当验证集上的准确率不再上升时则减小学习率为原来的0.5倍。

  • 2.指数衰减 α = α 0 e − k t \alpha = {\alpha}_0e^{-kt} α=α0ekt,其中 α 0 、 k {\alpha}_0、k α0k是超参数, t t t是迭代 s t e p step step

  • 3. 1 t \frac{1}{t} t1衰减 α = α 0 / ( 1 + k t ) \alpha = \alpha_0/(1+kt) α=α0/(1+kt),其中 α 0 、 k {\alpha}_0、k α0k是超参数, t t t是迭代 s t e p step step

6.2 二阶优化方法

首先看泰勒定理:

wikipedia

  • 牛顿法

对于函数F(x),考虑step=k,假设在 x k x_k xk处寻找增量 △ x k \triangle{x_k} xk,最直观的方式是将函数在 x k x_k xk处先进行泰勒展开:
F ( x k + △ x k ) ≈ F ( x k ) + J ( x k ) T △ x k + 1 2 △ x k T H ( x k ) △ x k F(x_k+\triangle{x_k})\approx F(x_k)+J(x_k)^T\triangle{x_k}+\frac{1}{2}\triangle{x_k}^TH(x_k)\triangle{x_k} F(xk+xk)F(xk)+J(xk)Txk+21xkTH(xk)xk

J ( x ) J(x) J(x) F ( x ) F(x) F(x)关于 x x x的一阶梯度也称为雅可比矩阵(Jacobin Matrix), H ( x ) H(x) H(x) F ( x ) F(x) F(x)关于 x x x的二阶梯度也称为海塞矩阵(Hessian Matrix)。前面讨论的优化方法使用的是一阶导数 △ x ∗ = − α J ( x ) \triangle{x}^*=-\alpha J(x) x=αJ(x),其中 α \alpha α即参数每步更新的步长,这种方法简单称为最速下降法。除此之外,还可以选择保留二阶梯度信息,此时参数更新方程可写为:

△ x ∗ = arg ⁡ min ⁡ △ x ( F ( x ) + J ( x ) T △ x + 1 2 △ x T H ( x ) △ x ) \triangle x^* = \mathop{\arg\min}\limits_{\triangle x}(F(x)+J(x)^T\triangle{x}+\frac{1}{2}\triangle{x}^TH(x)\triangle{x}) x=xargmin(F(x)+J(x)Tx+21xTH(x)x)

上述等式右侧是 △ x \triangle x x的二次多项式,求其对 △ x \triangle x x的导数并令其等于 0 0 0即可求得 △ x ∗ \triangle x^* x
J ( x ) + H ( x ) △ x = 0 ⇒ △ x ∗ = − H − 1 ( x ) J ( x ) J(x) + H(x)\triangle x = 0 \Rightarrow \triangle x^*=-H^{-1}(x)J(x) J(x)+H(x)x=0x=H1(x)J(x)

通过求解以上方程就得到了参数的更新量 △ x ∗ \triangle x^* x,这种方法被称为牛顿法(Newton’s method)。,可以看到这种方法中使用了二阶梯度来控制每次参数更新的步长,省去了超参数学习率 α \alpha α,可以有更快的收敛速度。但是,对于神经网络动辄上百万的参数,其Hessian Matrix占据的内存太大,目前还不能够直接使用,如对于100M参数的神经网络,其Hessian Matrix维度为 [ 1000000 , 1000000 ] [1000000,1000000] [1000000,1000000],将需要 3725 G B 3725GB 3725GBRAM。实际中运用的多是牛顿法的近似,如高斯牛顿法/列文伯格马夸尔特方法。

  • 高斯牛顿法

对于非线性最小二乘优化问题 F ( x ) = 1 2 ∣ ∣ f ( x ) ∣ ∣ 2 2 , m i n x F ( x ) F(x)=\frac{1}{2}||f(x)||_2^2,\mathop {min}\limits _x F(x) F(x)=21f(x)22,xminF(x),除了使用上述介绍的牛顿法外,还可以使用高斯牛顿法(Gauss Newton),避免计算Hessian Matrix。其原理是将f(x) x x x处进行一阶泰勒展开:

f ( x + △ x ) ≈ f ( x ) + J ( x ) T △ x f(x+\triangle x) \approx f(x) + J(x)^T\triangle{x} f(x+x)f(x)+J(x)Tx
注意这里是对 f ( x ) f(x) f(x)而非目标函数 F ( x ) F(x) F(x)进行泰勒展开。这里 f ( x ) f(x) f(x) F ( x ) F(x) F(x)的区别可通过一个例子来看就会更清楚,

https://zhuanlan.zhihu.com/p/42383070
在这里插入图片描述

根据对 f ( x ) f(x) f(x)的一阶泰勒展开,优化的目标写成最小化 ∣ ∣ f 2 ( x + △ x ) ∣ ∣ ||f^2(x+\triangle x)|| f2(x+x),目标是求 △ x \triangle x x能够使其最小,则:

△ x ∗ = a r g m i n △ x 1 2 ∣ ∣ f ( x ) + J ( x ) T △ x ∣ ∣ 2 \triangle x^*=\mathop{arg min}\limits_{\triangle x}\frac{1}{2}||f(x)+J(x)^T\triangle x||^2 x=xargmin21f(x)+J(x)Tx2
可将上述方程写成:
1 2 ∣ ∣ f ( x ) + J ( x ) T △ x ∣ ∣ 2 = 1 2 ( f ( x ) + J ( x ) T △ x ) T ( f ( x ) + J ( x ) T △ x ) = 1 2 ( ∣ ∣ f ( x ) ∣ ∣ 2 2 + 2 f ( x ) J ( x ) T △ x + △ x T J ( x ) J T ( x ) △ x ) \begin{aligned} \frac{1}{2}||f(x)+J(x)^T\triangle x||^2&=\frac{1}{2}(f(x)+J(x)^T\triangle x)^T(f(x)+J(x)^T\triangle x) \\ &=\frac{1}{2}(||f(x)||^2_2+2f(x)J(x)^T\triangle x+\triangle x^TJ(x)J^T(x)\triangle x) \end{aligned} 21f(x)+J(x)Tx2=21(f(x)+J(x)Tx)T(f(x)+J(x)Tx)=21(f(x)22+2f(x)J(x)Tx+xTJ(x)JT(x)x)
根据其求极值条件,可求得 △ x \triangle x x为:
J ( x ) f ( x ) + J ( x ) J T ( x ) △ x = 0 J ( x ) J T ( x ) ⏟ H ( x ) △ x = − J ( x ) f ( x ) ⏟ g ( x ) \begin{matrix} J(x)f(x)+J(x)J^T(x)\triangle x = 0\\ \mathop{\underbrace{J(x)J^T(x)} }\limits _{H(x)}\triangle x = \mathop{\underbrace{-J(x)f(x)} }\limits _{g(x)} \end{matrix} J(x)f(x)+J(x)JT(x)x=0H(x) J(x)JT(x)x=g(x) J(x)f(x)

上述方程被称为增量方程高斯牛顿方程(Gauss Newton Equation)正规方程(Normal Equation)。高斯牛顿方法避免了计算二阶梯度,使用 J J T JJ^T JJT近似 H H H,但 J J T JJ^T JJT只有半正定,因此 J J T JJ^T JJT有可能出现奇异或病态,导致计算出来的 △ x \triangle x x过大,这时可以考虑使用列文伯格-马尔夸特方法,其在每一步参数更新中添加了一个参数指标 ρ = f ( x + △ x ) − f ( x ) J T ( x ) △ x \rho=\frac{f(x+\triangle x)-f(x)}{J^T(x)\triangle x} ρ=JT(x)xf(x+x)f(x)来刻画每一步参数更新的后函数$f(x)近似的好坏,并通过添加约束 ∣ ∣ D △ x ∣ ∣ 2 ≤ μ ||D\triangle x||^2\le\mu Dx2μ来限制每一步参数更新量的大小,以应对 H H H病态的情况。

6.3自适应学习率方法

虽然前面介绍的二阶梯度方法很好,但因神经网络中参数量、训练数据量巨大,很难应用。像L-BFGS算法需要在整个数据集上同时计算二阶梯度在mini-batch上无法工作。因此,神经网络优化中使用最多的是Nestov Momentum方法加自适应学习率

  • Adagrad:该方法是John Duchi2011年提出的方法。

    # Assume the gradient dx and parameter vector x
    cache += dx**2
    x += - learning_rate * dx / (np.sqrt(cache) + eps)
    

    其中,参数cachedx有相同的维度,故其可以根据参数更新的大小自适应衰减学习率。缺点是研究表明神经网络过程中使用单调下降的学习率会导致参数下降过快,模型过早的停止收敛。

  • RMSprop: 该方法是机器学习专家Geoff Hinton在其Cousera课程讲义中提出,目前并没有发表。

    # decay_rate是超参数,常取[0.9, 0.99, 0.999]
    cache = decay_rate * cache + (1 - decay_rate) * dx**2
    x += - learning_rate * dx / (np.sqrt(cache) + eps)    
    
  • Adam:该方法是201412Diederik P. Kingma提出的方法,该方法和带动量的RMSprop有些相似,

    m = beta1*m + (1-beta1)*dx
    v = beta2*v + (1-beta2)*(dx**2)
    x += - learning_rate * m / (np.sqrt(v) + eps)
    

    可以看到除了使用 m m m替代了 d x dx dx外,AdamRMSprop是一样的,其中超参数的常用值为eps = 1e-8,beta1 = 0.9,beta2 = 0.999,一般来说Adam是目前最好的方法,比RMSprop好一些。

    #     ## raw
    #     cw, cb, cw1, cb1 = 1, 1, 1, 1
    #     ## Adagrad
    #     cw += dW**2
    #     cb += db**2
    #     cw1 += dW1**2
    #     cb1 += db1**2
        
    #     ## rmsprop
    #     cw = decay*cw + (1-decay)*dW**2
    #     cb = decay*cb + (1-decay)*db**2
    #     cw1 = decay*cw1 + (1-decay)*dW1**2
    #     cb1 = decay*cb1 + (1-decay)*db1**2
    
    ## Adam
    t = i + 1
    cw = decay*cw + (1-decay)*dW**2
    cwt = cw / (1-decay**t)
    cb = decay*cb + (1-decay)*db**2
    cbt = cb / (1-decay**t)
    cw1 = decay*cw1 + (1-decay)*dW1**2
    cw1t = cw1 / (1-decay**t)
    cb1 = decay*cb1 + (1-decay)*db1**2
    cb1t = cb1 / (1-decay**t)
    mw = dm*cw + (1-dm)*dW
    mwt = mw / (1-dm**t)
    mb = dm*cb + (1-dm)*db
    mbt = mb / (1-dm**t)
    mw1 = dm*cw1 + (1-dm)*dW1
    mw1t = mw1 / (1-dm**t)
    mb1 = dm*cb1 + (1-dm)*db1
    mb1t = mb1 / (1-dm**t)
    
    W += - step_size * mwt / (np.sqrt(cwt) + eps)
    b += - step_size * mbt / (np.sqrt(cbt) + eps)
    W1 += - step_size * mw1t / (np.sqrt(cw1t) + eps)
    b1 += - step_size * mb1t / (np.sqrt(cb1t) + eps)
    

在这里插入图片描述

如上实验中,RMSpropAdam优化效果较好,其中RMSpropAdam的优化效果还要好些。


欢迎访问个人网络日志🌹🌹知行空间🌹🌹


参考资料

  • 1.https://cs231n.github.io/neural-networks-3/
  • 2.视觉SLAM十四讲第六讲
  • 3.https://zhuanlan.zhihu.com/p/42383070

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/58741.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Java基础之接口与抽象类区别

Java基础之接口与抽象类区别一、Java基础之接口与抽象类二、抽象类和最终类三、Java移位运算符四、局部变量为什么要初始化一、Java基础之接口与抽象类 一个子类只能继承一个抽象类, 但能实现多个接口抽象类可以有构造方法, 接口没有构造方法抽象类可以有普通成员变量, 接口没…

minikube helm 安装 jenkins

文章目录1. 准备条件2. 安装 helm3. 部署 jenkins3.1 创建 namespace jenkins3.2 创建存储卷 jenkins-volume3.3 创建 service account & RBAC3.4 定制 jenkins-values.yml3.5 安装 jenkins3.6 登陆 jenkins“Jenkins是一个著名的可扩展开源 CI/CD 工具&#xff0c;用于自动…

手写数字识别Mnist数据集和读取代码分享

数据集下载 链接&#xff1a; https://pan.baidu.com/s/1qpzrSFhmyrdGmbSScN_ZXg?pwdd1ws 提取码&#xff1a;d1ws 数据集读取 from pathlib import Path import requests ​ DATA_PATH Path("data") PATH DATA_PATH / "mnist" ​ PATH.mkdir(parent…

Android Navigation基本使用

目录1. Navigation概述2. Navigation组成3. 设置环境4. 使用方法4.1. 创建导航图4.1.1. 具体操作4.2. 向Activity添加NavHost4.2.1. 通过 XML 添加4.2.2. 使用布局编辑器添加4.3. 在导航图中创建目的地4.3.1. 具体操作4.4. 连接目的地4.4.1. 具体操作4.5. 目的地之间的导航4.5.…

认识3dmax 对象属性对话框

可以从右键或编辑菜单访问此对话框&#xff1b; 在此可以查看和编辑参数&#xff0c;以确定选定对象在视口和渲染过程中的行为&#xff1b; 包含3个面板&#xff1a;常规&#xff0c;高级照明&#xff0c;用户定义&#xff1b; 常规面板包含6个组&#xff1a;对象信息&#x…

[附源码]计算机毕业设计校园疫情管理系统Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

三、Git本地仓库基本操作——git仓库忽略跟踪文件

1. .gitignore文件 在工作区中的文件内容&#xff0c;很多时候我们基本只关注源文件。所以&#xff0c;肯定有些文件是不想使用git去管理的&#xff0c;比如&#xff1a; 编译生成的中间文件相关的IDE工程文件&#xff08;当然也可以进行git管理&#xff09;一些固定内容的说…

国外打工人分享如何如何通过销售excel电子表格赚到 28 万美元

在不到 2 年的时间里,这是我在 Etsy 上销售电子表格模板(Google 表格 + Excel)的收入。 而且我无论如何都不是电子表格专家。 但我确实知道如何找到有需求的数字产品,并且会帮助人们在通常需要更长时间才能完成的事情上节省时间。 我还没有看到很多人谈论这个,所以我想…

【图像分类损失】Encouraging Loss:一个反直觉的分类损失

论文题目&#xff1a;《Well-classified Examples are Underestimated in Classification with Deep Neural Networks》 论文地址&#xff1a;https://arxiv.org/pdf/2110.06537.pdf 1.背景 深度分类模型背后的一般常识是专注于分类错误的样本&#xff0c;而忽略远离决策边界的…

Studio 3T工具下载安装及使用教程

一、下载安装 官方网址&#xff1a;The Professional Client, IDE and GUI for MongoDB | Studio 3T 二、使用教程 CRUD操作&#xff1a; 打开命令行窗口&#xff0c;Open intelliShell 插入一个文档&#xff0c;db.collection.insertOne() 插入多个文档&#xff0c;db.coll…

[附源码]计算机毕业设计springboot中小学课后延时服务管理系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

PS画布基本操作 新建保存 图片类型区分设置

观看本文需要您的电脑装配有ps软件 如果没有 可以查看我的文章 PS软件下载安装以基本配置 然后我们打开ps软件 然后点击右上角 文件 然后选择 新建 然后可以配置一下 右边的话 我们可以设置 高度 和 宽度 都是数值 其次是要指定你这个数值的单位 一般情况下 都会用像素 一…

Vue(第十五课)Pinia组件库的基本知识

为什么要使用 Pinia&#xff1f; Pinia 是 Vue 的存储库&#xff0c;它允许您跨组件/页面共享状态。 如果您熟悉 Composition API&#xff0c;您可能会认为您已经可以通过一个简单的 export const state reactive({}). 这对于单页应用程序来说是正确的&#xff0c;但如果它是…

jsp人力资源管理系统Myeclipse开发mysql数据库servlet开发java编程计算机网页项目

一、源码特点 JSP 人力资源管理系统 是一套完善的web设计系统&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统采用serlvetdaobean mvc 模式&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,Myeclipse8.5…

用户信息列表实现增删改查案例的实现【问题及解决过程记录】【综合案例】

目录 用户信息列表展示案例 1. 需求&#xff1a; 1. 简单功能 1. 列表查询 2. 登录 3. 添加 4. 删除 5. 修改 2. 复杂功能 1. 删除选中 2. 分页查询 * 好处&#xff1a; 1. 减轻服务器内存的开销 …

【云原生 | Kubernetes 实战】06、Pod高级实战:基于污点、容忍度、亲和性的多种调度策略(下)

目录 一、Pod节点亲和性 1.1 案例演示&#xff1a;pod 节点亲和性——podAffinity 1.2 案例演示&#xff1a;pod 节点反亲和性——podAntiAffinity 1.3 案例演示&#xff1a; 换一个 topologykey 值 二、污点和容忍度 2.1 案例演示&#xff1a; 把 node2 当成是生产环境专…

Python中常见的调色板: 颜色 color

Python中常见的调色板&#xff1a; 颜色 color 这个人对颜色的总结&#xff0c;非常到位哈&#xff01; https://blog.csdn.net/weixin_42943114/article/details/81811556

SPI 机制详解

SPI 全称为 Service Provider Interface &#xff0c;它是一种服务发现机制。它通过在 ClassPath 路径下的 META-INF/services 文件夹查找文件&#xff0c;自动加载文件里所定义的类。这一机制为很多框架拓展提供了可能&#xff0c;比如在Dubbo&#xff0c;JDBC中都使用到了SPI…

go return返回值屏蔽

前言 最近需要写一个云环境的可执行程序&#xff0c;一般使用go语言&#xff0c;毕竟GC原生运行&#xff0c;结合了不需要回收指针的能力和原生运行&#xff0c;但是在程序返回时&#xff0c;笔者看到一个sdk的源码懵了&#xff0c;返回的数据居然可以隐式返回。 准备 go 版本…

机器学习8线性回归法Linear Regression

文章目录一、线性回归算法简介典型的最小二乘法的问题目标&#xff1a;具体怎么推此处省略二、简单线性回归的实现三、向量化运算一、线性回归算法简介 1.解决回归问题&#xff1b; 2.思想简单&#xff0c;实现容易&#xff1b; 3.是许多强大的非线性模型的基础&#xff1b; 4…