pytorch迁移学习训练图像分类

2023-09-17 17:06:53

代码和图片等资源均来源于哔哩哔哩up主:同济子豪兄
讲解视频:Pytorch迁移学习训练自己的图像分类模型

一、环境配置

1,安装所需的包

pip install numpy pandas matplotlib seaborn plotly requests tqdm opencv-python pillow wandb -i https://pypi.tuna.tsinghua.edu.cn/simple

2,安装Pytorch

pip3 install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu113

3,创建目录

import os
# 存放训练得到的模型权重
os.mkdir('checkpoint')

4,下载数据集压缩包(下载之后需要解压数据集)

wget https://zihao-openmmlab.obs.cn-east-3.myhuaweicloud.com/20220716-mmclassification/dataset/fruit30/fruit30_split.zip

二、迁移学习关键代码

以下是迁移学习的三种选择,根据训练的需求选择不同的迁移方法:

  • 选择一:只微调训练模型最后一层(全连接分类层)
model = models.resnet18(pretrained=True) # 载入预训练模型
# 修改全连接层,使得全连接层的输出与 当前数据集类别数n_class 对应
model.fc = nn.Linear(model.fc.in_features, n_class)
# 只微调训练最后一层全连接层的参数,其它层冻结
optimizer = optim.Adam(model.fc.parameters())
  • 选择二:微调训练所有层。

适用于训练数据集与预训练模型相差大时,可以选择微调训练所有层,此时只使用预训练模型的部分权重和特征,例如原始模型为imageNet,而训练数据为医疗相关

model = models.resnet18(pretrained=True) # 载入预训练模型
model.fc = nn.Linear(model.fc.in_features, n_class)
optimizer = optim.Adam(model.parameters())
  • 选择三:随机初始化模型全部权重,从头训练所有层
model = models.resnet18(pretrained=False) # 只载入模型结构,不载入预训练权重参数
model.fc = nn.Linear(model.fc.in_features, n_class)
optimizer = optim.Adam(model.parameters())

三、完整代码

import time
import os

import numpy as np
from tqdm import tqdm

import torch
import torchvision
import torch.nn as nn

# 忽略出现的红色提示
import warnings
warnings.filterwarnings("ignore")

# 有 GPU 就用 GPU,没有就用 CPU
device = torch.device('cuda:0' if torch.cuda.is_available() else 'cpu')
print('device', device)

from torchvision import transforms

# 训练集图像预处理:缩放裁剪、图像增强、转 Tensor、归一化
train_transform = transforms.Compose([transforms.RandomResizedCrop(224),
                                      transforms.RandomHorizontalFlip(),
                                      transforms.ToTensor(),
                                      transforms.Normalize([0.485, 0.456, 0.406], [0.229, 0.224, 0.225])
                                     ])

# 测试集图像预处理-RCTN:缩放、裁剪、转 Tensor、归一化
test_transform = transforms.Compose([transforms.Resize(256),
                                     transforms.CenterCrop(224),
                                     transforms.ToTensor(),
                                     transforms.Normalize(
                                         mean=[0.485, 0.456, 0.406], 
                                         std=[0.229, 0.224, 0.225])
                                    ])

# 数据集文件夹路径
dataset_dir = 'fruit30_split'
train_path = os.path.join(dataset_dir, 'train')	# 测试集路径
test_path = os.path.join(dataset_dir, 'val')	# 测试集路径

from torchvision import datasets

# 载入训练集
train_dataset = datasets.ImageFolder(train_path, train_transform)

# 载入测试集
test_dataset = datasets.ImageFolder(test_path, test_transform)

# 各类别名称
class_names = train_dataset.classes
n_class = len(class_names)

# 定义数据加载器DataLoader
from torch.utils.data import DataLoader

BATCH_SIZE = 32

# 训练集的数据加载器
train_loader = DataLoader(train_dataset,
                          batch_size=BATCH_SIZE,
                          shuffle=True,
                          num_workers=4
                         )

# 测试集的数据加载器
test_loader = DataLoader(test_dataset,
                         batch_size=BATCH_SIZE,
                         shuffle=False,
                         num_workers=4
                        )

from torchvision import models
import torch.optim as optim

