本文是在卷积神经网络(LeNet)详解_欲游山河十万里的博客-CSDN博客博文的基础之上,对深度学习过程遇到的一些常见的问题进行总结。本文所总结的问题,不仅仅适用于这个简单的模型,更适用在其他的多个应用场景之下。
一、训练过程中模型的保存
我们在训练模型的时候经常出现各种问题导致训练中断,比方说断电,或者关机之类的导致电脑系统关闭,从而将模型训练中断,那么如何在模型中断后,能够保留之前的训练结果不被丢失,同时又可以继续之前的断点处继续训练?
首先在代码离需要保存模型,比方说我们模型设置训练5000轮,那么我们可以选择每100轮保存一次模型,这样的话,在训练的过程中就能保存下100,200,300.。。。等轮数时候的模型,那么当模型训练到400轮的时候突然训练中断,那么我们就可以通过加载400轮的参数来进行继续训练,其实这个过程就类似在预训练模型的基础上进行训练。下面简单粗暴上代码:
checkpoint = {
'model': net.state_dict(),
'optimizer': optimizer.state_dict(),
"epoch": epoch,
"train_loss":train_loss,
"test_loss":test_loss,
"train_acc":train_acc,
"test_acc":test_acc,
}
torch.save(checkpoint, checkpoint_path)
假如此时模型训练中断了,我们得在代码里设置一个参数,这个参数用来查找确定当前路径下是否有已存在得模型。
if os.path.exists(checkpoint_path):
checkpoint=torch.load(checkpoint_path)
net.load_state_dict(checkpoint['model'])
optimizer.load_state_dict(checkpoint['optimizer'])
start_epoch = checkpoint['epoch']
train_loss = checkpoint['train_loss']
train_acc = checkpoint['train_acc']
test_loss = checkpoint['test_loss']
test_acc = checkpoint['test_acc']
test_loss.pop()
test_acc.pop()
train_loss.pop()
train_acc.pop()
print('加载 epoch {} 成功!'.format(start_epoch))
#start_epoch=start_epoch+1#前面的那轮是保存成功的,所以从下一轮开始.比如保存了10轮,那么加1从第11轮开始训练
start_train_and_test(start_epoch, epochs,train_loss=train_loss,train_acc=train_acc,test_loss=test_loss,test_acc=test_acc)
else:
print('没有保存的模型,从头开始训练!')
start_epoch=0
start_train_and_test(start_epoch,epochs,train_loss=train_loss,train_acc=train_acc,test_loss=test_loss,test_acc=test_acc)
完整的范例如下所示:
#对模型的训练过程进行代码注释
import torch
import torchvision
import torch.nn as nn
from model import LeNet, LeNetDemo1
import torch.optim as optim
import torchvision.transforms as transforms
import matplotlib.pyplot as plt
import numpy as np
import time
import os
# Compose将多个步骤组合在一起
transform = transforms.Compose([
transforms.ToTensor(),
transforms.Resize((28,28)),
transforms.Normalize((0.1307), (0.3081))
])
# 导入60000张训练图片
train_set = torchvision.datasets.MNIST(root='./data', # 数据集存放目录
train=True, # 表示是数据集中的训练集
download=True, # 第一次运行时为True,下载数据集,下载完成后改为False
transform=transform) # 预处理过程
# 加载训练集,实际过程需要分批次(batch)训练
train_loader = torch.utils.data.DataLoader(train_set, # 导入的训练集
batch_size=100, # 每批训练的样本数
shuffle=False, # 是否打乱训练集
num_workers=0) # 使用线程数,在windows下设置为0
# 10000张验证图片
# 第一次使用时要将download设置为True才会自动去下载数据集
val_set = torchvision.datasets.MNIST(root='./data', train=False,
download=True, transform=transform)
val_loader = torch.utils.data.DataLoader(val_set, batch_size=1000,
shuffle=False, num_workers=0)
val_data_iter = iter(val_loader)
val_image, val_label = next(val_data_iter)
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(device)
net = LeNetDemo1()#导入模型网络
net.to(device) # 将网络分配到指定的device中
loss_function = nn.CrossEntropyLoss()#定义损失函数
optimizer = optim.Adam(net.parameters(), lr=0.001)#定义优化器,nets.parameters()是指网络中的参数,lr是学习率
checkpoint_path = './checkpoint/saved_lent.pth' # 保存的模型
def start_train_and_test(start_epoch,epochs,train_loss=[],test_acc=[],train_acc=[],test_loss=[]):#开始模型的训练
for epoch in range(start_epoch,epochs):#训练的轮数
net.train()#训练模式
running_loss = 0.0#训练集的损失
time_start = time.perf_counter()#计时开始
for step, data in enumerate(train_loader, start=0):#step是训练集的批次,data是训练集的数据
inputs, labels = data#inputs是训练集的图片,labels是训练集的标签
#print(f"labels.size(0)的值是{labels.size(0)}")
#print(f"step的值是{step}")
optimizer.zero_grad()#梯度清零
outputs = net(inputs.to(device)) # 将inputs分配到指定的device中
loss = loss_function(outputs, labels.to(device)) # 将labels分配到指定的device中
loss.backward()#反向传播
optimizer.step()#更新参数
running_loss += loss.item()#训练集的损失
y_hat= net(inputs.to(device))
train_accuary=(y_hat.argmax(dim=1) == labels.to(device)).sum().item()/labels.size(0)#训练集的准确率
if step % 100 == 99:
with torch.no_grad():#不计算梯度
net.eval()#验证模式
outputs = net(val_image.to(device)) # 将test_image分配到指定的device中
val_loss = loss_function(outputs, val_label.to(device)) # 将test_label分配到指定的device中
predict_y = torch.max(outputs, dim=1)[1]#取出最大值的索引
accuracy = (predict_y == val_label.to(device)).sum().item() / val_label.size(0) # 将test_label分配到指定的device中
print('[%d, %5d] train_loss: %.3f train_acc:%.3f test_accuracy: %.3f' %
(epoch, step + 1, running_loss/100,train_accuary, accuracy))
if step+1==600:
train_loss.append(running_loss/100)
test_acc.append(accuracy)
train_acc.append(train_accuary)#训练过程中的准确率
test_loss.append(val_loss.item())#测试过程中的损失
if epoch % 1 == 0 and step + 1 == 600: # 实现了每1轮保存一次模型
if not os.path.isdir('./checkpoint'):
os.makedirs("./checkpoint")
checkpoint = {
'model': net.state_dict(),
'optimizer': optimizer.state_dict(),
"epoch": epoch,
"train_loss":train_loss,
"test_loss":test_loss,
"train_acc":train_acc,
"test_acc":test_acc,
}
torch.save(checkpoint, checkpoint_path)
print('%f s' % (time.perf_counter() - time_start))
running_loss = 0.0
print('Finished Training')
print(len(train_loss),len(test_acc),len(train_acc))
save_path = f"./lennet+{epochs-1}+train_acc={train_acc[-1]}+test_acc={test_acc[-1]}.pt"
torch.save(net.state_dict(), save_path)
print(len(train_loss),len(test_acc),len(train_acc))
#绘制图形
index=[i for i in range(1,epochs+1)]
print(train_loss)
plt.plot(index,train_loss,'g',label="train_loss")
plt.plot(index,train_acc,'b',label="train_acc")
plt.plot(index,test_acc,'m',label="test_acc")
plt.plot(index,test_loss,'r',label="test_loss")
plt.xlabel("epoch")
plt.legend()
plt.pause(0.01) # 暂停0.01秒
def main():
# 画图用的变量
train_loss = [] # 训练集的损失集合
train_acc = [] # 训练集的准确率集合
test_acc = [] # 测试集的准确率集合
test_loss = [] # 测试集的损失集合
epochs = 3
start_epoch = -1
# 如果有保存的模型,则加载模型,并在其基础上继续训练
if os.path.exists(checkpoint_path):
checkpoint=torch.load(checkpoint_path)
net.load_state_dict(checkpoint['model'])
optimizer.load_state_dict(checkpoint['optimizer'])
start_epoch = checkpoint['epoch']
train_loss = checkpoint['train_loss']
train_acc = checkpoint['train_acc']
test_loss = checkpoint['test_loss']
test_acc = checkpoint['test_acc']
test_loss.pop()
test_acc.pop()
train_loss.pop()
train_acc.pop()
print('加载 epoch {} 成功!'.format(start_epoch))
#start_epoch=start_epoch+1#前面的那轮是保存成功的,所以从下一轮开始.比如保存了10轮,那么加1从第11轮开始训练
start_train_and_test(start_epoch, epochs,train_loss=train_loss,train_acc=train_acc,test_loss=test_loss,test_acc=test_acc)
else:
print('没有保存的模型,从头开始训练!')
start_epoch=0
start_train_and_test(start_epoch,epochs,train_loss=train_loss,train_acc=train_acc,test_loss=test_loss,test_acc=test_acc)
if __name__ == '__main__':
main()
写在最后
我觉得参考文献的第一个大佬的代码格式是非常规整的,我认为应该把我的代码改成这种规整的代码,这样才能够更好,更方便的进行代码的维护操作。
参考文献
Pytorch模型保存与加载,并在加载的模型基础上继续训练 - 简书
pytorch训练中断后,如何在之前的断点处继续训练_模型训练中途中断可以继续吗_程序小K的博客-CSDN博客
机器学习算法在训练过程中保存参数_Will_Ye的博客-CSDN博客