深度学习Week9-YOLOv5-C3模块实现(Pytorch)

news2024/11/26 0:47:23
  • 🍨 本文为🔗365天深度学习训练营 中的学习记录博客
  • 🍦 参考文章:Pytorch实战 | 第P8天:YOLOv5-C3模块实现(训练营内部成员可读)
  • 🍖 原作者:K同学啊|接辅导、项目定制

了解C3的结构,方便后续YOLOv5算法的学习。采用的数据集是天气识别的数据集。 

一、 前期准备

1. 设置GPU

import torch
import torch.nn as nn
import torchvision.transforms as transforms
import torchvision
from torchvision import transforms, datasets
import os,PIL,pathlib,warnings

warnings.filterwarnings("ignore")             #忽略警告信息

device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
print(device)

输出:cuda

2. 导入数据

import os,PIL,random,pathlib

data_dir = './data/'
data_dir = pathlib.Path(data_dir)

data_paths  = list(data_dir.glob('*'))
classeNames = [str(path).split("\\")[1] for path in data_paths]
print(classeNames)

图形变换,输出一下:用到torchvision.transforms.Compose()

train_transforms = transforms.Compose([
    transforms.Resize([224, 224]),  # 将输入图片resize成统一尺寸
    # transforms.RandomHorizontalFlip(), # 随机水平翻转
    transforms.ToTensor(),          # 将PIL Image或numpy.ndarray转换为tensor,并归一化到[0,1]之间
    transforms.Normalize(           # 标准化处理-->转换为标准正太分布(高斯分布),使模型更容易收敛
        mean=[0.485, 0.456, 0.406],
        std=[0.229, 0.224, 0.225])  # 其中 mean=[0.485,0.456,0.406]与std=[0.229,0.224,0.225] 从数据集中随机抽样计算得到的。
])
 
test_transform = transforms.Compose([
    transforms.Resize([224, 224]),  # 将输入图片resize成统一尺寸
    transforms.ToTensor(),          # 将PIL Image或numpy.ndarray转换为tensor,并归一化到[0,1]之间
    transforms.Normalize(           # 标准化处理-->转换为标准正太分布(高斯分布),使模型更容易收敛
        mean=[0.485, 0.456, 0.406],
        std=[0.229, 0.224, 0.225])  # 其中 mean=[0.485,0.456,0.406]与std=[0.229,0.224,0.225] 从数据集中随机抽样计算得到的。
])
 
total_data = datasets.ImageFolder("./data/",transform=train_transforms)
print(total_data.class_to_idx)

输出:{'cloudy': 0, 'rain': 1, 'shine': 2, 'sunrise': 3}

3. 划分数据集

train_size = int(0.8 * len(total_data))
test_size  = len(total_data) - train_size
train_dataset, test_dataset = torch.utils.data.random_split(total_data, [train_size, test_size])

batch_size = 32

train_dl = torch.utils.data.DataLoader(train_dataset,
                                           batch_size=batch_size,
                                           shuffle=True,
                                           num_workers=0)
test_dl = torch.utils.data.DataLoader(test_dataset,
                                          batch_size=batch_size,
                                          shuffle=True,
                                          num_workers=0)
for X, y in test_dl:
    print("Shape of X [N, C, H, W]: ", X.shape)
    print("Shape of y: ", y.shape, y.dtype)
    break

二、搭建YOLOv5-C3模型

 1.搭建模型

import torch.nn.functional as F


def autopad(k, p=None):  # kernel, padding
    # Pad to 'same'
    if p is None:
        p = k // 2 if isinstance(k, int) else [x // 2 for x in k]  # auto-pad
    return p


class Conv(nn.Module):
    # Standard convolution
    def __init__(self, c1, c2, k=1, s=1, p=None, g=1, act=True):  # ch_in, ch_out, kernel, stride, padding, groups
        super().__init__()
        self.conv = nn.Conv2d(c1, c2, k, s, autopad(k, p), groups=g, bias=False)
        self.bn = nn.BatchNorm2d(c2)
        self.act = nn.SiLU() if act is True else (act if isinstance(act, nn.Module) else nn.Identity())

    def forward(self, x):
        return self.act(self.bn(self.conv(x)))

    def forward_fuse(self, x):
        return self.act(self.conv(x))


class Bottleneck(nn.Module):
    # Standard bottleneck
    def __init__(self, c1, c2, shortcut=True, g=1, e=0.5):  # ch_in, ch_out, shortcut, groups, expansion
        super().__init__()
        c_ = int(c2 * e)  # hidden channels
        self.cv1 = Conv(c1, c_, 1, 1)
        self.cv2 = Conv(c_, c2, 3, 1, g=g)
        self.add = shortcut and c1 == c2

    def forward(self, x):
        return x + self.cv2(self.cv1(x)) if self.add else self.cv2(self.cv1(x))


