神经网络之反向传播算法(加入动量momentum的误差反向传播算法)

1、动量momentum

在普通的梯度下降法中,一般将梯度值定义对参数进行优化时的调整方向,将学习率定义为朝向调整方向上的移动步长,参数的梯度值的变化曲线通常是波动的不规则曲线,其中存在着多个较低的梯度值,若此时使用学习率对参数进行调整,容易出现陷入局部最优之中,但如果结合历史梯度值对参数进行调整,则可以对参数的优化方向进行调整,以此加快收敛速度并避免陷入局部最优,深度学习的动量(momentum)算法便是以此为目的而提出的。
动量这个概念起源于牛顿运动定律,在此定律中,动量是与物体的质量和速度相关的物理量,而一个物体的动量指的是这个物体在它运动方向上保持运动的趋势。深度学习中的动量则是引入了“速度”这一概念,以变量v作为参数在调整过程中的调整方向和速率,将变量v与学习率相乘并作为每次迭代中参数的调整量,其计算公式为:
在这里插入图片描述

其中μ为学习率,而变量v是通过对梯度值进行指数加权平均计算而来,因此在理解变量v之前首先需要对指数加权平均的原理进行了解。
通常我们通过计算n个数的和之后再除以n,以获得这些数的平均值,这个平均值为它们的算数平均值,而在对许多实际问题进行求平均值时,还需要考虑到每个数据的重要程度,即需要根据这些数据的重要程度来获取它们的加权值,进而通过求和、相除以获得加权平均值,算术平均值一般可以视作是加权平均值的一种特殊形式,即此时每个数据的重要程度是一模一样的。
在上述描述中,变量v是随着迭代的次数而在不断变化的,属于时间序列数据,它反映了变量v随着时间在不断变化的趋势,因此倘若对此类数据求平均值,则需要使用加权移动平均来描述它变化的趋势。指数加权平均又可被称为指数加权移动平均,可以被视作是加权移动平均的改进,是一种常见的序列处理方式。
在梯度下降法中引入动量算法的重要步骤之一就是对参数的梯度值进行指数加权平均,这也是变量v的计算方式,其公式如下:
在这里插入图片描述

其中变量v的初始值为0,ρ为动量参数,g为参数的梯度值。
结合上述两个公式可以看出,在迭代过程中,由于动量项主要是对历史梯度信息的加权平均,因此梯度值将主要受到前一段时刻的影响,当梯度值前一段时刻保持相同方向时,动量项将在此方向上逐渐增大,当梯度值前一段时刻方向发生改变时,动量项将会随之减小,这一现象可以在梯度值出现反复波动时帮助其跳出局部最优,达到更好的收敛效果。
梯度g的计算原理参考:
神经网络之反向传播算法(梯度、误差反向传播算法BP)

2、结合momentum的误差反向传播算法

将加入动量法的误差反向传播算法应用于神经网络参数优化时的算法步骤如下:

  1. 随机初始化神经网络中所有的参数;
  2. 设置学习率、动量参数,设置变量v的初始值为0;
  3. 输入数据,按照前向传播过程计算网络的输出;
  4. 使用损失函数计算误差值;
  5. 根据误差值计算出隐含层、输出层每个参数的梯度值;
  6. 利用梯度项计算变量v;
  7. 利用变量v对参数进行更新;
  8. 重复步骤3到步骤7,当满足停止迭代条件时输出训练后的参数。

误差反向传播算法BP原理参考:
神经网络之反向传播算法(梯度、误差反向传播算法BP)
参数初始化方法参考:
神经网络基础知识之参数初始化

3、算法实现

以数据预测为例,下面介绍加入动量momentum的误差反向传播算法的实现过程,将加入动量momentum的误差反向传播算法应用于普通的三层神经网络(输入层、隐含层、输出层)的反向传播过程。
选用某省市的表层土壤重金属元素数据集作为实验数据,该数据集总共96组,随机选择其中的24组作为测试数据集,72组作为训练数据集。选取重金属Ti的含量作为待预测的输出特征,选取重金属Co、Cr、Mg、Pb作为模型的输入特征。

3.1 训练过程

#库的导入
import numpy as np
import pandas as pd

#激活函数tanh
def tanh(x):
    return (np.exp(x)-np.exp(-x))/(np.exp(x)+np.exp(-x))
#激活函数偏导数
def de_tanh(x):
    return (1-x**2)

# 梯度加权平均函数,0.9为动量系数
def accumulation(v,delta):
    v = 0.9 * v + 0.1 * delta
    return  v

# 参数更新函数
def adjust(w,v):
    change = (-0.01)*v
    w = w + change
    return w

#输入数据的导入
df = pd.read_csv("train.csv")
df.columns = ["Co", "Cr", "Mg", "Pb", "Ti"]
Co = df["Co"]
Co = np.array(Co)
Cr = df["Cr"]
Cr = np.array(Cr)
Mg=df["Mg"]
Mg=np.array(Mg)
Pb = df["Pb"]
Pb =np.array(Pb)
Ti = df["Ti"]
Ti = np.array(Ti)
samplein = np.mat([Co,Cr,Mg,Pb])

