P17 完整的模型训练套路
前言
对前几章节的回顾,以CIFAR10为数据集模拟一套基础的完整训练模型,总结关键步骤和代码,规范整体流程。
一、准备数据集
1.下载数据集CIFAR10
训练数据集和测试数据集(train=True or False
)
train_data = torchvision.datasets.CIFAR10(root="../data", train=True, transform=torchvision.transforms.ToTensor(),
download=True)
test_data = torchvision.datasets.CIFAR10(root="../data", train=False, transform=torchvision.transforms.ToTensor(),
download=True)
2.获取length
# length
train_data_size = len(train_data)
test_data_size = len(test_data)
print("训练数据集的长度为:{}".format(train_data_size))
print("测试数据集的长度为:{}".format(test_data_size))
输出结果:
3.DataLoader加载数据集
# 利用 DataLoader 加载数据集
train_dataloader = DataLoader(train_data, batch_size=64)
test_dataloader = DataLoader(test_data, batch_size=64)
二、搭建神经网络
1.init & forward
代码如下:
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
self.model1 = Sequential( # seq序列
nn.Conv2d(3, 32, 5, 1, 2),
nn.MaxPool2d(2),
nn.Conv2d(32, 32, 5, 1, 2),
nn.MaxPool2d(2),
nn.Conv2d(32, 64, 5, 1, 2),
nn.MaxPool2d(2),
nn.Flatten(),
nn.Linear(1024, 64),
nn.Linear(64, 10)
)
def forward(self, x):
x = self.model1(x)
return x
2.from model import *`
也可以把神经网络放到一个单独的文件中:
之后再在主文件中引入from model import *
,一定注意model.py和train.py在一个文件夹下
三、测试网络正确性
1.测试样例
测试样例input = torch.ones((64, 3, 32, 32))
if __name__ == '__main__':
tudui = Tudui()
input = torch.ones((64, 3, 32, 32))
output = tudui(input)
print(output.shape)
2.输出结果
可见返回尺寸可以看出有64行数据,每行数据有10个参数分别代表这64个数据对应10个类别的概率。
四、定义损失函数、优化器和参数
1.交叉熵
loss_fn = nn.CrossEntropyLoss()
2.随机梯度下降
learning_rate = 0.01
optimizer = torch.optim.SGD(tudui.parameters(), lr=learning_rate)
3.参数
# 设置训练网络的一些参数
# 记录训练的次数
total_train_step = 0
# 记录测试的次数
total_test_step = 0
# 训练的轮数
epoch = 10
五、开始训练
训练过程中对dataloader
加载的训练数据集进行训练,依次经过神经网络后计算误差,根据误差反向传播获得梯度再进行优化器的优化,查看每次训练误差值
1.代码
for i in range(epoch):
print("------------第 {} 轮训练开始------------".format(i + 1))
# 训练步骤开始
tudui.train()
for data in train_dataloader:
imgs, targets = data
outputs = tudui(imgs)
loss = loss_fn(outputs, targets) # 放到loss_fn中计算与target的误差
# 优化器优化模型
optimizer.zero_grad() # 优化器,梯度清零
loss.backward() # 反向传播得到每一个参数结点的梯度
optimizer.step() # 根据梯度进行优化
total_train_step = total_train_step + 1
if total_train_step % 100 == 0:
print("训练次数:{}, Loss: {}".format(total_train_step, loss.item()))
writer.add_scalar("train_loss", loss.item(), total_train_step)
2.输出结果
六、思考:如何评估模型训练达到预期标准
1.测试
在每次训练完一轮之后进行测试,在测试数据集上跑一遍,以测试数据集测量的损失进行评估,该过程不需要进行调优
with torch.no_grad():
用torch.no_grad
保证没有梯度,也不需要调优
2.一般流程
#测试步骤开始
tudui.eval()
total_test_loss = 0 # 累计求和loss计算得出整个数据集的误差总和
with torch.no_grad(): # 不需要进行调优,保证没有梯度
for data in test_dataloader:
imgs, targets = data
outputs = tudui(imgs)
loss = loss_fn(outputs, targets)
total_test_loss = total_test_loss + loss.item()
print("整体数据集上的loss:{}".format(total_test_loss))
注意输出是整体数据集的loss,用
total_test_loss
记录
3.补充正确率
正确率的计算
运用到项目中:
accuracy = (outputs.argmax(1) == targets).sum() # 正确率,这是分类问题中,特有的一种,评价指标,语义分割之类的,不一定非要有这个东西
total_accuracy = total_accuracy + accuracy
通过argmax(1)
计算出output
的横向每一行的最大值,并与target进行比较,比较结果true or false进行求和即为正确的个数,并记录,最后通过total_accuracy / test_data_size
计算正确率
4.运行结果
七、结合Tensorboard
分别写到对应计算出loss的地方
# 添加tensorboard
writer = SummaryWriter("../logs_train")
writer.add_scalar("train_loss", loss.item(), total_train_step)
writer.add_scalar("test_loss", total_test_loss, total_test_step)
八、保存
# 保存,保存每一轮训练的结果
torch.save(tudui, "tudui_{}.pth".format(i)) # 保存方式一,其实后缀都可以自己取,习惯用 .pth。
print("模型已保存")
九、运行结果
1.控制台
2.Tensorboard(部分):
十、完整代码
import torchvision
from torch import nn
from torch.nn import Sequential
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
from model import *
# 准备数据集,训练数据集和测试数据集,train=True or False
train_data = torchvision.datasets.CIFAR10(root="../data", train=True, transform=torchvision.transforms.ToTensor(),
download=True)
test_data = torchvision.datasets.CIFAR10(root="../data", train=False, transform=torchvision.transforms.ToTensor(),
download=True)
# length
train_data_size = len(train_data)
test_data_size = len(test_data)
print("训练数据集的长度为:{}".format(train_data_size)) # 格式化字符串
print("测试数据集的长度为:{}".format(test_data_size))
# 利用 DataLoader 加载数据集
train_dataloader = DataLoader(train_data, batch_size=64)
test_dataloader = DataLoader(test_data, batch_size=64)
# 搭建神经网络
'''
class Tudui(nn.Module):
def __init__(self):
super(Tudui, self).__init__()
self.model1 = Sequential( # seq序列
nn.Conv2d(3, 32, 5, 1, 2),
nn.MaxPool2d(2),
nn.Conv2d(32, 32, 5, 1, 2),
nn.MaxPool2d(2),
nn.Conv2d(32, 64, 5, 1, 2),
nn.MaxPool2d(2),
nn.Flatten(),
nn.Linear(1024, 64),
nn.Linear(64, 10)
)
def forward(self, x):
x = self.model1(x)
return x
'''
# 搭建好网络后,实例化
tudui = Tudui()
# 创建损失函数
loss_fn = nn.CrossEntropyLoss() # 分类问题,交叉熵
# 定义优化器
learning_rate = 0.01
optimizer = torch.optim.SGD(tudui.parameters(), lr=learning_rate) # 选择随机梯度下降SGD
# 设置训练网络的一些参数
# 记录训练的次数
total_train_step = 0
# 记录测试的次数
total_test_step = 0
# 训练的轮数
epoch = 10
# 添加tensorboard
writer = SummaryWriter("logs_train")
for i in range(epoch):
print("------------第 {} 轮训练开始------------".format(i + 1))
# 训练步骤开始
tudui.train()
for data in train_dataloader:
imgs, targets = data
outputs = tudui(imgs)
loss = loss_fn(outputs, targets) # 放到loss_fn中计算与target的误差
# 优化器优化模型
optimizer.zero_grad() # 优化器,梯度清零
loss.backward() # 反向传播得到每一个参数结点的梯度
optimizer.step() # 根据梯度进行优化
total_train_step = total_train_step + 1
if total_train_step % 100 == 0:
print("训练次数:{}, Loss: {}".format(total_train_step, loss.item()))
writer.add_scalar("train_loss", loss.item(), total_train_step)
#测试步骤开始
tudui.eval()
total_test_loss = 0 # 累计求和loss计算得出整个数据集的误差总和
total_accuracy = 0 # 记录整体分类正确的个数
with torch.no_grad(): # 不需要进行调优,保证没有梯度
for data in test_dataloader:
imgs, targets = data
outputs = tudui(imgs)
loss = loss_fn(outputs, targets)
total_test_loss = total_test_loss + loss.item()
accuracy = (outputs.argmax(1) == targets).sum() # 正确率,这是分类问题中,特有的一种,评价指标,语义分割之类的,不一定非要有这个东西
total_accuracy = total_accuracy + accuracy
print("整体数据集上的loss:{}".format(total_test_loss))
print("整体测试集上的正确率: {}".format(total_accuracy / test_data_size)) # 即便是输出了上一行的 loss,也不能很好的表现出效果。
# 在分类问题上比较特有,通常使用正确率来表示优劣。因为其他问题,可以可视化地显示在tensorbo中。
writer.add_scalar("test_loss", total_test_loss, total_test_step)
writer.add_scalar("test_accuracy", total_accuracy / test_data_size, total_test_step)
total_test_step = total_test_step + 1
# 保存,保存每一轮训练的结果
torch.save(tudui, "tudui_{}.pth".format(i)) # 保存方式一,其实后缀都可以自己取,习惯用 .pth。
print("模型已保存")
writer.close()