Pytorch入门(三) 训练 / 测试模型

上一篇文章中讲解了神经网络模型的编写,一般情况下,我们只需要对现有的网络模型进行修改就可以了,那这篇文章就进入到最重要的部分了,也就是网络模型的训练和测试。
其实对于分类和回归的模型(可以理解为分类就是将回归模型离散化,而回归就是将分类问题连续化,说人话就是回归模型输出了一些具体的数值像坐标这种,而分类就是将回归的数值经过softmax等函数进行离散,就大概是这个意思),是有一套完整的套路的,就是说你完全可以自己写出来一套“模板”,在使用分类或者回归模型时,直接套用就ok了。


训练模型-基本框架

每个人的训练模型都是一样的套路(可能有的人会用一些更高级的语句等等),下面简单介绍一下训练模型的基本框架,后面我们对这些内容进行逐一的细致讲解:

# 设置随机种子,可选
seed(1)
# 开启tensorboard可视化工具,可选
writer = SummaryWriter(LOG_PATH)
# 载入数据,必选!!!
train_dataset = MyData(...)
# 加载数据,必选!!!
train_dataloader = DataLoader(train_dataset, ...)
# 上面的数据集载入和加载,需要将训练集载入和加载一次,验证集载入和加载一次。
# 创建网络模型,必选!!!
module = my_module()
# 初始化损失函数、优化器,必选!!!
loss_fn = LandmarkLoss(...)
optimizer = torch.optim(...)
# 下面开始训练模型,重要!!!
for i in epoch:
	for img, landmark in train_dataloader :
		# 从这里开始,进入for循环,每次for循环,从dataloarder中拿出来的数据,都是batch_size个,
		# 如batch_size=32,那么每次取出的数据就是32*(图片数据+标签数据)
		# 训练模型
		landmark_pred = model(img)
		# 计算损失
        loss = loss_fn(...)
        # 清空梯度
        optimizer.zero_grad()                        
        # 反向传播,计算梯度
        loss.backward()                               
        # 根据梯度更新权重
        optimizer.step()                              
    for data in val_dataloader:
        # 测试模型
		landmark_pred = model(img)
		# 计算损失
        loss = loss_fn(...)

训练模型常用函数

每个人的Pytorch程序,都包括上面基本框架中的东西(可选部分不一定),因为这些都是每个网络训练的基本函数,下面进行一一介绍:

随机种子(可选)

随机种子:是为了能够在训练网络模型时每次运行都使用相同的参数(也就得到相同的结果),这样别人运行你的程序的时候,可以保证能够跟你产生相同的结果,也便于出现问题时,复现问题使用。

# 设置一个随机种子
SEED = 1
# 下面分别对不同模组的随机数进行设定
np.random.seed(SEED)              # 对numpy模组进行随机数设定
random.seed(SEED)                 # 对venv2模组进行随机数设定,其实就是python中的普通模组
torch.manual_seed(SEED)           # 对torch中的CPU部分进行随机数设定
torch.cuda.manual_seed(SEED)      # 对torch中的GPU部分进行随机数设定
# 在运行程序的时候,可能并不清楚到底使用了哪些模组,因此最好就把上面的4句都使用

Tensorboard可视化(可选)

Tensorboard可视化工具,可以将数据直接制作成图片的形式,方便你的分析,在 Demo Task 1 : 人脸5个关键点检测 文章中的图片,就是使用tensorboard工具制作的,方法及其简单,因为我是比较推荐新手使用。

# 加载库
from torch.utils.tensorboard import SummaryWriter
# 创建实例文件夹,在程序根目录的logs文件夹
writer = SummaryWriter("logs")
# 之后你可以在程序的任何位置直接添加下面一行程序,就可以输出你想看的图表信息
# 如下面,我们打印训练的损失:
# writer.add_scalar(图表的名字,y轴,x轴)
writer.add_scalar('Train_Loss', train_loss, epoch)
# 最后记得关闭
writer.close()

