CV党福音:YOLOv8实现分类

YOLO作为目标检测领域的常青树,如今以及更新到了YOLOv10,并且还有YOLOXYOLOS等变体,可以说该系列已经在目标检测领域占据了半壁江山,如今,YOLOv8的发行者ultralytics竟有一统江山之意,其在提出的框架中不但集成了v3v10YOLO目标检测模型,还包揽了分类,语义分割、目标追踪和姿态估计等计算机视觉任务。

那么,今天我们就来看看YOLOv8是如何将这些计算机视觉任务融合在一起吧

其实从思路上很简单,YOLOv8继续沿用了YOLO的基本架构,即将整个模型分为特征提取骨干网络(Backbone),用于进行特征提取,特征融合网络(Neck),用于融合提取的特征信息以及最后的检测输出模块(Head),由于前面的BackboneNeck已经完成了特提取与特征融合的功能,而最后的输出头其实就是根据不同任务所设计的,因此,要想让YOLOv8具备分类、分割以及姿态估计的功能,只需要将我们的检测头(Detect)替换为相应的分类头、分割头以及姿态估计头即可,当然,相应的还要替换损失函数与数据集标签。

YOLOv8模型结构如下:

在这里插入图片描述

目标检测模型

那么,我们接下来便来看看YOLOv8是如何去替换的:

首先是模型结构,博主以分类为例,要修改模型结构,只需要修改对应的yaml文件即可,这里博主推荐可以参考这篇文章:YOLOv8模型yaml结构图理解(逐层分析)

YOLOv8的模型结构如下:

