第P2周:深度学习实验——CIFAR10彩色图片识别

部署运行你感兴趣的模型镜像

 

实验目的

  1. 巩固使用PyTorch构建卷积神经网络(CNN)的基本方法

  2. 理解神经网络训练中的两个超参数(学习率、迭代次数)对模型拟合程度的影响

  3. 实现CIFAR10彩色图片识别模型的训练与验证

 一、前期准备

1. 设置GPU

import torch
import torch.nn as nn
import matplotlib.pyplot as plt
import torchvision

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

device

     device(type='cuda')

     2. 导入数据

     使用dataset下载CIFAR10数据集,并划分好训练集与测试集

    使用dataloader加载数据,并设置好基本的batch_size(训练过程中,每次迭代所使用的样本数量)

    train_ds = torchvision.datasets.CIFAR10('data', 
                                          train=True, 
                                          transform=torchvision.transforms.ToTensor(), # 将数据类型转化为Tensor
                                          download=True)
    
    test_ds  = torchvision.datasets.CIFAR10('data', 
                                          train=False,  #加载测试数据集而非训练数据集
                                          transform=torchvision.transforms.ToTensor(), # 将数据类型转化为Tensor
                                          download=True)
    batch_size = 32  #指在深度学习模型训练过程中,每次迭代(iteration)所使用的样本数量
    
    train_dl = torch.utils.data.DataLoader(train_ds, 
                                           batch_size=batch_size, 
                                           shuffle=True)
    
    test_dl  = torch.utils.data.DataLoader(test_ds, 
                                           batch_size=batch_size)
    # 取一个批次查看数据格式
    # 数据的shape为:[batch_size, channel, height, weight]
    # 其中batch_size为自己设定,channel,height和weight分别是图片的通道数,高度和宽度。
    imgs, labels = next(iter(train_dl))
    imgs.shape

    torch.Size([32, 3, 32, 32])

    3. 数据可视化

    transpose((1, 2, 0))详解:

    ● 作用是对NumPy数组进行轴变换,transpose函数的参数是一个元组,定义了新轴的顺序。原始PyTorch张量通常是以(C, H, W)的格式存储的,其中:

      ○ C是通道数(例如,RGB图像有3个通道)。

      ○ H是图像的高度。

      ○ W是图像的宽度。

    ● transpose((1, 2, 0))将轴的顺序从(C, H, W)转换为(H, W, C),这使得数据格式更适合可视化和处理

    import numpy as np
    
    # 指定图片大小,图像大小为20宽、5高的绘图(单位为英寸inch)
    plt.figure(figsize=(20, 5))  #新建窗口
    for i, imgs in enumerate(imgs[:20]):
        # 进行轴变换
        npimg = imgs.numpy().transpose((1, 2, 0))
        # 将整个figure分成2行10列,绘制第i+1个子图。
        plt.subplot(2, 10, i+1)
        plt.imshow(npimg, cmap=plt.cm.binary)
        plt.axis('off')
        
    #plt.show()  如果你使用的是Pycharm编译器,请加上这行代码

    二、构建简单的CNN网络

    对于一般的CNN网络来说,都是由特征提取网络和分类网络构成,其中特征提取网络用于提取图片的特征,分类网络用于将图片进行分类。

    import torch.nn.functional as F
    
    num_classes = 10  # 图片的类别数
    
    class Model(nn.Module):
         def __init__(self):
            super().__init__()
             # 特征提取网络
            self.conv1 = nn.Conv2d(3, 64, kernel_size=3)   # 第一层卷积,卷积核大小为3*3
            self.pool1 = nn.MaxPool2d(kernel_size=2)       # 设置池化层,池化核大小为2*2
            self.conv2 = nn.Conv2d(64, 64, kernel_size=3)  # 第二层卷积,卷积核大小为3*3   
            self.pool2 = nn.MaxPool2d(kernel_size=2) 
            self.conv3 = nn.Conv2d(64, 128, kernel_size=3) # 第二层卷积,卷积核大小为3*3   
            self.pool3 = nn.MaxPool2d(kernel_size=2) 
                                          
            # 分类网络
            self.fc1 = nn.Linear(512, 256)          
            self.fc2 = nn.Linear(256, num_classes)
         # 前向传播
         def forward(self, x):
            x = self.pool1(F.relu(self.conv1(x)))     
            x = self.pool2(F.relu(self.conv2(x)))
            x = self.pool3(F.relu(self.conv3(x)))
            
            x = torch.flatten(x, start_dim=1)
    
            x = F.relu(self.fc1(x))
            x = self.fc2(x)
           
            return x

    加载并打印模型

    from torchinfo import summary
    # 将模型转移到GPU中(我们模型运行均在GPU中进行)
    model = Model().to(device)
    
    summary(model)

     ================================================================= Layer (type:depth-idx) Param # ================================================================= Model -- ├─Conv2d: 1-1 1,792 ├─MaxPool2d: 1-2 -- ├─Conv2d: 1-3 36,928 ├─MaxPool2d: 1-4 -- ├─Conv2d: 1-5 73,856 ├─MaxPool2d: 1-6 -- ├─Linear: 1-7 131,328 ├─Linear: 1-8 2,570 ================================================================= Total params: 246,474 Trainable params: 246,474 Non-trainable params: 0 =================================================================

     三、训练模型

    分别设置学习率为1e-2和0.001,epoch为10和25,观察模型拟合效果。

    1.设置超参数

    loss_fn    = nn.CrossEntropyLoss() # 创建损失函数
    learn_rate = 1e-2 # 学习率,or 0.001
    opt        = torch.optim.SGD(model.parameters(),lr=learn_rate)

    2.编写训练函数

    # 训练循环
    def train(dataloader, model, loss_fn, optimizer):
        size = len(dataloader.dataset)  # 训练集的大小,一共60000张图片
        num_batches = len(dataloader)   # 批次数目,1875(60000/32)
    
        train_loss, train_acc = 0, 0  # 初始化训练损失和正确率
        
        for X, y in dataloader:  # 获取图片及其标签
            X, y = X.to(device), y.to(device)
            
            # 计算预测误差
            pred = model(X)          # 网络输出
            loss = loss_fn(pred, y)  # 计算网络输出和真实值之间的差距,targets为真实值,计算二者差值即为损失
            
            # 反向传播
            optimizer.zero_grad()  # grad属性归零
            loss.backward()        # 反向传播
            optimizer.step()       # 每一步自动更新
            
            # 记录acc与loss
            train_acc  += (pred.argmax(1) == y).type(torch.float).sum().item()
            train_loss += loss.item()
                
        train_acc  /= size
        train_loss /= num_batches
    
        return train_acc, train_loss

     3. 编写测试函数

    def test (dataloader, model, loss_fn):
        size        = len(dataloader.dataset)  # 测试集的大小,一共10000张图片
        num_batches = len(dataloader)          # 批次数目,313(10000/32=312.5,向上取整)
        test_loss, test_acc = 0, 0
        
        # 当不进行训练时,停止梯度更新,节省计算内存消耗
        with torch.no_grad():
            for imgs, target in dataloader:
                imgs, target = imgs.to(device), target.to(device)
                
                # 计算loss
                target_pred = model(imgs)
                loss        = loss_fn(target_pred, target)
                
                test_loss += loss.item()
                test_acc  += (target_pred.argmax(1) == target).type(torch.float).sum().item()
    
        test_acc  /= size
        test_loss /= num_batches
    
        return test_acc, test_loss

    4.正式训练

    epochs     = 10 #or 25
    train_loss = []
    train_acc  = []
    test_loss  = []
    test_acc   = []
    
    for epoch in range(epochs):
        model.train()
        epoch_train_acc, epoch_train_loss = train(train_dl, model, loss_fn, opt)
        
        model.eval()
        epoch_test_acc, epoch_test_loss = test(test_dl, model, loss_fn)
        
        train_acc.append(epoch_train_acc)
        train_loss.append(epoch_train_loss)
        test_acc.append(epoch_test_acc)
        test_loss.append(epoch_test_loss)
        
        template = ('Epoch:{:2d}, Train_acc:{:.1f}%, Train_loss:{:.3f}, Test_acc:{:.1f}%,Test_loss:{:.3f}')
        print(template.format(epoch+1, epoch_train_acc*100, epoch_train_loss, epoch_test_acc*100, epoch_test_loss))
    print('Done')

    四、结果可视化

    (1)学习率1e-2,epoch=10

    训练和测试准确性都随着训练次数增加而增加,损失随之减小。模型拟合程度较好。

    (2)学习率1e-2,epoch=25

    epoch提高到25,尽管每次训练的准确率会提高,模型能较好地记忆训练数据,但是测试准确率低,损失较大,且震荡和非常不稳定,过拟合,泛化能力弱。说明在过多的迭代次数,模型可能已经过度学习训练数据的特征,包括噪声和不相关的模式,通过记忆去训练数据,而不是学习一般的、普遍的模式,因此在处理新数据时的性能会变差。

    (3)学习率1e-3,epoch=10

    学习率降低,训练准确率提升,但是测试准确率走低。因为学习率太低,模型收敛太慢,每一步更新都很小,模型还没真正学会特征,表现为欠拟合,所以测试效果不好。类比:小学习率像是慢慢走,容易找到最优点,但要走很久,如果走了没几步就停了(epoch 太少),当然就没走到目标。

    (4)学习率1e-3,epoch=25

    将学习率降低,epoch提高,训练和测试准确率和方案(3)差不多,准确率随着迭代次数增加而上升,但是出现震荡,说明模型过拟合,而效果并没有提高。由此,当学习率太低时,模型更新太慢,无法在有限 epoch 内找到最优点,即使加了 epoch,效果也可能达不到最好。

    您可能感兴趣的与本文相关的镜像

    PyTorch 2.5

    PyTorch 2.5

    PyTorch
    Cuda

    PyTorch 是一个开源的 Python 机器学习库,基于 Torch 库,底层由 C++ 实现,应用于人工智能领域,如计算机视觉和自然语言处理

    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

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

    抵扣说明:

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

    余额充值