• [2022-11-07]神经网络与深度学习第4章 - 卷积神经网络(part 5)


    卷积神经网络(part 5) - 使用预训练ResNet18实现CIFAR-10分类

    写在开头

    在经历了数据->黑白图像的应用实验后,我们在本次实验中将使用CIFAR10数据集进行多通道的彩色图像的分类。由于彩色图像内容更多,在模型训练时将更加收到数据集特性(大小、图像质量等)的影响,模型的效果也会有很大不同。我们在本次实验中,将分别使用经过预训练的和未经过与训练的ResNet18网络进行训练和评估。

    基于ResNet18网络完成图像分类任务

    库文件和初始化

    import torch #PyTorch
    import torch.nn as nn #PyTorch算子等
    import numpy as np # numpy,用于数据处理
    import matplotlib.pyplot as plt # matplotlib,用于图像绘制
    
    from torchvision.datasets.cifar import CIFAR10 # CIFAR10数据集
    from torchvision.models import resnet18 # resnet18网络
    from torchvision.transforms import ToTensor # 图像转换函数
    from torch.utils.data import DataLoader # 数据加载器
    %matplotlib inline
    
    plt.rcParams['font.family'] = 'Microsoft YaHei'
    
    device = torch.device("cuda" if torch.cuda.is_available() else "cpu") # 使用GPU加速
    transform = ToTensor()
    
    import ssl
    ssl._create_default_https_context = ssl._create_unverified_context # 由于证书问题,我们需要屏蔽ssl验证
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    数据处理

    作为一个经典的数据集,cifar也在torchvision库中包含。我们使用如下代码即可加载数据集:

    dataset_train = CIFAR10(
        './data/cifar', # 下载或加载的地址
        True, # 是否为训练集(用于梯度追踪设置)
        transform, # 数据转化
        download=True # 是否下载
    )
    dataset_test = CIFAR10(
        './data/cifar',
        False,
        transform
    )
    
    dataloader_train = DataLoader(dataset_train, 32)
    dataloader_test = DataLoader(dataset_test, 32)
    
    fig, *ax = plt.subplots(2,4)
    for i in range(8):
        ax[0][i//4][i%4].set_xticks([])
        ax[0][i//4][i%4].set_yticks([])
        ax[0][i//4][i%4].imshow(dataset_train.data[i])
        ax[0][i//4][i%4].set_xlabel(dataset_train.classes[i])
    
    print('train size:%s'%(','.join(torch.tensor(dataset_train.data.shape).numpy().astype(np.str_))))
    print('test size:%s'%(','.join(torch.tensor(dataset_test.data.shape).numpy().astype(np.str_))))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    输出结果如下:
    在这里插入图片描述

    模型构建

    模型我们在前面一个实验中已经构建完毕,这边我们给出网络结构:
    在这里插入图片描述
    勘误: 在前一篇中,我的模型代码写错了,正确的模型初始化部分代码应当修改为:

    def __init__(self, in_channels=1, num_classes=10, with_residual=True):
            super(ResNet18, self).__init__()
            self.sec1 = torch.nn.Sequential(
                torch.nn.Conv2d(in_channels, 64, 7, stride=2, padding=3),
                torch.nn.BatchNorm2d(64), 
                torch.nn.ReLU(),
                torch.nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
            )
            self.sec2 = torch.nn.Sequential(
                ResBlock(64,     64,     1, with_residual)
            )
            self.sec3 = torch.nn.Sequential(
                ResBlock(64,     128,    2, with_residual)
            )
            self.sec4 = torch.nn.Sequential(
                ResBlock(128,    256,    2, with_residual)
            )
            self.sec5 = torch.nn.Sequential(
                ResBlock(256,    512,    2, with_residual)
            )
            self.pool = torch.nn.AdaptiveAvgPool2d(1)
            self.flatten = torch.nn.Flatten()
            self.linear = torch.nn.Linear(512, num_classes)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    这边我们不需要再自己写模型了,只需要使用torchvision中内置的resnet18模型即可:

    model_not_pre = resnet18(pretrained=False).to(device) # 未预训练的模型
    model_yet_pre = resnet18(pretrained=True).to(device) # 预训练好的模型
    
    • 1
    • 2

    这边,我们发现了一个新的东西:预训练。这是什么呢?

    模型训练

    预训练和迁移学习

    预训练就是使用别人已经训练了一部分的模型,迁移学习因此是使用其他数据集进行预训练后、再自己的数据集上使用这个预训练模型再训练的学习过程。
    这边找到一个非常好的解释:
    在这里插入图片描述在这里插入图片描述

    模型训练

    由于疫情原因,我的2080Ti被囚禁在出租屋了,因此本次训练使用的电脑是商务本,使用MX250显卡进行模型训练,本次训练两个模型,训练的代码如下:

    crit = nn.CrossEntropyLoss()
    opti = torch.optim.SGD(model_not_pre.parameters(), 0.01)
    losses_not_pre = []
    for epoch in range(8):
        sum_loss = 0.0
        for i, data in enumerate(dataloader_train):
    
            inputs, labels = data
            inputs, labels = inputs.to(device), labels.to(device)
            opti.zero_grad()
            outputs = model_not_pre(inputs)
            loss = crit(outputs, labels)
            loss.backward()
            opti.step()
            sum_loss += loss.item()
            if i % 100 == 99:
                print('[%d, %d] loss: %.03f'
                      % (epoch + 1, i + 1, sum_loss / 100))
                losses_not_pre.append(sum_loss)
                sum_loss = 0.0
            with torch.no_grad():
                correct = 0
                total = 0
                for data in dataloader_test:
                    images, labels = data
                    images, labels = images.to(device), labels.to(device)
                    outputs = model_not_pre(images)
                    _, predicted = torch.max(outputs.data, 1)
                    total += labels.size(0)
                    correct += (predicted == labels).sum()
                print('第%d个epoch的识别准确率为:%d%%' % (epoch + 1, (100 * correct / total)))
        torch.save(model_not_pre.state_dict(), 'model_not_pre_%03d.pth' % ( epoch + 1))
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    结果如下。花了非常非常多的时间,效果还不好:
    在这里插入图片描述
    训练有预训练版本代码类似,这边给出训练结果:
    在这里插入图片描述

    比较

    我们将训练时的损失通过折线图的方式绘制出来:

    plt.plot(losses_not_pre, label='pretrain = False', color='#cc0000', alpha=0.5)
    plt.plot(losses_yet_pre, label='pretrain = True', color='#00cc00', alpha=0.5)
    plt.xlabel('epoch')
    plt.ylabel('loss')
    plt.legend()
    plt.title('comparison of "pretrain"')
    plt.show()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    输出结果如下:
    在这里插入图片描述

    由对比图我们发现。尽管一开始的损失,预训练后的模型还略高于没有预训练的模型,但是在后续训练的过程中,我们明显可以看出,预训练模型收敛速度更快,模型效果更好。
    **分析:**预训练就像老师教课,可能用到自己的题目上刚开始可能不太好,但是老师教过之后更容易融会贯通,因此表现会越来越好且远高于没有预训练。

    模型预测

    我们依然直接调用系统摄像头进行测试:

    import cv2 
    from PIL import Image
    import numpy as np
    import torch
    import torch.nn as nn
    import datetime
    
    
    cap = cv2.VideoCapture(0,cv2.CAP_DSHOW)
    FPS = 24
    cap.set(cv2.CAP_PROP_FPS, FPS)
    model_yet_pre = model_yet_pre.eval()
    
    while True:
        ret, frame = cap.read()
        if not ret:
            print("Can't receive frame (stream end?). Exiting ...")
            break
        frame = Image.fromarray(frame)
        frame = frame.resize((32,32),Image.BICUBIC)
        frame = np.asarray(frame)
        frame = np.array([frame])
        frame = frame.transpose(0,3,1,2)
        try:
            print(datetime.datetime.now(),dataset_train.classes[torch.argmax(model_yet_pre(torch.tensor(frame,dtype=torch.float).to(device)))],end='\r')
        except:
            pass
        cv2.namedWindow('frame', cv2.WND_PROP_FULLSCREEN)
        cv2.imshow('frame', frame.transpose(0,2,3,1).squeeze(0))
        if cv2.waitKey(1) == ord('q'):  break
    cap.release()
    cv2.destroyAllWindows()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    行吧,依然不准:
    在这里插入图片描述

    一些小题

    5种深度的ResNet

    通过阅读论文(传送门),我们了解到了几种不同深度的ResNet。汇总表格如下:
    在这里插入图片描述
    首先是共同点:无论是深度为多少的ResNet都将网络分成了五部分。分别是:conv1,conv2_x,conv3_x,conv4_x,conv5_x。
    然后是显而易见的不同点:每个网络深度不同。
    那么,深度更深有什么作用呢?随着深度的加深,ResNet对于抽象特征的提取能力加强,因而能够在更加复杂精细的分类任务上展现优越的性能。但是,随着网络一同增加的还有计算量,因此:
    在这里插入图片描述

    LeNet、AlexNet、VGG、GoogLeNet、ResNet

    看了很多论文,最终找到如下思维导图,觉得是对这些网络一个非常好的概括了:
    在这里插入图片描述

    写在最后

    本次实验是卷积神经网络的最后一个实验了。在经过前面的这些学习后,我们对卷积神经网络从概念、原理、实现等多方面进行了学习,通过自己动手搭建一个个网络,我们了解了其中部分的奥秘。当然,卷积神经网络由于其不保存历史输入,在一些时间关联性大的预测上效果不佳。因此,我们即将学习循环神经网络。
    然后,关于CNN的思维导图如下:
    在这里插入图片描述

    References

    1 — https://arxiv.org/pdf/1512.03385.pdf
    2 — https://arxiv.org/pdf/1603.05027.pdf
    3 — https://arxiv.org/pdf/1605.07146.pdf
    4 — https://arxiv.org/pdf/1603.09382.pdf

  • 相关阅读:
    L75.linux命令每日一练 -- 第11章 Linux系统管理命令 -- lsof和uptime
    玩转YAML配置文件占位符 ,同事纷纷直呼大佬
    7.DesignForSilkscreen\UpdateRefdes...
    华为OD机试 - 叠积木1 - 双指针(Java 2023 B卷 200分)
    HTTP协议
    基于云边协同架构的五大应用场景革新
    遭遇 .Wormhole 勒索病毒后该怎么办?数据恢复与安全建议
    Css属性深入
    mybatis在实际项目中常见的排坑配置
    【GAMES101】作业4: Bézier 曲线
  • 原文地址:https://blog.csdn.net/LupnisJ/article/details/127725976