# Ultralytics YOLO 🚀, AGPL-3.0 license
# YOLOv8 object detection model with P3-P5 outputs. For Usage examples see https://docs.ultralytics.com/tasks/detect
# Parameters
nc: 80  # 类别数目,nc代表"number of classes",即模型用于检测的对象类别总数。
scales: # 模型复合缩放常数,例如 'model=yolov8n.yaml' 将调用带有 'n' 缩放的 yolov8.yaml# [depth, width, max_channels]n: [0.33, 0.25, 1024]  # YOLOv8n概览:225层, 3157200参数, 3157184梯度, 8.9 GFLOPss: [0.33, 0.50, 1024]  # YOLOv8s概览:225层, 11166560参数, 11166544梯度, 28.8 GFLOPsm: [0.67, 0.75, 768]   # YOLOv8m概览:295层, 25902640参数, 25902624梯度, 79.3 GFLOPsl: [1.00, 1.00, 512]   # YOLOv8l概览:365层, 43691520参数, 43691504梯度, 165.7 GFLOPsx: [1.00, 1.25, 512]   # YOLOv8x概览:365层, 68229648参数, 68229632梯度, 258.5 GFLOPs
# YOLOv8.0n backbone 骨干层
backbone:# [from, repeats, module, args]- [-1, 1, Conv, [64, 3, 2]]  # 0-P1/2 第0层,-1代表将上层的输入作为本层的输入。第0层的输入是640*640*3的图像。Conv代表卷积层,相应的参数:64代表输出通道数,3代表卷积核大小k,2代表stride步长。- [-1, 1, Conv, [128, 3, 2]]  # 1-P2/4 第1层,本层和上一层是一样的操作(128代表输出通道数,3代表卷积核大小k,2代表stride步长)- [-1, 3, C2f, [128, True]] # 第2层,本层是C2f模块,3代表本层重复3次。128代表输出通道数,True表示Bottleneck有shortcut。- [-1, 1, Conv, [256, 3, 2]]  # 3-P3/8 第3层,进行卷积操作(256代表输出通道数,3代表卷积核大小k,2代表stride步长),输出特征图尺寸为80*80*256(卷积的参数都没变,所以都是长宽变成原来的1/2,和之前一样),特征图的长宽已经变成输入图像的1/8。- [-1, 6, C2f, [256, True]] # 第4层,本层是C2f模块,可以参考第2层的讲解。6代表本层重复6次。256代表输出通道数,True表示Bottleneck有shortcut。经过这层之后,特征图尺寸依旧是80*80*256。- [-1, 1, Conv, [512, 3, 2]]  # 5-P4/16 第5层,进行卷积操作(512代表输出通道数,3代表卷积核大小k,2代表stride步长),输出特征图尺寸为40*40*512(卷积的参数都没变,所以都是长宽变成原来的1/2,和之前一样),特征图的长宽已经变成输入图像的1/16。- [-1, 6, C2f, [512, True]] # 第6层,本层是C2f模块,可以参考第2层的讲解。6代表本层重复6次。512代表输出通道数,True表示Bottleneck有shortcut。经过这层之后,特征图尺寸依旧是40*40*512。- [-1, 1, Conv, [1024, 3, 2]]  # 7-P5/32 第7层,进行卷积操作(1024代表输出通道数,3代表卷积核大小k,2代表stride步长),输出特征图尺寸为20*20*1024(卷积的参数都没变,所以都是长宽变成原来的1/2,和之前一样),特征图的长宽已经变成输入图像的1/32。- [-1, 3, C2f, [1024, True]] #第8层,本层是C2f模块,可以参考第2层的讲解。3代表本层重复3次。1024代表输出通道数,True表示Bottleneck有shortcut。经过这层之后,特征图尺寸依旧是20*20*1024。- [-1, 1, SPPF, [1024, 5]]  # 9 第9层,本层是快速空间金字塔池化层(SPPF)。1024代表输出通道数,5代表池化核大小k。结合模块结构图和代码可以看出,最后concat得到的特征图尺寸是20*20*(512*4),经过一次Conv得到20*20*1024。
# YOLOv8.0n head 头部层
head:- [-1, 1, nn.Upsample, [None, 2, 'nearest']] # 第10层,本层是上采样层。-1代表将上层的输出作为本层的输入。None代表上采样的size(输出尺寸)不指定。2代表scale_factor=2,表示输出的尺寸是输入尺寸的2倍。nearest代表使用的上采样算法为最近邻插值算法。经过这层之后,特征图的长和宽变成原来的两倍,通道数不变,所以最终尺寸为40*40*1024。- [[-1, 6], 1, Concat, [1]]  # cat backbone P4 第11层,本层是concat层,[-1, 6]代表将上层和第6层的输出作为本层的输入。[1]代表concat拼接的维度是1。从上面的分析可知,上层的输出尺寸是40*40*1024,第6层的输出是40*40*512,最终本层的输出尺寸为40*40*1536。- [-1, 3, C2f, [512]]  # 12 第12层,本层是C2f模块,可以参考第2层的讲解。3代表本层重复3次。512代表输出通道数。与Backbone中C2f不同的是,此处的C2f的bottleneck模块的shortcut=False。- [-1, 1, nn.Upsample, [None, 2, 'nearest']] # 第13层,本层也是上采样层(参考第10层)。经过这层之后,特征图的长和宽变成原来的两倍,通道数不变,所以最终尺寸为80*80*512。- [[-1, 4], 1, Concat, [1]]  # cat backbone P3 第14层,本层是concat层,[-1, 4]代表将上层和第4层的输出作为本层的输入。[1]代表concat拼接的维度是1。从上面的分析可知,上层的输出尺寸是80*80*512,第6层的输出是80*80*256,最终本层的输出尺寸为80*80*768。- [-1, 3, C2f, [256]]  # 15 (P3/8-small) 第15层,本层是C2f模块,可以参考第2层的讲解。3代表本层重复3次。256代表输出通道数。经过这层之后,特征图尺寸变为80*80*256,特征图的长宽已经变成输入图像的1/8。- [-1, 1, Conv, [256, 3, 2]] # 第16层,进行卷积操作(256代表输出通道数,3代表卷积核大小k,2代表stride步长),输出特征图尺寸为40*40*256(卷积的参数都没变,所以都是长宽变成原来的1/2,和之前一样)。- [[-1, 12], 1, Concat, [1]]  # cat head P4 第17层,本层是concat层,[-1, 12]代表将上层和第12层的输出作为本层的输入。[1]代表concat拼接的维度是1。从上面的分析可知,上层的输出尺寸是40*40*256,第12层的输出是40*40*512,最终本层的输出尺寸为40*40*768。- [-1, 3, C2f, [512]]  # 18 (P4/16-medium) 第18层,本层是C2f模块,可以参考第2层的讲解。3代表本层重复3次。512代表输出通道数。经过这层之后,特征图尺寸变为40*40*512,特征图的长宽已经变成输入图像的1/16。- [-1, 1, Conv, [512, 3, 2]] # 第19层,进行卷积操作(512代表输出通道数,3代表卷积核大小k,2代表stride步长),输出特征图尺寸为20*20*512(卷积的参数都没变,所以都是长宽变成原来的1/2,和之前一样)。- [[-1, 9], 1, Concat, [1]]  # cat head P5 第20层,本层是concat层,[-1, 9]代表将上层和第9层的输出作为本层的输入。[1]代表concat拼接的维度是1。从上面的分析可知,上层的输出尺寸是20*20*512,第9层的输出是20*20*1024,最终本层的输出尺寸为20*20*1536。- [-1, 3, C2f, [1024]]  # 21 (P5/32-large) 第21层,本层是C2f模块,可以参考第2层的讲解。3代表本层重复3次。1024代表输出通道数。经过这层之后,特征图尺寸变为20*20*1024,特征图的长宽已经变成输入图像的1/32。- [[15, 18, 21], 1, Detect, [nc]]  # Detect(P3, P4, P5) 第20层,本层是Detect层,[15, 18, 21]代表将第15、18、21层的输出(分别是80*80*256、40*40*512、20*20*1024)作为本层的输入。nc是数据集的类别数。