class C3(nn.Module):
    # CSP Bottleneck with 3 convolutions
    def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):  # ch_in, ch_out, number, shortcut, groups, expansion
        super().__init__()
        c_ = int(c2 * e)  # hidden channels
        self.cv1 = Conv(c1, c_, 1, 1)
        self.cv2 = Conv(c1, c_, 1, 1)
        self.cv3 = Conv(2 * c_, c2, 1)  # act=FReLU(c2)
        self.m = nn.Sequential(*(Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)))

    def forward(self, x):
        return self.cv3(torch.cat((self.m(self.cv1(x)), self.cv2(x)), dim=1))


class model_K(nn.Module):
    def __init__(self):
        super(model_K, self).__init__()

        # 卷积模块
        self.Conv = Conv(3, 32, 3, 2)

        # C3模块1
        self.C3_1 = C3(32, 64, 3, 2)

        # 全连接网络层,用于分类
        self.classifier = nn.Sequential(
            nn.Linear(in_features=802816, out_features=100),
            nn.ReLU(),
            nn.Linear(in_features=100, out_features=4)
        )

    def forward(self, x):
        x = self.Conv(x)
        x = self.C3_1(x)
        x = torch.flatten(x, start_dim=1)
        x = self.classifier(x)

        return x


device = "cuda" if torch.cuda.is_available() else "cpu"
print("Using {} device".format(device))

model = model_K().to(device)
print(model)

2.查看模型详情 

统计模型参数量以及其他指标

import torchsummary as summary
summary.summary(model, (3, 224, 224))

 三、 训练模型

1. 编写训练和测试函数

和之前cnn网络、vgg一样

# 训练循环
def train(dataloader, model, loss_fn, optimizer):
    size = len(dataloader.dataset)  # 训练集的大小
    num_batches = len(dataloader)   # 批次数目, (size/batch_size,向上取整)
 
    train_loss, train_acc = 0, 0  # 初始化训练损失和正确率
    
    for X, y in dataloader:  # 获取图片及其标签
        X, y = X.to(device), y.to(device)
        
        # 计算预测误差
        pred = model(X)          # 网络输出
        loss = loss_fn(pred, y)  # 计算网络输出和真实值之间的差距,targets为真实值,计算二者差值即为损失
        
        # 反向传播
        optimizer.zero_grad()  # grad属性归零
        loss.backward()        # 反向传播
        optimizer.step()       # 每一步自动更新
        
        # 记录acc与loss
        train_acc  += (pred.argmax(1) == y).type(torch.float).sum().item()
        train_loss += loss.item()
            
    train_acc  /= size
    train_loss /= num_batches
 
    return train_acc, train_loss
def test (dataloader, model, loss_fn):
    size        = len(dataloader.dataset)  # 测试集的大小
    num_batches = len(dataloader)          # 批次数目
    test_loss, test_acc = 0, 0
    
    # 当不进行训练时,停止梯度更新,节省计算内存消耗
    with torch.no_grad():
        for imgs, target in dataloader:
            imgs, target = imgs.to(device), target.to(device)
            
            # 计算loss
            target_pred = model(imgs)
            loss        = loss_fn(target_pred, target)
            
            test_loss += loss.item()
            test_acc  += (target_pred.argmax(1) == target).type(torch.float).sum().item()
 
    test_acc  /= size
    test_loss /= num_batches
 
    return test_acc, test_loss

2. 正式训练

这里也设置了训练器,结合前几次实验经验,使用Adam模型

import copy

optimizer = torch.optim.Adam(model.parameters(), lr=1e-4)
loss_fn = nn.CrossEntropyLoss()  # 创建损失函数

epochs = 20

train_loss = []
train_acc = []
test_loss = []
test_acc = []

best_acc = 0  # 设置一个最佳准确率,作为最佳模型的判别指标

for epoch in range(epochs):
    # 更新学习率(使用自定义学习率时使用)
    # adjust_learning_rate(optimizer, epoch, learn_rate)

    model.train()
    epoch_train_acc, epoch_train_loss = train(train_dl, model, loss_fn, optimizer)
    # scheduler.step() # 更新学习率(调用官方动态学习率接口时使用)

    model.eval()
    epoch_test_acc, epoch_test_loss = test(test_dl, model, loss_fn)

    # 保存最佳模型到 best_model
    if epoch_test_acc > best_acc:
        best_acc = epoch_test_acc
        best_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)

    # 获取当前的学习率
    lr = optimizer.state_dict()['param_groups'][0]['lr']

    template = ('Epoch:{:2d}, Train_acc:{:.1f}%, Train_loss:{:.3f}, Test_acc:{:.1f}%, Test_loss:{:.3f}, Lr:{:.2E}')
    print(template.format(epoch + 1, epoch_train_acc * 100, epoch_train_loss,
                          epoch_test_acc * 100, epoch_test_loss, lr))

