学习目的:
- 知道梯度降落算法
- 知道网络训练过程中的epoch、batch、iter
- 相识反向传播算法过程
- 知道梯度降落的优化方法
- 知道学习率优化计谋
1. 梯度降落算法
1.1 什么是梯度降落算法
梯度降落法是一种探求使损失函数最小化的方法。从数学上的角度来看,梯度的方向是函数增长速度最快的方向,那么梯度的反方向就是函数淘汰最快的方向,所以有:
此中,η是学习率,如果学习率太小,那么每次训练之后得到的结果都太小,增大训练的时间成本。如果,学习率太大,那就有可能直接跳过最优解,进入无限的训练中。解决的方法就是,学习率也需要随着训练的进行而变化。
1.2 模型训练中的三个基础概念
在进行模型训练时,有三个基础的概念:
- Epoch: 利用全部数据对模型进行以此完整训练,训练轮次
- Batch_size: 利用训练会合的小部分样本对模型权重进行以此反向传播的参数更新,每次训练每批次样本数量
- 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 BatchsBGD(批量梯度降落)NN1SGD(随机梯度降落)N1NMini-Batch(小批量梯度降落)NBN/B +1 注:上表中 Mini-Batch 的 Batch 个数为 N / B + 1 是针对未整除的环境。整除则是 N / B。
2. 反向传播[BP算法]相识
2.1 什么是反向传播
前向传播:指的是数据输入的神经网络中,逐层向前传输,一直到运算到输出层为止。
反向传播(Back Propagation):利用损失函数 ERROR,从后往前,结合梯度降落算法,依次求各个参数的偏导,并进行参数更新。
反向传播对神经网络中的各个节点的权重进行更新。一个简单的神经网络用来举例:激活函数为sigmoid:
前向传播运算过程“
接下来是反向传播,我们先来求最简单的,求误差E对w5的导数。要求误差E对w5的导数,需要先求误差E对out o1的导数,再求out o1对net o1的导数,最后再求net o1对w5的导数,经过这个处理,我们就可以求出误差E对w5的导数(偏导),如下图所示:
导数(梯度)已经计算出来了,下面就是反向传播与参数更新过程:
如果要想求误差E对w1的导数,误差E对w1的求导路径不止一条,这会稍微复杂一点,但换汤不换药,计算过程如下所示:
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 梯度降落为什么需要优化
梯度降落优化算法中,可能会碰到以下环境:
- 碰到平缓区域,梯度值较小,参数优化变慢
- 碰到 “鞍点” ,梯度为 0,参数无法优化
- 碰到局部最小值,参数不是最优
对于这些题目, 出现了一些对梯度降落算法的优化方法,例如: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
梯度计算公式:
- St-1表示汗青梯度移动加权平均值
- Wt表示当前时候的梯度值
- Dt为当前时候的指数加权平均梯度值
- β为权重系数
假设:权重 β为 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 的学习率总体会逐渐减小。
其计算步骤如下:
- 初始化学习率 α、初始化参数 θ、小常数 σ = 1e-6
- 初始化梯度累积变量 s=0
- 从训练会合采样 m 个样本的小批量,计算梯度 g
- 累积平方梯度 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的优化. 最主要的不同是,其利用指数移动加权平均梯度替换汗青梯度的平方和。其计算过程如下:
- 初始化学习率 α、初始化参数 θ、小常数 σ = 1e-6
- 初始化参数 θ
- 初始化梯度累计变量 s
- 从训练会合采样 m 个样本的小批量,计算梯度 g
- 用指数移动平均累积汗青梯度,公式如下:
学习率 α 的计算公式如下: 参数更新公式如下: 代码实现算法:
- # 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 算法结合在一起。
- 修正梯度: 使⽤梯度的指数加权平均
- 修正学习率: 利用梯度平方的指数加权平均。
- # 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()
复制代码 输出结果:

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |