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

目录

  • 5.5 实践:基于ResNet18网络完成图像分类任务
    • 5.5.1 数据处理
      • 5.5.1.1 数据集介绍
      • 5.5.1.2 数据读取
    • 5.5.2 模型构建
      • 1. 什么是“预训练模型”?什么是“迁移学习”?
      • 2. 比较“使用预训练模型”和“不使用预训练模型”的效果。
    • 5.5.3 模型训练
    • 5.5.4 模型评价
    • 5.5.5 模型预测
  • 用自己的话简单评价:LeNet、AlexNet、VGG、GoogLeNet、ResNet
  • 总结

5.5 实践:基于ResNet18网络完成图像分类任务

在本实践中,我们实践一个更通用的图像分类任务。

图像分类(Image Classification)是计算机视觉中的一个基础任务,将图像的语义将不同图像划分到不同类别。很多任务也可以转换为图像分类任务。比如人脸检测就是判断一个区域内是否有人脸,可以看作一个二分类的图像分类任务。

这里,我们使用的计算机视觉领域的经典数据集:CIFAR-10数据集,网络为ResNet18模型,损失函数为交叉熵损失,优化器为Adam优化器,评价指标为准确率。

5.5.1 数据处理

5.5.1.1 数据集介绍

CIFAR-10数据集包含了10种不同的类别、共60,000张图像,其中每个类别的图像都是6000张,图像大小均为32×32像素。CIFAR-10数据集的示例如 图5.15 所示。
实验六 卷积神经网络(5)使用预训练resnet18实现CIFAR-10分类_第1张图片

5.5.1.2 数据读取

cifar-10 数据集由 60000 张分辨率为 32x32 彩色图像组成,共分为 10 类,每类包含 6000 张图像,cifar-10 数据集有 50000 个训练图像和 10000 个测试图像。
最终的数据集构成为:

  • 训练集:50 000条样本。
  • 验证集:10 000条样本。
  • 测试集:10 000条样本。
    读取一个batch数据的代码如下所示:
import torch
from torchvision.transforms import transforms
import torchvision
from torch.utils.data import DataLoader
 
transformer=transforms.Compose([transforms.ToTensor(),
                               transforms.Normalize(mean=[0.4914, 0.4822, 0.4465], std=[0.2023, 0.1994, 0.2010])])
 
trainset = torchvision.datasets.CIFAR10(root='./cifar10', train=True, download=True, transform=transformer)
devset=torchvision.datasets.CIFAR10(root='./cifar10',train=False,download=True,transform=transformer)
testset=torchvision.datasets.CIFAR10(root='./cifar10',train=False,download=True,transform=transformer)
 
classes = ('plane', 'car', 'bird', 'cat',
           'deer', 'dog', 'frog', 'horse', 'ship', 'truck')

可视化观察其中的一张样本图像和对应的标签,代码如下所示:

image,label=trainset[0]
print(image.size())
image, label = np.array(image), int(label)
plt.imshow(image.transpose(1,2,0))
plt.show()
 
print(classes[label])

运行结果:

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

实验六 卷积神经网络(5)使用预训练resnet18实现CIFAR-10分类_第2张图片

5.5.2 模型构建

使用torchvision API中的Resnet18进行图像分类实验。

from torchvision.models import resnet18
 
resnet18_model = resnet18(pretrained=True)

Pytorch 提供 torchvision.models 接口,里面包含了一些常用用的网络结构,并提供了预训练模型,可以通过简单调用来读取网络结构和预训练模型。

1. 什么是“预训练模型”?什么是“迁移学习”?

预训练模型

预训练模型是深度学习架构,已经过训练以执行大量数据上的特定任务(例如,识别图片中的分类问题)。这种训练不容易执行,并且通常需要大量资源,超出许多可用于深度学习模型的人可用的资源,我就没有大批次GPU。在谈论预训练模型时,通常指的是在Imagenet上训练的CNN(用于视觉相关任务的架构)。ImageNet数据集包含超过1400万个图像,其中120万个图像分为1000个类别(大约100万个图像含边界框和注释)。

迁移学习

迁移学习是一种机器学习技术,顾名思义就是指将知识从一个领域迁移到另一个领域的能力。
我们知道,神经网络需要用数据来训练,它从数据中获得信息,进而把它们转换成相应的权重。这些权重能够被提取出来,迁移到其他的神经网络中,我们"迁移"了这些学来的特征,就不需要从零开始训练一个神经网络了 。

