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

发布于:2022-11-09 ⋅ 阅读:(8) ⋅ 点赞:(0) ⋅ 评论:(0)

目录

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

5.5.1 数据处理

5.5.1.1 数据集介绍

5.5.1.2 数据读取

5.5.2 模型构建

5.5.3 模型训练

5.5.4 模型评价

5.5.5 模型预测

1.阅读《Deep Residual Learning for Image Recognition》,了解5种深度的ResNet(18,34,50,101和152),并简单谈谈自己的看法。

2.用自己的话简单评价: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×3232×32像素。CIFAR-10数据集的示例如 图5.15 所示。

 

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.5.2 模型构建

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

from torchvision.models import resnet18

resnet18_model = resnet18()

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

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: 7.09256
[Train] epoch: 3/30, step: 3000/23460, loss: 0.68586
[Evaluate]  dev score: 0.65970, dev loss: 0.99221
[Evaluate] best accuracy performence has been updated: 0.00000 --> 0.65970
[Train] epoch: 7/30, step: 6000/23460, loss: 0.57793
[Evaluate]  dev score: 0.68040, dev loss: 0.93663
[Evaluate] best accuracy performence has been updated: 0.65970 --> 0.68040
[Train] epoch: 11/30, step: 9000/23460, loss: 0.90398
[Evaluate]  dev score: 0.72970, dev loss: 0.80892
[Evaluate] best accuracy performence has been updated: 0.68040 --> 0.72970
[Train] epoch: 15/30, step: 12000/23460, loss: 0.54963
[Evaluate]  dev score: 0.70290, dev loss: 0.89479
[Train] epoch: 19/30, step: 15000/23460, loss: 0.73634
[Evaluate]  dev score: 0.73600, dev loss: 0.80622
[Evaluate] best accuracy performence has been updated: 0.72970 --> 0.73600
[Train] epoch: 23/30, step: 18000/23460, loss: 0.47213
[Evaluate]  dev score: 0.73230, dev loss: 0.80564
[Train] epoch: 26/30, step: 21000/23460, loss: 0.52694
[Evaluate]  dev score: 0.72850, dev loss: 0.81326
[Evaluate]  dev score: 0.73140, dev loss: 0.79042
[Train] Training done!

这个训练过程进行了几乎一天的时间 ,可能是因为模型太复杂,backward过程耗时太多。

 在本实验中,使用了第7章中介绍的Adam优化器进行网络优化,如果使用SGD优化器,会造成过拟合的现象,在验证集上无法得到很好的收敛效果。可以尝试使用第7章中其他优化策略调整训练配置,达到更高的模型精度。

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.7360/0.8062

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 ship and the predicted category is ship

 

1.阅读《Deep Residual Learning for Image Recognition》,了解5种深度的ResNet(18,34,50,101和152),并简单谈谈自己的看法。

表格中提到了五种深度的ResNet,分别是18,34,50,101,152。表格的最左侧是说明无论是深度为多少的ResNet都将网络分成了五部分。分别是:conv1,conv2_x,conv3_x,conv4_x,conv5_x。而仔细观察图,可以得出以下结论。

从50-layer之后,conv2——conv5都是采取三层块结构以减小计算量和参数数量
说明50-layer以后开始采用BottleBlock
从50-layer之后,层数的加深仅仅体现在conv4_x这一层中,也就是output size为14×14的图像


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

LeNet

Lenet也称Lenet-5,共5个隐藏层(不考虑磁化层),网络结构为:

 Conv(5*5,6,1)+Conv(5*5,16)+FC(120)+FC(84)+FC(10)

AlexNet

提出背景:解决Lenet识别大尺寸图片进行的效果不尽人意的问题

与LeNet相比,AlexNet具有更深的网络结构,共8个隐藏层,包含5层卷积和3层全连接,网络结构为:

Conv(11*11,96,2)+Conv(5*5,256,1)+Conv(3*3,384,1)+Conv(3*3,384,1)+Conv(3*3,256,1)+FC(4096)+FC(4096)+FC(1000)

