赞
踩
以 CIFAR10 数据集为例,分类问题(10分类)
- import torch
- from torch import nn
-
- # 搭建神经网络
- class Overwatch(nn.Module):
- def __init__(self):
- super(Overwatch, self).__init__()
- self.model = nn.Sequential(
- 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(64*4*4, 64),
- nn.Linear(64, 10),
- )
-
- def forward(self, x):
- x = self.model(x)
- return x
-
- if __name__ == '__main__': # 输入main即可跳出完整代码
- OW = Overwatch()
- input = torch.ones((64, 3, 32, 32))
- output = OW(input)
- print(output.shape)

- from model import * # 引入模型
- import torchvision.datasets
- from torch.utils.data import DataLoader
-
- # 准备数据集,CIFAR10数据集是PIL Image,要转换为tensor数据类型
- train_data = torchvision.datasets.CIFAR10('../data', train=True, transform=torchvision.transforms.ToTensor(),download=True)
- test_data = torchvision.datasets.CIFAR10('../data', train=False, transform=torchvision.transforms.ToTensor(),download=True)
-
- # 查看训练数据集和测试数据集数量
- 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)
-
- # 创建网络模型
- OW = Overwatch()
-
- # 损失函数
- loss_function = nn.CrossEntropyLoss()
-
- # 优化器
- learning_rate = 0.01
- optimizer = torch.optim.SGD(OW.parameters(), lr=learning_rate)
-
- # 设置训练网络的参数
- # 记录训练的次数
- total_train_step = 0
- # 记录测试的次数
- total_test_step = 0
- # 训练的轮数
- epoch = 10
-
- for i in range(epoch): # 0~9
- print('-----第 {} 轮训练开始-----'.format(i+1)) # 1~10
- # 训练步骤开始
- for data in train_dataloader:
- imgs, targets = data
- outputs = OW(imgs)
- loss = loss_function(outputs, targets) # 输出与target计算loss
-
- # 优化器优化模型
- optimizer.zero_grad() # 梯度清零:把上一步训练的每个参数的梯度清零
- loss.backward() # 反向传播:调用反向传播得到每个要更新参数的梯度
- optimizer.step() # 参数优化:每个参数根据上一步得到的梯度进行优化
-
- total_train_step = total_train_step + 1
- if total_train_step % 100 == 0: # 每100次才打印
- print('训练次数:{},Loss:{}'.format(total_train_step, loss.item()))

tips:
print(loss) 和 print(loss.item()) 的区别
- import torch
- a = torch.tensor(5)
- print(a) # tensor(5)
- print(a.item()) # 5
每次训练完一轮就进行测试,以测试数据集上的损失或正确率来评估模型有没有训练好
测试过程中不需要对模型进行调优,利用现有模型进行测试
- # 测试步骤开始(评估模型)
- total_test_loss = 0
- with torch.no_grad(): # 无需梯度调优
- for data in test_dataloader:
- imgs, targets = data
- outputs = OW(imgs)
- loss = loss_function(outputs, targets)
- total_test_loss = total_test_loss + loss.item()
- print('整体测试集上的Loss:{}'.format(total_test_loss))
即便得到整体测试集上的 loss,也不能很好说明在测试集上的表现效果
- outputs = torch.tensor([[0.1,0.2],
- [0.3,0.4]])
- print(outputs.argmax(1)) # 0或1表示方向,1为横向比较大小. 运行结果:tensor([1, 1])
argmax相当于就是返回数组中最大值的索引
preds 预测
- preds = outputs.argmax(1)
- targets = torch.tensor([0,1])
- print(preds == targets) # tensor([False, True])
- print(preds == targets.sum()) # tensor(1),对应位置相等的个数
- # 测试步骤开始(评估模型)
- # 初始化测试集上的总损失和总准确率
- total_test_loss = 0
- *total_accuracy = 0
-
- with torch.no_grad(): # 无需梯度调优
- for data in test_dataloader:
- imgs, targets = data
- outputs = OW(imgs)
- loss = loss_function(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))
-
- # 画test_accuracy & test_loss
- *writer.add_scalar('test_accuracy', total_accuracy/test_data_size, total_test_step)
- writer.add_scalar('test_loss', total_test_loss, total_test_step)
- total_test_step = total_test_step + 1

