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

目录

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

5.5.1 数据处理

5.5.1.1数据集介绍

5.5.1.3 构造Dataset类

5.5.2 模型构建

5.5.3 模型训练

5.5.4 模型评价

5.5.5 模型预测

思考题

总结


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

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

数据集:CIFAR-10数据集
网络:ResNet18模型
损失函数:交叉熵损失
优化器:Adam优化器,Adam优化器的介绍参考NNDL第7.2.4.3节。
评价指标:准确率。

5.5.1 数据处理


5.5.1.1数据集介绍

 

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

5.5.1.2 数据读取

在本实验中,将原始训练集拆分成了train_set、dev_set两个部分,分别包括40 000条和10 000条样本。将data_batch_1到data_batch_4作为训练集,data_batch_5作为验证集,test_batch作为测试集。
最终的数据集构成为:

  • 训练集:40 000条样本。
  • 验证集:10 000条样本。
  • 测试集:10 000条样本。

读取一个batch数据,查看数据的维度并可视化观察其中的一张样本图像和对应的标签:

import os
import pickle
import numpy as np


def load_cifar10_batch(folder_path, batch_id=1, mode='train'):
    if mode == 'test':
        file_path = os.path.join(folder_path, 'test_batch')
    else:
        file_path = os.path.join(folder_path, 'data_batch_' + str(batch_id))

    # 加载数据集文件
    with open(file_path, 'rb') as batch_file:
        batch = pickle.load(batch_file, encoding='latin1')

    imgs = batch['data'].reshape((len(batch['data']), 3, 32, 32)) / 255.
    labels = batch['labels']

    return np.array(imgs, dtype='float32'), np.array(labels)


imgs_batch, labels_batch = load_cifar10_batch(folder_path='D:\\QQFiles\\1831277247\\FileRecv\\cifar-10-python\\cifar-10-batches-py',
                                              batch_id=1, mode='train')
# 打印一下每个batch中X和y的维度
print("batch of imgs shape: ", imgs_batch.shape, "batch of labels shape: ", labels_batch.shape)

import matplotlib.pyplot as plt

image, label = imgs_batch[1], labels_batch[1]
print("The label in the picture is {}".format(label))
plt.figure(figsize=(2, 2))
plt.imshow(image.transpose(1,2,0))
plt.savefig('cnn-car.pdf')

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

5.5.1.3 构造Dataset类

 构造一个CIFAR10Dataset类

import torch
from torch.utils.data import Dataset, DataLoader
import torchvision.transforms as transforms
import PIL.Image as Image


class CIFAR10Dataset(Dataset):
    def __init__(self, folder_path='D:\\QQFiles\\1831277247\\FileRecv\\cifar-10-python\\cifar-10-batches-py', mode='train'):
        if mode == 'train':
            # 加载batch1-batch4作为训练集
            self.imgs, self.labels = load_cifar10_batch(folder_path=folder_path, batch_id=1, mode='train')
            for i in range(2, 5):
                imgs_batch, labels_batch = load_cifar10_batch(folder_path=folder_path, batch_id=i, mode='train')
                self.imgs, self.labels = np.concatenate([self.imgs, imgs_batch]), np.concatenate(
                    [self.labels, labels_batch])
        elif mode == 'dev':
            # 加载batch5作为验证集
            self.imgs, self.labels = load_cifar10_batch(folder_path=folder_path, batch_id=5, mode='dev')
        elif mode == 'test':
            # 加载测试集
            self.imgs, self.labels = load_cifar10_batch(folder_path=folder_path, mode='test')
        self.transform = transforms.Compose([
            transforms.Resize((224, 224)), transforms.RandomHorizontalFlip(p=0.5), transforms.ToTensor(),
            transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])])

    def __getitem__(self, idx):
        img, label = self.imgs[idx], self.labels[idx]
        img = img.transpose(1, 2, 0)
        img = Image.fromarray(np.uint8(img))
        img = self.transform(img)
        return img, label

    def __len__(self):
        return len(self.imgs)


