自学内容网 自学内容网

神经网络基础-网络优化方法


学习目标:

  1. 知道梯度下降算法
  2. 知道网络训练过程中的epoch、batch、iter
  3. 了解反向传播算法过程
  4. 知道梯度下降的优化方法
  5. 知道学习率优化策略

1. 梯度下降算法

1.1 什么是梯度下降算法

梯度下降法是一种寻找使损失函数最小化的方法。从数学上的角度来看,梯度的方向是函数增长速度最快的方向,那么梯度的反方向就是函数减少最快的方向,所以有:
在这里插入图片描述

其中,η是学习率,如果学习率太小,那么每次训练之后得到的效果都太小,增大训练的时间成本。如果,学习率太大,那就有可能直接跳过最优解,进入无限的训练中。解决的方法就是,学习率也需要随着训练的进行而变化。

在这里插入图片描述

1.2 模型训练中的三个基础概念

在进行模型训练时,有三个基础的概念:

  1. Epoch: 使用全部数据对模型进行以此完整训练,训练轮次
  2. Batch_size: 使用训练集中的小部分样本对模型权重进行以此反向传播的参数更新,每次训练每批次样本数量
  3. Iteration: 使用一个 Batch 数据对模型进行一次参数更新的过程

假设数据集有 50000 个训练样本,现在选择 Batch Size = 256 对模型进行训练。

每个 Epoch 要训练的图片数量:50000

训练集具有的 Batch 个数:50000/256+1=196

每个 Epoch 具有的 Iteration 个数:196

10个 Epoch 具有的 Iteration 个数:1960

1.3 三种梯度下降方式对比

在深度学习中,梯度下降的几种方式的根本区别就在于 Batch Size不同,如下表所示:

梯度下降方式Traing Set SizeBatch SizeNumber of Batchs
BGD(批量梯度下降)NN1
SGD(随机梯度下降)N1N
Mini-Batch(小批量梯度下降)NBN/B +1

注:上表中 Mini-Batch 的 Batch 个数为 N / B + 1 是针对未整除的情况。整除则是 N / B。

2. 反向传播[BP算法]了解

2.1 什么是反向传播

前向传播:指的是数据输入的神经网络中,逐层向前传输,一直到运算到输出层为止。
反向传播(Back Propagation):利用损失函数 ERROR,从后往前,结合梯度下降算法,依次求各个参数的偏导,并进行参数更新。

在这里插入图片描述

反向传播对神经网络中的各个节点的权重进行更新。一个简单的神经网络用来举例:激活函数为sigmoid:
在这里插入图片描述

前向传播运算过程“
在这里插入图片描述

接下来是反向传播,我们先来求最简单的,求误差Ew5的导数。要求误差Ew5的导数,需要先求误差E对out o1的导数,再求out o1net o1的导数,最后再求net o1w5的导数,经过这个处理,我们就可以求出误差Ew5的导数(偏导),如下图所示:

在这里插入图片描述

导数(梯度)已经计算出来了,下面就是反向传播与参数更新过程
在这里插入图片描述

如果要想求误差Ew1的导数,误差Ew1的求导路径不止一条,这会稍微复杂一点,但换汤不换药,计算过程如下所示:
在这里插入图片描述

2.2 python实现反向传播

python 实现反向传播:

# 反向传播
import torch
import torch.nn as nn
from torch import optim
# 创建神经网络
class Model(nn.Module):
    # 初始化参数
    def __init__(self):
        # 调用父类方法
        super(Model, self).__init__()
        # 创建网络层
        self.linear1 = nn.Linear(2,2)
        self.linear2 = nn.Linear(2,2)
        # 初始化神经网络参数
        self.linear1.weight.data = torch.tensor([[0.15,0.20],[0.25,0.30]])
        self.linear2.weight.data = torch.tensor([[0.40,0.45],[0.50,0.55]])
        self.linear1.bias.data = torch.tensor([0.35,0.35])
        self.linear2.bias.data = torch.tensor([0.60,0.60])

    # 前向传播方法
    def forward(self, x):
        # 数据经过第一层隐藏层
        x = self.linear1(x)
        # 计算第一层激活值
        x = torch.sigmoid(x)
        # 数据经过第二层隐藏层
        x = self.linear2(x)
        # 计算第二次激活值
        x = torch.sigmoid(x)
        return x