- from model import * # 引入模型
- import torchvision.datasets
- from torch.utils.data import DataLoader
- from torch.utils.tensorboard import SummaryWriter
-
- # 准备数据集,CIFAR10数据集是PIL Image,要转换为tensor数据类型
- train_data = torchvision.datasets.CIFAR10('../data', train=True, transform=torchvision.transforms.ToTensor(), download=True)
- test_data = torchvision.datasets.CIFAR10('../data', train=False, transform=torchvision.transforms.ToTensor(), download=True)
-
- # 查看训练数据集和测试数据集数量
- 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)
-
- # 创建网络模型
- OW = Overwatch()
-
- # 损失函数
- loss_function = nn.CrossEntropyLoss()
-
- # 优化器
- learning_rate = 0.01
- optimizer = torch.optim.SGD(OW.parameters(), lr=learning_rate)
-
- # 设置训练网络的参数
- total_train_step = 0 # 记录训练的次数
- total_test_step = 0 # 记录测试的次数
- epoch = 10 # 训练的轮数
-
- # 添加TensorBoard
- writer = SummaryWriter('../logs')
-
- for i in range(epoch):
- print('-----第 {} 轮训练开始-----'.format(i+1))
- # 训练步骤开始
- OW.train()
- for data in train_dataloader:
- imgs, targets = data
- outputs = OW(imgs)
- loss = loss_function(outputs, targets) # 输出与target计算loss
-
- # 优化器优化模型
- optimizer.zero_grad() # 梯度清零:把上一步训练的每个参数的梯度清零
- loss.backward() # 反向传播:调用反向传播得到每个要更新参数的梯度
- optimizer.step() # 参数优化:每个参数根据上一步得到的梯度进行优化
-
- total_train_step = total_train_step + 1
- if total_train_step % 100 == 0: # 每100次才打印
- print('训练次数:{},Loss:{}'.format(total_train_step, loss.item()))
-
- # 画train_loss
- writer.add_scalar('train_loss', loss.item(), total_train_step)
-
- # 测试步骤开始(评估模型)
- OW.eval()
- # 初始化测试集上的总损失和总准确率
- total_test_loss = 0
- total_accuracy = 0
- with torch.no_grad(): # 无需梯度调优
- for data in test_dataloader:
- imgs, targets = data
- outputs = OW(imgs)
- loss = loss_function(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))
-
- # 画test_accuracy & test_loss
- writer.add_scalar('test_accuracy', total_accuracy/test_data_size, total_test_step)
- writer.add_scalar('test_loss', total_test_loss, total_test_step)
- total_test_step = total_test_step + 1
-
- # 保存模型
- torch.save(OW, 'OW_{}.pth'.format(i)) # 每一轮保存一个结果
- # torch.save(OW.state_dict,'OW_{}.pth'.format(i)) save2
- print('模型已保存')
-
- writer.close()