为什么我们要做预训练模型?

首先,预训练模型是一种迁移学习的应用,利用几乎无限的文本,学习输入句子的每一个成员的上下文相关的表示,它隐式地学习到了通用的语法语义知识。
第二,它可以将从开放领域学到的知识迁移到下游任务,以改善低资源任务,对低资源语言处理也非常有利。
第三,预训练模型在几乎所有NLP任务中都取得了目前最佳的成果。
最后,这个预训练模型+微调机制具备很好的可扩展性,在支持一个新任务时,只需要利用该任务的标注数据进行微调即可,一般工程师就可以实现。

2. 比较“使用预训练模型”和“不使用预训练模型”的效果。

resnet = models.resnet18(pretrained=True)
resnet = models.resnet18(pretrained=False)
pytorch学习笔记之加载预训练模型_AI算法札记的博客-CSDN博客_pytorch加载预训练模型

5.5.3 模型训练

复用RunnerV3类,实例化RunnerV3类,并传入训练配置。
使用训练集和验证集进行模型训练,共训练30个epoch。
在实验中,保存准确率最高的模型作为最佳模型。代码实现如下:

import torch.nn.functional as F
import torch.optim as opt
from Runner import RunnerV3
from metric import Accuracy
 
#指定运行设备
torch.cuda.set_device('cuda:0')
 
# 学习率大小
lr = 0.001
# 批次大小
batch_size = 64
# 加载数据
train_loader = DataLoader(trainset, batch_size=batch_size, shuffle=True)
dev_loader = DataLoader(devset, batch_size=batch_size)
test_loader = DataLoader(testset, batch_size=batch_size)
# 定义网络
model = resnet18_model
# 定义优化器,这里使用Adam优化器以及l2正则化策略,相关内容在7.3.3.2和7.6.2中会进行详细介绍
optimizer = opt.Adam(lr=lr, params=model.parameters(), weight_decay=0.005)
# 定义损失函数
loss_fn = F.cross_entropy
# 定义评价指标
metric = Accuracy(is_logist=True)
# 实例化RunnerV3
runner = RunnerV3(model, optimizer, loss_fn, metric)
# 启动训练
log_steps = 3000
eval_steps = 3000
runner.train(train_loader, dev_loader, num_epochs=30, log_steps=log_steps,
             eval_steps=eval_steps, save_path="best_model.pdparams")

运行结果:

[Train] epoch: 0/30, step: 0/23460, loss: 14.40919
[Train] epoch: 3/30, step: 3000/23460, loss: 0.77919
[Evaluate]  dev score: 0.69040, dev loss: 0.92100
[Evaluate] best accuracy performence has been updated: 0.00000 --> 0.69040
[Train] epoch: 7/30, step: 6000/23460, loss: 0.64090
[Evaluate]  dev score: 0.72860, dev loss: 0.83435
[Evaluate] best accuracy performence has been updated: 0.69040 --> 0.72860
[Train] epoch: 11/30, step: 9000/23460, loss: 0.72482
[Evaluate]  dev score: 0.73170, dev loss: 0.80458
[Evaluate] best accuracy performence has been updated: 0.72860 --> 0.73170[Train] epoch: 15/30, step: 12000/23460, loss: 0.76655
[Evaluate]  dev score: 0.72200, dev loss: 0.83292
[Train] epoch: 19/30, step: 15000/23460, loss: 0.44270[Evaluate]  dev score: 0.74660, dev loss: 0.77437
[Evaluate] best accuracy performence has been updated: 0.73170 --> 0.74660
[Train] epoch: 23/30, step: 18000/23460, loss: 0.60689
[Evaluate]  dev score: 0.73190, dev loss: 0.79955
[Train] epoch: 26/30, step: 21000/23460, loss: 0.64202
[Evaluate]  dev score: 0.75310, dev loss: 0.74181
[Evaluate] best accuracy performence has been updated: 0.74660 --> 0.75310
[Evaluate]  dev score: 0.71180, dev loss: 0.88972
[Train] Training done!

额,这个模型用的时间好长。。。可能是有一点点复杂吧

5.5.4 模型评价

使用测试数据对在训练过程中保存的最佳模型进行评价,观察模型在测试集上的准确率以及损失情况。代码实现如下:

# 加载最优模型
runner.load_model('best_model.pdparams')
# 模型评价
score, loss = runner.evaluate(test_loader)
print("[Test] accuracy/loss: {:.4f}/{:.4f}".format(score, loss))

运行结果:

[Test] accuracy/loss: 0.7270/1.8212

5.5.5 模型预测

同样地,也可以使用保存好的模型,对测试集中的数据进行模型预测,观察模型效果,具体代码实现如下:

#获取测试集中的一个batch的数据
for X, label in test_loader:
 
    logits = runner.predict(X)
    #多分类,使用softmax计算预测概率
    pred = F.softmax(logits)
    #获取概率最大的类别
    pred_class = torch.argmax(pred[2]).numpy()
    label = label[2].data.numpy()
    #输出真实类别与预测类别
    print("The true category is {} and the predicted category is {}".format(classes[label], classes[pred_class]))
    #可视化图片
    X=np.array(X)
    X=X[1]
    plt.imshow(X.transpose(1, 2, 0))
    plt.show()
    break

运行结果:

The true category is 8 and the predicted category is 8

用自己的话简单评价:LeNet、AlexNet、VGG、GoogLeNet、ResNet

LeNet
手写体数字识别模型,是一个广为人知的商用的卷积神经网络, 当年美国大多数银行用它来识别支票上面的手写数字。Lenet-5 原始结构如下图所示,包括:卷积层,降采样,卷积层,降采样,卷积层(实现全连接),全连接层,高斯连接层(进行分类)。
实验六 卷积神经网络(5)使用预训练resnet18实现CIFAR-10分类_第3张图片
LeNet特点:

(1)每个卷积层包含三个部分:卷积、池化和非线性激活函数
(2)使用卷积提取空间特征
(3)降采样(Subsample)的平均池化层(Average Pooling)
(4)双曲正切(Tanh)或S型(Sigmoid)的激活函数
MLP作为最后的分类器
(5)层与层之间的稀疏连接减少计算复杂度

AlexNet

AlexNet,它本质上就是扩展 LeNet 的深度,并应用一些 ReLU、Dropout 等技巧。AlexNet 有 5 个卷积层和 3 个最大池化层,它可分为上下两个完全相同的分支,这两个分支在第三个卷积层和全连接层上可以相互交换信息。与 Inception 同年提出的优秀网络还有 VGG-Net,它相比于 AlexNet 有更小的卷积核和更深的层级。
实验六 卷积神经网络(5)使用预训练resnet18实现CIFAR-10分类_第4张图片
AlexNet使用两个GPU训练来提升速度,使用GPU训练。与CPU不同的是,GPU转为执行复杂的数学和几何计算而设计,AlexNet使用了2个GPU来提升速度,分别放置一半卷积核。并且使用Dropout防止过拟合,

VggNet

VGG的泛化性能非常好,常用于图像特征的抽、目标检测候选框生成等。VGG 最大的问题就在于参数数量,VGG-19 基本上是参数量最多的卷积网络架构。这一问题也是第一次提出 Inception 结构的 GoogLeNet 所重点关注的,它没有如同 VGG-Net 那样大量使用全连接网络,因此参数量非常小。
实验六 卷积神经网络(5)使用预训练resnet18实现CIFAR-10分类_第5张图片

GoogLeNet

GoogLeNet 最大的特点就是使用了 Inception 模块,它的目的是设计一种具有优良局部拓扑结构的网络,即对输入图像并行地执行多个卷积运算或池化操作,并将所有输出结果拼接为一个非常深的特征图。因为 11、33 或 5*5 等不同的卷积运算与池化操作可以获得输入图像的不同信息,并行处理这些运算并结合所有结果将获得更好的图像表征。
实验六 卷积神经网络(5)使用预训练resnet18实现CIFAR-10分类_第6张图片

ResNet

Resnet从避免梯度消失或爆炸的角度,使用残差连接结构使网络可以更深

主要的创新在残差网络,其实这个网络的提出本质上还是要解决层次比较深的时候无法训练的问题。这种借鉴了Highway Network思想的网络相当于旁边专门开个通道使得输入可以直达输出,而优化的目标由原来的拟合输出H(x)变成输出和输入的差H(x)-x,其中H(X)是某一层原始的的期望映射输出,x是输入。

总结

实验六 卷积神经网络(5)使用预训练resnet18实现CIFAR-10分类_第7张图片

你可能感兴趣的:(深度学习,python,python)