if __name__ == '__main__':
    # 定义网络输入值和目标值
    inputs = torch.tensor([0.05,0.10])
    target = torch.tensor([0.01,0.99])
    # 实例化神经网络对象
    model = Model()
    output = model(inputs)
    print('output-->',output)
    # 计算误差
    loss = torch.sum((output - target)**2)/2
    print('loss-->',loss)
    # 优化方法和反向传播算法
    optimizer = optim.SGD(model.parameters(), lr=0.5)
    optimizer.zero_grad()
    loss.backward()
    print('w1,w2,w3,w4-->',model.linear1.weight.grad.data)
    print('w5,w6,w7,w8-->',model.linear2.weight.grad.data)
    optimizer.step()
    # 打印神经网络参数
    print('打印神经网络参数')
    print(model.state_dict())

输出结果:

output--> tensor([0.7514, 0.7729], grad_fn=<SigmoidBackward0>)
loss--> tensor(0.2984, grad_fn=<DivBackward0>)
w1,w2,w3,w4--> tensor([[0.0004, 0.0009],
        [0.0005, 0.0010]])
w5,w6,w7,w8--> tensor([[ 0.0822,  0.0827],
        [-0.0226, -0.0227]])
打印神经网络参数
OrderedDict([('linear1.weight', tensor([[0.1498, 0.1996],
        [0.2498, 0.2995]])), ('linear1.bias', tensor([0.3456, 0.3450])), ('linear2.weight', tensor([[0.3589, 0.4087],
        [0.5113, 0.5614]])), ('linear2.bias', tensor([0.5308, 0.6190]))])

总结:

前向传播和反向传播是什么?

  • 前向传播:指的是数据输入到神经网络中,逐层向前传输,一直运算到输出层为止。
  • 反向传播:利用损失函数,从后往前,结合梯度下降算法,依次求各个参数的偏导,并进行参数更新。

3 . 梯度下降的优化方法

3.1 梯度下降为什么需要优化

梯度下降优化算法中,可能会碰到以下情况:
在这里插入图片描述

  1. 碰到平缓区域,梯度值较小,参数优化变慢
  2. 碰到 “鞍点” ,梯度为 0,参数无法优化
  3. 碰到局部最小值,参数不是最优

对于这些问题, 出现了一些对梯度下降算法的优化方法,例如:Momentum、AdaGrad、RMSprop、Adam 等

3.2 梯度下降优化方法

3.2.1 指数加权平均

指数移动加权平均则是参考各数值,并且各数值的权重都不同,距离越远的数字对平均数计算的贡献就越小(权重较小),距离越近则对平均数的计算贡献就越大(权重越大)。

比如:明天气温怎么样,和昨天气温有很大关系,而和一个月前的气温关系就小一些。

计算公式可以用下面的式子来表示:
在这里插入图片描述

  • St 表示指数加权平均值;
  • Yt 表示 t 时刻的值;
  • β 调节权重系数,该值越大平均数越平缓。

下面通过代码来看结果,随机产生 30 天的气温数据:

# 指数加权移动平均
import os
os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE"
import torch
import matplotlib.pyplot as plt

ELEMENT_NUMBER = 30
# 1. 实际平均温度
def test01():
    # 固定随机数种子
    torch.manual_seed(0)
    # 产生30天的随机温度
    temperature = torch.rand(size=(ELEMENT_NUMBER, )) * 10
    print("30天的温度")
    print(temperature)
    # 绘制平均温度
    days = torch.arange(1,ELEMENT_NUMBER+1,1)
    plt.plot(days, temperature,color='r')
    plt.scatter(days, temperature)
    plt.show()