总结:
准备数据→加载数据→准备模型→设置损失函数→设置优化器→开始训练→最后验证→结果聚合展示
网络模型:
数据(包括输入、标注):
训练过程
损失函数:
更好的写法:这种写法在CPU和GPU上都可以跑,优先在GPU上跑
为了比较时间,引入 time 这个 package
以下两种写法对于单显卡来说等价:
- device = torch.device("cuda")
- device = torch.device("cuda:0")
语法糖(一种语法的简写),程序在 CPU 或 GPU/cuda 环境下都能运行:
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
通过这个变量可以控制是在CPU上运行还是GPU(改成 "cuda" 或 "cuda:0" )上运行
- OW = OW.to(device)
- loss_function.to(device)
- imgs = imgs.to(device)
- targets = targets.to(device)
- from model import * # 引入模型
- import torch
- import time
- import torchvision.datasets
- from torch import nn
- from torch.utils.data import DataLoader
- from torch.utils.tensorboard import SummaryWriter
-
- # 定义训练设备
- device = torch.device('cuda')
-
- # 准备数据集,CIFAR10数据集是PIL Image,要转换为tensor数据类型
- train_data = torchvision.datasets.CIFAR10('../data', train=True, transform=torchvision.transforms.ToTensor(), download=True)
- test_data = torchvision.datasets.CIFAR10('../data', train=False, transform=torchvision.transforms.ToTensor(), download=True)
-
- # 查看训练数据集和测试数据集数量
- 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)
-
- # 创建网络模型
- OW = Overwatch()
- # 将OW送给device
- OW = OW.to(device)
-
- # 损失函数
- loss_function = nn.CrossEntropyLoss()
- loss_function.to(device)
-
- # 优化器
- learning_rate = 0.01
- optimizer = torch.optim.SGD(OW.parameters(), lr=learning_rate)
-
- # 设置训练网络的参数
- # 记录训练的次数
- total_train_step = 0
- # 记录测试的次数
- total_test_step = 0
- # 训练的轮数
- epoch = 10
-
- # 添加TensorBoard
- writer = SummaryWriter('../logs')
- start_time = time.time() # 记录下此时的时间,赋值给开始时间
- for i in range(epoch):
- print('-----第 {} 轮训练开始-----'.format(i+1))
- # 训练步骤开始
- OW.train()
- for data in train_dataloader:
- imgs, targets = data
- imgs = imgs.to(device)
- targets = targets.to(device)
- outputs = OW(imgs)
- loss = loss_function(outputs, targets) # 输出与target计算loss
-
- # 优化器优化模型
- optimizer.zero_grad() # 梯度清零:把上一步训练的每个参数的梯度清零
- loss.backward() # 反向传播:调用反向传播得到每个要更新参数的梯度
- optimizer.step() # 参数优化:每个参数根据上一步得到的梯度进行优化
-
- total_train_step = total_train_step + 1
- if total_train_step % 100 == 0: # 每100次才打印
- end_time = time.time()
- print(end_time - start_time) # 第一次训练100次所花费的时间
- print('训练次数:{},Loss:{}'.format(total_train_step, loss.item()))
-
- # 画train_loss
- writer.add_scalar('train_loss', loss.item(), total_train_step)
-
- # 测试步骤开始(评估模型)
- # 初始化测试集上的总损失和总准确率
- OW.eval()
- total_test_loss = 0
- total_accuracy = 0
- with torch.no_grad(): # 无需梯度调优
- for data in test_dataloader:
- imgs, targets = data
- imgs = imgs.to(device)
- targets = targets.to(device)
- outputs = OW(imgs)
- loss = loss_function(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))
-
- # 画test_accuracy & test_loss
- writer.add_scalar('test_accuracy', total_accuracy/test_data_size, total_test_step)
- writer.add_scalar('test_loss', total_test_loss, total_test_step)
- total_test_step = total_test_step + 1
-
- # 保存模型
- torch.save(OW, 'OW_{}.pth'.format(i)) # 每一轮保存一个结果
- # torch.save(OW.state_dict,'OW_{}.pth'.format(i)) save2
- print('模型已保存')
-
- writer.close()

核心:利用已经训练好的模型,给它提供输入进行测试
注意:load的模型当时保存的时候是用gpu训练的,因此这里需要定义device到cuda,再把image进行to(device)操作:device = torch.device('cuda')、image = image.to(device)
或者 model =torch.load("OW_30.pth",map_location=torch.device('cpu'))
采用GPU训练的模型,在CPU上加载,要从GPU上映射到CPU上
模型要求是四维的输入 [batch_size,channel,length,width],但是获得的图片是三维的 —— 图片没有指定 batch_size(网络训练过程中是需要 batch_size 的,而图片输入是三维的,需要reshape() 一下)
test.py:
- import torch
- import torchvision.transforms
- from PIL import Image
- # from model import *
-
- # device = torch.device('cuda')
- image_path = '../imgs/dog.png' # 或绝对路径
- image = Image.open(image_path)
- print(image)
- image = image.convert('RGB')
- '''因为png格式是四通道,除了RGB三通道外,还有一个透明度通道,所以要调用上述语句保留其颜色通道
- 如果图片本来就是三颜色通道,经过此操作,不变。加上这一步后,可以适应 png jpg 各种格式的图片'''
-
- # 网络模型的输入只能是32x32,进行一个Resize()
- transform = torchvision.transforms.Compose([torchvision.transforms.Resize((32, 32)), # 32x32大小的PIL Image
- torchvision.transforms.ToTensor()]) # 转为Tensor数据类型
- image = transform(image)
- print(image.shape) # torch.Size([3, 32, 32])
-
- # model = torch.load('OW_9.pth')
- model =torch.load("OW_9.pth",map_location=torch.device('cpu'))
- print(model)
- image = torch.reshape(image,(1, 3, 32, 32))
- # image = image.to(device)
- model.eval() # 模型转化为测试类型
- with torch.no_grad(): # 节约内存和性能
- output = model(image)
- print(output)
- print(output.argmax(1))

CIFAR10 对应的真实类别:
下载代码用 PyCharm 打开后,查看代码里有没有 required=True,若有,删掉 required=True ,加一个默认值 default="./dataset/maps" ,就可以在 PyCharm 里右键运行了
即找到所有 required=True 的参数,将它删去并添加上默认值default
required的意思就是执行代码的时候需要手动输入文件目录,改为默认之后就不用再输入了
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。