同时使用了如下方法改进模型的训练过程:

数据增广:深度学习中常用的一种处理方式,通过对训练随机加一些变化,比如平移、缩放、裁剪、旋转、翻转或者增减亮度等,产生一系列跟原始图片相似但又不完全相同的样本,从而扩大训练数据集。通过这种方式,可以随机改变训练样本,避免模型过度依赖于某些属性,能从一定程度上抑制过拟合。
使用Dropout抑制过拟合
使用ReLU激活函数减少梯度消失现象(梯度消失和梯度爆炸):在AlexNet之前,神经网络一般都使用sigmoid或tanh作为激活函数,这类函数在自变量非常大或者非常小时,函数输出基本不变,称之为饱和函数。为了提高训练速度,AlexNet使用了修正线性函数ReLU,它是一种非饱和函数,与 sigmoid 和tanh 函数相比,ReLU分片的线性结构实现了非线性结构的表达能力,梯度消失现象相对较弱,有助于训练更深层的网络。
使用GPU训练。与CPU不同的是,GPU转为执行复杂的数学和几何计算而设计,AlexNet使用了2个GPU来提升速度,分别放置一半卷积核。
局部响应归一化。AlexNet使用局部响应归一化技巧,将ImageNet上的top-1与top-5错误率分别减少了1.4%和1.2%。
重叠池化层。与不重叠池化层相比,重叠池化层有助于缓解过拟合,使得AlexNet的top-1和top-5错误率分别降低了0.4%和0.3%。
 

VggNet

提出背景:alexNet虽然效果好,但是没有给出深度神经网络的设计方向。即,如何把网络做到更深。

在论文中有VGG-11,VGG-13,VGG-16,VGG-19的实验比较,VGG-16的效果最佳,这里给出网络结构。

VGG11:Conv(3*3,64,1)*1+Conv(3*3,128,1)*1+Conv(3*3,256,1)*2+Conv(3*3,512,1)*2+Conv(3*3,512,1)*2+FC(4096)+FC(4096)+FC(1000)
VGG13:Conv(3*3,64,1)*2+Conv(3*3,128,1)*2+Conv(3*3,256,1)*2+Conv(3*3,512,1)*2+Conv(3*3,512,1)*2+FC(4096)+FC(4096)+FC(1000)
VGG16:Conv(3*3,64,1)*2+Conv(3*3,128,1)*2+Conv(3*3,256,1)*3+Conv(3*3,512,1)*3+Conv(3*3,512,1)*3+FC(4096)+FC(4096)+FC(1000)
VGG19:Conv(3*3,64,1)*2+Conv(3*3,128,1)*2+Conv(3*3,256,1)*4+Conv(3*3,512,1)*4+Conv(3*3,512,1)*4+FC(4096)+FC(4096)+FC(1000)

vggnet严格使用3*3小尺寸卷积和池化层构造深度CNN,取得较好的效果。小卷积能减少参数,方便堆叠卷积层来增加深度(加深了网络,减少了卷积)。即vggnet=更深的Alex net+conv(3*3)

googlenet

背景:alexNet虽然效果好,但是没有给出深度神经网络的设计方向。即,如何把网络做到更深。

googlenet设计了inception结构来降低通道数,减少计算复杂度,其中inception结构包括以下几种

 

  • inception v1(使用1*1卷积降低通道数)
  • inception v2=inception v1+3*3(学习自vggnet)+BN
  • inception v3=inception v2+n*1+1*n(将3*3变成1*3+3*1)

googlenet:Alex net+inception=conv*1+inception*9+FC*1

Resnet

提出背景:alexNet虽然效果好,但是没有给出深度神经网络的设计方向。即,如何把网络做到更深。

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

 

总结

使用思维导图全面总结CNN

心得体会 

        这次实验使用torchvision.models里的resnet模型训练了cifar10数据集,感觉这个预训练的模型参数量很大,导致训练时间耗费很多。至此卷积神经网络的学习就告一段落,感觉对于resnet等经典网络的理解还不够深,等到期末复习的时候再好好学一下。