【实验13】使用预训练ResNet18进行CIFAR10分类

ops/2024/11/29 15:14:20/

目录

1 数据处理

1.1 数据集介绍

1.2数据处理与划分

 2 模型构建- Pytorch高层API中的Resnet18

3 模型训练

4 模型评价

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

6 模型预测

7 完整代码

8 参考链接


1 数据处理

1.1 数据集介绍

  •  数据规模: CIFAR10数据集共有60000个样本,每个样本都是一张32*32像素的RGB图像(彩色图像)。
  • 数据集划分:60000个样本被分成了50000个训练样本和10000个测试样本
  • 类别内容:    CIFAR10中有10类物体,标签值分别按照0~9来区分,他们分别是飞机( airplane )、汽车( automobile )、鸟( bird )、猫( cat )、鹿( deer )、狗( dog )、青蛙( frog )、马( horse )、船( ship )和卡车( truck )

           

  • 数据来源:是从一个叫做【the 80 million tiny images dataset】(“8000 万张小图” 数据集)中精炼剥离出来的一部分,由 Hinton 的学生 Alex Krizhevsky 和 Ilya Sutskever 整理 。

1.2数据处理与划分

        本实验中,对数据集进行归一化处理后,将原始训练集拆分成了train_set、dev_set两个部分,分别包括40 000条和10 000条样本。

# ==================数据处理================
transforms = transforms.Compose([transforms.Resize((32,32)), # 重新设置图片尺寸transforms.ToTensor(), # 转换为tensor格式transforms.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225])] # 归一化
)trainset = torchvision.datasets.CIFAR10(root='./cifar10', train=True, download=False, transform=transforms)
testset = torchvision.datasets.CIFAR10(root='./cifar10', train=False, download=False, transform=transforms)classes = ('plane', 'car', 'bird', 'cat','deer', 'dog', 'frog', 'horse', 'ship', 'truck')# 数据划分
train_size = int(0.8 * len(trainset))  # 80%的数据作为训练集
dev_size = len(trainset) - train_size  # 剩余的20%作为验证集
test_size =len(testset)
print(f"train_size: {train_size},dev_size :{dev_size},test_szie :{test_size}")
# 随机划分训练集和验证集
train_data, dev_data = random_split(trainset, [train_size, dev_size])

运行结果: 

torch.Size([3, 32, 32])
train_size: 40000,dev_size :10000,test_szie :10000

可视化观察其中的一张样本图像和对应的标签:

# 可视化第一张图像
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])

frog

 2 模型构建- Pytorch高层API中的Resnet18

        Pytorch 提供 torchvision.models 接口,里面包含了一些常用用的网络结构,并提供了预训练模型。预训练模型可以通过设置pretrained=True来构建。只需要网络结构,不加载参数来初始化,可以将pretrained = False

什么是“预训练模型”?

        预训练模型是指在大规模数据集上预先进行训练好的神经网络模型,通常在通用任务上学习到的特征可以被迁移到其他特定任务中。预训练模型的思想是利用大规模数据的信息来初始化模型参数,然后通过微调或迁移学习,将模型适应在特定的目标任务上。即在训练结束时结果比较好的一组权重值,研究人员分享出来供其他人使用。

        “预训练模型”可以理解为一个“已经学过一部分知识”的模型。举个例子,如果学习英语,先会通过一段时间学习基础的语法和词汇,这段时间就像是模型的“预训练”。然后,在这个基础上,你可能会学习更具体的内容,比如写作文、翻译等。这时,你可以用预训练的知识来加速你的学习过程。(在“GPT”中,P代表的是“Pre-trained”(预训练)的意思)

  • 预训练模型期望的输入是RGB图像的mini-batch:(batch_size, 3, H, W),并且H和W不能低于224。
  • 图像的像素值必须在范围[0,1]间,并且用均值mean=[0.485, 0.456, 0.406]和方差std=[0.229, 0.224, 0.225]进行归一化。