train_dataset = CIFAR10Dataset(folder_path='D:\\QQFiles\\1831277247\\FileRecv\\cifar-10-python\\cifar-10-batches-py', mode='train')
dev_dataset = CIFAR10Dataset(folder_path='D:\\QQFiles\\1831277247\\FileRecv\\cifar-10-python\\cifar-10-batches-py', mode='dev')
test_dataset = CIFAR10Dataset(folder_path='D:\\QQFiles\\1831277247\\FileRecv\\cifar-10-python\\cifar-10-batches-py', mode='test')

5.5.2 模型构建

from torchvision.models import resnet18
 
resnet18_model = resnet18()

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

预训练模型就是预先训练一个模型,然后根据任务所需对预先训练出的模型进行调整,使之更符合自己的目标任务,从而提高效率与精度。迁移学习是一种机器学习技术,就是指将知识从一个领域迁移到另一个领域的能力。神经网络需要用数据来训练,它从数据中获得信息,进而把它们转换成相应的权重。这些权重能够被提取出来,迁移到其他的神经网络中。

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

resnet = models.resnet18(pretrained=True)
resnet = models.resnet18(pretrained=False)

 未进行预训练

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

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

可以看出进行预训练后准确率更高 

 【深度学习】使用预训练模型_DrCrypto的博客-CSDN博客_深度学习预训练模型操作

pytorch学习笔记之加载预训练模型_AI算法札记的博客-CSDN博客_pytorch加载预训练模型 

5.5.3 模型训练

import torch.nn.functional as F
import torch.optim as opt
 
# 指定运行设备
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
print(device)
# 学习率大小
lr = 0.01
# 批次大小
batch_size = 64 
# 加载数据
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)
dev_loader = DataLoader(dev_dataset, batch_size=batch_size)
test_loader = DataLoader(test_dataset, batch_size=batch_size) 
# 定义网络
model = resnet18_model
model.to(device)
# 定义优化器,这里使用Adam优化器以及l2正则化策略,相关内容在7.3.3.2和7.6.2中会进行详细介绍
optimizer = opt.SGD(model.parameters(),lr=lr, momentum=0.9)
# 定义损失函数
loss_fn = F.cross_entropy
# 定义评价指标
metric = Accuracy()
# 实例化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")

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

 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.8140/0.5936

5.5.5 模型预测

 同样地,也可以使用保存好的模型,对测试集中的数据进行模型预测,观察模型效果

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

 

 思考题

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

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

 图中是5种深度的ResNet,分别是18,34,50,101和152,每个网络都有5部分——conv1,conv2_x,conv3_x,conv4_x,conv5_x。看一下ResNet18,有一个7x7x64的卷积,下面是(2+2+2+2)*2=16层,还有一个fc层,总共18层

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

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

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

LeNet-5的输入一般都是尺寸较小,并且灰度的图像。如果想处理更高分辨率图像的话,就需要叠加更多的卷积层,增加模型的容量。

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

 AlextNet跟LeNet网络结构相似,只是因为叠加了更多的卷积层使得网络更深了。此外,在卷积层也使用了更多的过滤器。它含有8层网络,整体的结构是:5个卷积层(每个卷积都接了池化层),2个全连接层,以及softmax层。其中,每个卷积层和全连接层都使用了ReLU激活函数。

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

 它有13个卷积层,2个全连接层,以及softmax层。其中,在所有的卷积层中,都使用3*3的过滤器。作者实验了不同网络层数的VGGNet,从11->13->16层,效果一直都有明显的改进。但是从16->19层后,效果有微降的趋势。这可能是网络加深到一定层数后,梯度消失/爆炸的问题开始突显。

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

 为了避免网络过深导致的巨大参数量,GoogLeNet使用了1*1的卷积操作,并且在网络最后使用了全局的平均池化来代替全连接层。网络的参数量大约是AlexNet的1/10。

ResNet

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

 主要解决深层网络梯度消失问题。

总结

 使用思维导图全面总结CNN

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

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

参考

经典深度学习CNN总结 - LeNet、AlexNet、GoogLeNet、VGG、ResNet

经典的卷积神经网络:LeNet,AlexNet,GoogLeNet,VGG,ResNet

NNDL 实验5(下)

Deep Residual Learning for Image Recognition

你可能感兴趣的:(python,matplotlib,开发语言)