test01()

30 天的温度:
在这里插入图片描述

30 天移动平均加权后的温度:

import os
os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE"
import torch
import matplotlib.pyplot as plt

ELEMENT_NUMBER = 30
# 指数加权平均温度
def test02(beta=0.9):
    # 固定随机数种子
    torch.manual_seed(0)
    # 产生30天的随机温度
    temperature = torch.rand(size=(ELEMENT_NUMBER,)) * 10
    # 指数加权
    exp_weight_avg = []
    for idx,temp in enumerate(temperature,1): # 从下标1开始
        # 第一个原始的 EWA 值等于自身
        if idx == 1:
            exp_weight_avg.append(temp)
            continue
        # 第二个元素的 EWA 值定于上一个 EWA 乘以 β + 当前气温乘以 (1-β)
        new_temp = exp_weight_avg[idx-2] * beta + (1 - beta) * temp
        exp_weight_avg.append(new_temp)
    # 绘制平均温度
    days = torch.arange(1, ELEMENT_NUMBER + 1, 1)
    title = 'β='+str(beta)
    plt.title(title)
    plt.plot(days, exp_weight_avg, color='r')
    plt.scatter(days, temperature)
    plt.show()

test02(0.9)

上图是β为0.5和0.9时的结果,从中可以看出:

  • 指数加权平均绘制出的气氛变化曲线更加平缓,
  • β 的值越大,则绘制出的折线越加平缓,波动越小。
3.2.2 动量算法Momentum
梯度计算公式:
  1. St-1表示历史梯度移动加权平均值
  2. Wt表示当前时刻的梯度值
  3. Dt为当前时刻的指数加权平均梯度值
  4. β为权重系数

假设:权重 β0.9,例如:

​ 第一次梯度值:s1 = d1 = w1

​ 第二次梯度值:d2 = s2 = 0.9 * s1 + w2 * 0.1

​ 第三次梯度值:d3 = s3 = 0.9 * s2 + w3 * 0.1

​ 第四次梯度值:d4 = s4 = 0.9 * s3 + w4 * 0.1

梯度下降公式中梯度的计算,就不再是当前时刻 t 的梯度值,而是历史梯度值的指数移动加权平均值。公式修改为:

Monmentum优化方法是如何一定程度上克服 “平缓”、”鞍点” 的问题呢?
在这里插入图片描述

  • 当处于鞍点位置时,由于当前的梯度为 0,参数无法更新。但是 Momentum动量梯度下降算法在先前积累了一些梯度值,很有可能使得跨过鞍点。
  • 由于 mini-batch普通的梯度下降算法,每次选取少数的样本梯度确定前进方向,可能会出现震荡,使得训练时间变长。Momentum使用移动加权平均,平滑了梯度的变化,使得前进方向更加平缓,有利于加快训练过程。
# 动量算法(Momentum)优化梯度下降
import os
os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE"
import torch
import matplotlib.pyplot as plt
def test01():
    # 1. 初始化参数
    w = torch.tensor([1.0],requires_grad=True,dtype=torch.float32)
    y = ((w ** 2)/2.0).sum()
    # 2. 实例化优化方法:SGD 指定参数 bata=0.9
    optimizer = torch.optim.SGD([w], lr=0.01,momentum=0.9)
    # 3. 第一次更新计算梯度,并对参数进行更新
    optimizer.zero_grad()
    y.backward()
    optimizer.step()
    print('第一次:梯度w.grad:%f,更新后的权重:%f'%(w.grad.numpy(),w.detach().numpy()))
    # 4. 第二次更新计算梯度,并对参数进行更新
    # 使用更新后的参数输出结果
    y = ((w ** 2)/2.0).sum()

    optimizer.zero_grad()
    y.backward()
    optimizer.step()
    print('第二次:梯度w.grad:%f,更新后的权重:%f'%(w.grad.numpy(),w.detach().numpy()))