# 保存最佳模型到文件中
PATH = './best_model.pth'  # 保存的参数文件名
torch.save(model.state_dict(), PATH)

print('Done')

遇到了问题:RuntimeError: CUDA out of memory。这个在之前也遇到过,我显卡(3050ti)性能一般,但是可以把batch_size减小一半,本实验由32改为16即可运行。

四、 结果可视化

1. Loss与Accuracy图

import matplotlib.pyplot as plt
#隐藏警告
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()

 2. 指定图片进行预测

from PIL import Image

classes = list(total_data.class_to_idx)


def predict_one_image(image_path, model, transform, classes):
    test_img = Image.open(image_path).convert('RGB')
    plt.imshow(test_img)  # 展示预测的图片

    test_img = transform(test_img)
    img = test_img.to(device).unsqueeze(0)

    model.eval()
    output = model(img)

    _, pred = torch.max(output, 1)
    pred_class = classes[pred]
    print(f'预测结果是:{pred_class}')


# 预测训练集中的某张照片
predict_one_image(image_path='./data/sunrise/sunrise8.jpg',
                  model=model,
                  transform=train_transforms,
                  classes=classes)

 3. 模型评估

 以往都是看看最后几轮得到准确率,但是跳动比较大就不太好找准确率最高的一回,所以我们用函数返回进行比较。

best_model.eval()
epoch_test_acc, epoch_test_loss = test(test_dl, best_model, loss_fn)
print(epoch_test_acc, epoch_test_loss)
print(epoch_test_acc)

*五、优化模型

C3模块
作用:
1 在新版yolov5中,作者将BottleneckCSP(瓶颈层)模块转变为了C3模块,其结构作用基本相同均为CSP架构,只是在修正单元的选择上有所不同,其包含了3个标准卷积层以及多个Bottleneck模块(数量由配置文件.yaml的n和depth_multiple参数乘积决定)

2 C3相对于BottleneckCSP模块不同的是,经历过残差输出后的Conv模块被去掉了,concat后的标准卷积模块中的激活函数也由LeakyRelu变为了SiLU(同上)。

3 该模块是对残差特征进行学习的主要模块,其结构分为两支,一支使用了上述指定多个Bottleneck堆叠和3个标准卷积层,另一支仅经过一个基本卷积模块,最后将两支进行concat操作。

class C3(nn.Module):
    # CSP Bottleneck with 3 convolutions
    def __init__(self, c1, c2, n=1, shortcut=True, g=1, e=0.5):  # ch_in, ch_out, number, shortcut, groups, expansion
        super().__init__()
        c_ = int(c2 * e)  # hidden channels
        self.cv1 = Conv(c1, c_, 1, 1)
        self.cv2 = Conv(c1, c_, 1, 1)
        self.cv3 = Conv(2 * c_, c2, 1)  # act=FReLU(c2)
        self.m = nn.Sequential(*(Bottleneck(c_, c_, shortcut, g, e=1.0) for _ in range(n)))

    def forward(self, x):
        return self.cv3(torch.cat((self.m(self.cv1(x)), self.cv2(x)), dim=1))

提升:

修改BottleNeck层数为4

 最后准确率提升了7%左右

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

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

相关文章

前端Gitee + Jenkins自动化实战(转)

# 前端Gitee Jenkins自动化实战 当我们熟悉了Jenkins 的使用后,接下来我们来配置一个基于 vue-element-admin 的实战项目,来感受一下自动化流程带给我们的优势。 首先我们需要创建一个 git 项目,至于代码仓库可以自选,这里为了…

RocketMq: Windows环境-单机部署和多种主从集群场景部署

关于默认端口 broker的默认端口有3个,10911, 10912, 10909。 10911是remotingServer使用的监听端口,remotingServer主要处理以下三类消息: producer发送的消息 conumser在消费失败或者消费超时发送的消息 consumer拉取消息10912是主broker用于监听从broker请求…

HTML学生个人网站作业设计:公益专题扫黑除恶(HTML+CSS)