分类模型结构

接下来,我们看一下YOLOv8用于分类的模型结构,可以看到,除了最后的Head模块发生了改变,Backbone部分并没有明显改动(分类的Backbone还是去掉了SPPF模块的),并且,分类直接去掉了特征融合模块(Neck),直接连接了分类头,这说明分类相较于检测更简单些。

nc: 1000 # number of classes
scales: # model compound scaling constants, i.e. 'model=yolov8n-cls.yaml' will call yolov8-cls.yaml with scale 'n'# [depth, width, max_channels]n: [0.33, 0.25, 1024]s: [0.33, 0.50, 1024]m: [0.67, 0.75, 1024]l: [1.00, 1.00, 1024]x: [1.00, 1.25, 1024]# YOLOv8.0n backbone
backbone:# [from, repeats, module, args]- [-1, 1, Conv, [64, 3, 2]] # 0-P1/2- [-1, 1, Conv, [128, 3, 2]] # 1-P2/4- [-1, 3, C2f, [128, True]]- [-1, 1, Conv, [256, 3, 2]] # 3-P3/8- [-1, 6, C2f, [256, True]]- [-1, 1, Conv, [512, 3, 2]] # 5-P4/16- [-1, 6, C2f, [512, True]]- [-1, 1, Conv, [1024, 3, 2]] # 7-P5/32- [-1, 3, C2f, [1024, True]]# YOLOv8.0n head
head:- [-1, 1, Classify, [nc]] # Classify

具体的,可以通过分类头的代码来查看其输出的结果:\ultralytics\nn\modules\head.py

class Classify(nn.Module):"""YOLOv8 classification head, i.e. x(b,c1,20,20) to x(b,c2)."""def __init__(self, c1, c2, k=1, s=1, p=None, g=1):"""Initializes YOLOv8 classification head with specified input and output channels, kernel size, stride,padding, and groups."""super().__init__()c_ = 1280  # efficientnet_b0 sizeself.conv = Conv(c1, c_, k, s, p, g)self.pool = nn.AdaptiveAvgPool2d(1)  # to x(b,c_,1,1)self.drop = nn.Dropout(p=0.0, inplace=True)self.linear = nn.Linear(c_, c2)  # to x(b,c2)def forward(self, x):"""Performs a forward pass of the YOLO model on input image data."""if isinstance(x, list):x = torch.cat(x, 1)x = self.linear(self.drop(self.pool(self.conv(x)).flatten(1)))return x if self.training else x.softmax(1)

