1.环境配置
windows 10
python 3.9
pytorch 2.2.0
torchvision 0.17.0
tensorboard 2.17.0
numpy 1.26.0
2.CIFAR10介绍
CIFAR10 是由Alex Krizhevsky 和 Ilya Sutskever 整理的一个用于识别普适物体的小型数据集。图片的尺寸为 32×32 ,数据集中一共有 50000 张训练图片和 10000 张测试图片。一共包含 10 个类别的 RGB 彩色图片:飞机( airplane )、汽车( automobile )、鸟类( bird )、猫( cat )、鹿( deer )、狗( dog )、蛙类( frog )、马( horse )、船( ship )和卡车( truck )。CIFAR-10 的图片样例如图所示。
3.下载CIFAR10数据集并加载
示例代码:
import torchvision
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter
from torchvision.transforms import ToTensor
class Data:
def __init__(self):
self.datapath = 'data_CIFAR'
# 获取数据集
self.train_data = torchvision.datasets.CIFAR10(self.datapath,train=True,download=True,transform=ToTensor())
self.test_data = torchvision.datasets.CIFAR10(self.datapath,train=False,download=True,transform=ToTensor())
# 查看数据集大小
self.train_data_size = len(self.train_data)
self.test_data_size = len(self.test_data)
def dataload(self,batchsize=64):
# 加载数据集
train_dataload = DataLoader(self.train_data,batch_size=batchsize,drop_last=False)
test_dataload = DataLoader(self.test_data,batch_size=batchsize,drop_last=False)
return train_dataload,test_dataload
if __name__ == "__main__":
cifar_data = Data()
print(f'训练数据集大小:{cifar_data.train_data_size}')
print(f'测试数据集大小:{cifar_data.test_data_size}')
writer = SummaryWriter('logs')
step = 0
for imgs,targets in cifar_data.dataload(64)[0]:
writer.add_image('traindata',imgs,step,dataformats='NCHW')
step += 1
writer.close()
运行结果:
Files already downloaded and verified
Files already downloaded and verified
训练数据集大小:50000
测试数据集大小:10000
在终端窗口对应路径下输入命令tensorboard --logdir=logs,打开部分显示如下:
说明数据集下载及加载正常。
4.搭建卷积神经网络
神经网络结构如下:
示例代码:
import torch
from torch import nn
# 搭建神经网络
class Mynn(nn.Module):
def __init__(self):
super().__init__()
self.nnmodel = nn.Sequential(
nn.Conv2d(3, 32, 5, 1, 2), # 3,32,32 --> 32,32,32
nn.ReLU(),
nn.MaxPool2d(2), # 32,32,32 --> 32,16,16
nn.Conv2d(32, 32, 5, 1, 2), # 32,16,16 --> 32,16,16
nn.ReLU(),
nn.MaxPool2d(2), # 32,16,16 --> 32,8,8
nn.Conv2d(32, 64, 5, 1, 2), # 32,8,8 --> 64,8,8
nn.ReLU(),
nn.MaxPool2d(2), # 64,8,8 --> 64,4,4
nn.Flatten(), # 64,4,4 --> 1024
nn.Linear(1024, 64), # 1024 --> 64
nn.Linear(64, 10), # 64 --> 10
)
def forward(self,input):
output = self.nnmodel(input)
return output
if __name__ == '__main__':
testnn = Mynn()
print(testnn)
testdata = torch.ones((64,3,32,32)) # 生成64*3*32*32全为1的tensor测试数据
outdata = testnn(testdata)
print(outdata.shape)
运行结果:
Mynn(
(nnmodel): Sequential(
(0): Conv2d(3, 32, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
(1): ReLU()
(2): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(3): Conv2d(32, 32, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
(4): ReLU()
(5): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(6): Conv2d(32, 64, kernel_size=(5, 5), stride=(1, 1), padding=(2, 2))
(7): ReLU()
(8): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)
(9): Flatten(start_dim=1, end_dim=-1)
(10): Linear(in_features=1024, out_features=64, bias=True)
(11): Linear(in_features=64, out_features=10, bias=True)
)
)
torch.Size([64, 10])
5.CIFAR10数据训练
示例代码:
import time
import torch
from torch import nn, optim
from torch.utils.tensorboard import SummaryWriter
from cifar10_zxnn.zxnn import Mynn
from cifar10_zxnn.data import Data
# 选择训练设备,数据、网络、损失函数可以使用cuda
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
# 加载数据
cifar_data = Data()
train_dataload,test_dataload = cifar_data.dataload()
# 实例化网络
mynn = Mynn().to(device)
# 测试
# testdata = torch.ones((64,3,32,32))
# out = mynn(testdata)
# print(out.shape)
learnrate = 0.01 # 学习率
loss_fn = nn.CrossEntropyLoss().to(device) # 交叉熵损失函数
optimizer = optim.SGD(mynn.parameters(),lr=learnrate) # 优化器,随机梯度下降
epoch = 10 # 训练轮数
writer = SummaryWriter('logs_train')
x = 0
t1 = time.time()
for i in range(epoch):
mynn.train()
# 单轮训练总次数
total_train_step = 0
# 单轮评估总次数
total_test_step = 0
print(f"----------第{i+1}轮训练开始:----------")
for imgs,label in train_dataload:
imgs,label = imgs.to(device),label.to(device)
train_out = mynn(imgs) # 前向传播
optimizer.zero_grad() # 梯度清零
loss = loss_fn(train_out,label) # 计算损失
loss.backward() # 反向传播
optimizer.step() # 参数优化
total_train_step += 1
if total_train_step%200 == 0:
print(f"第{total_train_step}次训练,loss:{loss.item():.4f}")
# 添加标量,查看loss变化曲线,scalar_value相当于y轴,global_step相当于x轴
writer.add_scalar('loss',scalar_value=loss.item(),global_step=x)
x += 1
torch.save(mynn,f'cifar_zxnn{i+1}.pth') # 保存训练模型
mynn.eval()
# 禁用梯度计算,在测试阶段使用,可以减少内存消耗并加速计算
with torch.no_grad():
totaltest_loss = 0
total_right_num = 0
for imgs,label in test_dataload:
imgs, label = imgs.to(device), label.to(device)
test_out = mynn(imgs) # test_out 输出10个类别的分别得分
right_num= (test_out.argmax(1)==label).sum() # argmax(1) 对每行进行比较,返回每行最大值的索引,axis=1:行,0:列
total_right_num += right_num
loss = loss_fn(test_out,label)
totaltest_loss += loss
total_test_step += 1
if total_test_step % 100 == 0:
print(f"第{total_test_step}次评估,平均损失:{totaltest_loss.item()/total_test_step:.4f}")
print(f"评估正确总数:{total_right_num}")
print(f"评估正确率:{total_right_num/cifar_data.test_data_size*100:.3f}%")
writer.add_scalar('eval', scalar_value=total_right_num/cifar_data.test_data_size,global_step=i)
t2 = time.time()
print(f"训练及评估总耗时:{t2-t1:.3f}s")
运行结果:
----------第1轮训练开始:----------
第200次训练,loss:2.3031
第400次训练,loss:2.2804
第600次训练,loss:2.2658
第100次评估,平均损失:2.2072
评估正确总数:1267
评估正确率:12.670%
----------第2轮训练开始:----------
第200次训练,loss:2.0926
第400次训练,loss:2.0716
第600次训练,loss:1.9758
第100次评估,平均损失:1.9288
评估正确总数:2997
评估正确率:29.970%
----------第3轮训练开始:----------
第200次训练,loss:1.8946
第400次训练,loss:1.7677
第600次训练,loss:1.8681
第100次评估,平均损失:1.7793
评估正确总数:3485
评估正确率:34.850%
----------第4轮训练开始:----------
第200次训练,loss:1.7038
第400次训练,loss:1.5568
第600次训练,loss:1.7373
第100次评估,平均损失:1.7453
评估正确总数:3402
评估正确率:34.020%
----------第5轮训练开始:----------
第200次训练,loss:1.6295
第400次训练,loss:1.4419
第600次训练,loss:1.6746
第100次评估,平均损失:1.6526
评估正确总数:3833
评估正确率:38.330%
----------第6轮训练开始:----------
第200次训练,loss:1.5746
第400次训练,loss:1.3644
第600次训练,loss:1.6077
第100次评估,平均损失:1.6013
评估正确总数:4068
评估正确率:40.680%
----------第7轮训练开始:----------
第200次训练,loss:1.5220
第400次训练,loss:1.3160
第600次训练,loss:1.5092
第100次评估,平均损失:1.4952
评估正确总数:4509
评估正确率:45.090%
----------第8轮训练开始:----------
第200次训练,loss:1.4530
第400次训练,loss:1.2517
第600次训练,loss:1.4150
第100次评估,平均损失:1.4142
评估正确总数:4785
评估正确率:47.850%
----------第9轮训练开始:----------
第200次训练,loss:1.3748
第400次训练,loss:1.2132
第600次训练,loss:1.3328
第100次评估,平均损失:1.3478
评估正确总数:5049
评估正确率:50.490%
----------第10轮训练开始:----------
第200次训练,loss:1.2997
第400次训练,loss:1.1884
第600次训练,loss:1.2789
第100次评估,平均损失:1.2888
评估正确总数:5306
评估正确率:53.060%
训练及评估总耗时:309.702s
通过tensorboard查看训练损失和验证正确率曲线如下:
可以看到训练损失从2.3逐渐降低到1.3,验证正确率从12%逐渐上升到53%,由于训练采用的是单cpu处理,没有gpu,速度较慢,只训练了10轮,如果进一步训练,效果会更好。
6.模型验证
示例代码:
import torch
from torchvision.transforms import ToTensor
from PIL import Image
# 数据分类标签
# train_data = torchvision.datasets.CIFAR10('data_CIFAR',train=True,download=True,transform=ToTensor())
# labeldict = train_data.class_to_idx # 查看数据分类标签
# print(labeldict)
labeldict = {'飞机': 0, '汽车': 1, '鸟': 2, '猫': 3, '鹿': 4,
'狗': 5, '蛙': 6, '马': 7, '船': 8, '卡车': 9}
def get_keys_by_value(dictionary, value):
"""通过value查找字典key值"""
keys = []
for key, val in dictionary.items():
if val == value:
keys.append(key)
return keys # 返回值为列表,有时不同key对应的value可能是一样的
# 打开图片,进行格式转换
pic = 'pic/automobile.png'
img = Image.open(pic) # 打开图片
img = img.resize((32,32)) # 调整图片大小32*32
img = img.convert('RGB') # 转换为RGB图片,png格式图片通常为RGBA图片
img = ToTensor()(img) # 转换为tensor数据类型
img = torch.reshape(img,(1,3,32,32)) # 转换图片为NCWH四维数据格式
# print(img.shape)
# 加载训练模型
mynn = torch.load('cifar_zxnn10.pth')
# 图片验证
mynn.eval()
with torch.no_grad():
out = mynn(img)
out = out.softmax(1)
print(out)
result = get_keys_by_value(labeldict,out.argmax().item())
print("---------分析结果---------")
print("图片是:"+result[0]+f" \t概率:{out.max(1)[0].item()*100:.2f}%")
随机查找以下图片进行加载验证,结果如下:
tensor([[3.3220e-04, 9.8959e-01, 1.5811e-06, 6.2333e-07, 8.4251e-06, 6.2578e-08,
7.9743e-07, 1.5386e-06, 7.1923e-05, 9.9890e-03]])
---------分析结果---------
图片是:汽车 概率:98.96%
tensor([[9.3987e-01, 1.0737e-03, 3.8304e-02, 4.7094e-04, 1.2460e-02, 4.2714e-04,
1.0442e-04, 8.5412e-04, 6.3740e-03, 5.7672e-05]])
---------分析结果---------
图片是:飞机 概率:93.99%
tensor([[0.0823, 0.0056, 0.0389, 0.0119, 0.1977, 0.0214, 0.0256, 0.5960, 0.0038,
0.0169]])
---------分析结果---------
图片是:马 概率:59.60%