test01()

输出结果:

第一次:梯度w.grad:1.000000,更新后的权重:0.990000
第二次:梯度w.grad:0.990000,更新后的权重:0.971100
3.2.3 AdaGrad算法

AdaGrad通过对不同的参数分量使用不同的学习率,AdaGrad 的学习率总体会逐渐减小

其计算步骤如下:

  1. 初始化学习率 α、初始化参数 θ、小常数 σ = 1e-6
  2. 初始化梯度累积变量 s=0
  3. 从训练集中采样 m 个样本的小批量,计算梯度 g
  4. 累积平方梯度 s = s + g ⊙ g,⊙ 表示各个分量相乘

学习率 α 的计算公式如下:
在这里插入图片描述

参数更新公式如下:
在这里插入图片描述

重复 2-4 步骤,即可完成网络训练。

AdaGrad缺点是可能会使得学习率过早、过量的降低,导致模型训练后期学习率太小,较难找到最优解。

# AdaGrad算法优化梯度下降
import os
os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE"
import torch

def test():
    # 1. 初始化权重参数
    w = torch.tensor([1.0],requires_grad=True,dtype=torch.float32)
    y = ((w**2)/2.0).sum()
    # 2. 实例化优化方法:Adagrad 优化方法
    optimizer = torch.optim.Adagrad([w], lr=0.01)
    # 3 第一次更新计算梯度,并对参数进行更新
    optimizer.zero_grad()
    y.backward()
    optimizer.step()
    print('第一次:梯度w.grad:%f,更新后的权重:%f'%(w.grad.numpy(),w.detach().numpy()))
    # 4. 第二次更新计算梯度,并对参数进行更新
    # 使用更新后的参数输出结果
    y = ((w ** 2)/2.0).sum()

    optimizer.zero_grad()
    y.backward()
    optimizer.step()
    print('第二次:梯度w.grad:%f,更新后的权重:%f'%(w.grad.numpy(),w.detach().numpy()))

test()

输出结果:

第一次:梯度w.grad:1.000000,更新后的权重:0.990000
第二次:梯度w.grad:0.990000,更新后的权重:0.982965
3.2.4 RMSProp算法

RMSProp优化算法是对 AdaGrad的优化. 最主要的不同是,其使用指数移动加权平均梯度替换历史梯度的平方和。其计算过程如下:

  1. 初始化学习率 α、初始化参数 θ、小常数 σ = 1e-6

  2. 初始化参数 θ

  3. 初始化梯度累计变量 s

  4. 从训练集中采样 m 个样本的小批量,计算梯度 g

  5. 用指数移动平均累积历史梯度,公式如下:
学习率 α 的计算公式如下:
参数更新公式如下:

代码实现算法:

# RMSProp算法优化梯度下降
import os

from sympy.abc import alpha

os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE"
import torch

def test():
    # 1. 初始化权重参数
    w = torch.tensor([1.0],requires_grad=True,dtype=torch.float32)
    y = ((w**2)/2.0).sum()
    # 2. 实例化优化方法:RMSProp 优化方法,其中alpha对应这beta
    optimizer = torch.optim.RMSprop([w], lr=0.01,alpha=0.9)
    # 3 第一次更新计算梯度,并对参数进行更新
    optimizer.zero_grad()
    y.backward()
    optimizer.step()
    print('第一次:梯度w.grad:%f,更新后的权重:%f'%(w.grad.numpy(),w.detach().numpy()))
    # 4. 第二次更新计算梯度,并对参数进行更新
    # 使用更新后的参数输出结果
    y = ((w ** 2)/2.0).sum()

    optimizer.zero_grad()
    y.backward()
    optimizer.step()
    print('第二次:梯度w.grad:%f,更新后的权重:%f'%(w.grad.numpy(),w.detach().numpy()))

test()

输出结果:

第一次:梯度w.grad:1.000000,更新后的权重:0.968377
第二次:梯度w.grad:0.968377,更新后的权重:0.945788
3.2.5 Adam算法
  • Momentum 使用指数加权平均计算当前的梯度值
  • AdaGrad、RMSProp 使用自适应的学习率
  • Adam优化算法(Adaptive Moment Estimation,自适应矩估计)将 Momentum 和RMSProp 算法结合在一起。
    1. 修正梯度: 使⽤梯度的指数加权平均
    2. 修正学习率: 使用梯度平方的指数加权平均。
# Adam 算法优化梯度下降
import os
import torch
os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE"

def test():
    # 1.初始化权重参数
    w = torch.tensor([1.0],requires_grad=True)
    y = ((w ** 2)/2.0).sum()
    # 2.实例化优化方法:Adam算法,其中 betas 是指数加权的系数
    optimizer = torch.optim.Adam([w], lr=0.001,betas=(0.9,0.99))
    # 3.第一次更新计算梯度,并对参数进行更新
    optimizer.zero_grad()
    y.backward()
    optimizer.step()
    print('第一次:梯度w.grad:%f,更新后的权重:%f'%(w.grad.numpy(),w.detach().numpy()))
    # 4.第二次更新计算梯度,并对参数进行更新
    # 使用更新后的参数机选输出结果
    y = ((w ** 2)/2.0).sum()
    optimizer.zero_grad()
    y.backward()
    optimizer.step()
    print('第二次:梯度w.grad:%f,更新后的权重:%f'%(w.grad.numpy(),w.detach().numpy()))
test()

输出结果:

第一次:梯度w.grad:1.000000,更新后的权重:0.999000
第二次:梯度w.grad:0.999000,更新后的权重:0.998000

4. 学习率衰减方法

学习率衰减是指在训练过程中逐渐减少学习率的策略,其目的是在训练初期保持较高的学习率以快速收敛,而在接近最优解时减小学习率,从而细致调整模型参数,提高模型的稳定性和收敛性。‌

4.1 等间隔学习率衰减

等间隔学习率衰减方式如下所示:
在这里插入图片描述

lr_scheduler.StepLR(optimizer, step_size, gamma=0.1)
# 功能:等间隔-调整学习率
# 参数:
# step_size:调整间隔数=50
#   gamma:调整系数=0.5
# 调整方式:lr = lr * gamma

python实现:

# 等间隔学习率衰减法
import os
import torch
os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE"
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
def test():
    # 0. 参数初始化
    LR = 0.1 # 设置学习率初始值为 0.1
    iteration = 10
    max_epoch = 200
    # 1.初始化参数
    y_true= torch.tensor([0])
    x = torch.tensor([1.0])
    w = torch.tensor([1.0],requires_grad=True)
    # 2.优化器
    optimizer = torch.optim.SGD([w], lr=LR, momentum=0.9)
    # 3.设置学习率下降策略
    scheduler_lr = torch.optim.lr_scheduler.StepLR(optimizer, step_size=50,gamma=0.5)
    # 4.获取学习率的值和当前的epoch
    lr_list,epoch_list = list(),list()
    for epoch in range(max_epoch):
        # 获取当前学习率
        lr_list.append(scheduler_lr.get_last_lr())
        # 获取当前的epoch
        epoch_list.append(epoch)
        # 遍历每一个batch数据
        for i in range(iteration):
            loss = ((w*x - y_true)**2)/2.0 # 目标函数
            optimizer.zero_grad()
            # 方向传播
            optimizer.step()
        # 更新下一个epoch的学习率
        scheduler_lr.step()
    # 5.绘制学习率变化曲线
    plt.plot(epoch_list,lr_list,label='等间隔学习率衰减')
    plt.xlabel('Epoch')
    plt.ylabel('LR')
    plt.legend()
    plt.show()

test()

输出结果如下:

在这里插入图片描述