程序执行完之后,并不会直接就显示这些图表信息,而是需要你运行一段程序:tensorboard --logdir=logs (其中logs就是你在创建实例文件夹时的路径),之后会控制台会输出一些东西,不需要管,直接打开最后输出的一段网址(一般是http://localhost:6006/),你就会看见了所有的图表信息。

数据集加载和创建网络模型

这两部分其实就对应了之前的两篇文章:数据集加载 和 创建网络模型
注:一般情况下,需要加载训练数据集和验证数据集,但是验证集可选的,可不设置验证。

  • 训练集:训练模型,会使用反向传播对模型的权重(卷积核的内容,全连接层的W和d等等)进行修改,会得到每次训练的损失。
  • 验证集:验证模型的好坏,不对模型进行训练,因此没有反向传播,只计算损失(也可以计算一些准确度等等值),通过比较验证集和训练集的损失值(准确值),对模型的训练参数进行适当的调整(就是吴恩达老师所说的方差与偏差)。

损失函数

损失函数的使用其实也就是那么一行(有点像函数声明的感觉),但是损失函数的具体都在LandmarkLoss函数中,是需要你自己写的,因为处理的问题不同,这个损失函数也不同,然而在 官网上 ,已经提供了部分损失函数,可以直接使用(以交叉熵损失函数为例):

# 加载库
from torch import nn
# 直接使用nn.CrossEntropyLoss(),官方提供的交叉熵损失函数
# LandmarkLoss函数中可以直接使用这句
loss_cross = nn.CrossEntropyLoss()
# 在需要计算损失的时候,直接使用下面的
# 在for循环内部的时候使用,result_cross 就是计算的损失
result_cross = loss_cross(predict, target)

优化器

优化器torch.optim是一个实现了多种优化算法的包,大多数通用的方法都已支持,提供了丰富的接口调用。它能够根据你设定的算法进行更新权重,本文只介绍两个简单的优化算法:

# 加载库
import torch
# 构造一个优化器对象optimizer 
# 使用Adam优化算法,需要指定lr(学习率),weight_decay(权重衰减)
optimizer = optim.Adam(module.parameters(), lr=0.1, weight_decay = 1e-4)
# 再使用一个自定义学习率的算法
# 函数的参数分别是:optimizer优化器对象,milestones学习率下降时所需迭代次数,gamma每次学习练习下降的倍数
# 按上面的lr=0.1,那么使用下面的算法后,在迭代20次之后,学习率下降为0.01,在迭代40次之后,下降为0.001
# 一般这两个算法一起使用,效果会更好一些
scheduler = torch.optim.lr_scheduler.MultiStepLR(optimizer, milestones=[20,40], gamma=0.1)

# 使用上面的算法后,需要在外部的for循环(就是epoch的循环)中使用如下代码
# 就可以实现在迭代到对应次数时,使学习率下降
scheduler.step()

训练模型和计算损失

这里开始进入for循环中,第一个for循环(外部循环)是epoch次数,也就是说,每循环一次都将所有的数据集(训练+验证)跑了一遍。第二个for循环(内部循环)是根据batch_size来决定有多少次的,因为每次从dataloader中取出batch_size个数据,如batch_size=32,那么每次内部for循环都是同时对32个数据(图片+标签)进行处理的。
这里使用的函数上面其实已经提到了,只是在这运行而已。

反向传播和更新权重

这里的三行代码是固定的,我们需要知道每行代码意味着什么就好:

# 清空梯度
# 因为每次计算梯度,都会在上次梯度的基础上进行叠加的
# 因此我们要计算本次梯度,就需要每次计算梯度前对梯度信息进行清空
optimizer.zero_grad()                        
# 反向传播,计算梯度
# 自动计算模型中权重的梯度
loss.backward()                               
# 根据梯度更新权重
# 会使用optimizer优化器中的算法进行权重更新
optimizer.step()

训练过程中的验证

在训练过程中,一般会添加验证集,而验证集我们是不需要计算梯度信息的,所以我们可以使用如下函数,让程序不去关注验证时的梯度:

# 这句话就将这里面的语句不去关注梯度信息
with torch.no_grad():
    for data in val_dataloader:
        # 测试模型
		landmark_pred = model(img)
		# 计算损失
        loss = loss_fn(...)

总结

训练模型的常用模板,就是将上面这些东西叠加起来。我们可以将训练模型的基本框架在浓缩一下:

  1. 加载数据集
  2. 创建网络模型
  3. 初始化损失函数,优化器和相关超参数等
  4. for循环中:训练模型
  5. 计算损失
  6. 清空梯度
  7. 反向传播,计算梯度
  8. 更新权重

测试模型

其实测试模型,与上面的验证集的运行时一样的,都不需要计算梯度信息,只需要一些损失值或者准确度等信息就可以,我们简单的说一下基本框架:

  1. 加载数据集
  2. 加载网络模型
  3. 初始化损失函数和相关超参数等
  4. for循环中:测试模型
  5. 计算损失

加载网络模型

测试模型与训练模型不同的地方在于,测试模型时需要加载现有的模型,而不是创建一个新的模型(既然需要加载模型,那么就需要在训练结束后将模型保存下来),因此需要使用其他的函数来指定:

# 保存模型,在训练模型后
# 使用torch.save函数可以将模型保存下来,这行代码需要在训练模型的最后使用,把模型保存下来
# 参数:module.state_dict()指模型的参数,也可以保存整个模型,但是整个模型数据量太大
# 参数:path模型保存的路径,一般是个pth文件
torch.save(module.state_dict(), path)

# 加载模型,在测试模型
# 参数:path就是模型保存路径,是个pth文件
# 注:由于只是保存的参数文件,所以模型的所有东西都不能动,必须与训练模型一致,否则会报错
# model就是加载的模型
model.load_state_dict(torch.load(path))

整个Pyorch入门的文章就此结束了,这三篇文章仅仅是针对纯新手的入门篇,这其中的很多东西我其实并没有详细的展开说明,因为对于一个小白来说,知道这些就足够自己去完成一个模型的训练和测试了,而对于那些更加深层次的东西,相信各位小白在后续做一些小项目时会自己去挖掘学习的。
由于我也是刚刚接触深度学习Pytorch不久,如果文中有错误,可以评论告知,欢迎指正!

你可能感兴趣的:(深度学习实战,pytorch,深度学习,神经网络)