Pytorch从零开始实战11

Pytorch从零开始实战——ResNet-50V2算法实战

本系列来源于365天深度学习训练营

原作者K同学

文章目录

  • Pytorch从零开始实战——ResNet-50V2算法实战
    • 环境准备
    • 数据集
    • 模型选择
    • 开始训练
    • 可视化
    • 总结

环境准备

本文基于Jupyter notebook,使用Python3.8,Pytorch2.0.1+cu118,torchvision0.15.2,需读者自行配置好环境且有一些深度学习理论基础。本次实验的目的是理解并使用ResNet-50V2模型,其他部分与上次几乎相同。
第一步,导入常用包

import torch
import torch.nn as nn
import matplotlib.pyplot as plt
import torchvision
import torchvision.transforms as transforms
import torchvision.datasets as datasets
import torch.nn.functional as F
import random
from time import time
import numpy as np
import pandas as pd
import datetime
import gc
import os
import copy
import warnings
os.environ['KMP_DUPLICATE_LIB_OK']='True'  # 用于避免jupyter环境突然关闭
torch.backends.cudnn.benchmark=True  # 用于加速GPU运算的代码

设置随机数种子

torch.manual_seed(428)
torch.cuda.manual_seed(428)
torch.cuda.manual_seed_all(428)
random.seed(428)
np.random.seed(428)

检查设备对象

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
device, torch.cuda.device_count() # # (device(type='cuda'), 2)

数据集

本次数据集是使用鸟的图片,分别有四种类别的鸟,根据鸟的类别名称存放在不同的文件夹中。
使用pathlib查看类别

import pathlib
data_dir = './data/bird_photos/'
data_dir = pathlib.Path(data_dir) # 转成pathlib.Path对象
data_paths = list(data_dir.glob('*')) 
classNames = [str(path).split("/")[2] for path in data_paths]
classNames # ['Black Throated Bushtiti', 'Cockatoo', 'Black Skimmer', 'Bananaquit']

使用transforms对数据集进行统一处理,并且根据文件夹名映射对应标签

all_transforms = transforms.Compose([transforms.Resize([224, 224]),transforms.ToTensor(),transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225]) # 标准化
])total_data = datasets.ImageFolder("./data/bird_photos/", transform=all_transforms)
total_data.class_to_idx# {'Bananaquit': 0,# 'Black Skimmer': 1,# 'Black Throated Bushtiti': 2,# 'Cockatoo': 3}

随机查看5张图片

def plotsample(data):fig, axs = plt.subplots(1, 5, figsize=(10, 10)) #建立子图for i in range(5):num = random.randint(0, len(data) - 1) #首先选取随机数,随机选取五次#抽取数据中对应的图像对象,make_grid函数可将任意格式的图像的通道数升为3,而不改变图像原始的数据#而展示图像用的imshow函数最常见的输入格式也是3通道npimg = torchvision.utils.make_grid(data[num][0]).numpy()nplabel = data[num][1] #提取标签 #将图像由(3, weight, height)转化为(weight, height, 3),并放入imshow函数中读取axs[i].imshow(np.transpose(npimg, (1, 2, 0))) axs[i].set_title(nplabel) #给每个子图加上标签axs[i].axis("off") #消除每个子图的坐标轴plotsample(total_data)

在这里插入图片描述
根据8比2划分数据集和测试集,并且利用DataLoader划分批次和随机打乱

train_size = int(0.8 * len(total_data))
test_size  = len(total_data) - train_size
train_ds, test_ds = torch.utils.data.random_split(total_data, [train_size, test_size])batch_size = 32
train_dl = torch.utils.data.DataLoader(train_ds,batch_size=batch_size,shuffle=True,)
test_dl = torch.utils.data.DataLoader(test_ds,batch_size=batch_size,shuffle=True,)len(train_dl.dataset), len(test_dl.dataset) # (452, 113)

模型选择

ResNetV2与ResNet区别
其中(a)original 表示原始的 ResNet 的残差结构,(b)proposed 表示新的 ResNet 的残差结构。主要差别就是(a)结构先卷积后进行 BN 和激活函数计算,最后执行 addition 后再进行ReLU 计算; (b)结构先进行 BN 和激活函数计算后卷积,把 addition 后的 ReLU 计算放到了残差结构内部。
根据论文所说,改进的模型能够降低错误率。
在这里插入图片描述
本次使用的模型整体架构如下图,借用K同学所绘制的图片,红色和灰色预激活位置有点问题。
在这里插入图片描述
首先实现Block块,首先进行预激活层,包括标准化和ReLu激活函数,接着进行shortcut操作,如果conv_shortcut为True,会使用一个1x1卷积层进行变换,否则,如果stride为1,则进行恒等映射,否则使用1x1的最大池化。随后经过三个卷积层。在forward方法中,输入 x 经过预激活层,然后进行三个卷积操作,最后将shortcut和经过卷积的结果相加。这种结构使得梯度更容易反向传播,从而有助于训练深层网络。

class Block2(nn.Module):def __init__(self, in_channels, filters, kernel_size=3, stride=1, conv_shortcut=False):super(Block2, self).__init__()self.preact = nn.Sequential(nn.BatchNorm2d(in_channels),nn.ReLU())if conv_shortcut:self.shortcut = nn.Conv2d(in_channels, 4 * filters, kernel_size=1, stride=stride)else:if stride == 1:self.shortcut = nn.Identity()else: self.shortcut = nn.MaxPool2d(1, stride=stride)self.conv1 = nn.Sequential(nn.Conv2d(in_channels, filters, kernel_size=1, stride=1, bias=False),nn.BatchNorm2d(filters),nn.ReLU())self.conv2 = nn.Sequential(nn.ZeroPad2d(padding=(1, 1, 1, 1)),nn.Conv2d(filters, filters, kernel_size=kernel_size, stride=stride, bias=False),nn.BatchNorm2d(filters),nn.ReLU())self.conv3 = nn.Conv2d(filters, 4 * filters, kernel_size=1)def forward(self, x):preact = self.preact(x)shortcut = self.shortcut(preact)x = self.conv1(preact)x = self.conv2(x)x = self.conv3(x)out = shortcut + xreturn out

下面实现堆叠块,通过传入不同的参数去调用Block块,其中[Block2(4 * filters, filters) for i in range(0, blocks)],使用 Python 中的列表解析创建了 blocks 个残差块。这些残差块的输入通道数为 4 * filters,以匹配前一个残差块的输出通道数。

class Stack2(nn.Module):def __init__(self, in_channels, filters, blocks, stride1=2):super(Stack2, self).__init__()self.blocks = nn.Sequential(Block2(in_channels, filters, conv_shortcut=True),*[Block2(4 * filters, filters) for i in range(0, blocks)],Block2(4 * filters, filters, stride=stride1))def forward(self, x):return self.blocks(x)

下面就是网络主体,按照上图实现就行了

class ResNet50V2(nn.Module):def __init__(self, include_top=True, preact=True, num_classes=1000):super(ResNet50V2, self).__init__()self.conv1 = nn.Sequential(nn.Conv2d(3, 64, kernel_size=7, stride=2, padding=3, bias=False),nn.BatchNorm2d(64),nn.ReLU())self.pool1 = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)self.conv2 = Stack2(64, 64, 3)self.conv3 = Stack2(256, 128, 4)self.conv4 = Stack2(512, 256, 6)self.conv5 = Stack2(1024, 512, 3, stride1=1)self.post = nn.Sequential(nn.BatchNorm2d(2048),nn.ReLU())self.include_top = include_topif include_top:self.avg_pool = nn.AdaptiveAvgPool2d(1)self.fc = nn.Linear(2048, num_classes)def forward(self, x):x = self.conv1(x)x = self.pool1(x)x = self.conv2(x)x = self.conv3(x)x = self.conv4(x)x = self.conv5(x)x = self.post(x)if self.include_top:x = self.avg_pool(x)x = torch.flatten(x, 1)x = self.fc(x)return x

使用summary查看网络
在这里插入图片描述

开始训练

定义训练函数

def train(dataloader, model, loss_fn, opt):size = len(dataloader.dataset)num_batches = len(dataloader)train_acc, train_loss = 0, 0for X, y in dataloader:X, y = X.to(device), y.to(device)pred = model(X)loss = loss_fn(pred, y)opt.zero_grad()loss.backward()opt.step()train_acc += (pred.argmax(1) == y).type(torch.float).sum().item()train_loss += loss.item()train_acc /= sizetrain_loss /= num_batchesreturn train_acc, train_loss

定义测试函数

def test(dataloader, model, loss_fn):size = len(dataloader.dataset)num_batches = len(dataloader)test_acc, test_loss = 0, 0with torch.no_grad():for X, y in dataloader:X, y = X.to(device), y.to(device)pred = model(X)loss = loss_fn(pred, y)test_acc += (pred.argmax(1) == y).type(torch.float).sum().item()test_loss += loss.item()test_acc /= sizetest_loss /= num_batchesreturn test_acc, test_loss

定义学习率、损失函数、优化算法

loss_fn = nn.CrossEntropyLoss()
learn_rate = 0.0001
opt = torch.optim.Adam(model.parameters(), lr=learn_rate)

开始训练,epoch设置为30

import time
epochs = 30
train_loss = []
train_acc = []
test_loss = []
test_acc = []T1 = time.time()best_acc = 0
best_model = 0for epoch in range(epochs):model.train()epoch_train_acc, epoch_train_loss = train(train_dl, model, loss_fn, opt)model.eval() # 确保模型不会进行训练操作epoch_test_acc, epoch_test_loss = test(test_dl, model, loss_fn)if epoch_test_acc > best_acc:best_acc = epoch_test_accbest_model = copy.deepcopy(model)train_acc.append(epoch_train_acc)train_loss.append(epoch_train_loss)test_acc.append(epoch_test_acc)test_loss.append(epoch_test_loss)print("epoch:%d, train_acc:%.1f%%, train_loss:%.3f, test_acc:%.1f%%, test_loss:%.3f"% (epoch + 1, epoch_train_acc * 100, epoch_train_loss, epoch_test_acc * 100, epoch_test_loss))T2 = time.time()
print('程序运行时间:%s秒' % (T2 - T1))PATH = './best_model.pth'  # 保存的参数文件名
if best_model is not None:torch.save(best_model.state_dict(), PATH)print('保存最佳模型')
print("Done")

虽然结果过拟合了,但这不是本次实验的重点,如果使用更好的参数,效果可能会不错。
在这里插入图片描述

可视化

可视化训练过程和测试过程

import warnings
warnings.filterwarnings("ignore")               #忽略警告信息
plt.rcParams['font.sans-serif']    = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False      # 用来正常显示负号
plt.rcParams['figure.dpi']         = 100        #分辨率epochs_range = range(epochs)plt.figure(figsize=(12, 3))
plt.subplot(1, 2, 1)plt.plot(epochs_range, train_acc, label='Training Accuracy')
plt.plot(epochs_range, test_acc, label='Test Accuracy')
plt.legend(loc='lower right')
plt.title('Training and Validation Accuracy')plt.subplot(1, 2, 2)
plt.plot(epochs_range, train_loss, label='Training Loss')
plt.plot(epochs_range, test_loss, label='Test Loss')
plt.legend(loc='upper right')
plt.title('Training and Validation Loss')
plt.show()

在这里插入图片描述

总结

本次实验主要实现了ResNet-50V2模型,ResNet模型本身就是用来缓解梯度爆炸和梯度消失问题的,在V2结构中,激活函数ReLU应用在残差块的输出上,而不是在整个块的输入上。这使得网络能够学习到更复杂的非线性映射,并且使得原本的网络进行相对的恒等映射,提高了网络的表达能力。当然,网络可以有不同的残差连接,本文这种修改并不是一成不变适用于所有情况,而是在某些场景下可能有助于训练,提高网络性能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/183215.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VUE语法-ref和reactive响应式数据引用

1、响应式概述 在vue中定义一个参数,当这个参数在使用中发生了变化,在页面中对这个数据应用的地方都会同步的发生变化,这个就是数据响应式。 2、创建一个非响应式的参数 该程序中采用的是VUE3的用法: 1、在程序中定义了一个局…

GraphCast:基于机器学习的全球中期天气预测模型

文章信息 文章题为”GraphCast: Learning skillful medium-range global weather forecasting”,该文章于2023年发表至Science,文章内容主要关于利用机器学习模型,实现高效、准确的全球中期天气预测。由于文章内容较多,本文仅对研…

同城配送软件:让生活更简单,让物流更高效

同城配送软件是一种提供同城快递、跑腿、外卖等服务的软件,可以让用户方便快捷地发送和接收订单,同时也为配送员提供了接单和送单的便捷渠道。 同城配送软件的开发主要包括以下几个方面的内容: 确定开发目标:在开始开发前&#…

雷达目标跟踪标注的数据格式转换为MOT格式

1. 点云标注 标注软件我用的是annotate软件: GitHub - Earthwings/annotate: Create 3D labelled bounding boxes in RViz 标注软件的使用教程这名博主讲的很详细: 3D目标检测(1):点云标注工具之annotate - 知乎 2…

力扣刷题篇之分治

系列文章目录 目录 系列文章目录 前言 一、分解问题 二、解决子问题 三、合并结果 总结 前言 刷题按照: [力扣刷题攻略] Re:从零开始的力扣刷题生活 - 力扣(LeetCode) 参考: 「五大常用算法」一文搞懂分治算法…

大数据HCIE成神之路之数学(4)——最优化实验

最优化实验 1.1 最小二乘法实现1.1.1 算法介绍1.1.2 代码实现1.2 梯度下降法实现1.2.1 算法介绍1.2.2 代码实现1.3 拉格朗日乘子法1.3.1 实验1.3.2 实验操作步骤1.1 最小二乘法实现 1.1.1 算法介绍 最小二乘法(Least Square Method),做为分类回归算法的基础,有着悠久的历…

第20章:多线程

20.1 线程简介 在Java中,并发机制非常重要,程序员可以在程序中执行多个线程,每个线程完成一个功能,并与其他线程并发执行,这种机制被称为多线程。但是,并不是所有编程语言都支持多线程。 线程的特点&#…

visual studio 2022 更改字体和大小

工具--->选项 文本编辑器 输出窗口

wsl设置ssh时kex_exchange_identification: Connection closed by remote host问题

问题: 在wsl所有ssh相关的东西设置好的情况下出现:kex_exchange_identification: Connection closed by remote host。 解决方案 如果是使用密码登陆的,修改/etc/ssh/sshd_config,将PasswordAuthentication改为yes即可。

Linux系统之uptime命令的基本使用

Linux系统之uptime命令的基本使用 一、uptime介绍二、uptime命令使用帮助2.1 uptime的help帮助信息2.2 uptime的语法解释 三、uptime的基本使用3.1 直接使用uptime命令3.2 显示uptime版本信息3.3 显示系统运行时间3.4 显示系统最后一次启动时间 四、uptime命令的使用注意事项 一…

docker中安装mysql,远程连接

docker中安装mysql,远程连接 安装mysql 拉取mysql镜像 搜索mysql镜像 docker search mysql建议使用Oracle官方标记的 拉取镜像 docker pull mysql# 查询镜像是否拉取成功 docker images启动mysql镜像 需要做端口映射 docker run --name mysql02 -p3306:3306 -e …

基于YOLOv8深度学习的生活垃圾分类目标检测系统【python源码+Pyqt5界面+数据集+训练代码】目标检测

《博主简介》 小伙伴们好,我是阿旭。专注于人工智能、AIGC、python、计算机视觉相关分享研究。 ✌更多学习资源,可关注公-仲-hao:【阿旭算法与机器学习】,共同学习交流~ 👍感谢小伙伴们点赞、关注! 《------往期经典推…

springboot 使用脚本进行启动部署

一 springboot 使用脚本进行启动部署 1.1 使用脚本进行启动部署 我们公司使用了 6 年的Spring Boot 项目部署方案!打包 Shell 脚本部署详解,稳的一批! 待完善....

C++前缀和算法的应用:优化了6版的1324模式

本文涉及的基础知识点 C算法:前缀和、前缀乘积、前缀异或的原理、源码及测试用例 包括课程视频 本题其它解法 C前缀和算法的应用:统计上升四元组 类似题解法 包括题目及代码C二分查找算法:132 模式解法一枚举3C二分查找算法:…

k8s中Pod控制器简介,ReplicaSet、Deployment、HPA三种处理无状态pod应用的控制器介绍

目录 一.Pod控制器简介 二.ReplicaSet(简写rs) 1.简介 (1)主要功能 (2)rs较完整参数解释 2.创建和删除 (1)创建 (2)删除 3.扩容和缩容 &#xff08…

vuepress-----7、发布在GitHub

# 7、发布在GitHub 在你的项目中,创建一个如下的 deploy.sh 文件(请自行判断去掉高亮行的注释): #!/usr/bin/env sh# 确保脚本抛出遇到的错误 set -e# 生成静态文件 npm run docs:build# 进入生成的文件夹 cd docs/.vuepress/dist# 如果是发…

WebUI自动化学习(Selenium+Python+Pytest框架)004

接下来,WebUI基础知识最后一篇。 1.下拉框操作 关于下拉框的处理有两种方式 (1)按普通元素定位 安装普通元素的定位方式来定位下拉框,使用元素的操作方法element.click()方法来操作下拉框内容的选择 (2&#xff09…

PCB设计注意事项

四个二极管不能省略 pwm波跟电机频率不要是倍频 运放越靠近取样电阻越好 反向输入端跟输出端很敏感,有寄生电容就容易震荡 距离取样电阻近就会距离单片机远,那么线上会有寄生电容,这时候在输出端接一个10k电阻到地

深度学习-模型调试经验总结

1、 这句话的意思是:期望张量的后端处理是在cpu上,但是实际是在cuda上。排查代码发现,数据还在cpu上,但是模型已经转到cuda上,所以可以通过把数据转到cuda上解决。 解决代码: tensor.to("cuda")…

【开源视频联动物联网平台】Node-RED规则引擎

Node-RED是一个开源的流程编排工具,它基于JavaScript运行时Node.js构建,专门为简化物联网(IoT)设备之间的集成而设计,但在其他领域,它也被广泛用作规则引擎。 规则引擎是一种系统,用于定义和执…