4.2 指定间隔学习率衰减

指定间隔学习率衰减方式如下所示:
在这里插入图片描述

lr_scheduler.MultiStepLR(optimizer, milestones, gamma=0.1)
# 功能:指定间隔-调整学习率
# 主要参数:
    # milestones:设定调整轮次:[50, 125, 160]
    # gamma:调整系数
    # 调整方式:lr = lr * gamma

python实现:

# 指定间隔学习率衰减法
import os
import torch
os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE"
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
def test():
    # 0. 参数初始化
    LR = 0.1 # 设置学习率初始值为 0.1
    iteration = 10
    max_epoch = 200
    # 1.初始化参数
    y_true= torch.tensor([0])
    x = torch.tensor([1.0])
    w = torch.tensor([1.0],requires_grad=True)
    # 2.优化器
    optimizer = torch.optim.SGD([w], lr=LR, momentum=0.9)
    # 3.设置学习率下降策略
    # 设定调整时刻数
    milestones = [50, 125, 160]
    gamma = 0.5
    scheduler_lr = torch.optim.lr_scheduler.MultiStepLR(optimizer, milestones=milestones,gamma=gamma)
    # 4.获取学习率的值和当前的epoch
    lr_list,epoch_list = list(),list()
    for epoch in range(max_epoch):
        # 获取当前学习率
        lr_list.append(scheduler_lr.get_last_lr())
        # 获取当前的epoch
        epoch_list.append(epoch)
        # 遍历每一个batch数据
        for i in range(iteration):
            loss = ((w*x - y_true)**2)/2.0 # 目标函数
            optimizer.zero_grad()
            # 方向传播
            optimizer.step()
        # 更新下一个epoch的学习率
        scheduler_lr.step()
    # 5.绘制学习率变化曲线
    plt.plot(epoch_list,lr_list,label='等间隔学习率衰减')
    plt.xlabel('Epoch')
    plt.ylabel('LR')
    plt.legend()
    plt.show()

test()

输出结果:
在这里插入图片描述

4.3按指数学习率衰减

指数学习率衰减方式如下所示:

在这里插入图片描述

lr_scheduler.ExponentialLR(optimizer, gamma)
# 功能:按指数衰减-调整学习率
# 主要参数:
# gamma:指数的底
# 调整方式
# lr= lr∗ gamma^epoch

python实现:

# 指数学习率衰减法
import os
import torch
os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE"
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
def test():
    # 0. 参数初始化
    LR = 0.1 # 设置学习率初始值为 0.1
    iteration = 10
    max_epoch = 200
    # 1.初始化参数
    y_true= torch.tensor([0])
    x = torch.tensor([1.0])
    w = torch.tensor([1.0],requires_grad=True)
    # 2.优化器
    optimizer = torch.optim.SGD([w], lr=LR, momentum=0.9)
    # 3.设置学习率下降策略
    gamma = 0.95
    scheduler_lr = torch.optim.lr_scheduler.ExponentialLR(optimizer, gamma=gamma)
    # 4.获取学习率的值和当前的epoch
    lr_list,epoch_list = list(),list()
    for epoch in range(max_epoch):
        # 获取当前学习率
        lr_list.append(scheduler_lr.get_last_lr())
        # 获取当前的epoch
        epoch_list.append(epoch)
        # 遍历每一个batch数据
        for i in range(iteration):
            loss = ((w*x - y_true)**2)/2.0 # 目标函数
            optimizer.zero_grad()
            # 方向传播
            optimizer.step()
        # 更新下一个epoch的学习率
        scheduler_lr.step()
    # 5.绘制学习率变化曲线
    plt.plot(epoch_list,lr_list,label='等间隔学习率衰减')
    plt.xlabel('Epoch')
    plt.ylabel('LR')
    plt.legend()
    plt.show()

test()

输出结果:

在这里插入图片描述


原文地址:https://blog.csdn.net/dwjf321/article/details/145078515

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!