赞
踩
课程资源:
7、模型验证与训练过程可视化【小学生都会的Pytorch】【提供源码】_哔哩哔哩_bilibili
推荐与上一节笔记搭配食用~:
pytorch进阶学习(五):神经网络迁移学习应用的保姆级详细介绍,如何将训练好的模型替换成自己所需模型_好喜欢吃红柚子的博客-CSDN博客
https://download.pytorch.org/models/resnet34-333f7ec4.pth
目录
2. 使用迁移学习方法修改resnet34神经网络框架并加载预训练权重
生成训练集和测试集,分别保存在tes.txt、train.txt和eval.txt文件中;相当于模型的输入。后面做数据加载器dataload的时候从里面读数据。
- '''
- 生成训练集和测试集,保存在txt文件中
- '''
- ##相当于模型的输入。后面做数据加载器dataload的时候从里面读他的数据
- import os
- import random#打乱数据用的
-
- def CreateTrainingSet():
- # 百分之80用来当训练集
- train_ratio = 0.8
-
- # 用来当测试集
- test_ratio = 1-train_ratio
-
- rootdata = r"data"#数据的根目录
-
- train_list, test_list = [],[]#读取里面每一类的类别
- data_list = []
-
- #生产train.txt和test.txt
- class_flag = -1
- for a,b,c in os.walk(rootdata):
- print(a)
- for i in range(len(c)):
- data_list.append(os.path.join(a,c[i]))
-
- for i in range(0,int(len(c)*train_ratio)):
- train_data = os.path.join(a, c[i])+'\t'+str(class_flag)+'\n'
- train_list.append(train_data)
-
- for i in range(int(len(c) * train_ratio),len(c)):
- test_data = os.path.join(a, c[i]) + '\t' + str(class_flag)+'\n'
- test_list.append(test_data)
-
- class_flag += 1
-
- print(train_list)
- random.shuffle(train_list)#打乱次序
- random.shuffle(test_list)
-
- with open('train.txt','w',encoding='UTF-8') as f:
- for train_img in train_list:
- f.write(str(train_img))
-
- with open('test.txt','w',encoding='UTF-8') as f:
- for test_img in test_list:
- f.write(test_img)
-
- def CreateEvalData():
- data_list = []
- test_root = r"testdata"
- for a, b, c in os.walk(test_root):
- for i in range(len(c)):
- data_list.append(os.path.join(a, c[i]))
- print(data_list)
- with open('eval.txt', 'w', encoding='UTF-8') as f:
- for test_img in data_list:
- f.write(test_img + '\t' + "0" + '\n')
-
- if __name__ == "__main__":
- CreateEvalData()
- CreateTrainingSet()
可以看到产生了3个TXT文件。
eval.txt文件中每一行由图片路径和0组成,在图片后面补一个0是为了和train.txt和test.txt前面是路径后面是标签的格式统一起来,后面方便统一提取TXT中的信息。
在对应网址中下载resnet34预训练参数,修改文件为resnet34_pretrain.pth,保存在项目文件中。
具体细节可见笔记:pytorch进阶学习(五):神经网络迁移学习应用的保姆级详细介绍,如何将训练好的模型替换成自己所需模型_好喜欢吃红柚子的博客-CSDN博客
设置epoch=50,在训练过程中:
- # 一共训练50次
- epochs = 50
- best = 0.0
- for t in range(epochs):
- print(f"Epoch {t + 1}\n-------------------------------")
- train_loss = train(train_dataloader, model, loss_fn, optimizer)
- accuracy, avg_loss = test(test_dataloader, model)
- # 记录训练过程值,写入mobilenet_36_traindata.txt文件进行保存
- write_result("mobilenet_36_traindata.txt", t+1, train_loss, avg_loss, accuracy)
-
- #10个 epoch保存一次resnet_epoch_xx_acc_xx.pth文件
- if (t+1) % 10 == 0:
- torch.save(model.state_dict(), "resnet_epoch_"+str(t+1)+"_acc_"+str(accuracy)+".pth")
-
- # 如果一个epoch的acc比上一个要高,就保存一个BEST_resnet_epoch_xx_acc_xx.pth文件,记录当前最高的准确率
- if float(accuracy) > best:
- best = float(accuracy)
- torch.save(model.state_dict(), "BEST_resnet_epoch_" + str(t+1) + "_acc_" + str(accuracy) + ".pth")
'运行
在train方法中我们会返回一批batchsize数据的平均loss。
- def train(dataloader, model, loss_fn, optimizer):
- size = len(dataloader.dataset)
- avg_total = 0.0
- # 从数据加载器中读取batch(一次读取多少张,即批次数),X(图片数据),y(图片真实标签)。
- for batch, (X, y) in enumerate(dataloader):
- # 将数据存到显卡
- X, y = X.cuda(), y.cuda()
- # 得到预测的结果pred
- pred = model(X)
- # 计算预测的误差
- loss = loss_fn(pred, y)
- avg_total = avg_total+loss.item()
-
- # 反向传播,更新模型参数
- optimizer.zero_grad()
- loss.backward()
- optimizer.step()
-
- # 每训练10次,输出一次当前信息
- if batch % 10 == 0:
- loss, current = loss.item(), batch * len(X)
- #这行代码的作用是在训练模型时输出当前的loss值和训练进度。
- #其中,loss值会被格式化为浮点数,current表示当前已经训练的样本数,size表示总的样本数。
- #输出的格式为"loss:(loss值][[current/{size]”。其中,“>“表示右对齐,数字表示输出的最小宽度。
- print(f"loss: {loss:>5f} [{current:>5d}/{size:>5d}]")
-
- # 定义平均损失
- avg_loss = f"{(avg_total % batch_size):>5f}"
- return avg_loss
test函数返回测试集数据的准确率和损失值
- def test(dataloader, model):
- size = len(dataloader.dataset)
- # 将模型转为验证模式
- model.eval()
- # 初始化test_loss 和 correct, 用来统计每次的误差
- test_loss, correct = 0, 0
- # 测试时模型参数不用更新,所以no_gard()
- # 非训练, 推理期用到
- with torch.no_grad():
- # 加载数据加载器,得到里面的X(图片数据)和y(真实标签)
- for X, y in dataloader:
- # 将数据转到GPU
- X, y = X.cuda(), y.cuda()
- # 将图片传入到模型当中就,得到预测的值pred
- pred = model(X)
- # 计算预测值pred和真实值y的差距
- test_loss += loss_fn(pred, y).item()
- # 统计预测正确的个数
- correct += (pred.argmax(1) == y).type(torch.float).sum().item()
- test_loss /= size
- correct /= size
- accuracy = f"{(100*correct):>0.1f}"
- avg_loss = f"{test_loss:>8f}"
- print(f"correct = {correct}, Test Error: \n Accuracy: {accuracy}%, Avg loss: {avg_loss} \n")
- # 增加数据写入功能
- return accuracy, avg_loss
'运行
- '''
- 纪录训练信息,包括:
- 1. train loss
- 2. test loss
- 3. test accuracy
- '''
- import torch
- from torch import nn
- from torch.utils.data import DataLoader
- from torchvision.models import resnet34
- from utils import LoadData, write_result
-
- def train(dataloader, model, loss_fn, optimizer):
- size = len(dataloader.dataset)
- avg_total = 0.0
- # 从数据加载器中读取batch(一次读取多少张,即批次数),X(图片数据),y(图片真实标签)。
- for batch, (X, y) in enumerate(dataloader):
- # 将数据存到显卡
- X, y = X.cuda(), y.cuda()
- # 得到预测的结果pred
- pred = model(X)
- # 计算预测的误差
- loss = loss_fn(pred, y)
- avg_total = avg_total+loss.item()
-
- # 反向传播,更新模型参数
- optimizer.zero_grad()
- loss.backward()
- optimizer.step()
-
- # 每训练10次,输出一次当前信息
- if batch % 10 == 0:
- loss, current = loss.item(), batch * len(X)
- print(f"loss: {loss:>5f} [{current:>5d}/{size:>5d}]")
-
- # 定义平均损失
- avg_loss = f"{(avg_total % batch_size):>5f}"
- return avg_loss
-
- def test(dataloader, model):
- size = len(dataloader.dataset)
- # 将模型转为验证模式
- model.eval()
- # 初始化test_loss 和 correct, 用来统计每次的误差
- test_loss, correct = 0, 0
- # 测试时模型参数不用更新,所以no_gard()
- # 非训练, 推理期用到
- with torch.no_grad():
- # 加载数据加载器,得到里面的X(图片数据)和y(真实标签)
- for X, y in dataloader:
- # 将数据转到GPU
- X, y = X.cuda(), y.cuda()
- # 将图片传入到模型当中就,得到预测的值pred
- pred = model(X)
- # 计算预测值pred和真实值y的差距
- test_loss += loss_fn(pred, y).item()
- # 统计预测正确的个数
- correct += (pred.argmax(1) == y).type(torch.float).sum().item()
- test_loss /= size
- correct /= size
- accuracy = f"{(100*correct):>0.1f}"
- avg_loss = f"{test_loss:>8f}"
- print(f"correct = {correct}, Test Error: \n Accuracy: {accuracy}%, Avg loss: {avg_loss} \n")
- # 增加数据写入功能
- return accuracy, avg_loss
-
- if __name__ == '__main__':
- batch_size = 32
-
- # # 给训练集和测试集分别创建一个数据集加载器
- train_data = LoadData("train.txt", True)
- valid_data = LoadData("test.txt", False)
-
- train_dataloader = DataLoader(dataset=train_data, num_workers=4, pin_memory=True, batch_size=batch_size, shuffle=True)
- test_dataloader = DataLoader(dataset=valid_data, num_workers=4, pin_memory=True, batch_size=batch_size)
-
- # 如果显卡可用,则用显卡进行训练
- device = "cuda" if torch.cuda.is_available() else "cpu"
- print(f"Using {device} device")
-
- '''
- 修改ResNet34模型的最后一层
- '''
- pretrain_model = resnet34(pretrained=False)
- num_ftrs = pretrain_model.fc.in_features # 获取全连接层的输入
- pretrain_model.fc = nn.Linear(num_ftrs, 5) # 全连接层改为不同的输出
-
- # 预先训练好的参数, 'https://download.pytorch.org/models/resnet34-333f7ec4.pth'
- pretrained_dict = torch.load('./resnet34_pretrain.pth')
-
- # # 弹出fc层的参数
- pretrained_dict.pop('fc.weight')
- pretrained_dict.pop('fc.bias')
-
- # # 自己的模型参数变量,在开始时里面参数处于初始状态,所以很多0和1
- model_dict = pretrain_model.state_dict()
-
- # # 去除一些不需要的参数
- pretrained_dict = {k: v for k, v in pretrained_dict.items() if k in model_dict}
-
- # # 模型参数列表进行参数更新,加载参数
- model_dict.update(pretrained_dict)
-
- # 改进过的预训练模型结构,加载刚刚的模型参数列表
- pretrain_model.load_state_dict(model_dict)
-
- '''
- 冻结部分层
- '''
- # 将满足条件的参数的 requires_grad 属性设置为False
- for name, value in pretrain_model.named_parameters():
- if (name != 'fc.weight') and (name != 'fc.bias'):
- value.requires_grad = False
- #
- # filter 函数将模型中属性 requires_grad = True 的参数选出来
- params_conv = filter(lambda p: p.requires_grad, pretrain_model.parameters()) # 要更新的参数在parms_conv当中
-
- model = pretrain_model.to(device)
-
- # 定义损失函数,计算相差多少,交叉熵,
- loss_fn = nn.CrossEntropyLoss()
-
- ''' 控制优化器只更新需要更新的层 '''
- optimizer = torch.optim.SGD(params_conv, lr=1e-3) # 初始学习率
- #
- # 一共训练50次
- epochs = 50
- best = 0.0
- for t in range(epochs):
- print(f"Epoch {t + 1}\n-------------------------------")
- train_loss = train(train_dataloader, model, loss_fn, optimizer)
- accuracy, avg_loss = test(test_dataloader, model)
- # 记录训练过程值,写入mobilenet_36_traindata.txt文件进行保存
- write_result("mobilenet_36_traindata.txt", t+1, train_loss, avg_loss, accuracy)
-
- #10个 epoch保存一次resnet_epoch_xx_acc_xx.pth文件
- if (t+1) % 10 == 0:
- torch.save(model.state_dict(), "resnet_epoch_"+str(t+1)+"_acc_"+str(accuracy)+".pth")
-
- # 如果一个epoch的acc比上一个要高,就保存一个BEST_resnet_epoch_xx_acc_xx.pth文件,记录当前最高的准确率
- if float(accuracy) > best:
- best = float(accuracy)
- torch.save(model.state_dict(), "BEST_resnet_epoch_" + str(t+1) + "_acc_" + str(accuracy) + ".pth")
-
- print("Train PyTorch Model Success!")
'运行
使用我们训练好的神经网络,对验证集中的图片进行数据验证。
定义我们修改过fc层输出的resnet34网络。
- '''
- 1. 导入模型结构
- '''
- # 设置自己的模型
- model = resnet34(pretrained=False)
- num_ftrs = model.fc.in_features # 获取全连接层的输入
- model.fc = nn.Linear(num_ftrs, 5) # 全连接层改为不同的输出
- device = "cuda" if torch.cuda.is_available() else "cpu"
- print(f"Using {device} device")
使用训练的准确率最高的一组参数的权重文件,我的名为"./BEST_resnet_epoch_50_acc_87.1.pth",把参数加载到神经网络中,然后把模型转换到cuda中;
- '''
- 2. 加载模型参数
- '''
- # 调用最好的acc的一组参数权重
- model_loc = "./BEST_resnet_epoch_50_acc_87.1.pth"
- model_dict = torch.load(model_loc)
- model.load_state_dict(model_dict)
- # 把模型转换到cuda中
- model = model.to(device)
使用LoadData和DataLoader加载验证集中的图片。
- '''
- 3. 加载图片
- '''
- # 加载验证集中的图片
- valid_data = LoadData("eval.txt", train_flag=False)
- test_dataloader = DataLoader(dataset=valid_data, num_workers=2, pin_memory=True, batch_size=1)
把对验证数据集中每一张图片的预测标签和概率都存储在label_list,likelihood_list两个列表里
- def eval(dataloader, model):
- label_list = []
- likelihood_list = []
- model.eval()
- with torch.no_grad():
- # 加载数据加载器,得到里面的X(图片数据)和y(真实标签)
- for X, y in dataloader:
- # 将数据转到GPU
- X = X.cuda()
- # 将图片传入到模型当中就,得到预测的值pred
- pred = model(X)
- # 获取可能性最大的标签
- label = torch.softmax(pred,1).cpu().numpy().argmax()
- label_list.append(label)
- # 获取可能性最大的值(即概率)
- likelihood = torch.softmax(pred,1).cpu().numpy().max()
- likelihood_list.append(likelihood)
- return label_list,likelihood_list
把标签列表里的标签号转换为对应的类别文字,使用pandas进行列表的绘制,输出每一张图片的类别和概率,同时还可以把该表格保存在csv文件中。
- '''
- 4. 获取结果
- '''
- #
- label_list, likelihood_list = eval(test_dataloader, model)
- label_names = ["daisy", "dandelion","rose","sunflower","tulip"]
-
- result_names = [label_names[i] for i in label_list]
-
- list = [result_names, likelihood_list]
- df = pd.DataFrame(data=list)
- df2 = pd.DataFrame(df.values.T, columns=["label", "likelihood"])
- print(df2)
- # 使用pandas把预测结果保存
- df2.to_csv('testdata.csv', encoding='gbk')
'运行
pycharm控制台输出的结果:
保存在testdata.csv文件中的预测表格:
- '''
- 1.单幅图片验证
- 2.多幅图片验证
- '''
- import torch
- from torch import nn
- from torch.utils.data import DataLoader
- from torchvision.models import resnet34
- from utils import LoadData, write_result
- import pandas as pd
-
-
- def eval(dataloader, model):
- label_list = []
- likelihood_list = []
- model.eval()
- with torch.no_grad():
- # 加载数据加载器,得到里面的X(图片数据)和y(真实标签)
- for X, y in dataloader:
- # 将数据转到GPU
- X = X.cuda()
- # 将图片传入到模型当中就,得到预测的值pred
- pred = model(X)
-
-
- # 获取可能性最大的标签
- label = torch.softmax(pred,1).cpu().numpy().argmax()
- label_list.append(label)
- # 获取可能性最大的值(即概率)
- likelihood = torch.softmax(pred,1).cpu().numpy().max()
- likelihood_list.append(likelihood)
- return label_list,likelihood_list
-
-
- if __name__ == "__main__":
-
- '''
- 1. 导入模型结构
- '''
- # 设置自己的模型
- model = resnet34(pretrained=False)
- num_ftrs = model.fc.in_features # 获取全连接层的输入
- model.fc = nn.Linear(num_ftrs, 5) # 全连接层改为不同的输出
- device = "cuda" if torch.cuda.is_available() else "cpu"
- print(f"Using {device} device")
-
- '''
- 2. 加载模型参数
- '''
- # 调用最好的acc的一组参数权重
- model_loc = "./BEST_resnet_epoch_50_acc_87.1.pth"
- model_dict = torch.load(model_loc)
- model.load_state_dict(model_dict)
- # 把模型转换到cuda中
- model = model.to(device)
-
- '''
- 3. 加载图片
- '''
- # 加载验证集中的图片
- valid_data = LoadData("eval.txt", train_flag=False)
- test_dataloader = DataLoader(dataset=valid_data, num_workers=2, pin_memory=True, batch_size=1)
-
-
- '''
- 4. 获取结果
- '''
- #
- label_list, likelihood_list = eval(test_dataloader, model)
- label_names = ["daisy", "dandelion","rose","sunflower","tulip"]
-
- result_names = [label_names[i] for i in label_list]
-
- list = [result_names, likelihood_list]
- df = pd.DataFrame(data=list)
- df2 = pd.DataFrame(df.values.T, columns=["label", "likelihood"])
- print(df2)
- # 使用pandas把预测结果保存
- df2.to_csv('testdata.csv', encoding='gbk')
使用我们在前面训练过程中保存的mobilenet_36_traindata.txt文件,该文件中保存着训练过程中每一个epoch的准确率acc和损失函数TrainLoss,TestLoss和TestAccuracy
- import matplotlib.pyplot as plt
- import numpy as np
-
- # 画图表
-
- def getdata(data_loc):
- epoch_list = []
- train_loss_list = []
- test_loss_list = []
- acc_list = []
- with open(data_loc, "r") as f:
- for i in f.readlines():
- data_i = i.split("\t")
- epoch_i = float(data_i[0][7:])
- train_loss_i = float(data_i[1][10:])
- test_loss_i = float(data_i[2][9:])
- acc_i = float(data_i[3][13:])
- epoch_list.append(epoch_i)
- train_loss_list.append(train_loss_i)
- test_loss_list.append(test_loss_i)
- acc_list.append(acc_i)
- print(len(epoch_list), len(train_loss_list))
- return epoch_list, train_loss_list, test_loss_list, acc_list
-
-
-
- if __name__ == "__main__":
- data_loc = r"mobilenet_36_traindata.txt"
- epoch_list, train_loss_list, test_loss_list, acc_list = getdata(data_loc)
-
- # #train_loss
- # plt.plot(epoch_list, train_loss_list)
- #
- # plt.legend(["model"])
- # plt.xticks(np.arange(0, 50, 5)) # 横坐标的值和步长
- # plt.yticks(np.arange(0, 100, 10)) # 横坐标的值和步长
- # plt.xlabel("Epoch")
- # plt.ylabel("train_loss")
- # plt.title("Train Loss")
- # plt.show()
-
- # 准确率曲线
- # plt.plot(epoch_list, acc_list)
- #
- # plt.legend(["model"])
- # plt.xticks(np.arange(0, 50, 5)) # 横坐标的值和步长
- # plt.yticks(np.arange(0, 100, 10)) # 横坐标的值和步长
- # plt.xlabel("Epoch")
- # plt.ylabel("Accurancy(100%)")
- # plt.title("Model Accuracy")
- # plt.show()
-
- # test_loss
- plt.plot(epoch_list, test_loss_list)
-
- plt.legend(["model"])
- plt.xticks(np.arange(0, 50, 5)) # 横坐标的值和步长
- plt.yticks(np.arange(0, 1, 10)) # 横坐标的值和步长
- plt.xlabel("Epoch")
- plt.ylabel("test_loss(100%)")
- plt.title("Test Loss")
- plt.show()
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。