Debug可知,其输入到分类头的数据维度为torch.Size([1, 256, 7, 7]),分类头结构如下,可以看到,其最终输出结果的维度为(1,1000),对应1000个类别。

Classify((conv): Conv((conv): Conv2d(256, 1280, kernel_size=(1, 1), stride=(1, 1))(act): SiLU(inplace=True))(pool): AdaptiveAvgPool2d(output_size=1)(drop): Dropout(p=0.0, inplace=True)(linear): Linear(in_features=1280, out_features=1000, bias=True)
)

分类推理

from ultralytics import YOLO
model = YOLO("yolov8n-cls.pt")  # load an official model
model.predict("image.jpg",save=True)

YOLO模型能够根据pt文件来获取当然模型所进行的任务,pt文件中包含任务类型task,模型的yaml文件,predict方法也因此可以根据其任务类型选择不同的推理形式,结果如下:其分类为泰迪,应该是泰迪熊的意思,当然它应该是分错的,人家明明是猫好吧。

在这里插入图片描述

分类训练

分类开始训练代码如下,当然这里可以直接传入YOLO8n-cls的预训练模型,因为pt文件包含这些yaml文件内容的

from ultralytics import YOLO
if __name__ == '__main__':
# 代码model = YOLO("ultralytics\cfg\models/v8\yolov8-cls.yaml").load("yolov8n-cls.pt")  # build from YAML and transfer weightsresults = model.train(data="imagenette160", epochs=100, imgsz=64)

此外,需要更改的便是训练过程中的数据集与损失函数了
分类损失定义在:\ultralytics\utils\loss.py

class v8ClassificationLoss:"""Criterion class for computing training losses."""def __call__(self, preds, batch):"""Compute the classification loss between predictions and true labels."""loss = F.cross_entropy(preds, batch["cls"], reduction="mean")loss_items = loss.detach()return loss, loss_items

batch即真实类别标签,由于batch=16,因此共有16个标签,数字即对应的类别索引,preds为预测结果,维度为(16,10),即有16个预测结果,10为类别格式,其结果为16张图像的各个类别得分。

在这里插入图片描述

loss.detach()返回一个新的tensor,从当前计算图中分离下来的,但是仍指向原变量的存放位置,不同之处只是requires_gradfalse,得到的这个tensor永远不需要计算其梯度,不具有grad

在这里插入图片描述

最终的分类训练结果

在这里插入图片描述

F.cross_entropy函数是torch提供的用于求交叉熵损失函数的工具包,其用法为:F.cross_entropy(input, target)
交叉熵公式:

在这里插入图片描述

其中P为真实值,Q 为预测值。
计算交叉熵的详细步骤:

①将predict_scores进行softmax运算,将运算结果记为pred_scores_soft
②将pred_scores_soft进行log运算,将运算结果记为pred_scores_soft_log
③将pred_scores_soft_log与真实值进行计算处理。
思路即:

scores→softmax→log→compute

计算案例如下:
在这里插入图片描述

分类评价指标

accuracy_top-1
就是你预测的label取最后概率向量里面最大的那一个作为预测结果,如果你的预测结果中概率最大的那个分类正确,则预测正确。否则预测错误
accuracy_top-5
就是最后概率向量最大的前五名中,只要出现了正确概率即为预测正确。否则预测错误。

由此可以看出,top5一般比top1
与目标检测一样,YOLOv8的分类结果也会被保存,如下:

在这里插入图片描述
混淆矩阵

