同学你好!本文章于2021年末编写,获得广泛的好评!
故在2022年末对本系列进行填充与更新,欢迎大家订阅最新的专栏,获取基于Pytorch1.10版本的理论代码(2023版)实现,
Pytorch深度学习·理论篇(2023版)目录地址为:
CSDN独家 | 全网首发 | Pytorch深度学习·理论篇(2023版)目录
本专栏将通过系统的深度学习实例,从可解释性的角度对深度学习的原理进行讲解与分析,通过将深度学习知识与Pytorch的高效结合,帮助各位新入门的读者理解深度学习各个模板之间的关系,这些均是在Pytorch上实现的,可以有效的结合当前各位研究生的研究方向,设计人工智能的各个领域,是经过一年时间打磨的精品专栏!
https://v9999.blog.csdn.net/article/details/127587345
欢迎大家订阅(2023版)理论篇
以下为2021版原文~~~~
1 优化器模块的作用
1.1 反向传播的核心思想
反向传播的意义在于告诉模型我们需要将权重修改到什么数值可以得到最优解,在开始探索合适权重的过程中,正向传播所生成的结果与实际标签的目标值存在误差,反向传播通过这个误差传递给权重,要求权重进行适当的调整来达到一个合适的输出,最终使得正向传播所预测的结果与标签的目标值的误差达到最小,以上即为反向传播的核心思想
1.2 优化器简介
正向结构与损失函数获取完毕之后,通过优化器模块中优化函数来实现对学习参数的优化,其内部原理主要是梯度下降的方法实现的
1.2.1 优化器与梯度下降
优化器是指通过算法帮助模型在训练过程中更快更好地将参数调整到位,梯度下降主要在损失函数求解出损失值时,它利用梯度下降的方向为前进方向,沿着梯度下降的方向求解最小值,帮助模型找到最小的那个损失值,从而可以反向推算出学习参数的权重,达到优化的目的。
1.3 优化器的类型
1.3.1 批量梯度下降
主要特点:遍历全部数据集计算一次损失函数,根据函数结果更新梯度。
缺点:每次都要遍历全部数据集,计算开销大,计算慢,不支持在线学习。
1.3.2 随机梯度下降
主要特点:每检查一个数据就进行损失函数计算,求梯度更新函数
缺点:计算速度快,收敛性不好,易在最优点附近摆动,两次参数也可能互相抵消,函数震荡剧烈。
1.3.3 小批量梯度下降(折中)
主要特点:将数据集分成若干批,按照批次来实现更新参数,一批中的一组数据共同决定梯度的反向,梯度下降方向不易跑偏,减少随机性,计算量小。
缺点:————————
2 优化器
2.1 常见的优化器
2.1.1 Adam
较为常用,学习率设为3e-4
2.1.2 Ranger
基于RAdam与Lookhead优化器基础上融合而来,兼顾两者的优点,综合性能占优,使得其在各种模型中都具有较高的精度、收敛速度快、使用方便、无需手动调参。
- RAdam:带有整流器的Adam,能够利用潜在散度动态地打开或者管理自适应学习率。
- Lookhead:通过迭代更新两组权重的方法,提前观察另一个优化器生成的序列进而选择探索方向。
2.1.3 AMSGrad
在Adam优化器基础上使用二阶冲量,在计算机视觉模型上表现更为出色
2.1.4 Adamax
在带有词向量的自然语言处理模型中表现得更好
2.2 如何选取优化器
2.2.1 手动精确调整模型方面
一般先使用Adam优化器训练模型,在模型无法进一步收敛后,再使用SGD优化器进行手动调节学习率,进一步提升模型性能。
2.2.2 自动精确调整模型方面
一般以Adam优化器为最常用,在收敛速度、模型训练精度都具有较好的效果,对于学习率设置较为宽松,更易于使用。
2.3 优化器的使用
2.3.1 优化器调用方法
优化器在工作时,先算出梯度(根据损失值对某个参数求偏导),再沿着该梯度的方向算出一段距离(由学习率决定),该差值作为变化值更新到原有参数上。
import torch ### Adam()是优化器方法 #model.parameters():待优化的权重参数,调用模型的parameters(),将返回值传入 #lr:学习率,学习率越大收敛越快! optimizer = torch.optim.Adam(model.parameters(),lr = lreaning_rate)
2.3.2 查看优化器的参数结构
Pytorch中的每个优化器类中均有param_groups属性,该属性包括每个待优化权重的配置参数,是一个列表对象
list(optimizer.param_group[0].keys()) #返回: ['params','lr','eps','weight_deacy','amsgrad'] #返回: ['优化器要作用的权重参数','学习率','','权重参数的衰减率','是否使用二阶冲量的方式'] ### 权重参数的衰减率weight_deacy是指模型在训练过程中使用L2郑泽华的衰减参数,L2正则化是一种防止过拟合的方法
3 退化学习率
3.1 退化学习率简介
退化学习率/学习率衰减,即在刚训练开始时,使用大的学习率加速,训练到一定程度后使用小的学习率来提高精度。
3.1.1 退化学习率/学习率衰减 手动代码实现
import sklearn.datasets import torch import numpy as np import matplotlib.pyplot as plt from LogicNet_fun import LogicNet, plot_losses, predict, plot_decision_boundary # 准备数据 np.random.seed(0) # 设置随机种子 X, Y = sklearn.datasets.make_moons(200, noise=0.2) # 生成两组半圆形数据 arg = np.squeeze(np.argwhere(Y == 0), axis=1) # 获取第1组数据索引 arg2 = np.squeeze(np.argwhere(Y == 1), axis=1) # 获取第2组数据索引 plt.title("moons data") # 设置可视化标题 plt.scatter(X[arg, 0], X[arg, 1], s=100, c='b', marker='+', label='data1') # 显示第一组数据索引 plt.scatter(X[arg2, 0], X[arg2, 1], s=40, c='r', marker='o', label='data2') # 显示第二组数据索引 plt.legend() # 显示图例 plt.show() # 搭建网络模型 model = LogicNet(inputdim=2,hiddendim=3,outputdim=2) #实例化模型 输入数据的维度、隐藏节点的数量、模型最终结果的分类数 optimizer = torch.optim.Adam(model.parameters(),lr=0.01) # 定义优化器 在反向传播时使用 # 训练模型 xt = torch.from_numpy(X).type(torch.FloatTensor) #将数据转化为张量形式 yt = torch.from_numpy(Y).type(torch.LongTensor) epochs = 1000 #训练次数 losses = [] # 损失值列表:用来接受每一步的损失值 lr_list = [] # 学习率列表:用来接受每一步的学习率 for i in range(epochs): loss = model.getloss(xt,yt) losses.append(loss.item()) # 保存中间状态的损失值 optimizer.zero_grad() # 清空之前的梯度 loss.backward() # 反向传播损失值 optimizer.step() # 更新参数 if i % 50 == 0: # 每五十步将学习率 lr X 0.99 for p in optimizer.param_groups: p['lr'] = p['lr'] * 0.99 lr_list.append(optimizer.state_dict()['param_groups'][0]['lr']) # 学习率可视化 plt.plot(range(epochs),lr_list,color='r') plt.show()
3.2 使用lr_scheduler接口实现退化学习率
在Ptorch的optim模块中,将退化学习率的多种实现方法封装到lr_scheduler接口中
3.2.1 使用lr_scheduler接口实现退化学习率 代码实现
import sklearn.datasets import torch import numpy as np import matplotlib.pyplot as plt from LogicNet_fun import LogicNet, plot_losses, predict, plot_decision_boundary # 准备数据 np.random.seed(0) # 设置随机种子 X, Y = sklearn.datasets.make_moons(200, noise=0.2) # 生成两组半圆形数据 arg = np.squeeze(np.argwhere(Y == 0), axis=1) # 获取第1组数据索引 arg2 = np.squeeze(np.argwhere(Y == 1), axis=1) # 获取第2组数据索引 plt.title("moons data") # 设置可视化标题 plt.scatter(X[arg, 0], X[arg, 1], s=100, c='b', marker='+', label='data1') # 显示第一组数据索引 plt.scatter(X[arg2, 0], X[arg2, 1], s=40, c='r', marker='o', label='data2') # 显示第二组数据索引 plt.legend() # 显示图例 plt.show() # 搭建网络模型 model = LogicNet(inputdim=2,hiddendim=3,outputdim=2) #实例化模型 输入数据的维度、隐藏节点的数量、模型最终结果的分类数 optimizer = torch.optim.Adam(model.parameters(),lr=0.01) # 定义优化器 在反向传播时使用 # 训练模型 xt = torch.from_numpy(X).type(torch.FloatTensor) #将数据转化为张量形式 yt = torch.from_numpy(Y).type(torch.LongTensor) epochs = 1000 #训练次数 losses = [] # 损失值列表:用来接受每一步的损失值 lr_list = [] # 学习率列表:用来接受每一步的学习率 scheduler = torch.optim.lr_scheduler.StepLR(optimizer,step_size=50,gamma=0.99) # 设置退化学习率,每50步乘以0.99 for i in range(epochs): loss = model.getloss(xt,yt) losses.append(loss.item()) # 保存中间状态的损失值 optimizer.zero_grad() # 清空之前的梯度 loss.backward() # 反向传播损失值 optimizer.step() # 更新参数 scheduler.step() # 调用退化学习率对象 lr_list.append(optimizer.state_dict()['param_groups'][0]['lr']) # 学习率可视化 plt.plot(range(epochs),lr_list,color='r') plt.show()
3.2.2 lr_scheduler接口中的退化学习率种类
- 等间隔调整学习率StepLR():每训练指定步数,学习率调整为 lr = lr×gamma (gamma为手动设置的退化率参数)。
#optimizer (Optimizer) – 包装的优化器。 #step_size (int) – 学习率衰减间隔,例如若为 30,则会在 30、 60、 90…个 epoch 时,将学习率调整为 lr * gamma。 #gamma (float) – 学习率衰减的乘积因子。 #last_epoch (int) – 最后一个epoch的指数。这个变量用来指示学习率是否需要调整。当last_epoch 符合设定的间隔时,就会对学习率进行调整。当为-1 时,学习率设置为初始值。 class torch.optim.lr_scheduler.StepLR(optimizer, step_size, gamma=0.1, last_epoch=-1) #调整倍数为gamma 倍,调整间隔为step_size。当last_epoch = -1时,将初始lr设置为lr。
- 多间隔调整学习率MultiStepLR():按照指定的步数来调整学习率。调整方式也是lr= lr x gamma。
- 指数衰减调整学习率ExponentialLR():每训练一步,学习率呈指数型衰减,即学习率调整为lr=lr ∗ gamma^epoch (step为训练步数)。
class torch.optim.lr_scheduler.ExponentialLR(optimizer, gamma, last_epoch=-1) #根据epoch数和gamma调整学习率,每个epoch都在改变,调整公式:lr∗gamma^epoch。 # 参数: ## optimizer (Optimizer) – 包装的优化器。 ## gamma (float) – 学习率衰减的乘积因子。 ## last_epoch (int) – 最后一个epoch的指数。这个变量用来指示学习率是否需要调整。当last_epoch 符合设定的间隔时,就会对学习率进行调整。当为-1 时,学习率设置为初始值。
- 余弦退火函数调整学习率CosineAnnealingLR():余弦退火指的就是按照弦函数的曲线进行衰减,每训练一步,学习率呈余弦函数型衰减。
torch.optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max, eta_min=0, last_epoch=-1) # 参数: ## T_max(int) – 一次学习率周期的迭代次数,即 T_max 个 epoch 之后重新设置学习率。 ## eta_min(float) – 最小学习率,即在一个周期中,学习率最小会下降到 eta_min,默认值为 0。
- 根据指标调整学习率ReduceLROnPlateau:当某指标(loss或accuracy)在最近几次训练中均没有变化(下降或升高超过给定阈值)时,调整学习率。
- 自定义调整学习率LambdaLR:将每个参数组的学习速率设置为初始的lr乘以一个给定的函数。
class torch.optim.lr_scheduler.LambdaLR(optimizer, lr_lambda, last_epoch=-1) #参数: ##optimizer (Optimizer) – 包装的优化器。 ##lr_lambda (function or list) – 一个函数来计算一个乘法因子给定一个整数参数的epoch,或列表等功能,为每个组optimizer.param_groups。 ##last_epoch (int) – 最后一个epoch的指数。这个变量用来指示学习率是否需要调整。当last_epoch 符合设定的间隔时,就会对学习率进行调整。当为-1 时,学习率设置为初始值。
3.2.3 多种学习率衰减总结
LambdaLR最为灵活,可以根据需求指定任何策略的学习率变化。它在fne-tune(微调模型的一种方法)中特别有用,不但可以为不同层设置不同的学习率,而且可以为不同层设置不同的学习率调整策略。
3.3 使用lr_scheduler接口实现多种退化学习率
MultiStepLR():在论文中使用较多,简单可控。
ReducelROnPlateau():自动化程度高,参数多。
3.3.1 使用lr_scheduler接口实现MultiStepLR
import sklearn.datasets import torch import numpy as np import matplotlib.pyplot as plt from LogicNet_fun import LogicNet, plot_losses, predict, plot_decision_boundary # 准备数据 np.random.seed(0) # 设置随机种子 X, Y = sklearn.datasets.make_moons(200, noise=0.2) # 生成两组半圆形数据 arg = np.squeeze(np.argwhere(Y == 0), axis=1) # 获取第1组数据索引 arg2 = np.squeeze(np.argwhere(Y == 1), axis=1) # 获取第2组数据索引 plt.title("moons data") # 设置可视化标题 plt.scatter(X[arg, 0], X[arg, 1], s=100, c='b', marker='+', label='data1') # 显示第一组数据索引 plt.scatter(X[arg2, 0], X[arg2, 1], s=40, c='r', marker='o', label='data2') # 显示第二组数据索引 plt.legend() # 显示图例 plt.show() # 搭建网络模型 model = LogicNet(inputdim=2,hiddendim=3,outputdim=2) #实例化模型 输入数据的维度、隐藏节点的数量、模型最终结果的分类数 optimizer = torch.optim.Adam(model.parameters(),lr=0.01) # 定义优化器 在反向传播时使用 # 训练模型 xt = torch.from_numpy(X).type(torch.FloatTensor) #将数据转化为张量形式 yt = torch.from_numpy(Y).type(torch.LongTensor) epochs = 1000 #训练次数 losses = [] # 损失值列表:用来接受每一步的损失值 lr_list = [] # 学习率列表:用来接受每一步的学习率 scheduler = torch.optim.lr_scheduler.MultiStepLR(optimizer,milestones=[200,700,900],gamma=0.99) #在100 700 900 次时,调整学习率 for i in range(epochs): loss = model.getloss(xt,yt) losses.append(loss.item()) # 保存中间状态的损失值 optimizer.zero_grad() # 清空之前的梯度 loss.backward() # 反向传播损失值 optimizer.step() # 更新参数 scheduler.step() # 调用退化学习率对象 lr_list.append(optimizer.state_dict()['param_groups'][0]['lr']) # 学习率可视化 plt.plot(range(epochs),lr_list,color='r') plt.show()
3.3.2 使用lr_scheduler接口实现ReduceLROnPlateau
import sklearn.datasets import torch import numpy as np import matplotlib.pyplot as plt from LogicNet_fun import LogicNet, plot_losses, predict, plot_decision_boundary # 准备数据 np.random.seed(0) # 设置随机种子 X, Y = sklearn.datasets.make_moons(200, noise=0.2) # 生成两组半圆形数据 arg = np.squeeze(np.argwhere(Y == 0), axis=1) # 获取第1组数据索引 arg2 = np.squeeze(np.argwhere(Y == 1), axis=1) # 获取第2组数据索引 plt.title("moons data") # 设置可视化标题 plt.scatter(X[arg, 0], X[arg, 1], s=100, c='b', marker='+', label='data1') # 显示第一组数据索引 plt.scatter(X[arg2, 0], X[arg2, 1], s=40, c='r', marker='o', label='data2') # 显示第二组数据索引 plt.legend() # 显示图例 plt.show() # 搭建网络模型 model = LogicNet(inputdim=2,hiddendim=3,outputdim=2) #实例化模型 输入数据的维度、隐藏节点的数量、模型最终结果的分类数 optimizer = torch.optim.Adam(model.parameters(),lr=0.01) # 定义优化器 在反向传播时使用 # 训练模型 xt = torch.from_numpy(X).type(torch.FloatTensor) #将数据转化为张量形式 yt = torch.from_numpy(Y).type(torch.LongTensor) epochs = 1000 #训练次数 losses = [] # 损失值列表:用来接受每一步的损失值 lr_list = [] # 学习率列表:用来接受每一步的学习率 scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau( optimizer, mode='min', # 要监控模型的最大值or最小值 factor= 0.5, # 退化学习率参数 gamma patience=5, # 不再减少/增加的累计次数 verbose=True, # 触发规则时是否打印信息 threshold=0.001, # 监控值触发规则的阈值 threshold_mode='abs', # 计算触发条件的规则 cooldown=0, # 触发规则后的停止监控步数,避免lr下降过快 min_lr=0, # 允许的最小退化学习率 eps=1e-08 # 当退化学习率小于该值时,停止调整 ) for i in range(epochs): loss = model.getloss(xt,yt) losses.append(loss.item()) # 保存中间状态的损失值 scheduler.step(loss.item()) # 调用退化学习率对象,需要传入被监控的值,否则代码出错 【ReduceLROnPlateau()特别的地方】 optimizer.zero_grad() # 清空之前的梯度 loss.backward() # 反向传播损失值 optimizer.step() # 更新参数 lr_list.append(optimizer.state_dict()['param_groups'][0]['lr']) # 学习率可视化 plt.plot(range(epochs),lr_list,color='r') plt.show()
上述代码 参数Threshold_mode有两种取值:
'rel':在参数mode为max时,如果监控值超过best(1+threshold),则触发规则;在参数mode为min时,如果监控值低于best(1 - threshold),则触发规则。【best为训练过程中的历史最好值】
'abs':在参数mode为max时,如果监控值超过best + threshold,则触发规则;在参数mode为min时,如果监控值低于best - threshold,则触发规则。【best为训练过程中的历史最好值】