🎉精彩专栏推荐 💭文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 💂 作者主页: 【主页——🚀获取更多优质源码】 🎓 web前端期末大作业: 【📚毕设项目精品实战案例 (10…

扩散模型Diffusion轻松入门

如果要问最近模型领域什么东西最火,应该非扩散模型莫属,火得我觉得不系统学习一下都不行! 本文是我的学习笔记,只能叫入门文章,因为扩散模型的严谨数学推导公式很多,还没有研究透彻,不敢说一文…

c语言:关键字(二)

关键字一.声明的关键词—extern二.最名不符实的关键字—static一.声明的关键词—extern 首先这里问两个问题 1.变量可以跨文件访问吗? 2.函数可以跨文件访问吗? 答案是都可以 那么什么是跨文件呢? 我在game.c里定义show函数,但…

Vue2基础篇-01-Vue2 入门概述

1. 概述 该教程站在小白的角度,去帮助理解和使用,达到入门的效果;该教程覆盖面比较浅,更多详情请查阅 Vue2文档 ,目前最新是Vue3文档,但是Vue2是公司目前大多数使用的;欢迎大家一起指正并下方留…

Unity 符号表

目录 前言 关于Unity符号表 正文 程序crash日志: 解析 后记 记一次 Bugly 崩溃查找过程 unity-il2cpp: 前言 关于Unity符号表 关于项目真机调试时的崩溃问题,一般可以 logcat 或 xcode 看到相关的crash日志,拿到崩溃时的堆…

Python均匀分布和三角形分布

文章目录均匀分布三角形分布均匀分布和三角形分布之间的关系函数备注triangular(left, mode, right)三角形分布uniform([low, high])均匀分布 均匀分布 所谓均匀分布,就是在事件空间中,所有事件的概率都是相等的连续分布,其概率密度为 f(x)1…

麻省理工人工智能实验室新研究!有远见的机器学习方法:能预知未来行为的AI智能体

原创/文 BFT机器人 近日,麻省理工学院的研究人员开发出一种新技术,让AI智能体能够思考更远的未来,寻找更合适的合作与竞争长期解决方案。 想象一个游戏规则:两支足球队在球场上PK,玩家们可以选择相互合作合作来实现目…

[附源码]Python计算机毕业设计Django学生宿舍管理系统

项目运行 环境配置: Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术: django python Vue 等等组成,B/S模式 pychram管理等等。 环境需要 1.运行环境:最好是python3.7.7,…

web前端期末大作业:云南旅游网页主题网站设计——云南城市旅游5页HTML+CSS+JavaScript

👨‍🎓静态网站的编写主要是用 HTML DⅣV CSSJS等来完成页面的排版设计👩‍🎓,一般的网页作业需要融入以下知识点:div布局、浮动定位、高级css、表格、表单及验证、js轮播图、音频视频Fash的应用、uli、下拉…

c + linux + cmake + arm + MQTT

先给你们看个最终代码结构吧,因为我改过的代码会加密,所以我只能放一部分源码,另外一部分源码我会直接贴在博客,具体使用我会在博客里面说明! 1.MQTTPacket源码库(MQTTPacket源码地址) 2.MQTTClient.c /*********************************************************************…

Linux中线程池的制作

一.介绍 1.1概念 一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用&#x…

如何从github上克隆库、跑库

第一步:在Github上找到想要的库,以YOLOv3项目为例。 第二步:拷贝这个库到自己的电脑上,下载到本地。 方法一:在GitHub上,Code -> Download ZIP(有的时候会有一些问题,不建议&…

DBW*的trace文件过大的bug

问题描述: 近期某现场发现trace目录下的dbw*文件达到了大几G的大小导致/oracle目录占用率突增,删除这些trace文件,几天后又重新生成较大的dbw*的trace 11G Dec 4 10:38 rb_dbw0_2086848.trc 3.6G Dec 4 10:38 rb_dbw1_2086852.trc 4.4G De…

前端工程师常考手写面试题指南

实现 add(1)(2)(3) 函数柯里化概念: 柯里化(Currying)是把接受多个参数的函数转变为接受一个单一参数的函数,并且返回接受余下的参数且返回结果的新函数的技术。 1)粗暴版 function add (a) { return function (b) …

码云线上误删主项目文件夹的恢复

码云线上误删主项目文件夹的恢复前言描述解决办法解决问题前言描述 本来某个项目即将上线,然后同事不知道怎么的,直接打开了自己的码云,在网站上把主项目目录删除了。。。。是的,删除了!!!&…

职场生涯亮红灯要注意这些

很多时候,当事业变红的时候,很多年轻人还在傻傻地工作。他们做的事情越多,在不被领导看重的情况下,就越不会得到领导的重用。在关心下属的时候,会在无形中释放出一些不好的信号,这是一种被领导抛弃的行为。…

winform 处理tabcontrol控件,隐藏顶部的tab标签,及tabcontrol的边框线

处理tabcontrol控件,隐藏顶部的tab标签,及tabcontrol的边框线处理tabcontrol控件,隐藏顶部的tab标签,及tabcontrol的边框线隐藏顶部的tab标签隐藏边框线运行效果图处理tabcontrol控件,隐藏顶部的tab标签,及…

智能电销机器人《各版本机器人部署》

科技在进步,时代在发展,越来越多人工智能产品出现在我们的生活中,从各种工业机器人到智能家居产品,人工智能在越来越多的行业出现,代替人们做重复枯燥的工作。在企业中出现最多的是电销机器人,并逐渐被越来…