在机器学习领域,混淆矩阵(Confusion Matrix),又称为可能性矩阵或错误矩阵。混淆矩阵是可视化工具,特别用于监督学习,在无监督学习一般叫做匹配矩阵。在图像精度评价中,主要用于比较分类结果和实际测得值,可以把分类结果的精度显示在一个混淆矩阵里面。

混淆矩阵要表达的含义:

混淆矩阵的每一列代表了预测类别,每一列的总数表示预测为该类别的数据的数目;
每一行代表了数据的真实归属类别,每一行的数据总数表示该类别的数据实例的数目;每一列中的数值表示真实数据被预测为该类的数目。
在这里插入图片描述

在这里插入图片描述
batch推断结果可视化

在这里插入图片描述

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

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

相关文章

【书生大模型实战营(暑假场)】入门任务二 Git 关卡

入门任务二 Git 关卡 参考: 教程任务 注意: 项目Github链接 1 闯关任务 1.1 使用 Git 完成破冰介绍 本任务将基于开发机实现,重点在于熟悉Git操作。首先要了解 Git操作的常见四部曲,即:舔 Add,提 Co…

计算机毕业设计选题推荐-电缆行业生产管理系统-Java/Python项目实战

✨作者主页:IT研究室✨ 个人简介:曾从事计算机专业培训教学,擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

Gradle 入门指南:从安装到基础用法详解

文章目录 Gradle 简介安装 Gradle创建和配置 Gradle 项目将 Gradle 项目打成jar包多项目聚合示例项目结构步骤详解 Gradle 简介 Gradle 是一个基于 Groovy 和 Kotlin 的构建工具,用于自动化构建、依赖管理和项目管理。它结合了 Maven 的依赖管理和 Ant 的灵活性&am…

【MySQL】库操作,数据类型

目录 MySQL简介SQL语句分类库操作语句展示数据库创建数据库使用数据库删除数据库 数据类型整型浮点型字符串日期类型 MySQL简介 数据库有关系型数据库和非关系型数据库。 关系型数据库:是指采用了关系模型来组织数据的数据库。 简单来说,关系模型指的就…

智能化的Facebook未来:AI如何重塑社交网络的面貌?

随着人工智能(AI)技术的飞速发展,社交网络的面貌正在经历深刻的变革。Facebook(现Meta Platforms)作为全球最大的社交媒体平台之一,正积极探索如何利用AI技术来提升用户体验、优化内容管理并推动平台创新。…

线上预约陪诊平台医院陪诊系统源码就医陪护小程序APP开发

项目分析 随着医疗行业的数字化转型和人们对健康需求的日益增长,线上预约陪诊系统作为一种新兴的医疗服务模式,正逐渐受到市场的关注和认可。本文将从市场前景、使用人群、盈利模式以及竞品分析等多个角度,全面探讨线上预约陪诊系统的技术性…

【稳定ACM出版、EI检索|武汉场线上线下结合】2024年第五届医学人工智能国际学术会议(ISAIMS 2024,8月13-17)

第五届医学人工智能国际学术会议(ISAIMS2024)将于2024年8月13-17日于荷兰阿姆斯特丹自由大学召开,国内分会场将于2024年10月25-27日于中国武汉召开。 本届会议将继续围绕人工智能在医学领域的最新研究成果,为来自国内外高等院校、…

反转链表(LeetCode)

题目 给你单链表的头节点,请你反转链表,并返回反转后的链表 解题 class ListNode:def __init__(self, value0, nextNone):self.value valueself.next nextdef reverse_linked_list_recursive(head: ListNode) -> ListNode:# 空链表或单节点链表if …

计算机网络-传输层

网络层不具有重传,需要传输层来控制。 Tcp:需要将传输的数据进行分段传输,并且能够建立会话,具备流量控制,是一种可靠的传输协议UDP:一个数据包就能完成数据通信,不分段,不需要建立…

人工智能|人工智能教育的发展现状及趋势

