PyTorch学习笔记——CIFAR10图像数据模型训练及验证(CNN)

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%

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值