# 选择一:只微调训练模型最后一层(全连接分类层)
model = models.resnet18(pretrained=True) # 载入预训练模型
# 修改全连接层,使得全连接层的输出与当前数据集类别数对应
# 新建的层默认 requires_grad=True,指定张量需要梯度计算
model.fc = nn.Linear(model.fc.in_features, n_class)
model.fc	# 查看全连接层
# 只微调训练最后一层全连接层的参数,其它层冻结
optimizer = optim.Adam(model.fc.parameters())    # optim 是 PyTorch 的一个优化器模块,用于实现各种梯度下降算法的优化方法


# 选择二:微调训练所有层
# 训练数据集与预训练模型相差大时,可以选择微调训练所有层,只使用预训练模型的部分权重和特征,例如原始模型为imageNet,训练数据为医疗相关
# model = models.resnet18(pretrained=True) # 载入预训练模型
# model.fc = nn.Linear(model.fc.in_features, n_class)
# optimizer = optim.Adam(model.parameters())


# 选择三:随机初始化模型全部权重,从头训练所有层
# model = models.resnet18(pretrained=False) # 只载入模型结构,不载入预训练权重参数
# model.fc = nn.Linear(model.fc.in_features, n_class)
# optimizer = optim.Adam(model.parameters())

# 训练配置
model = model.to(device)

# 交叉熵损失函数
criterion = nn.CrossEntropyLoss()

# 训练轮次 Epoch
EPOCHS = 30

# 遍历每个 EPOCH
for epoch in tqdm(range(EPOCHS)):

    model.train()

    for images, labels in train_loader:  # 获取训练集的一个 batch,包含数据和标注
        images = images.to(device)
        labels = labels.to(device)

        outputs = model(images)           # 前向预测,获得当前 batch 的预测结果
        loss = criterion(outputs, labels) # 比较预测结果和标注,计算当前 batch 的交叉熵损失函数
        
        optimizer.zero_grad()
        loss.backward()                   # 损失函数对神经网络权重反向传播求梯度
        optimizer.step()                  # 优化更新神经网络权重

# 测试集上初步测试
model.eval()
with torch.no_grad():
    correct = 0
    total = 0
    for images, labels in tqdm(test_loader): # 获取测试集的一个 batch,包含数据和标注
        images = images.to(device)
        labels = labels.to(device)
        outputs = model(images)              # 前向预测,获得当前 batch 的预测置信度
        _, preds = torch.max(outputs, 1)     # 获得最大置信度对应的类别,作为预测结果
        total += labels.size(0)
        correct += (preds == labels).sum()   # 预测正确样本个数

    print('测试集上的准确率为 {:.3f} %'.format(100 * correct / total))

# 保存模型
torch.save(model, 'checkpoint/fruit30_pytorch_A1.pth') # 选择一:微调全连接层
# torch.save(model, 'checkpoint/fruit30_pytorch_A2.pth') # 选择二:微调所有层
# torch.save(model, 'checkpoint/fruit30_pytorch_A3.pth') # 选择三:随机权重

四、结果对比

调用不同迁移学习得到的模型对比测试集准确率

# 测试集导入和图像预处理等代码和上述完整代码中一致,此处省略……

# 调用自己训练的模型
model = torch.load('checkpoint/fruit30_pytorch_A1.pth')

# 测试集上进行测试
model.eval()
with torch.no_grad():
    correct = 0
    total = 0
    for images, labels in tqdm(test_loader): # 获取测试集的一个 batch,包含数据和标注
        images = images.to(device)
        labels = labels.to(device)
        outputs = model(images)              # 前向预测,获得当前 batch 的预测置信度
        _, preds = torch.max(outputs, 1)     # 获得最大置信度对应的类别,作为预测结果
        total += labels.size(0)
        correct += (preds == labels).sum()   # 预测正确样本个数

    print('测试集上的准确率为 {:.3f} %'.format(100 * correct / total))

结果如下:
对于微调全连接层的选择一,测试集准确率为 72.078%
在这里插入图片描述
而所有权重随机的选择三测试集准确率为 43.228%
43.228

总体而言,迁移学习能够利用已有的知识和经验,加速模型的训练过程,提高模型的性能。

更多推荐

python

一、认识python(一)python起源python的创始人为GuidovonRossum(吉多·范·罗苏姆),俗称”龟叔“,荷兰人。1989年圣诞节期间,在阿姆斯特丹,Guido为了打发圣诞节的无趣,决心开发一个新的脚本解释程序,做为ABC语言的一种继承。之所以选中Python(大蟒蛇的意思)作为该编程语言的名字,