什么是“迁移学习”?

        迁移学习(Transfer Learning)通俗来讲就是学会举一反三的能力,通过运用已有的知识来学习新的知识,其核心是找到已有知识和新知识之间的相似性,通过这种相似性的迁移达到迁移学习的目的。

        迁移学习就像是学会了弹钢琴后,去学电子琴,不需要从头开始学,因为已经掌握了很多钢琴的技巧和知识,这些可以直接迁移到电子琴的学习上。同样,模型从某个任务中学到的知识,可以直接用来帮助解决另一个任务。

        过程:选择预训练模型--->冻结预训练模型参数--->在新数据集上训练新增加的层--->微调预训练模型的层--->评估和测试。

        【冻结参数:设置参数的requires_grad属性为False,表示在训练过程中这些参数不需要计算梯度】

使用预训练模型

# ======================模型构建=====================
resnet18_model = resnet18(pretrained=True)
#resnet18_model = resnet18(pretrained=False)

不使用预训练模型

# ======================模型构建=====================
#resnet18_model = resnet18(pretrained=True)
resnet18_model = resnet18(pretrained=False)

3 模型训练

# ======================模型训练======================
import torch.nn.functional as F
import torch.optim as opt
from Runner import RunnerV3,Accuracy,plot# 指定运行设备
torch.cuda.set_device('cuda:0')
# 学习率大小
lr = 0.001
# 批次大小
batch_size = 64
# 创建 DataLoader
train_loader = DataLoader(train_data, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(testset, batch_size=batch_size)
dev_loader = DataLoader(dev_data, 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()
# 实例化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")
# 加载最优模型
runner.load_model('best_model.pdparams')
plot(runner, fig_name='cnn-loss4.pdf')

使用预训练模型

[Train] epoch: 0/30, step: 0/18750, loss: 13.31647
[Train] epoch: 4/30, step: 3000/18750, loss: 0.84583
[Evaluate]  dev score: 0.66950, dev loss: 0.95410
[Evaluate] best accuracy performance has been updated: 0.00000 --> 0.66950
[Train] epoch: 9/30, step: 6000/18750, loss: 0.61820
[Evaluate]  dev score: 0.73190, dev loss: 0.79586
[Evaluate] best accuracy performance has been updated: 0.66950 --> 0.73190
[Train] epoch: 14/30, step: 9000/18750, loss: 0.64871
[Evaluate]  dev score: 0.74370, dev loss: 0.77339
[Evaluate] best accuracy performance has been updated: 0.73190 --> 0.74370
[Train] epoch: 19/30, step: 12000/18750, loss: 0.59597
[Evaluate]  dev score: 0.72890, dev loss: 0.83410
[Train] epoch: 24/30, step: 15000/18750, loss: 0.29033
[Evaluate]  dev score: 0.75560, dev loss: 0.74253
[Evaluate] best accuracy performance has been updated: 0.74370 --> 0.75560
[Train] epoch: 28/30, step: 18000/18750, loss: 0.80589
[Evaluate]  dev score: 0.74720, dev loss: 0.77720
[Evaluate]  dev score: 0.75970, dev loss: 0.73110
[Evaluate] best accuracy performance has been updated: 0.75560 --> 0.75970
[Train] Training done!

        验证集上的准确率(dev score)随着训练轮次的推进呈现出不断上升的趋势,从最初的0.00000逐渐提升到最后的0.75970 。

 不使用预训练模型 

[Train] epoch: 0/30, step: 0/18750, loss: 7.30914
[Train] epoch: 4/30, step: 3000/18750, loss: 0.87599
[Evaluate]  dev score: 0.66180, dev loss: 0.99899
[Evaluate] best accuracy performance has been updated: 0.00000 --> 0.66180
[Train] epoch: 9/30, step: 6000/18750, loss: 0.83667
[Evaluate]  dev score: 0.68640, dev loss: 0.93156
[Evaluate] best accuracy performance has been updated: 0.66180 --> 0.68640
[Train] epoch: 14/30, step: 9000/18750, loss: 0.73321
[Evaluate]  dev score: 0.71090, dev loss: 0.87719
[Evaluate] best accuracy performance has been updated: 0.68640 --> 0.71090
[Train] epoch: 19/30, step: 12000/18750, loss: 0.81280
[Evaluate]  dev score: 0.71270, dev loss: 0.89412
[Evaluate] best accuracy performance has been updated: 0.71090 --> 0.71270
[Train] epoch: 24/30, step: 15000/18750, loss: 0.36561
[Evaluate]  dev score: 0.71520, dev loss: 0.86391
[Evaluate] best accuracy performance has been updated: 0.71270 --> 0.71520
[Train] epoch: 28/30, step: 18000/18750, loss: 0.31370
[Evaluate]  dev score: 0.72560, dev loss: 0.84174
[Evaluate] best accuracy performance has been updated: 0.71520 --> 0.72560
[Evaluate]  dev score: 0.72780, dev loss: 0.82727
[Evaluate] best accuracy performance has been updated: 0.72560 --> 0.72780
[Train] Training done!

         验证集上的准确率(dev score)随着训练轮次的推进呈现出逐步上升的趋势,从最初的0.00000逐渐提升到最后的0.72780

4 模型评价

# ======================模型评价=====================
score, loss = runner.evaluate(test_loader)
print("[Test] accuracy/loss: {:.4f}/{:.4f}".format(score, loss))

使用预训练模型

[Test] accuracy/loss: 0.7488/0.7621

不使用预训练模型 

[Test] accuracy/loss: 0.7320/0.8259

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

        (1)训练初期,由于预训练模型已经学习到了一些通用特征,模型能较快地适应新任务的数据分布,损失函数往往下降得相对较快。而不使用预训练模型,需要从零开始学习数据中的所有特征,所以损失函数下降速度通常相对较慢。【在未使用预训练模型的训练结果中,epoch: 0/30, step: 0/18750, loss: 7.30914到epoch: 9/30, step: 6000/18750, loss: 0.83667,而t同样轮数使用预训练模型的从epoch: 0/30, step: 0/18750, loss: 13.31647到epoch: 9/30, step: 6000/18750, loss: 0.61820。观察前期损失变化图像也能发现使用预训练模型的斜率比未使用的斜率要大】

        (2)由最终在测试集上的评价可以看出,使用预训练模型的泛化能力要好一点。(但这个实验中好像没有太大的差别)

        (3)使用预训练模型的训练时间相对较短,对计算资源的需求也相对较少。因为主要是进行微调操作,只需在预训练模型的基础上,根据新任务的数据对部分参数进行调整,不需要从头开始训练整个模型架构。微调过程相比于从头训练一个复杂的模型,计算量大幅降低,不需要长时间占用大量的 GPU 等计算资源。

  

6 模型预测

#=========================模型预测===================# 获取测试集中的一个batch的数据
for X, label in test_loader:logits = runner.predict(X)# 多分类,使用softmax计算预测概率pred = F.softmax(logits)# 获取概率最大的类别pred_class = torch.argmax(pred[2]).cpu().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

可见模型成功预测了。 

7 完整代码

'''
@Function: 使用预训练resnet18(调用API)实现CIFAR-10分类
@Author: lxy
@date: 2024/11/27
'''
import torch
from torchvision.transforms import transforms
import torchvision
from torch.utils.data import DataLoader,random_split
import numpy as np
from torchvision.models import resnet18
import matplotlib.pyplot as plt# ==================数据处理================
transforms = transforms.Compose([transforms.Resize((32,32)), # 重新设置图片尺寸transforms.ToTensor(), # 转换为tensor格式transforms.Normalize([0.485,0.456,0.406],[0.229,0.224,0.225])] # 归一化
)trainset = torchvision.datasets.CIFAR10(root='./cifar10', train=True, download=False, transform=transforms)
testset = torchvision.datasets.CIFAR10(root='./cifar10', train=False, download=False, transform=transforms)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])# ======================模型构建=====================
resnet18_model = resnet18(pretrained=True)
#resnet18_model = resnet18(pretrained=False)# ======================模型训练======================
import torch.nn.functional as F
import torch.optim as opt
from Runner import RunnerV3,Accuracy,plot# 指定运行设备
torch.cuda.set_device('cuda:0')
# 学习率大小
lr = 0.001
# 批次大小
batch_size = 64
# 加载数据
train_size = int(0.8 * len(trainset))  # 80%的数据作为训练集
dev_size = len(trainset) - train_size  # 剩余的20%作为验证集
test_size =len(testset)
print(f"train_size: {train_size},dev_size :{dev_size},test_szie :{test_size}")
# 随机划分训练集和验证集
train_data, dev_data = random_split(trainset, [train_size, dev_size])
# 创建 DataLoader
train_loader = DataLoader(train_data, batch_size=batch_size, shuffle=True)
test_loader = DataLoader(testset, batch_size=batch_size)
dev_loader = DataLoader(dev_data, 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()
# 实例化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")
# 加载最优模型
runner.load_model('best_model.pdparams')
plot(runner, fig_name='cnn-loss4.pdf')# ======================模型评价=====================
score, loss = runner.evaluate(test_loader)
print("[Test] accuracy/loss: {:.4f}/{:.4f}".format(score, loss))
#=========================模型预测===================# 获取测试集中的一个batch的数据
for X, label in test_loader:logits = runner.predict(X)# 多分类,使用softmax计算预测概率pred = F.softmax(logits)# 获取概率最大的类别pred_class = torch.argmax(pred[2]).cpu().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

8 参考链接

参考链接:

CIFAR-10数据集简介_cifar10-CSDN博客

测试集可用作验证集

迁移学习之——什么是迁移学习(Transfer Learning)

torchvision.models_torchvision models

一文读懂预训练模型(非常详细)

Adam优化器(理论、公式、代码)


http://www.ppmy.cn/ops/137675.html

相关文章

通信网络安全分层及关键技术解决

要实现信息化,就必须重视信息网络安全。信息网络安全绝不仅是IT行业的问题,而是一个社会问题,是一个包括多学科的系统安全工程问题,并直接关系到国家安全。因此,知名安全专家沈昌祥院士呼吁,要像重视两弹一…

R 因子

R 因子 引言 在金融领域,风险管理和投资策略的优化一直是核心议题。传统的风险度量工具,如波动率、Beta系数等,虽然在一定程度上能够帮助投资者理解市场的波动和资产的相对风险,但它们往往无法全面捕捉到市场动态的复杂性。因此…

2024年12月计划(ue5太阳系+ue独立游戏+freex+GPU精粹泛读催眠)

根据以往, 如果周一到周四白天 一,UE5太阳系每天一节,原因是尽可能地接手外包的源码,全职去做UE,提高核心竞争力。剩下26节,868分钟。如果按照每天10分钟,显然是不够的。分5周进行,…

【R库包安装】R库包安装总结:conda、CRAN等

【R库包安装】R库包安装总结:conda、CRAN等 方法1:基于 R 的 CRAN 仓库安装CRAN库包查询从 CRAN 安装 方法2:使用conda安装库包确保已安装 R 和 Conda 环境使用 Conda 官网浏览是否存在相应库包Conda 安装 R 库 方法3:从 GitHub 安…

详解登录MySQL时出现SSL connection error: unknown error number错误

目录 登录MySQL时出错SSL connection error: unknown error number 出错原因 使用MySQL自带的工具登录MySQL 登陆之后,使用如下命令进行查看 解决方法 找到MySQL8安装目录下的my.ini配置文件 记事本打开my.ini文件,然后按下图所示添加配置 此时再…

AI时代的PPT革命:智能生成PPT工具为何备受青睐?

在日常工作和学习中,PPT是我们不可或缺的表达工具。制作一份精美的PPT常常需要耗费数小时,甚至几天的时间。从选择主题到调整排版,琐碎的细节让人筋疲力尽。但现在一种名为“AI生成PPT”的技术正悄然崛起,彻底颠覆了传统PPT制作的…

Rust代写 OCaml代做 Go R语言 SML Haskell Prolog DrRacket Lisp

Rust: Rust是一种注重性能和安全性的系统编程语言。它具有严格的内存管理,能够防止许多常见的内存错误。Rust作业可能涉及编写高效的算法、处理并发问题、与操作系统接口等。OCaml: OCaml是一种函数式编程语言,具有强大的类型系统…

Java开发中对List<Map<String, Object>>集合去重并按大小拆分子列表

Java开发中对List< Map< String, Object > >集合去重并按大小拆分子列表 一、使用场景二、实现步骤三、相关知识四、代码示例 一、使用场景 在处理大量List<Map<String, Object>>集合的数据时&#xff0c;为确保数据的唯一性&#xff0c;需要先根据Ma…