智能的热潮正席卷全球。国家在人工智能领域展开战略布局,人工智能人才成为国家急需的高层次技术人才。据领英发布的《全球 Al 领域人才报告》显示,国内人工智能人才缺口达到 500 多万。 毫无疑问,人工智能将不可阻挡地影响所有产业。给自己一…

【数据结构】线性表(线性表的定义和基本操作)

计算机考研408-数据结构笔记本之——第二章 线性表 2.1 线性表的定义和基本操作 1 线性表的定义(数据结构三要素——逻辑结构) 线性表是具有相同数据类型的n(n≥0)个数据元素的有限序列. 其中n为表长,当n0 时线性表是一个空表。 若用L命名线性表&…

【C++】:智能指针 -- RAII思想shared_ptr剖析

目录 一,内存泄漏二,智能指针的使用及原理2.1 RAII思想2.2 auto_ptr2.3 unique_ptr 三,shared_ptr(重点)3.1 shared_ptr的原理及使用3.2 shared_ptr的模拟实现1. 基本框架2. 引用计数的设计3. 拷贝构造4. 析构函数5. 赋值拷贝 3.3 shared_ptr…

详解Xilinx FPGA高速串行收发器GTX/GTP(4)--TX/RX接口的数据位宽和时钟设计

目录 1、时钟设计 2、TX接口 3、接口位宽与时钟的关系 4、时钟来源方案 5、TX端内部的时钟分频设计 6、RX接口 文章总目录点这里:《FPGA接口与协议》专栏的说明与导航 1、时钟设计 GT收发器内部比较复杂,所使用的时钟就不止一个,比较主要的时钟有两个,架构…

Zookeeper的监听机制及原理解析

系列文章目录 手把手教你安装Zookeeper 及可视化插件ZooInspector、ZKUI Zookeeper入门篇,了解ZK存储特点 使用Zookeeper的监听及原理解析 系列文章目录前言一、监听机制的基本概念二、Zookeeper监听原理1. 事件类型2. 监听模式与监听器类型(1&#xff…

健康管理系统

目录 第1章 系统概述 第2章 可行性研究 2.1 项目背景及意义 2.2 可行性研究 第3章 需求分析 3.1 功能性需求 3.2 非功能性需求 3.2.1 性能需求 第4章 总体设计 4.1 技术架构 4.2功能模块设计 第5章 详细设计 5.1 主页 5.2 写剧本杀 5.3 剧本杀分类管理 5.4 个人…

数组下标越界异常(ArrayIndexOutOfBoundsException)以及解决方案

在Java学习的初期,我们往往可能会遇到一些程序的错误提示,告诉我们,程序出现了某些不正常的情况,在这种情况发生时,我们一般称之为出现了异常。 我们目前有两类常见的错误: 一个是编译时异常 &#xff0c…

数据分析与应用:微信-情人节红包流向探索分析

目录 0 需求描述 1 红包发送方用户的基本信息缺失率有多高?(即有多少红包发送方用户无法在用户基本信息表中匹配? 2 哪一组红包金额的拒收率最高? 3、最受二线城市欢迎的红包金额为?(即发出次数最多) 4 北上广深 4 大城市中,哪座城市的男性用户发出的 520 红包比例…

三大口诀不一样的代码,小小的制表符和换行符玩的溜呀

# 小案例,打印输出加法口诀 for i in range(1,10):for j in range(1,10):if j>i:breakprint(f"{j}{i}{ji}".strip(),end\t)print() print(\n) for i in range(1,10):for j in range(1,10):if j>i:breakprint(f"{j}x{i}{j*i}",end\t)print…

计算机毕业设计选题推荐-房屋租赁系统-Java/Python项目实战

✨作者主页:IT研究室✨ 个人简介:曾从事计算机专业培训教学,擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

app逆向抓包技巧:SSL Pinning检测绕过

本篇博客旨在记录学习过程,不可用于商用等其它途径 场景 在charles抓包下,某斑马app在注册时发现点击登录毫无反应,看抓包结果提示SSL handshake with client failed,确定是触发了SSL/TLS Pinning(证书锁定&#xff…