写在前面
实战
- 分析结构:
- 代码实现
- 完善补充1
- 完善补充2
- 使用GPU来训练网络
- 测试模型
写在前面
本系列笔记为pytorch入门学习,所以主要学习使用pytorch框架进行神经网络的实现过程,对于神经网络的基本原理可能不会做过多解释,主要着重于用法。 传送门:PyTorch入门(一)数据集的一些基础操作PyTorch入门(二)从零开始搭建一个神经网络PyTorch入门(三)损失函数与反向传播
实战
那么,现在我们以CIFAR10的网络模型为例子,尝试写一个完整的神经网络
网络架构如图:(这个架构我们在第二篇文章中其实已经写过了,但是这次写的更加完整一些)
分析结构:
有了前面的基础,那么现在我们已经了解了构建网络的步骤了:
- 准备数据: dataset
- 加载数据: dataloader
- 准备模型: 写好网络模型,引入进来
- 设置损失函数:分类问题一般用交叉熵
- 设置优化器:
- 开始训练
- 验证训练进度:每训练一轮就到测试集上去试试效果
- 模型保存: 每一轮的训练结果都保存下来
代码实现
这代码都是严格按照上面说的步骤写的,注释也给的很详细。 这就是完整的神经网络训练过程
import torch
import torchvision
from model import * # 引入我们写好的神经网络
from torch import nn
from torch.utils.data import DataLoader
# 全局取消证书验证
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
# 准备训练数据集
train_data = torchvision.datasets.CIFAR10("../dataset",train=True,transform=torchvision.transforms.ToTensor(),
download=True)
# 准备测试数据集
test_data = torchvision.datasets.CIFAR10("../dataset",train=False,transform=torchvision.transforms.ToTensor(),
download=True)
# 输出数据集的长度
train_data_size = len(train_data)
test_data_siez = len(test_data)
print("训练数据集的长度:{}".format(train_data_size))
print("测试数据集的长度:{}".format(test_data_siez))
# 加载数据集
train_dataloader = DataLoader(train_data,batch_size=64)
test_dataloader = DataLoader(test_data,batch_size=64)
# 创建网络模型
# 这个网络模型是我们提前写好的 写在model.py文件里面 所以from model import *
test = Network()
# 创建损失函数
loss_fn = nn.CrossEntropyLoss()
# 构造优化器
# 这里我们选择的优化器是SGD 随机梯度下降 传入模型参数和学习速率
learning_rate = 0.01
optimizer = torch.optim.SGD(test.parameters(),lr=learning_rate)
# 设置训练网络的一些参数
# 记录训练的次数
total_train_step = 0
# 记录测试的次数
total_test_step = 0
# 记录训练的轮数
epoch = 10
# 开始训练
for i in range(epoch):
print("开始第{}轮训练".format(i+1))
# 遍历训练集
for data in train_dataloader:
imgs,targets = data # 取出图像和target
outputs = test(imgs) # 数据送进网络 拿到输出
loss = loss_fn(outputs,targets) # 计算损失函数
# 优化
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))
# 当一次训练结束之后 我们就让他在测试数据集上验证一下
# 这样就知道模型是否训练好了 是否符合我们的要求了
# 测试步骤
total_test_loss = 0 # 用来记录模型在全部测试数据上的loss之和
with torch.no_grad():
"""
在使用pytorch时,并不是所有的操作都需要进行计算图的生成(计算过程的构建,以便梯度反向传播等操作)。
而对于tensor的计算操作,默认是要进行计算图的构建的,
在这种情况下,可以使用 with torch.no_grad():,强制之后的内容不进行计算图构建。
"""
for data in test_dataloader:
imgs,targets = data
outputs = test(imgs)
loss = loss_fn(outputs,targets)
total_test_loss = total_test_loss + loss
print("测试集上的loss:{}".format(total_test_loss))
# 把每一轮训练得到的结果保存下来
torch.save(test,"result_model_{}.pth".format(i))
print("模型已保存")
这里面引入的神经网络,是我们提前写好的,放在moudel.py文件里面
import torch
from torch import nn
# 搭建神经网络
class Network(nn.Module):
def __init__(self):
super(Network, self).__init__()
self.model1 = nn.Sequential(
nn.Conv2d(3, 32, 5, padding=2),# 卷积层
nn.MaxPool2d(2), # 池化层
nn.Conv2d(32, 32, 5, padding=2),
nn.MaxPool2d(2),
nn.Conv2d(32, 64, 5, padding=2),
nn.MaxPool2d(2),
nn.Flatten(),
nn.Linear(1024, 64),
nn.Linear(64, 10),
)
def forward(self,x):
x = self.model1(x)
return x
if __name__ == '__main__':
# 实例化这个网络
test = Network()
# 测试一下网络 查看输出结果是否符合要求
input = torch.ones((64,3,32,32))
output = test(input)
print(output.shape)
完善补充1
我们在训练的过程中,每一轮都进行了测试,看看训练结果在测试集上的loss表现。不过呢,我们这是一个分类网络,目的是为了识别图像类别,那么其实我们可以把识别率这个指标也展示出来。之所以上面没写,是因为这不是所有网络都需要的步骤,不同的任务我们做出不同的操作,对于这个分类任务,我们可以在验证的时候,给出识别率来,这样更加清晰。 如何得到准确率呢? 需要用到几个函数:
import torch
outputs = torch.tensor([[0.2,0.5],
[0.3,0.4]])
print(outputs.argmax(1)) # 横着看
print(outputs.argmax(0)) # 竖着看
输出结果:tensor([1, 1]) tensor([1, 0])
这个函数argmax的意思就是:找出最大的值的下标,当设置为1的时候,就是横着找出这一排的最大值,输出下标(下标从0开始),设置为0的时候,就是竖着找出最大值,输出下标。 那么我们网络训练出来的,最终得到的一些值,比如十分类问题就是十个值,我们其实就是选出最大的那个,就认为网络识别的图像为这一类。 然后再拿我们运算出来的结果和真实的target做运算,就可以求出分类准确的数量了,所以准确率就出来了。
import torch
outputs = torch.tensor([[0.2,0.5],
[0.3,0.4]])
# print(outputs.argmax(1)) # 横着看
# print(outputs.argmax(0)) # 竖着看
preds = outputs.argmax(1)
targets = torch.tensor([0,1])
print(preds == targets)
print((preds == targets).sum())
输出结果:tensor([False, True]) tensor(1) 所以,我们只需要在我们的代码里简单修改几处,即可显示测试的准确率
# 开始训练
for i in range(epoch):
print("开始第{}轮训练".format(i+1))
# 遍历训练集
for data in train_dataloader:
imgs,targets = data
outputs = test(imgs)
loss = loss_fn(outputs,targets)
# 优化
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))
# 测试步骤
total_test_loss = 0 # 用来记录模型在全部测试数据上的loss之和
total_accuracy = 0 #用来记录准确的数量
with torch.no_grad():
for data in test_dataloader:
imgs,targets = data
outputs = test(imgs)
loss = loss_fn(outputs,targets)
total_test_loss = total_test_loss + loss
accuracy = (outputs.argmax(1) == targets).sum() # 求得准确的数量
total_accuracy = total_accuracy + accuracy # 每次累加
print("测试集上的loss:{}".format(total_test_loss))
print("整体测试集上的准确率为:{}".format(total_accuracy/test_data_size))
# 把每一轮训练得到的结果保存下来
torch.save(test,"result_model_{}.pth".format(i))
print("模型已保存")
运行结果:
保存的模型:
完善补充2
还有两处需要强调一下:
在网络开始训练之前和开始测试之前,你可能看别人的代码会看到两句话
其实这个的作用呢,我们可以看下官网
可以看到,调用这个就是把网络设置为了训练模式,但是我们之前没写这个,我们的网络也没啥问题,原因就是因为,这个模式也就对一些网络层有影响,图上指示的那块。之前我们写的网络没用到这些,所以写这行代码也可,不写也可。eval同理
使用GPU来训练网络
其实使用GPU很简单,只需要改动几行代码。 我们只需要找到三种模块,使用他们的CUDA方法即可:网络模型、数据、损失函数 那我们分别找到这三部分,进行修改 网络模型
# 创建网络模型
test = Network()
if torch.cuda.is_available():
test = test.cuda()
数据 训练集和测试集都可以用,取数据的时候调用
# 遍历训练集
for data in train_dataloader:
imgs,targets = data
if torch.cuda.is_available(): # 如果cuda可用 就调用cuda
imgs = imgs.cuda()
targets = targets.cuda()
outputs = test(imgs)
loss = loss_fn(outputs,targets)
optimizer.zero_grad()
loss.backward()
optimizer.step()
损失函数
# 创建损失函数
loss_fn = nn.CrossEntropyLoss()
if torch.cuda.is_available():
loss_fn = loss_fn.cuda()
除了这种方式,还有另一种方式使用GPU训练: 提前定义好训练的设备:device = torch.device("cuda") 如果你电脑上只有一张显卡的话,那么下面这两种写法都是指定这个显卡device = torch.device("cuda")device = torch.device("cuda:0") 如果有多个显卡,那就指定你想指定的显卡device = torch.device("cuda:1") 然后代码里面,调用To即可
# 创建网络模型
test = Network()
test = test.to(device)
损失函数和数据也是一样的方式,通过这样来调用GPU
测试模型
那么,模型训练好了,接下来我们测试一下效果
其实就是输入一个图片,看一下这个模型能不能完成任务
我们写一个test.py文件
我们找一张狗的图片:路径写好
import torch
import torchvision
from PIL import Image
image_path = "../dataset/dog.png"
image = Image.open(image_path)
print(image)
image = image.convert('RGB') # 确保其颜色通道为RGB
# 设置对图像的处理(调整大小、转换tensor)
transform = torchvision.transforms.Compose([torchvision.transforms.Resize((32,32)),
torchvision.transforms.ToTensor()])
# 对图像处理
image = transform(image)
# batch size
image = torch.reshape(image,(1,3,32,32))
# 加载模型
model = torch.load("result_model_0.pth",map_location=torch.device('cpu'))
print(model)
# 把模型转换为测试类型
model.eval()
with torch.no_grad():
output = model(image)
print(output)
print(output.argmax(1)) # 输出结果值最大的那个下标
运行它,试一下,输出结果:tensor([[-0.3786, 0.3839, 0.2930, 0.3845, -0.3165, 0.4834, -0.3922, -0.2882, 0.0089, -0.3664]])
tensor([5])
可以看到,值最高的是0.4834,这是第5个(从0开始数)
也就是说,网络认为这是第5类。那么我们看下数据集的类别,第五类是dog,说明预测正确了。
当然了,你这里预测错误也是很有可能的,因为我使用的模型是训练了一轮得到的模型,准确率并不高。我们可以使用训练多轮的模型试试,准确率会有所提升。
ps:注意一个问题
假如你运行代码,报错如下
RuntimeError: Input type (torch.FloatTensor) and weight type (torch.cuda.FloatTensor) should be the same or input should be a MKLDNN tensor and weight is a dense tensor
这个多半是因为,你使用的模型是你用GPU训练出来的,但是呢现在测试,你是使用cpu跑的,所以提示你这个
解决办法就是加载模型的时候,添加这么一个参数:map_locatinotallow=torch.device(‘cpu’)
做一个映射
# 加载模型
model = torch.load("result_model_0.pth",map_location=torch.device('cpu'))