#数据归一化,将输入数据压缩至0到1之间,便于计算,后续通过反归一化恢复原始值
sampleinminmax = np.array([samplein.min(axis=1).T.tolist()[0],samplein.max(axis=1).T.tolist()[0]]).transpose()
sampleout = np.mat([Ti])
sampleoutminmax = np.array([sampleout.min(axis=1).T.tolist()[0],sampleout.max(axis=1).T.tolist()[0]]).transpose()
sampleinnorm = (2*(np.array(samplein.T)-sampleinminmax.transpose()[0])/(sampleinminmax.transpose()[1]-sampleinminmax.transpose()[0])-1).transpose()
sampleoutnorm = (2*(np.array(sampleout.T)-sampleoutminmax.transpose()[0])/(sampleoutminmax.transpose()[1]-sampleoutminmax.transpose()[0])-1).transpose()
sampleinmax = np.array([sampleinnorm.max(axis=1).T.tolist()]).transpose()
sampleinmin = np.array([sampleinnorm.min(axis=1).T.tolist()]).transpose()
#为归一化后的数据添加噪声
noise = 0.03*np.random.rand(sampleoutnorm.shape[0],sampleoutnorm.shape[1])
sampleoutnorm += noise
sampleinnorm = np.mat(sampleinnorm)

maxepochs = 1000  #训练次数
errorfinal = 0.65*10**(-3)  #停止训练误差阈值
samnum = 72  #输入数据数量
indim = 4  #输入层节点数
outdim = 1  #输出层节点数
hiddenunitnum = 8  #隐含层节点数

#利用归一化后的输入数据初始化参数w1、b1、w2、b2
dvalue = sampleinmax-sampleinmin
valuemid=(sampleinmin+sampleinmax)/2
wmag=0.7*(hiddenunitnum**(1/indim))
rand1=np.random.rand(hiddenunitnum,outdim)
rand2=np.random.randn(hiddenunitnum,indim)
rand1=rand1*wmag
rand2=rand2*wmag
b1=rand1-np.dot(rand2,valuemid)
for i in range(hiddenunitnum):
    for j in range(indim):
        rand2[i][j]=(2*rand2[i][j])/dvalue[j]
w1=rand2
w2 = np.random.uniform(low=-1, high=1, size=[outdim,hiddenunitnum])
b2 = np.random.uniform(low=-1, high=1, size=[outdim,1])

#参数w1、b1、w2、b2均为矩阵形式参与计算,其形状依次为8*4,8*1,1*8,1*1
w1 = np.mat(w1)
b1 = np.mat(b1)
w2 = np.mat(w2)
b2 = np.mat(b2)

#errhistory存储每次训练后的预测值与真实值的误差
errhistory = []

#vw2、vb2、vw1、vb1分别保存参数w1、b1、w2、b2的梯度加权平均值,其形状与w1、b1、w2、b2一一对应
vw2 = np.zeros((1,8))
vb2 = np.zeros((1,1))
vw1 = np.zeros((8,4))
vb1 = np.zeros((8,1))

for i in range(maxepochs):
    #前向传播
    #计算隐含层输出hiddenout,输出层输出networkout
    hiddenout = tanh((np.dot(w1,sampleinnorm).transpose()+b1.transpose())).transpose()
    networkout = np.dot(w2,hiddenout).transpose()+b2.transpose()
    for j in range(samnum):
        networkout[j,:] = tanh(networkout[j,:])
    networkout = networkout.transpose()
    # 计算损失函数
    err = sampleoutnorm - networkout
    loss = np.sum(np.abs(err))/samnum
    sse = np.sum(np.square(err))
    #判断是否满足停止训练条件
    errhistory.append(sse)
    if sse < errorfinal:
        break
    #反向传播
    #利用损失函数计算结果和激活函数偏导数,来计算参数w1、b1、w2、b2的梯度值
    delta2 = np.zeros((outdim,samnum))
    for n in range(samnum):
        delta2[:,n] = (-1) * err[:,n] * de_tanh(networkout[:,n])
    delta1 = np.zeros((hiddenunitnum,samnum))
    for e in range(samnum):
        for f in range(hiddenunitnum):
            delta1[f,e] = w2[:,f] * delta2[:,e] * de_tanh(hiddenout[f,e])
    dw2now = np.dot(delta2,hiddenout.transpose()) #1*8
    db2now = np.dot(delta2,np.ones((samnum,1))) #1*1
    dw1now = np.dot(delta1,sampleinnorm.transpose()) #8*4
    db1now = np.dot(delta1,np.ones((samnum,1))) #8*1

    # 先更新输出层参数
    # w2更新,依次更新w2的梯度加权平均值,w2
    for m in range(hiddenunitnum):
        vw2[:,m] = accumulation(vw2[:,m],dw2now[:,m])
        w2[:,m]= adjust(w2[:,m],vw2[:,m])

    #b2更新,依次更新b2的梯度加权平均值,b2
    vb2 = accumulation(vb2,db2now)
    b2 = adjust(b2,vb2)

    # 更新隐含层参数
    #w1更新,依次更新w1的梯度加权平均值,w1
    for a in range(hiddenunitnum):
        for b in range(indim):
            vw1[a,b] = accumulation(vw1[a,b],dw1now[a,b])
            w1[a,b] = adjust(w1[a,b],vw1[a,b])

    #b1更新,依次更新b1的梯度加权平均值,b1
    for n in range(hiddenunitnum):
        vb1[n,:] = accumulation(vb1[n,:],db1now[n,:])
        b1[n,:] = adjust(b1[n,:],vb1[n,:])


    print("the generation is:",i,",the loss is:",loss)

#达到最大训练次数,保存此时的参数w1、b1、w2、b2
np.save("w1.npy",w1)
np.save("b1.npy",b1)
np.save("w2.npy",w2)
np.save("b2.npy",b2)

3.2 测试过程

测试过程只需要利用训练过程生成的相关参数,对测试数据执行一次前向传播过程来获得预测值,之后可使用相关的误差指标对预测值进行评价,详细的测试过程源码见参考源码及数据集。
在这里插入图片描述

4、参考源码及数据集

参考源码及数据集

猜你喜欢

转载自blog.csdn.net/weixin_42051846/article/details/128970538
今日推荐