基本概念【入门、 发展简史、核心优势、各版本的含义、特性和优势、JVM、JRE 和 JDK 】(二)-全面详解(学习总结---从入门到深化)

目录JAVA入门Java发展简史Java的核心优势Java各版本的含义Java的特性和优势Java程序的运行机制JVM、JRE和JDKJava开发环境搭建JDK下载和安装JDK环境变量的配置测试JDK安装成功开发第一个Java程序第一个程序常见错误第一个Java程序的总结和提升最常用DOS命令常用Java开发工具JAV

怒刷LeetCode的第6天(Java版)

目录第一题题目来源题目内容解决方法方法一:哈希表方法二:逐个判断字符方法三:模拟减法第二题题目来源题目内容解决方法方法一:水平扫描法方法二:垂直扫描法方法三:分治法方法四:二分查找第三题题目来源题目内容解决方法方法一:双指针第一题题目来源13.罗马数字转整数-力扣(LeetCode)题目内容解决方法方法一:哈希表根据题

R语言学习笔记

R语言学习笔记一.准备环境二.认识控制台三.R包四.数据结构1.向量Vector1.1创建向量1.2访问向量中的数据1.3向量的循环补齐2.矩阵matrix2.1创建矩阵2.2访问矩阵中的数据3数组Array3.1创建数组3.2访问数组中的数据4.数据框Dataframe4.1创建数据框4.2访问数据框中的数据5因子F

linux上mysql数据备份(全量备份策略+增量备份策略)

执行备份策略前,先做好scp命令的准备解决思路:生成SSH公钥/私钥后,您需要将公钥添加到服务器上,从而使服务器可以使用该公钥来验证您的身份。生成SSH公钥/私钥的命令为ssh-keygen-trsa-b4096什么都不用输入,直接下一步即可生成SSH公钥/私钥后,您需要将公钥添加到服务器上,从而使服务器可以使用该公钥

企业该如何选择数字化转型工具?_光点科技

随着科技的不断进步和数字化的浪潮席卷全球,企业数字化转型已经成为了保持竞争力和持续增长的关键因素之一。无论企业规模大小,数字化转型都可以提高效率、降低成本、改善客户体验,从而实现更好的业务结果。然而,要成功进行数字化转型,企业首先需要选择适合自己的数字化工具。1.明确数字化转型目标在选择数字化转型工具之前,企业需要明确

【RocketMQ】路由中心NameServer

【RocketMQ】路由中心NameServer参考资料:RocketMQNameserver背后的设计理念RocketMQ之NameServer详解深入剖析RocketMQ源码-NameServer——vivo互联网技术《RocketMQ技术内幕》文章目录【RocketMQ】路由中心NameServerNameSer

java接入烽火科技拾音器详细步骤

1背景项目中需要拾音器去采集音频数据并保存成mp3这种音频文件,以便以后如果有纠纷后可以作为证据去减少纠纷,于是采购了一台烽火科技的拾音器设备,包括一个采音器及一个处理终端。2接线设备拿过来第一件事是接线,通电,让设备运行起来。采音器一共有三根线,红、黑、白,白线接音频输入端R、黑线接音频输入端的G、红色接音频输入端的

GaussDB技术解读系列:运维自动驾驶探索

近日,在第14届中国数据库技术大会(DTCC2023)的GaussDB“五高两易”核心技术,给世界一个更优选择专场,华为云数据库运维研发总监李东详细解读了GaussDB运维系统自动驾驶探索和实践。随着企业数字化转型进入深水区,数据库系统越来越复杂,运维团队维护的数据库规模越来越大,传统工具化的运维已无法满足当前运维的要

PHP初中高级1000道面试题大全(持续更新中50/1000)

目录1、echo(),print(),print_r(),var_dump()的区别?2、表单中get与post提交方法的区别?3、session与cookie的区别?4、请说明PHP中传值与传引用的区别。什么时候传值什么时候传引用?5、请解释PHP中的PDO是什么?6、请解释PHP中的抽象类和接口的区别是什么?7、请

一、Java面试题基础第十天

一、Java面试题基础第十天1.什么是反射?Java是一门静态语言,它通过编译以后才能执行的编程语言,但是可以通过反射使Java成为一个准动态语言,Java在运行过程中动态获取获取对象的属性,调用它的方法,就叫做反射2.反射有哪些应用场景呢?1.jdbc连接数据库的时候加载驱动时Class.forName()2.Jav

热文推荐