我自己的原文哦~ https://blog.51cto/whaosoft/12830682
1、Yolo5
1.1、YOLOv5~DNN模块部署
一、什么是模型部署?
在典型的机器学习和深度学习项目中,我们通常从定义问题陈述开始,然后是数据收集和准备(数据预处理)和模型构建(模型训练),对吧?但是,最后,我们希望我们的模型能够提供给最终用户,以便他们能够利用它。模型部署是任何机器学习项目的最后阶段之一,可能有点棘手。如何将机器学习模型传递给客户/利益相关者?模型的部署大致分为以下三个步骤:
- 模型持久化
持久化,通俗得讲,就是临时数据(比如内存中的数据,是不能永久保存的)持久化为持久数据(比如持久化至数据库中,能够长久保存)。那我们训练好的模型一般都是存储在内存中,这个时候就需要用到持久化方式,在Python中,常用的模型持久化方式一般都是以文件的方式持久化。
- 选择适合的服务器加载已经持久化的模型
- 提高服务接口,拉通前后端数据交流
二、案例,运行操作:
准备ONNX模型
我们在tests/testdata下准备了一个分类模型mnasnet0_5.onnx,可用于测试。
通过如下手段可以获取更多的ONNX模型:
- 可以从OpenMMLab/PyTorch导出ONNX模型:model-convert-guide.md
- 从ONNX Model Zoo获取模型:https://github/onnx/models
ONNX Model Zoo的模型opset版本都较低,可以通过tools下的convert_onnx_opset_version.py将opset转换为11:
python convert_onnx_opset_version.py --input_model input_model.onnx --output_model output_model.onnx --output_opset 11
转换opset具体请参考:onnx-model-opset-convert-guide.md
准备测试图片
测试图片使用任何格式均可。我们在tests/testdata下准备了cat0.png和cat1.jpg(ImageNet 的验证集图片):
任意大小的图片都可以正常运行,如果想要resize到224 x 224的话,可以修改程序里的如下变量:
const bool resize_input = false; // 想要resize的话,修改为true即可
测试推理服务
运行
pplnn-build/samples/cpp/run_model/classification <image_file> <onnx_model_file>
推理完成后,会得到如下输出:
image preprocess succeed!
[INFO][2021-07-23 17:29:31.341][simple_graph_partitioner:107] total partition(s) of graph[torch-jit-export]: 1.
successfully create runtime builder!
successfully build runtime!
successfully set input data to tensor [input]!
successfully run network!
successfully get outputs!
top 5 results:
1th: 3.416199 284 n02123597 Siamese cat, Siamese
2th: 3.049764 285 n02124075 Egyptian cat
3th: 2.989676 606 n03584829 iron, smoothing iron
4th: 2.812310 283 n02123394 Persian cat
5th: 2.796991 749 n04033901 quill, quill pen
不难看出,这个程序正确判断猫是真猫。至此OpenPPL的安装与图像分类模型推理已完成。另外,在pplnn-build/tools目录下有可执行文件pplnn,可以进行任意模型推理、dump输出数据、benchmark等操作,具体用法可使用--help选项查看。大家可以基于该示例进行改动,从而更熟悉OpenPPL的用法。
三、DNN模块部署Yolov5
用opencv的dnn模块做yolov5目标检测的程序,包含两个步骤:1)、把pytorch的训练模型pth文件转换到onnx文件;2)、opencv的dnn模块读取onnx文件做前向计算。
1)、把pytorch的训练模型pth文件转换到onnx文件
yolov5官方代码:https://github/ultralytics/yolov5
这套程序里的代码比较乱,在pytorch里,通常是在py文件里定义网络结构的,但是官方代码是在yaml文件定义网络结构,利用pytorch动态图特性,解析yaml文件自动生成网络结构。
在yaml文件里有depth_multiple和width_multiple,它是控制网络的深度和宽度的参数。这么做的好处是能够灵活的配置网络结构,但是不利于理解网络结构,假如你想设断点查看某一层的参数和输出数值,那就没办法了。
因此,在编写的转换到onnx文件的程序里,网络结构是在py文件里定义的。其次,在官方代码里,还有一个奇葩的地方,那就是pth文件。起初,下载官方代码到本地运行时,torch.load读取pth文件总是出错,后来把pytorch升级到1.7,就读取成功了。可以看到版本兼容性不好,这是它的一个不足之处。设断点查看读取的pth文件里的内容,可以看到ultralytics的pt文件里既存储有模型参数,也存储有网络结构,还储存了一些超参数,包括anchors,stride等等。
self.register_buffer('anchors', a) #shape(nl,na,2)
self.register_buffer('anchor_grid', a.clone().view(self.nl, 1, -1, 1, 1, 2))
尝试过把这两行代码改成:
self.anchors = a
self.anchor_grid = a.clone().view(self.nl, 1, -1, 1, 1, 2)
程序依然能正常运行,但是torch.save保存模型文件后,可以看到pth文件里没有存储anchors和anchor_grid了,在百度搜索register_buffer,解释是:pytorch中register_buffer模型保存和加载的时候可以写入和读出。在这两行代码的下一行:
self.m = nn.ModuleList(nn.Conv2d(x, self.no * self.na, 1) for x in ch) # output conv
它的作用是做特征图的输出通道对齐,通过1x1卷积把三种尺度特征图的输出通道都调整到num_anchors*(num_classes+5)。阅读Detect类的forward函数代码,可以看出它的作用是根据偏移公式计算出预测框的中心坐标和高宽,这里需要注意的是,计算高和宽的代码:
pwh = (ps[:, 2:4].sigmoid() * 2) ** 2 * anchors[i]
没有采用exp操作,而是直接乘上anchors[i],这是yolov5与yolov3v4的一个最大区别(还有一个区别就是在训练阶段的loss函数里,yolov5采用邻域的正样本anchor匹配策略,增加了正样本。其它的是一些小区别,比如yolov5的第一个模块采用FOCUS把输入数据2倍下采样切分成4份,在channel维度进行拼接,然后进行卷积操作,yolov5的激活函数没有使用Mish)。
现在可以明白Detect类的作用是计算预测框的中心坐标和高宽,简单来说就是生成proposal,作为后续NMS的输入,进而输出最终的检测框。我觉得在Detect类里定义的1x1卷积是不恰当的,应该把它定义在Detect类的外面,紧邻着Detect类之前定义1x1卷积。
在官方代码里,有转换到onnx文件的程序:
python models/export.py --weights yolov5s.pt --img 640 --batch 1
在pytorch1.7版本里,程序是能正常运行生成onnx文件的。观察export.py里的代码,在执行torch.onnx.export之前,有这么一段代码:
# Input
img = torch.zeros(opt.batch_size, 3, *opt.img_size) # image size (1, 3, 320, 192) iDetection
# Update model
for k, m in model.named_modules():
m._non_persistent_buffers_set = set() # pytorch 1.6.0 compatibility
if isinstance(m, modelsmon.Conv): # assign export-friendly activations
if isinstance(m.act, nn.Hardswish):
m.act = Hardswish()
elif isinstance(m.act, nn.SiLU):
m.act = SiLU()
# elif isinstance(m, models.yolo.Detect):
# m.forward = m.forward_export #assign forward (optional)
model.model[-1].export = True # set Detect() Layer export = True
y = model(img) # dry run
注意其中的for循环,我试验过注释掉它,重新运行就会出错,打印出的错误如下:
由此可见,这段for循环代码是必需的。SiLU其实就是swish激活函数,而在onnx模型里是不直接支持swish算子的,因此在转换生成onnx文件时,SiLU激活函数不能直接使用nn.Module里提供的接口,而需要自定义实现它。
2)、opencv的dnn模块读取.onnx文件做前向计算
在生成onnx文件后,就可以用opencv的dnn模块里的cv2.dnn.readNet读取它。然而,在读取时,出现了如下错误:
其实是:
其次,在models\yolo.py里的Detect类里,也有切片操作,代码如下:
y[..., 0:2] = (y[..., 0:2] * 2. - 0.5 + self.grid[i].to(x[i].device)) * self.stride[i]
y[..., 2:4] = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i] # wh
前面说过,Detect类的作用是计算预测框的中心坐标和高宽,生成proposal,这个是属于后处理的,因此不需要把它写入到onnx文件里。
总结一下,按照上面的截图代码,修改Focus类,把Detect类里面的1x1卷积定义在紧邻着Detect类之前的外面,然后去掉Detect类,组成新的model,作为torch.onnx.export的输入:
torch.onnx.export(model, inputs, output_onnx, verbose=False, opset_version=12, input_names=['images'], output_names=['out0', 'out1', 'out2'])
最后生成的onnx文件,opencv的dnn模块就能成功读取了,接下来对照Detect类里的forward函数,用python或者C++编写计算预测框的中心坐标和高宽的功能。
在github上,地址是https://github/hpc203/yolov5-dnn-cpp-python
四、后处理模块
后处理模块,python版本用numpy array实现的,C++版本的用vector和数组实现的,整套程序只依赖opencv库(opencv4版本以上的)就能正常运行,彻底摆脱对深度学习框架pytorch,tensorflow,caffe,mxnet等等的依赖。用openvino作目标检测,需要把onnx文件转换到.bin和.xml文件,相比于用dnn模块加载onnx文件做目标检测是多了一个步骤的。因此,我就想编写一套用opencv的dnn模块做yolov5目标检测的程序,用opencv的dnn模块做深度学习目标检测,在win10和ubuntu,在cpu和gpu上都能运行,可见dnn模块的通用性更好,很接地气。
生成yolov5s_param.pth 的步骤:
首先下载https://github/ultralytics/yolov5的源码到本地,在yolov5-master主目录(注意不是我发布的github代码目录)里新建一个.py文件,把下面的代码复制到.py文件里。
import torch
from collections import OrderedDict
import pickle
import os
device = 'cuda' if torch.cuda.is_available() else 'cpu'
if __name__=='__main__':
choices = ['yolov5s', 'yolov5l', 'yolov5m', 'yolov5x']
modelfile = choices[0]+'.pt'
utl_model = torch.load(modelfile, map_location=device)
utl_param = utl_model['model'].model
torch.save(utl_param.state_dict(), os.path.splitext(modelfile)[0]+'_param.pth')
own_state = utl_param.state_dict()
print(len(own_state))
numpy_param = OrderedDict()
for name in own_state:
numpy_param[name] = own_state[name].data.cpu().numpy()
print(len(numpy_param))
with open(os.path.splitext(modelfile)[0]+'_numpy_param.pkl', 'wb') as fw:
pickle.dump(numpy_param, fw)
运行这个.py文件,这时候就可以生成yolov5s_param.pth文件。之所以要进行这一步,我在上面讲到过:ultralytics的.pt文件里既存储有模型参数,也存储有网络结构,还储存了一些超参数,包括anchors,stride等等的。torch.load加载ultralytics的官方.pt文件,也就是utl_model = torch.load(modelfile, map_location=device)这行代码,在这行代码后设断点查看utl_model里的内容,截图如下:
可以看到utl_model里含有既存储有模型参数,也存储有网络结构,还储存了一些超参数等等的,这会严重影响转onnx文件。此外,我还发现,如果pytorch的版本低于1.7,那么在torch.load加载.pt文件时就会出错的。
因此在程序里,我把模型参数转换到cpu.numpy形式的,最后保存在.pkl文件里。这时候在win10系统cpu环境里,即使你的电脑没有安装pytorch,也能通过python程序访问到模型参数。
五、pytorch转onnx常见坑:
- onnx只能输出静态图,因此不支持if-else分支。一次只能走一个分支。如果代码中有if-else语句,需要改写。
- onnx不支持步长为2的切片。例如a[::2,::2]
- onnx不支持对切片对象赋值。例如a[0,:,:,:]=b, 可以用torch.cat改写
- onnx里面的resize要求output shape必须为常量。可以用以下代码解决
if isinstance(size, torch.Size):
size = tuple(int(x) for x in size)
此外,在torch.onnx.export(model, inputs, output_onnx)的输入参数model里,应该只包含网络结构,也就是说model里只含有nn.Conv2d, nn.MaxPool2d, nn.BatchNorm2d, F.relu等等的这些算子组件,而不应该含有后处理模块的。图像预处理和后处理模块需要自己使用C++或者Python编程实现。
在明白了这些之后,在转换生成onnx文件,你需要执行两个步骤,第一步把原始训练模型.pt文件里的参数保存到新的.pth文件里,第二步编写yolov5.py文件,把yolov5的往来结构定义在.py文件里,此时需要注意网络结构里不能包含切片对象赋值操作,F.interpolate里的size参数需要加int强制转换。在执行完这两步之后才能生成一个opencv能成功读取并且做前向推理的onnx文件。
不过,最近我发现在yolov5-pytorch程序里,其实可以直接把原始训练模型.pt文件转换生成onnx文件的,而且我在一个yolov5检测人脸+关键点的程序里实验成功了。
1.2、yolo5~半监督域自适应
全新的 Semisupervised Domain Adaptive YOLO, SSDA-YOLO, 即基于半监督域自适应的 YOLO 方法,通过将最火爆的单阶段目标检测器 YOLOv5 与域自适应相结合从而提高跨域检测的性能。
Paper: https://arxiv/pdf/2211.02213.pdf
Github: https://github/hnuzhy/SSDA-YOLO
Domain Adaptive Object Detection, DAOD
, 即域自适应对象检测,旨在减轻由跨域问题而引起的模型泛化性能下降。现有的大部分 DAOD 方法都比较老旧,几乎都是以两阶段的 Faster R-CNN
算法实现的,其计算量非常大导致耗时严重。因此,今天为大家带来一篇全新的 Semisupervised Domain Adaptive YOLO, SSDA-YOLO
, 即基于半监督域自适应的 YOLO 方法,通过将最火爆的单阶段目标检测器 YOLOv5
与域自适应相结合从而提高跨域检测的性能。具体做法分三步:
- 将知识蒸馏框架与
Mean Teacher
模型结合起来,以帮助学生模型获得未标记目标域的实例级特征; - 利用场景风格迁移在不同领域交叉生成伪图像,以弥补图像层次差异;
- 提出一种更加直观的一致性损失来进一步对齐跨域预测。
通过最终的实验表明,本文方法在包括 PascalVOC
、Clipart1k
、Cityscapes
和 Foggy Cityscapes
在内的四个公共基准上均取得了不错的效果。此外,为了验证其泛化性,作者还收集了真实场景下的检测数据进行了评估验证,结果表明 SSDA-YOLO 在这些 DAOD 任务中有了相当大的改进,充分揭示了所提出的自适应模块的有效性以及在 DAOD 中应用更先进检测器的必要性。
目标检测
目标检测算法大致可以分为三种:
- 基于单阶段的目标检测器如
YOLO v2-v8
、SSD
、Retinanet
等; - 基于双阶段的目标检测器如
RCNN
、Fast-RCNN
、Faster-RCNN
等; - 基于 VIsion Transformer 的目标检测器如
Relation Net
、DETR
等;
域自适应
域自适应学习(Domain Adaptation Learning)能够有效地解决训练样本和测试样本概率分布不一致的学习问题,是当前机器学习的热点研究领域,在计算机视觉、自然语言处理,文本分析,生物信息学,跨语言分析,视频分析,情感分析和手写体识别等领域均有广泛应用。这块内容平常比较少讲,今天先简单的介绍下跨域目标检测和半监督域自适应两部分,后期有时间的话可以专门出一篇文章详细介绍 Domain Adaptation
这个方向,大家有兴趣的可以关注『CVHub』官方卫星号,敬请期待!
现有的跨域目标检测方法大都基于两阶段目标检测器 Faster R-CNN 实现的。
DA-Faster
DA-Faster
属于开创性的工作,主要贡献是引入梯度反转层(GRL)并首先设计实例级和图像级对齐以提升在未知域的性能。
SWDA
SWDA
则提出了相似的强局部和弱全局特征对齐以进一步改善了 DA-Faster 的性能。
SCL
SCL
SCL
同样也是基于 Faster R-CNN,同时提出了一种基于梯度分离的堆叠互补损失方法。
NLDA
NLDA
从鲁棒学习的角度解决了域适应问题,同时将其表述为带有噪声标签的训练。为此,作者提出了一个强大的对象检测框架,它对边界框类标签、位置和大小注释中的噪声均具有鲁棒性。最后,为了适应域转移的问题,使用一组噪声目标边界框在目标域上训练模型,这些边界框是由仅在源域中训练的检测模型获得的。
MEAA
MEAA
提出了一种由局部不确定性注意力对齐模块 LUAA 和多级不确定性感知上下文对齐 MUCA 模块所组成的双阶段目标检测算法以更好的解决跨域目标检测的问题。
UMT
UMT
则提出了一种新的用于跨域对象检测的无偏均值教师模型。其揭示了在跨域场景中简单均值教师模型通常存在相当大的模型偏差,并通过几种简单但高效的策略消除了模型偏差。特别的,对于教师模型,作者提出了一种用于 MT 的跨域蒸馏方法,以最大限度地利用教师模型的专业知识。此外,对于学生模型,通过增加具有像素级自适应的训练样本来减轻其偏差。最后,对于教学过程,则是采用分布外估计策略来选择最适合当前模型的样本,以进一步增强跨域蒸馏过程。
MSDA
MSDA
着眼于来自多个源域的标记数据,提出了分而治之的主轴网络 DMSN 来增强域不变性并保持判别力。
USDAF
USDAF
实现了具有多标签学习的通用尺度感知域自适应 Faster R-CNN
,以减少训练期间的负迁移效应。
SIGMA
SIGMA
提出了一种新颖的语义完全图匹配框架,通过将源数据和目标数据表示为图形,并将自适应重新表述为图形匹配问题。简言之便是利用图节点建立语义感知节点亲和力,并利用图边作为结构感知匹配损失中的二次约束,通过节点到节点图匹配实现细粒度的自适应。
无监督域适应(UDA)被定义为使模型从标记的源域适应未标记的目标域,起初被广泛研究用于图像分类任务。
DTPL
DTPL
在 UDA 基础上通过提供目标域图像的图像级注释提出了一个弱监督的渐进域适应框架。
MTOR
MTOR
首先学习了关系图,分别捕捉教师和学生的区域对之间的相似性。然后通过三个一致性正则化优化整个架构:
- 区域级一致性以对齐教师和学生之间的区域级预测;
- 图间一致性以匹配教师和学生之间的图结构;
- 内部图一致性,以增强学生图中同一类区域之间的相似性。
除此之外,最近新提出的 TRKP
、TDD
和 PT
则基本是通过应用 MT
模型应用知识蒸馏框架来补救跨域差异和感知目标相关特征。此外,用于处理跨域语义分割任务的 DAFormer
在其自训练 pipeline 中也采用了 MT 模型。
方法
受上述方法的启发,本文提出了一种新颖的半监督域自适应 SSDA-YOLO
,通过知识蒸馏结构中构建方法,并集成了流行的 MT 模型。如下图所示,它在源数据集中利用监督学习,并在目标数据集中执行无监督学习。此外,未标记的目标训练图像在输入教师模型之前用类源全局场景进行样式转换。
上图为 SSDA-YOLO
的整体架构图,从左到右分别为:
- 输入部分:除了在训练过程中将真实源图像 和目标图像 作为输入外,本文还使用相应的预训练 CUT 模型生成类目标假源图像 和类源假目标图像 ,以缓解图像级之间的域差异问题。
- 网络部分:基于原版的 YOLOv5 模型作为知识蒸馏框架中教师和学生模型的基本检测器。
- 输出部分:基于对多个输入(以不同颜色示例)的预测,构建了相应的损失函数来支持半监督学习。
而 SSDA-YOLO
模型本身由四部分组成:
- 具有知识蒸馏框架的 Mean Teacher 模型;
- 用于指导稳健的学生网络更新、用于减轻图像级域差异的伪交叉生成训练图像;
- 用于补救跨域差异的更新蒸馏损失;
- 新颖的一致性方法损失用于进一步纠正跨域目标偏见误差。
下面详细描述下这四个部分。
Mean Teacher Model
Mean Teacher
,即 MT 模型最初是由《Weight-averaged consistency targets improve semi-supervised deep learning result》一文提出并用于图像分类中的半监督学习。它由一个典型的知识蒸馏结构和两个相同的模型架构(即学生网络和教师网络)组成。对于域自适应任务,学生模型常使用梯度下降优化器在源域中使用标记数据进行训练。根据 MT 模型设置,教师模型由学生模型的指数移动平均,即 EMA
进行权重更新。
Pseudo Training Images Generation
通过第一步我们成功的构建了一个最基础的蒸馏网络,不过遗憾的是,此处学生模型的权重更新主要由源域中的图像主导。相比之下,教师模型则不会接触到源图像并由目标域特征进行引导。所以我们要如何缓解这种图像级别的域差异呢?毕竟这样一来会导致两个模型偏向于过拟合单一的伪标签了。
本文受 SWDA 的方法启发(上面介绍了),同样基于 CycleGAN 在全局场景级别通过弱对齐来学习域不变特征。在本文中,作者选择生成类目标伪造源图像和类源伪造目标图像来进行训练。如上图所示,这里采用更高级的未配对图像转换器 CUT 以实现更快、更稳健的场景传输,是不是整体看起来有点诡异的诙谐感~哈哈哈。
Remedying Cross-Domain Discrepancy
- 在相应的特征图之间应用中间监督策略;
- 在最终预测之间应用误差约束;
- 同时结合以上两种策略。
中间监督策略是由卷积姿态机 CPM 提出的,起初是用于单人姿态估计任务。其实这种方法跟深监督机制的思想是类似的,所以我们如果拿来应用在监督训练中解决梯度消失问题也是挺合理的嘛,你说是不是这个道理老铁?但问题是此处我们不希望模型输出相似的中间特征,而是期望其预测输出尽可能一致。因此,这种方案我们还是 pass 掉吧。作者最终是采用第二种方式,即通过计算两个最终输出之间的 L2 距离来进行约束,公式可以表述如下:
看起来有点复杂,但其实大家把它拆成几部分单独理解也是蛮简单的,建议对照下代码去看。
实验
如上述表格所示,本文选取了 11 种具有代表性的方法进行比较,有意思的是这些方法全是基于 Faster R-CNN。从实验结果看出,本文提出的域自适应模块效能好像不是很哇塞,不过 YOLOv5 的推理效率各方面还是挺不错的,对落地比较友好,貌似 YOLOv8 也快发版了哦,目前相关的资料都被曝光了,大家也可以尝试自行替换下。
上面除了 [17,16,19] 是基于 FCOS 实现的,其他都是基于 Faster R-CNN。上述表格报告了 DAOD 在 Foggy Cityscapes 验证集上的所有结果。可以看出,由于 YOLOv5 的数据增强策略,Source Only 方法实现了与最近最先进的方法如 EPMDA 相当的 mAP 值达到了35.9。通过添加蒸馏损失和一致性损失,本文方法在 BaseDC 更是达到了 55.9 的 mAP,远高于迄今为止 TDD 中的最佳结果 49.2。
从定性结果来看,尽管本文方法在 Rel 场景下表现不比 PT 和 TDD 好,不过大体还是优于同年提出的方法如 TIA、MGADA 和 SIGMA,大概率是得益于所提出的自适应策略的有效性以及 YOLOv5 出色的性能。
这张图更有意思,是作者自己采集的真实场景下的图片,可以看到,尽管与 Oracle 结果相比仍然存在差距,但本文所提出的方法可以明显缓解真实课堂中跨域行为检测的准确性下降。消融实验部分这里不讲啦,明天还要上班有点累了,感兴趣的小伙伴自己去看看吧,今天先讲到这里。
总结
本文提出了一种名为 SSDA-YOLO
的新型半监督跨域目标检测方法。同以往大部分基于二阶段的目标检测器 Faster-RCNN 方法不同,本文采用更实用的 YOLOv5 作为基础的检测器。具体来说,这个框架包含三个有效的组件。
首先,基于知识蒸馏结构,我们分别学习作为学生网络的 YOLOv5 和基于教师网络的 Mean Teacher 模型,以构建稳健的训练。其次,通过执行风格转移以交叉生成伪标签训练图像以减轻全局域差异。最后,应用一致性损失函数来校正来自不同域但具有相同标签的图像的预测偏移。
通过对公共基准和自制的打哈欠行为数据集进行的广泛实验证明,SSDA-YOLO 在实际跨域目标检测应用中的有效性和优越性,同时也揭示了采用先进检测器推进 DAOD 这个领域的必要性。
2、Yolo6
2.1、YOLOv6 v3.0
yolo6又升级了 还有人说都比v8好了~~
速速解读YOLOv6 3.0版本,YOLOv6 v3.0 版本主要创新点还是集中在网络设计和训练策略这两方方面改进,极大的推动了 YOLOv6 达到实时目标检测的最新精度。
Paper: https://arxiv/pdf/2301.05586.pdf
Code: https://github/meituan/YOLOv6
没啥好说的,YOLO
这发新的速度,股市打新都没这么猛,卷王来了都不敢吭声。今天主要是带大家光速过一遍 YOLOv6 v3.0 版本带来了更新,不做技术解读,先占个坑。YOLOv8 才正式宣布开源没几天,YOLOv6 就在新年到来前发布版本了,该版本主要是对网络架构和训练方案进行了改进。其中:
-
YOLOv6-N
在 COCO 数据集上以 1187 FPS
的吞吐量在 NVIDIA Tesla T4 GPU
上测试达到了 `37.5%`` 的 AP。 -
YOLOv6-S
在 484
FPS 下达到 `45.0%`` AP,优于同等规模的其他主流检测器(YOLOv5-S、YOLOv8-S、YOLOX-S 和 PPYOLOE-S)。 - 在接近的推理速度下,
YOLOv6-M/L
也比其他检测器实现了更好的精度性能(分别为 50.0%/52.8%)。
此外,通过扩展的骨干网络(Backbone)和颈部(Head)设计,YOLOv6-L6
最终实时实现了 YOLO
家族的 SOTA
。好了,留给百度的时间已经不多了,现在离 2023 年农历新年还有6天。
贡献
应用双向串联(BiC)模块更新了检测器的颈部,以提供更准确的定位信号。
将 YOLOv5/v8 中的 SPPF
模块简化为 SimCSSPSPP
模块,在速度几乎保持不变的同时提升精度。
提出了一种 anchor-aided
的辅助训练(AAT)策略,以在不影响推理效率的情况下同时享受到 Anchor-based
和 Anchor-free
范式的优势。
将 YOLOv6 的 Backbone 和 Head 加多一个 Stage,以加强高分辨率输入图像的性能。
采用了一种新的自蒸馏策略来提高 YOLOv6 小模型的性能,其中 DFL 的较重分支在训练期间被用作增强的辅助回归分支,并在推理时被移除以避免显着的速度下降。
方法
YOLOv6 v3.0 Framework
Network Design
在实践中,多尺度特征集成已被证明是目标检测的关键和有效组成部分。特征金字塔网络(FPN)被提议通过自上而下的路径聚合高级语义特征和低级特征,从而提供更准确的定位。随后,为了增强分层特征表示的能力,在双向 FPN 上出现了新的工作,如 PANet、BiFPN 等。
PANet 在 FPN 之上添加了一个额外的自底向上路径,以缩短低级和顶层特征的信息路径,这有助于从低级特征传播准确的信号。BiFPN 则为不同的输入特征引入了可学习的权重,并简化了 PAN 以实现更好的性能和更高的效率。PRB-FPN 被提议通过具有双向融合和相关改进的并行 FP 结构来保留高质量的特征以进行准确定位。
此外,作者将 SPPF
模块简化为类似 CSP 的版本,称为 SimCSSPPPF
模块,增强了特征表示能力。特别的,通过缩小隐藏层的通道和 SPP 来修改 YOLOv7
中的 SimSPPCSPC
块。最后,再将 CSPBlock
升级为 RepBlock
(适用于小型模型)或 CSPStackRepBlock
(适用于大型模型),并相应地调整宽度和深度。
总的来说,YOLOv6 的颈部命名为 RepBi-PAN
,其框架如上图所示。
Anchor-Aided Training
YOLOv6
是一个 anchor-free
检测器,追求更高的推理速度。然而,通过实验发现,与 anchor-free
模式相比,anchor-base
的范式在相同设置下为 YOLOv6-N
带来了额外的性能提升,如下表所示:
此外,采用 anchor-base
范式的 ATSS 作为 YOLOv6 早期版本中的 warm-up label assignment strategy
,可以稳定训练,厉害了。
鉴于此,本文提出了锚定辅助训练(AAT),其中引入了基于锚定的辅助分支以结合基于锚定和无锚定范式的优点。它们同时应用于分类和回归头,下图清晰的显示了带有辅助装置的检测头:
在训练阶段,辅助分支和无锚框分支从独立的损失中学习,同时一同反馈信号。因此,来自辅助分支的辅助嵌入式引导信息将会被集成到 anchor-free heads 中。
Self-distillation
在 YOLOv6 的早期版本中,自蒸馏仅在大型模型(即 YOLOv6-M/L)中引入,它通过最小化教师和学生的类别预测之间的 KL 散度来应用普通知识蒸馏技术。同时采用 DFL 作为回归损失,对框回归执行类似于 LD 中提出的自蒸馏方法。其中,知识蒸馏的损失函数定义如下:
值得注意的是,DFL 的引入需要回归分支的额外参数,这会显着影响小模型的推理速度。因此,YOLOv6 专门为小型模型设计了解耦局部蒸馏 (DLD),在不降低速度的情况下提高性能。
具体来说,其附加了一个重辅助增强回归分支来合并 DFL。在自蒸馏过程中,学生配备了朴素回归分支和增强回归分支,而教师仅使用辅助分支。需要注意的是,这里朴素回归分支仅使用硬标签进行训练,而辅助分支根据来自教师和硬标签的信号进行更新。蒸馏后,朴素回归分支被保留,而辅助分支被移除。通过这种策略,在不影响推理效率的情况下,DFL 在蒸馏中的重回归分支的优势得到了相当大的保持。
实验
Performance comparison with SOTA methods
废话不多说,反正全面吊打就对了。
Ablation Study
总结
YOLOv6 v3.0
版本主要创新点还是集中在网络设计和训练策略这两方方面改进,这些方法极大的推动了 YOLOv6 达到实时目标检测的最新精度。整体来说创新性不是很足,但实验部分做得还算 Solid,主要还是偏向于工程实践,借用凯明最喜欢用的一句话就是:"Without bells and whistles."。挺不错的,代码也已经开源了,大家赶紧用起来。
2.2、更准更快的YOLOv6,美团出品开源
说这个之前好像 yolo7不试都出现了吗 做为ai从业者还是发一下6吧
YOLOv6 是美团视觉智能部研发的一款目标检测框架,致力于工业应用。本框架同时专注于检测的精度和推理效率,在工业界常用的尺寸模型中:YOLOv6-nano 在 COCO 上精度可达 35.0% AP,在 T4 上推理速度可达 1242 FPS;YOLOv6-s 在 COCO 上精度可达 43.1% AP,在 T4 上推理速度可达 520 FPS。在部署方面,YOLOv6 支持 GPU(TensorRT)、CPU(OPENVINO)、ARM(MNN、TNN、NCNN)等不同平台的部署,极大地简化工程部署时的适配工作。目前,项目已开源至 Github,欢迎有需要的小伙伴们 Star 收藏,随时取用。
项目地址:https://github/meituan/YOLOv6
精度与速度远超 YOLOv5 和 YOLOX 的新框架
目标检测作为计算机视觉领域的一项基础性技术,在工业界得到了广泛的应用,其中 YOLO 系列算法因其较好的综合性能,逐渐成为大多数工业应用时的首选框架。至今,业界已衍生出许多 YOLO 检测框架,其中以 YOLOv5[1]、YOLOX[2] 和 PP-YOLOE[3] 最具代表性,但在实际使用中,我们发现上述框架在速度和精度方面仍有很大的提升的空间。基于此,我们通过研究并借鉴了业界已有的先进技术,开发了一套新的目标检测框架——YOLOv6。该框架支持模型训练、推理及多平台部署等全链条的工业应用需求,并在网络结构、训练策略等算法层面进行了多项改进和优化,在 COCO 数据集上,YOLOv6 在精度和速度方面均超越其他同体量算法,相关结果如下图 1 所示:
图 1-1 YOLOv6 各尺寸模型与其他模型性能对比
图 1-2 YOLOv6 与其他模型在不同分辨率下性能对比
图 1-1 展示了不同尺寸网络下各检测算法的性能对比,曲线上的点分别表示该检测算法在不同尺寸网络下(s/tiny/nano)的模型性能,从图中可以看到,YOLOv6 在精度和速度方面均超越其他 YOLO 系列同体量算法。图 1-2 展示了输入分辨率变化时各检测网络模型的性能对比,曲线上的点从左往右分别表示图像分辨率依次增大时(384/448/512/576/640)该模型的性能,从图中可以看到,YOLOv6 在不同分辨率下,仍然保持较大的性能优势。
YOLOv6 关键技术介绍
YOLOv6 主要在 Backbone、Neck、Head 以及训练策略等方面进行了诸多的改进:
- 我们统一设计了更高效的 Backbone 和 Neck :受到硬件感知神经网络设计思想的启发,基于 RepVGG style[4] 设计了可重参数化、更高效的骨干网络 EfficientRep Backbone 和 Rep-PAN Neck。
- 优化设计了更简洁有效的 Efficient Decoupled Head,在维持精度的同时,进一步降低了一般解耦头带来的额外延时开销。
- 在训练策略上,我们采用 Anchor-free 无锚范式,同时辅以 SimOTA[2] 标签分配策略以及 SIoU[9] 边界框回归损失来进一步提高检测精度。
2.1 Hardware-friendly 的骨干网络设计
YOLOv5/YOLOX 使用的 Backbone 和 Neck 都基于 CSPNet[5] 搭建,采用了多分支的方式和残差结构。对于 GPU 等硬件来说,这种结构会一定程度上增加延时,同时减小内存带宽利用率。下图 2 为计算机体系结构领域中的 Roofline Model[8] 介绍图,显示了硬件中计算能力和内存带宽之间的关联关系。
图 2 Roofline Model 介绍图
于是,我们基于硬件感知神经网络设计的思想,对 Backbone 和 Neck 进行了重新设计和优化。该思想基于硬件的特性、推理框架 / 编译框架的特点,以硬件和编译友好的结构作为设计原则,在网络构建时,综合考虑硬件计算能力、内存带宽、编译优化特性、网络表征能力等,进而获得又快又好的网络结构。对上述重新设计的两个检测部件,我们在 YOLOv6 中分别称为 EfficientRep Backbone 和 Rep-PAN Neck,其主要贡献点在于:
1. 引入了 RepVGG[4] style 结构。
2. 基于硬件感知思想重新设计了 Backbone 和 Neck。
RepVGG[4] Style 结构是一种在训练时具有多分支拓扑,而在实际部署时可以等效融合为单个 3x3 卷积的一种可重参数化的结构(融合过程如下图 3 所示)。通过融合成的 3x3 卷积结构,可以有效利用计算密集型硬件计算能力(比如 GPU),同时也可获得 GPU/CPU 上已经高度优化的 NVIDIA cuDNN 和 Intel MKL 编译框架的帮助。实验表明,通过上述策略,YOLOv6 减少了在硬件上的延时,并显著提升了算法的精度,让检测网络更快更强。以 nano 尺寸模型为例,对比 YOLOv5-nano 采用的网络结构,本方法在速度上提升了 21%,同时精度提升 3.6% AP。
图 3 Rep 算子的融合过程 [4]
EfficientRep Backbone:在 Backbone 设计方面,我们基于以上 Rep 算子设计了一个高效的 Backbone。相比于 YOLOv5 采用的 CSP-Backbone,该 Backbone 能够高效利用硬件(如 GPU)算力的同时,还具有较强的表征能力。下图 4 为 EfficientRep Backbone 具体设计结构图,我们将 Backbone 中 stride=2 的普通 Conv 层替换成了 stride=2 的 RepConv 层。同时,将原始的 CSP-Block 都重新设计为 RepBlock,其中 RepBlock 的第一个 RepConv 会做 channel 维度的变换和对齐。另外,我们还将原始的 SPPF 优化设计为更加高效的 SimSPPF。
图 4 EfficientRep Backbone 结构图
Rep-PAN:在 Neck 设计方面,为了让其在硬件上推理更加高效,以达到更好的精度与速度的平衡,我们基于硬件感知神经网络设计思想,为 YOLOv6 设计了一个更有效的特征融合网络结构。Rep-PAN 基于 PAN[6] 拓扑方式,用 RepBlock 替换了 YOLOv5 中使用的 CSP-Block,同时对整体 Neck 中的算子进行了调整,目的是在硬件上达到高效推理的同时,保持较好的多尺度特征融合能力(Rep-PAN 结构图如下图 5 所示)。
图 5 Rep-PAN 结构图
2.2 更简洁高效的 Decoupled Head
在 YOLOv6 中,我们采用了解耦检测头(Decoupled Head)结构,并对其进行了精简设计。原始 YOLOv5 的检测头是通过分类和回归分支融合共享的方式来实现的,而 YOLOX 的检测头则是将分类和回归分支进行解耦,同时新增了两个额外的 3x3 的卷积层,虽然提升了检测精度,但一定程度上增加了网络延时。因此,我们对解耦头进行了精简设计,同时综合考虑到相关算子表征能力和硬件上计算开销这两者的平衡,采用 Hybrid Channels 策略重新设计了一个更高效的解耦头结构,在维持精度的同时降低了延时,缓解了解耦头中 3x3 卷积带来的额外延时开销。通过在 nano 尺寸模型上进行消融实验,对比相同通道数的解耦头结构,精度提升 0.2% AP 的同时,速度提升 6.8%。
图 6 Efficient Decoupled Head 结构图
2.3 更有效的训练策略
为了进一步提升检测精度,我们吸收借鉴了学术界和业界其他检测框架的先进研究进展:Anchor-free 无锚范式 、SimOTA 标签分配策略以及 SIoU 边界框回归损失。
- Anchor-free 无锚范式
YOLOv6 采用了更简洁的 Anchor-free 检测方法。由于 Anchor-based 检测器需要在训练之前进行聚类分析以确定最佳 Anchor 集合,这会一定程度提高检测器的复杂度;同时,在一些边缘端的应用中,需要在硬件之间搬运大量检测结果的步骤,也会带来额外的延时。而 Anchor-free 无锚范式因其泛化能力强,解码逻辑更简单,在近几年中应用比较广泛。
经过对 Anchor-free 的实验调研,我们发现,相较于 Anchor-based 检测器的复杂度而带来的额外延时,Anchor-free 检测器在速度上有 51% 的提升。
- SimOTA 标签分配策略
为了获得更多高质量的正样本,YOLOv6 引入了 SimOTA [4] 算法动态分配正样本,进一步提高检测精度。YOLOv5 的标签分配策略是基于 Shape 匹配,并通过跨网格匹配策略增加正样本数量,从而使得网络快速收敛,但是该方法属于静态分配方法,并不会随着网络训练的过程而调整。近年来,也出现不少基于动态标签分配的方法,此类方法会根据训练过程中的网络输出来分配正样本,从而可以产生更多高质量的正样本,继而又促进网络的正向优化。
例如,OTA[7] 通过将样本匹配建模成最佳传输问题,求得全局信息下的最佳样本匹配策略以提升精度,但 OTA 由于使用了 Sinkhorn-Knopp 算法导致训练时间加长,而 SimOTA[4] 算法使用 Top-K 近似策略来得到样本最佳匹配,大大加快了训练速度。故 YOLOv6 采用了 SimOTA 动态分配策略,并结合无锚范式,在 nano 尺寸模型上平均检测精度提升 1.3% AP。
- SIoU 边界框回归损失
为了进一步提升回归精度,YOLOv6 采用了 SIoU[9] 边界框回归损失函数来监督网络的学习。目标检测网络的训练一般需要至少定义两个损失函数:分类损失和边界框回归损失,而损失函数的定义往往对检测精度以及训练速度产生较大的影响。
近年来,常用的边界框回归损失包括 IoU、GIoU、CIoU、DIoU loss 等等,这些损失函数通过考虑预测框与目标框之前的重叠程度、中心点距离、纵横比等因素来衡量两者之间的差距,从而指导网络最小化损失以提升回归精度,但是这些方法都没有考虑到预测框与目标框之间方向的匹配性。SIoU 损失函数通过引入了所需回归之间的向量角度,重新定义了距离损失,有效降低了回归的自由度,加快网络收敛,进一步提升了回归精度。通过在 YOLOv6s 上采用 SIoU loss 进行实验,对比 CIoU loss,平均检测精度提升 0.3% AP。
实验结果
经过以上优化策略和改进,YOLOv6 在多个不同尺寸下的模型均取得了卓越的表现。下表 1 展示了 YOLOv6-nano 的消融实验结果,从实验结果可以看出,我们自主设计的检测网络在精度和速度上都带来了很大的增益。
表 1 YOLOv6-nano 消融实验结果
下表 2 展示了 YOLOv6 与当前主流的其他 YOLO 系列算法相比较的实验结果。从表格中可以看到:
表 2 YOLOv6 各尺寸模型性能与其他模型的比较
- YOLOv6-nano 在 COCO val 上 取得了 35.0% AP 的精度,同时在 T4 上使用 TRT FP16 batchsize=32 进行推理,可达到 1242FPS 的性能,相较于 YOLOv5-nano 精度提升 7% AP,速度提升 85%。
- YOLOv6-tiny 在 COCO val 上 取得了 41.3% AP 的精度, 同时在 T4 上使用 TRT FP16 batchsize=32 进行推理,可达到 602FPS 的性能,相较于 YOLOv5-s 精度提升 3.9% AP,速度提升 29.4%。
- YOLOv6-s 在 COCO val 上 取得了 43.1% AP 的精度, 同时在 T4 上使用 TRT FP16 batchsize=32 进行推理,可达到 520FPS 的性能,相较于 YOLOX-s 精度提升 2.6% AP,速度提升 38.6%;相较于 PP-YOLOE-s 精度提升 0.4% AP 的条件下,在 T4 上使用 TRT FP16 进行单 batch 推理,速度提升 71.3%。
延庆川北小区45孙老师 东屯 收卖废品破烂垃圾炒股 废品孙。
总结与展望
本文介绍了美团视觉智能部在目标检测框架方面的优化及实践经验,我们针对 YOLO 系列框架,在训练策略、主干网络、多尺度特征融合、检测头等方面进行了思考和优化,设计了新的检测框架 - YOLOv6,初衷来自于解决工业应用落地时所遇到的实际问题。
在打造 YOLOv6 框架的同时,我们探索和优化了一些新的方法,例如基于硬件感知神经网络设计思想自研了 EfficientRep Backbone、Rep-Neck 和 Efficient Decoupled Head,同时也吸收借鉴了学术界和工业界的一些前沿进展和成果,例如 Anchor-free、SimOTA 和 SIoU 回归损失。在 COCO 数据集上的实验结果显示,YOLOv6 在检测精度和速度方面都属于佼佼者。未来我们会持续建设和完善 YOLOv6 生态,主要工作包括以下几个方面:
1. 完善 YOLOv6 全系列模型,持续提升检测性能。
2. 在多种硬件平台上,设计硬件友好的模型。
3. 支持 ARM 平台部署以及量化蒸馏等全链条适配。
4. 横向拓展和引入关联技术,如半监督、自监督学习等等。
5. 探索 YOLOv6 在更多的未知业务场景上的泛化性能。
3、Yolo8
3.1、YOLOv8训练
这是在YOLOv8的官方仓库上直接配置和训练yolov5的全过程。
YOLOv8_Efficient的介绍
- Github地址:https://github/isLinXu/YOLOv8_Efficient
本项目基于ultralytics及yolov5等进行综合参考,致力于让yolo系列的更加高效和易用。
目前主要做了以下的工作:
- 参考https://docs.ultralytics/config/中的Configuration参数,分别针对train.py、detect.py、val.py等做了相应参数的配置对齐。
- 结合yolov5的使用习惯以及代码结构做了兼容和优化。
- 通过在coco数据集上在自己的机器上进行验证和计算的权重的指标参数,实验记录存放在https://github/isLinXu/YOLOv8_Efficient/tree/main/log.实验数据记录在:
根据计算出来的结果绘制了相应的指标参数对比图,这个绘图程序也开源在https://github/isLinXu/model-metrics-plot中。
- 融合其他更多网络模型结构进行集成整合和配置,正在进行中...
关于ultralytics的名字
为什么这个仓库取名为ultralytics,而不是yolov8,结合这个issue,笔者认为主要有以下几个方面的原因:
- 1.因为ultralytics团队希望将这个项目设计和建成一个集合分类,检测,分割等视觉任务的集成训练推理框架,而不仅仅只是yolov8。后续可能会有更多更全的网络模型会集成进来。
- 2.因为pypi上的第三方已经把yolov6,yolov7,yolov8等名字给取了,pip install名称的规则是不允许有重复名的。
issue链接:https://github/ultralytics/ultralytics/issues/179
关于自定义配置模型训练
结合上面的讨论,自然而然会有这个想法,既然ultralytics要建一个集成训练框架,那么能否直接在ultralytics仓库上直接配置和训练yolov5呢,笔者做了下面一系列的尝试:
- 在models中加入相应的.yaml文件和yolov5沿用的模块,如common.py、experimental.py、google_utils.py
- 在
models/common.py
中,加入了yolov5所需的网络结构
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) # optional 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)), 1))
- 在运行时加入模块用于测试
最后一通操作下来,已经可以根据yolov5s.yaml去读取网络结构了,但是在跑的时候还是报错。
报错信息如下:
于是针对"train_args"
做了一个全局搜索,发现了下面的结果:
可以看到,之前训练出来的v8的权重内包含了"train_args"
的信息。顺着程序运行的流程,相应地发现了yolo/engine/model
中的"__init__(self)"
函数,
def __init__(self, model='yolov8n.yaml', type="v8") -> None:
"""
Initializes the YOLO object.
Args:
model (str, Path): model to load or create
type (str): Type/version of models to use. Defaults to "v8".
"""
self.type = type
self.ModelClass = None # model class
self.TrainerClass = None # trainer class
self.ValidatorClass = None # validator class
self.PredictorClass = None # predictor class
self.model = None # model object
self.trainer = None # trainer object
self.task = None # task type
self.ckpt = None # if loaded from *.pt
self.ckpt_path = None
self.cfg = None # if loaded from *.yaml
self.overrides = {} # overrides for trainer object
self.init_disabled = False # disable model initialization
# Load or create new YOLO model
{'.pt': self._load, '.yaml': self._new}[Path(model).suffix](model)
读取模型和配置是在"__init__"
的最后一行:
# Load or create new YOLO model
{'.pt': self._load, '.yaml': self._new}[Path(model).suffix](model)
而def _load(self, weights: str):
中实际读取模型权重的实现是self.model = attempt_load_weights(weights)
。可以看到,相比于yolov5,v8读取权重的函数attempt_load_weights
,多了下面这行
args = {**DEFAULT_CONFIG_DICT, **ckpt['train_args']} # combine model and default args, preferring model args
那么,能否直接将v5的项目中,将相应的函数补充过来给v8做适配呢,自然是可以的,当笔者将model.py的_load
函数中这行代码:
self.model = attempt_load_weights(weights)
替换为下面这行时:
self.model = attempt_load(weights)
重新运行了一遍,发现又出现了下面的问题:
错误信息为AttributeError: 'Model' object has no attribute 'args'
,既然是Model定义和配置上的问题,那么就没有再往下修改的必要了,还是等官方团队的更新和修改吧,等等党永远不亏。
关于v8的多任务使用
根据官方的文档介绍,还有对代码的分析,目前v8项目是支持检测、分类和分割的。设定是通过"task"
进行区分任务,又通过mode
来设置是训练还是检测的模式,如下使用:
yolo task=detect mode=train model=yolov8n.yaml epochs=1 ...
... ... ...
segment predict yolov8n-seg.pt
classify val yolov8n-cls.pt
训练
预测
验证
- !关于这三个任务,YOLOv8_Efficient项目后续会分别设置相应的模块用于执行,目前正在更新中。
附件
YOLOv8读取权重
def attempt_load_weights(weights, device=None, inplace=True, fuse=False):
# Loads an ensemble of models weights=[a,b,c] or a single model weights=[a] or weights=a
from ultralytics.yolo.utils.downloads import attempt_download
model = Ensemble()
for w in weights if isinstance(weights, list) else [weights]:
ckpt = torch.load(attempt_download(w), map_location='cpu') # load
args = {**DEFAULT_CONFIG_DICT, **ckpt['train_args']} # combine model and default args, preferring model args
ckpt = (ckpt.get('ema') or ckpt['model']).to(device).float() # FP32 model
...
YOLOv5读取权重
def attempt_load(weights, device=None, inplace=True, fuse=True):
# Loads an ensemble of models weights=[a,b,c] or a single model weights=[a] or weights=a
from models.yolo import Detect, Model
model = Ensemble()
for w in weights if isinstance(weights, list) else [weights]:
ckpt = torch.load(attempt_download(w), map_location='cpu') # load
ckpt = (ckpt.get('ema') or ckpt['model']).to(device).float() # FP32 model
...
参考
[1].https://github/isLinXu/YOLOv8_Efficient.
[2].https://github/isLinXu/model-metrics-plot.
3.2、YOLOv8训练2
之前有一个了
图1.1:YOLOv8初始测试
YOLOv8🔥于 2023年1月10日由Ultralytics发布。它在计算机视觉方面提供了进展,带来了对我们感知、分析和理解视觉世界的巨大创新。它将为各个领域带来前所未有的可能性。
在速度、准确性和架构方面进行了相当大的改进。它是从头开始实现的,没有使用任何来自YOLOv5的主要模块(即模型架构)。它的速度更快,比其先前版本(YOLOv7)更准确,并且在平均精度均值(MAP)方面获得了53.7的新高。
图1.2:YOLOv8平均精度均值
在本文中,我们将重点介绍训练YOLOv8自定义数据集所需的步骤。您可以按照下面提到的步骤在自己的数据上训练YOLOv8。所有提到的步骤都经过了适当的测试,在Windows和Linux操作系统上运行良好。
- 安装模块
- 预训练的目标检测
- 使用自定义数据训练YOLOv8
- 使用自定义权重进行推理
安装模块
YOLOv8发布了一个名为“ultralytics”的软件包,您可以使用下面提到的命令进行安装。
pip install ultralytics==8.0.0
or
# latestversion
pip install ultralytics
以上命令将安装所有必要的软件包,以便您可以在自己的数据上使用YOLOv8进行检测和训练。
注意:请确保您的系统上安装了Python 3.7.0或更高版本。
预训练的目标检测
如果您只需要运行单个命令来以高效的方式进行目标检测并提供更准确和快速的结果,那您会有什么感受呢?
您可以在终端/(命令提示符)中运行以下命令,在所选视频/图像上使用预训练权重进行检测,使用YOLOv8。
#for image
yolo task=detect mode=predict model=yolov8n.pt source="test.png"
#for video
yolo task=detect mode=predict model=yolov8n.pt source="test.mp4"
如果一切顺利,您将在当前目录内的“runs/detect/exp”文件夹中获得结果。
Fig-1.3: 预训练对象检测(作者提供的图像)
在自定义数据上训练 YOLOv8
训练 YOLOv8 对象检测模型的步骤可以概括如下:
- 收集数据
- 标记数据
- 划分数据集(训练集、测试集和验证集)
- 创建配置文件
- 开始训练
步骤 1:收集数据
为 YOLOv8 自定义训练创建一个数据集。如果没有数据,可以使用来自 openimages 数据库的数据集或以下网站提供的数据集:https://medium/nerd-for-tech/extraction-of-frames-from-multiple-videos-3ddbced6f3c2
YOLOv8 将标签数据存储在文本(.txt)文件中,格式如下:
<object-class-id> <x> <y> <width> <height>
步骤 2:标记数据
您可以使用 labelImg 工具或 Roboflow 平台进行数据标注,具体取决于您的需求。如果您想了解 labelImg 工具的工作流程,可以查看以下文章:
https://medium/nerd-for-tech/labeling-data-for-object-detection-yolo-5a4fa4f05844
步骤 3:划分数据集(训练集、测试集和验证集)
当您想在自定义数据上训练计算机视觉模型时,将数据分成训练集和测试集非常重要。训练集用于教授模型如何进行预测,而测试集用于评估模型的准确性。常见的分割比例是 80-20%,但实际比例可能取决于数据集的大小和您正在处理的具体任务。例如,如果您有一个小数据集,您可能希望使用更高的百分比进行训练,而如果您有一个大数据集,您可以使用较小的百分比进行训练。
对于数据拆分,您可以查看 split-folders,它会将数据随机拆分为训练集、测试集和验证集。split-folders链接:https://pypi/project/split-folders/
文件夹结构:
├── yolov8
## └── train
####└── images (folder including all training images)
####└── labels (folder including all training labels)
## └── test
####└── images (folder including all testing images)
####└── labels (folder including all testing labels)
## └── valid
####└── images (folder including all testing images)
####└── labels (folder including all testing labels)
步骤 4:创建配置文件
创建自定义配置文件可以是组织和存储计算机视觉模型的所有重要参数的有用方式。
在你已经打开终端/(命令提示符)的当前目录内创建一个文件名为“custom.yaml”的文件。将下面的代码粘贴到该文件中。设置数据集文件夹的正确路径,更改类及其名称,然后保存它。
path: (dataset directory path)
train: (Complete path to dataset train folder)
test: (Complete path to dataset test folder)
valid: (Complete path to dataset valid folder)
#Classes
nc: 5# replace according to your number of classes
#classes names
#replace all class names list with your classes names
names: ['person', 'bicycle', 'car', 'motorcycle', 'airplane']
注意:确保设置正确的训练和测试目录路径,因为训练过程将完全依赖于该文件。
步骤 5:开始训练
一旦你完成了预处理步骤,例如数据收集,数据标注,数据拆分和创建自定义配置文件,你可以使用下面在终端/(命令提示符)中提到的命令开始在自定义数据上训练YOLOv8。
yolo task=detect mode=train model=yolov8n.pt data=custom.yaml epochs=3 imgsz=640
task = detect(可以是分割或分类)
mode = train(可以是预测或验证)
model = yolov8n.pt(可以是yolov8s / yolov8l / yolov8x)
epochs = 3(可以是任何数字)
imgsz = 640(可以是320、416等,但请确保它是32的倍数)
图1.5:在自定义数据上训练YOLOv8
如果有任何图像损坏,YOLOv8将不会开始在自定义数据上进行训练。如果一些标签文件损坏,那么训练不会有问题,因为YOLOv8将忽略这些(图像和标签)文件。
等待训练完成,然后使用新创建的权重进行推断。自定义训练的权重将保存在下面提到的文件夹路径中。
[runs/train/exp/weights/best.pt]
使用自定义权重推理
使用自定义权重进行推断时,请使用下面提到的命令进行检测。
yolo task=detect mode=predict model="runs/train/exp/weights/best.pt" source="test.png"
or
yolo task=detect mode=predict model="runs/train/exp/weights/best.pt" source="test.mp4"
3.3、优化YOLOv8以实现更快的推理速度
为了进行研究,我需要减少 YOLOv8 的推理时间。在网上搜索并咨询 ChatGPT 后,我找到了以下方法。它们真的有效吗,还是具有误导性?
在我的研究中,我发现有些方法对我来说不起作用。不过,我会解释所有这些方法,你可以将本文作为各种方法的总结。
在这项研究中,我使用了我自己的电脑,而不是 Google Colab。我的电脑有一台 Intel i5(第 12 代)处理器,我的 GPU 是 NVIDIA GeForce RTX 3050。这些信息很重要,因为我对某些方法使用了 CPU,而对其他方法使用了 GPU。
原始模型用法
为了进行测试,我们使用了 Ultralytics 的 YOLOv8n.pt 模型并使用bus.jpg图像对其进行了评估。我们将分析获得的时间值和结果。
为了了解模型的性能,了解它在哪个设备上运行也很重要——它是使用 CUDA GPU 还是 CPU。
# cuda GPU推理
import cv2
import matplotlib.pyplot as plt
from ultralytics import YOLO
import torch
yolov8model = YOLO("yolov8n.pt")
img = cv2.imread("bus.jpg")
results = yolov8model.predict(source=img, device='cuda')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
for result in results:
boxes = result.boxes
for box in boxes:
x1, y1, x2, y2 = box.xyxy[0].tolist()
confidence = box.conf[0].item()
class_id = int(box.cls[0].item())
cv2.rectangle(img, (int(x1), int(y1)), (int(x2), int(y2)), (255, 0, 0), 2)
cv2.putText(img, f'ID: {class_id} Conf: {confidence:.2f}',
(int(x1), int(y1)-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)
used_device = next(yolov8model.model.parameters()).device
print("Model is running on:", used_device)
plt.figure(figsize=(10, 10))
plt.imshow(img)
plt.axis('off')
plt.show()
# cpu推理
import cv2
import matplotlib.pyplot as plt
from ultralytics import YOLO
import torch
yolov8model = YOLO("yolov8n.pt")
img = cv2.imread("bus.jpg")
results = yolov8model.predict(source=img, device='cpu')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
for result in results:
boxes = result.boxes
for box in boxes:
x1, y1, x2, y2 = box.xyxy[0].tolist()
confidence = box.conf[0].item()
class_id = int(box.cls[0].item())
cv2.rectangle(img, (int(x1), int(y1)), (int(x2), int(y2)), (255, 0, 0), 2)
cv2.putText(img, f'ID: {class_id} Conf: {confidence:.2f}',
(int(x1), int(y1)-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)
plt.figure(figsize=(10, 10))
plt.imshow(img)
plt.axis('off')
plt.show()
used_device = next(yolov8model.model.parameters()).device
print("Model is running on:", used_device)
现在,我们有了一个起点。具体来说,对于bus.jpg图像,该模型的推理时间在 CPU 上为 199.7 毫秒,在 GPU 上为 47.2 毫秒。
模型修剪
我们使用的第一种方法是修剪模型。修剪会改变模型并创建更高效的版本。有些方法会修改模型本身,而另一些方法则会更改输入或直接影响推理。在修剪过程中,会删除模型中不太重要或影响最小的连接。这会使模型更小、更快,但会对准确性产生负面影响。
import torch
import torch.nn.utils.prune as prune
from ultralytics import YOLO
def prune_model(model,amount=0.3):
for module in model.modules():
if isinstance(module,torch.nn.Conv2d):
prune.l1_unstructured(module,name="weight",amount=amount)
prune.remove(module,"weight")
return model
model = YOLO("yolov8n.pt")
#results= model.val(data="coco.yaml")
#print(f"mAP50-95: {results.box.map}")
torch_model = model.model
print(torch_model)
print("Prunning model...")
pruned_torch_model = prune_model(torch_model,amount=0.1)
print("Model pruned.")
model.model =pruned_torch_model
print("Saving pruned model...")
model.save("yolov8n_trained_pruned.pt")
print("Pruned model saved.")
通常使用一种方法来比较数据集,但本例中yolov8n.pt使用了通用模型和约18GB的数据集,本例中coco.yaml未使用该文件。
我将分享所用 GPU 的结果,我们将更新比较图,因为应用不同参数时时间可能会发生变化。通常,我无法弄清楚时间变化的原因,但这可能是由于内存或其他因素造成的。
# cuda pruned
import cv2
import matplotlib.pyplot as plt
from ultralytics import YOLO
import torch
yolov8model = YOLO("yolov8n_trained_pruned.pt")
img = cv2.imread("bus.jpg")
results = yolov8model.predict(source=img, device='cuda')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
for result in results:
boxes = result.boxes
for box in boxes:
x1, y1, x2, y2 = box.xyxy[0].tolist()
confidence = box.conf[0].item()
class_id = int(box.cls[0].item())
cv2.rectangle(img, (int(x1), int(y1)), (int(x2), int(y2)), (255, 0, 0), 2)
cv2.putText(img, f'ID: {class_id} Conf: {confidence:.2f}',
(int(x1), int(y1)-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)
used_device = next(yolov8model.model.parameters()).device
print("Model is running on:", used_device)
plt.figure(figsize=(10, 10))
plt.imshow(img)
plt.axis('off')
plt.show()
正如您所见,结果有点令人困惑;ID 和 blob 不准确。
然而,当我们比较推理时间时,修剪后的模型在 CPU 和 GPU 上的表现略优于原始模型。修剪后的模型的问题在于它会影响结果,但会减少模型的推理时间。
更改Batch Size
在确定模型训练或预测的批处理大小时,模型中同时处理的帧数至关重要。我创建了一个循环来确定最佳批处理大小,因为增加批处理大小有时会产生负面影响。但是,我注意到最佳批处理大小会随着每次尝试而改变。我尝试对结果求平均值,但这种方法不够。
为了说明我的发现,我将分享我最初试验的一张表格,并用红点突出显示最佳批量大小。
import cv2
import matplotlib.pyplot as plt
from ultralytics import YOLO
import torch
import time
yolov8model = YOLO("yolov8n.pt")
img = cv2.imread("bus.jpg")
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
inference_times = []
for batch_size in range(1, 41):
start_time = time.time()
results = yolov8model.predict(source=img_rgb, device='cuda', batch=batch_size)
end_time = time.time()
inference_time = end_time - start_time
inference_times.append((batch_size, inference_time))
print(f"Batch Size: {batch_size}, Inference Time: {inference_time:.4f} seconds")
plt.figure(figsize=(10, 5))
batch_sizes = [bt[0] for bt in inference_times]
times = [bt[1] for bt in inference_times]
min_time_index = times.index(min(times))
min_batch_size = batch_sizes[min_time_index]
min_inference_time = times[min_time_index]
plt.plot(batch_sizes, times, marker='o')
plt.plot(min_batch_size, min_inference_time, 'ro', markersize=8)
plt.title('Inference Time vs. Batch Size')
plt.xlabel('Batch Size')
plt.ylabel('Inference Time (seconds)')
plt.xticks(batch_sizes)
plt.grid()
plt.show()
best_results = yolov8model.predict(source=img_rgb, device='cuda', batch=min_batch_size)
for result in best_results:
boxes = result.boxes
for box in boxes:
x1, y1, x2, y2 = box.xyxy[0].cpu().numpy()
conf = box.conf[0].cpu().numpy()
cls = int(box.cls[0].cpu().numpy())
cv2.rectangle(img, (int(x1), int(y1)), (int(x2), int(y2)), (0, 0, 255), 2)
cv2.putText(img, f'Class: {cls}, Conf: {conf:.2f}', (int(x1), int(y1) - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)
plt.figure(figsize=(10, 10))
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.title(f'Results with Batch Size {min_batch_size}')
plt.axis('off')
plt.show()
硬件加速方法
为了提高 YOLOv8 模型的性能,另一个选择是使用硬件加速。有几种工具可用于此目的,例如 TensorRT 和 OpenVINO。
TensorRT
TensorRT 是一种利用 NVIDIA 硬件优化推理效率的方法。在这一部分中,我使用带有 T4 GPU 的 Google Colab 来比较标准模型与 TensorRT 优化模型的性能。让我们从如何将我们的模型转换为 TensorRT 格式开始。首先,我们需要将我们的模型文件上传到 Colab,然后编写以下代码:
from ultralytics import YOLO
model = YOLO("yolov8n.pt")
model.export(format="engine")
然后我们使用模型预测bus.jpg,TensorRT 优化模型的推理时间为 6.6 毫秒。相比之下,标准模型的推理时间为 6.9 毫秒。从结果可以看出,由于 T4 硬件更先进,TensorRT 模型的性能略优于标准模型。
import cv2
import matplotlib.pyplot as plt
from ultralytics import YOLO
import torch
yolov8model = YOLO('yolov8n.engine')
img = cv2.imread("bus.jpg")
results = yolov8model.predict(source=img, device='cuda')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
for result in results:
boxes = result.boxes
for box in boxes:
x1, y1, x2, y2 = box.xyxy[0].tolist()
confidence = box.conf[0].item()
class_id = int(box.cls[0].item())
cv2.rectangle(img, (int(x1), int(y1)), (int(x2), int(y2)), (255, 0, 0), 2)
cv2.putText(img, f'ID: {class_id} Conf: {confidence:.2f}',
(int(x1), int(y1)-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)
used_device = yolov8model.device
print("Model is running on:", used_device)
plt.figure(figsize=(10, 10))
plt.imshow(img)
plt.axis('off')
plt.show()
参考链接:
https://docs.ultralytics/integrations/tensorrt/#tensorrt
OpenVINO
OpenVINO 是一个主要用于优化模型性能的工具包,尤其是在英特尔硬件上。它可以显著提高 CPU 性能,在常规使用中通常可提高 3 倍。让我们首先将模型转换为 OpenVINO 格式。
from ultralytics import YOLO
# Load a YOLOv8n PyTorch model
model = YOLO("yolov8n.pt")
# Export the model
model.export(format="openvino") # creates 'yolov8n_openvino_model/'
# Load the exported OpenVINO model
ov_model = YOLO("yolov8n_openvino_model/")
import cv2
import matplotlib.pyplot as plt
from ultralytics import YOLO
yolov8model = YOLO('yolov8n_openvino_model/', task="detect")
img = cv2.imread("bus.jpg")
results = yolov8model.predict(source=img)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
for result in results:
boxes = result.boxes
for box in boxes:
x1, y1, x2, y2 = box.xyxy[0].tolist()
confidence = box.conf[0].item()
class_id = int(box.cls[0].item())
cv2.rectangle(img, (int(x1), int(y1)), (int(x2), int(y2)), (255, 0, 0), 2)
cv2.putText(img, f'ID: {class_id} Conf: {confidence:.2f}',
(int(x1), int(y1)-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)
plt.figure(figsize=(10, 10))
plt.imshow(img)
plt.axis('off')
plt.show()
可以看到,在 OpenVINO 模型中,CPU 性能上的推理时间略有下降。以下是我尝试的不同方法的比较结果。
总之,如果您拥有先进的 GPU,使用 TensorRT 是最佳选择。但是,如果您使用的是配备 Intel CPU 的计算机,OpenVINO 是首选。不同的方法会导致不同的推理时间,因此对每种方法进行了多次测试以观察差异。
3.4、分割模型实现垃圾识别
本文将介绍如何使用YOLOv8的分割模型实现垃圾识别,其中所使用的训练数据来自TACO垃圾数据集。
0. 引言
YOLOv8
是Ultralytics
开源的一个非常火的AI
算法,目前支持目标检测、实例分割、姿态估计等任务。
本文将介绍如何使用YOLOv8
的分割模型实现垃圾识别,其中所使用的训练数据来自TACO垃圾数据集。
1. 数据集介绍
TACO
是一个包含在不同环境下(室内、树林、道路和海滩)拍摄的垃圾图像数据集,这些图像中的垃圾对象被精细地用方框和多边形进行了标注,标注信息采用与COCO
数据集一样的格式,总共有60
个类别,不过有的类别标注得很少甚至没有。下图是TACO
数据集中的一些标注示例:
如果需要下载数据集,先执行下面的命令拉取官方的GitHub
仓库:
git clone https://github/pedropro/TACO.git
然后用Python
运行脚本即可下载数据集:
python3 download.py
如果下载过程中被中断了,只需重新执行download
脚本即可继续下载。
2.训练模型
2.1 转换标注格式
TACO
数据集原始的标注信息被保存在一个名为annotations.json
的文件中,在使用该数据集训练YOLOv8
分割模型前,需要先把原始的标注信息转换为YOLOv8
要求的格式。YOLOv8
分割模型训练时需要的标注格式如下:
<id> <x_1> <y_1> ... <x_n> <y_n>
一个对象的标注信息放在一行,首先是该对象类别的id
(从0
开始算),接着将多边形各点像素坐标的x
和y
值依次排列,其中x
和y
的值需要分别除以图像的宽度和高度进行归一化,一幅图像的所有标注信息放在一个与图像同名的txt
文件中。
进行格式转换后,txt
文件中的内容类似于这样:
5 0.5183 0.4892 0.5480 0.4840 0.4840 0.5627 0.4840 0.5724 0.4853 0.5822 0.4879 0.5900
7 0.6227 0.5211 0.6232 0.5250 0.5074 0.6154 0.5081 0.6183 0.5107 0.5068 0.6120 0.6290
用于格式转换的关键Python
代码如下:
img = cv2.imread(image_path)
height, width, _ = img.shape
label_writer = open(label_path, "w")
for annotation in annotations:
category_id = annotation["category_id"]
seg_labels = []
for segmentation in annotation["segmentation"]:
points = np.array(segmentation).reshape((int(len(segmentation) / 2), 2))
for point in points:
x = point[0] / width
y = point[1] / height
seg_labels.append(x)
seg_labels.append(y)
label_writer.write(str(category_id) + " " + " ".join([str(a) for a in seg_labels]) + "\n")
label_writer.close()
2.2 创建配置文件
首先仿照ultralytics/cfg/datasets/coco128-seg.yaml
创建一个TACO
数据集的配置文件taco-seg.yaml
,文件内容如下:
path: /home/test/TACO/data #数据集所在的目录
train: train.txt # 训练集路径,相对于path目录
val: val.txt # 验证集路径,相对于path目录
test: test.txt # 测试集路径,相对于path目录,可以不写
# 类别id和名称
names:
0: Aluminium foil
1: Battery
2: Aluminium blister pack
3: Carded blister pack
4: Other plastic bottle
5: Clear plastic bottle
6: Glass bottle
7: Plastic bottle cap
8: Metal bottle cap
9: Broken glass
10: Food Can
...
数据集的设置的方式有几种形式,我的方式是建立images
和labels
两个目录,分别用于存放图像和txt
标注文件,然后把数据集按照8:1:1
的比例划分训练集、验证集、测试集,再把三个数据集图片的绝对路径分别写入train.txt
、val.txt
和test.txt
三个文件中。所以上面的taco-seg.yaml
文件中设置的路径path
就是train.txt
、val.txt
和test.txt
这三个文件所在的目录,这三个文件中包含的是对应数据集中图片的绝对路径,类似于这样:
/home/test/TACO/data/images/batch_13/000077.jpg
/home/test/TACO/data/images/batch_11/000032.jpg
/home/test/TACO/data/images/batch_15/000073.jpg
配置好数据集后,还要设置模型参数。首先将ultralytics/cfg/models/v8/yolov8-seg.yaml
文件拷贝一份,命名为yolov8-seg-taco.yaml
,然后把文件中的类别数量nc
从80
改为TACO
数据集的60
:
...
# Parameters
nc: 60 # number of classes
scales: # model compound scaling constants, i.e. 'model=yolov8n-seg.yaml' will call yolov8-seg.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, 768]
l: [1.00, 1.00, 512]
x: [1.00, 1.25, 512]
...
其他有关模型结构的参数如果没有必要就不需要修改了。
2.3 训练
训练YOLOv8
可以使用命令行也可以编写Python
代码实现,个人觉得还是使用命令行比较方便,所以本文采用命令行的方式进行训练,调用的命令如下:
yolo task=segment mode=train data=taco-seg.yaml model=yolov8n-seg-taco.yaml epochs=100 batch=16 imgsz=640 device=0 name=taco-seg
这里data
参数用于指定数据集配置文件,model
参数用于指定模型配置文件,如果不知道有哪些参数可以参考ultralytics/cfg/default.yaml
文件,这个文件里面包含所有需要的参数。需要注意的是,我这里指定的模型配置文件名为yolov8n-seg-taco.yaml
,但是前面我创建的文件名为yolov8-seg-taco.yaml
,这是为什么呢?因为我这里想使用的模型是yolov8n
。假如我想使用yolov8x
模型,那么训练的时候设置参数model=yolov8x-seg-taco.yaml
就可以了。
训练的结果保存在runs/segment/taco-seg
目录下,其中权重保存在该目录下的weights
文件夹中。
3. 结果
训练完成后,我们可以调用命令测试一下模型的效果:
yolo task=segment mode=predict model=runs/segment/taco-seg/weights/best.pt source=/home/test/TACO/data/images/batch_9/000096.jpg show=True
下面是我在测试集的两张图片上测试的结
3.5、YOLO8~Pose Estimation
不知不觉间,YOLOv8已经发布三个月了,等待中的YOLOv8论文没来,昨天官方默默又加了新模型:姿态估计。
说好的"目标检测"工业界标杆,正向着“CV全家桶”阔步向前。
现在你可以用YOLOv8做目标检测、实例分割、图像分类、目标跟踪、姿态估计了,也许还有更多惊喜在后面。
要想使用最新的姿态估计功能,你需要更新到最新版的YOLOv8:
pip install --upgrade ultralytics
官方的模型可以在这里下载:
https://github/ultralytics/assets/releases
其实你也可以不用下载,如果你仅调用官方模型,程序运行时没找到模型的话,它会自己下载的,就是这么贴心!(不过要保证你的网络良好,亲测国内略慢)
模型是在COCO数据集训练的,目前支持的是人体的检测和姿态估计。
CV君用一张网络图片测试一下:
yolo pose predict model=yolov8n-pose.pt source='http://neweuropeans/wp-content/uploads/2016/01/2015_Chisinau_Crossing_Europe_Poike-Stomps_MG_1047.jpg' show=True save=True
其中pose指定任务类型,predict代表我们是要做推断,模型这里我选择的是最轻量级的YOLOv8n-pose,”show=True save=True“代表显示并保存。
运行结果:
Ultralytics YOLOv8.0.68 Python-3.9.13 torch-1.13.1+cu117 CUDA:0 (NVIDIA GeForce GTX 1080 Ti, 11264MiB)
YOLOv8n-pose summary (fused): 187 layers, 3289964 parameters, 0 gradients, 9.2 GFLOPs
Downloading http:\neweuropeans\wp-content\uploads\2016\01\2015_Chisinau_Crossing_Europe_Poike-Stomps_MG_1047.jpg to 2015_Chisinau_Crossing_Europe_Poike-Stomps_MG_1047.jpg...
100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 564k/564k [00:00<00:00, 1.29MB/s]
image 1/1 E:\download\2015_Chisinau_Crossing_Europe_Poike-Stomps_MG_1047.jpg: 448x640 17 persons, 24.6ms
Speed: 0.0ms preprocess, 24.6ms inference, 7.0ms postprocess per image at shape (1, 3, 640, 640)
Results saved to runs\pose\predict
我的GPU是1080Ti,这张1500x977大小的图片推断时间24.6ms。
如果要用YOLOv8调用摄像头的话,也非常简单:
yolo pose predict model=yolov8n-pose.pt source=0 show=True save=True
官方在COCO数据集上做了更多测试:
总计发布了YOLOv8n-pose、YOLOv8s-pose...YOLOv8x-pose-p6等6个模型,在A100上的推断速度从1.18ms到10.04ms,模型参数3.3M到99.1M。
方便在不同硬件和算力的平台上使用。
当然从CPU上的测试结果看,还不是一个CPU实时的算法。
不过更有价值的可能是,要训练我们自己的模型,是非常方便的。
按照coco128-pose.yaml的"样例"组织好数据并修改coco128-pose.yaml后,你只需要一句命令:
yolo pose train data=coco128-pose.yaml model=yolov8n-pose.pt epochs=100 imgsz=640
相关文档:https://docs.ultralytics/tasks/pose/
写在最后:YOLOv8的功能越来越多,而且相比于其他开源库,对于工业界来说更友好,涵盖训练、评估、推断、部署全流程,是快速进行项目开发的首选。
下一个进入YOLOv8 "CV全家桶"会是谁呢?Face?
4、Yolo10
4.1、yolo10
好多人应该都发了 我也跟风一下~~ yolo更新好快啊~ 我还停留在5
过去几年里,YOLOs因在计算成本和检测性能之间实现有效平衡而成为实时目标检测领域的主流范式。研究人员针对YOLOs的结构设计、优化目标、数据增强策略等进行了深入探索,并取得了显著进展。然而,对非极大值抑制(NMS)的后处理依赖阻碍了YOLOs的端到端部署,并对推理延迟产生负面影响。此外,YOLOs中各种组件的设计缺乏全面和彻底的审查,导致明显的计算冗余并限制了模型的性能。这导致次优的效率,以及性能提升的巨大潜力。在这项工作中,我们旨在从后处理和模型架构两个方面进一步推进YOLOs的性能-效率边界。为此,我们首先提出了用于YOLOs无NMS训练的持续双重分配,该方法同时带来了竞争性的性能和较低的推理延迟。此外,我们为YOLOs引入了全面的效率-准确性驱动模型设计策略。我们从效率和准确性两个角度全面优化了YOLOs的各个组件,这大大降低了计算开销并增强了模型能力。我们的努力成果是新一代YOLO系列,专为实时端到端目标检测而设计,名为YOLOv10。广泛的实验表明,YOLOv10在各种模型规模下均达到了最先进的性能和效率。例如,在COCO数据集上,我们的YOLOv10-S在相似AP下比RT-DETR-R18快1.8倍,同时参数和浮点运算量(FLOPs)减少了2.8倍。与YOLOv9-C相比,YOLOv10-B在相同性能下延迟减少了46%,参数减少了25%。代码链接:https://github/THU-MIG/yolov10。
YOLOv10有哪些改进?
首先通过为无NMS的YOLOs提出一种持续双重分配策略来解决后处理中的冗余预测问题,该策略包括双重标签分配和一致匹配度量。这使得模型在训练过程中能够获得丰富而和谐的监督,同时消除了推理过程中对NMS的需求,从而在保持高效率的同时获得了竞争性的性能。
其次,为模型架构提出了全面的效率-准确度驱动模型设计策略,对YOLOs中的各个组件进行了全面检查。在效率方面,提出了轻量级分类头、空间-通道解耦下采样和rank引导block设计,以减少明显的计算冗余并实现更高效的架构。
在准确度方面,探索了大核卷积并提出了有效的部分自注意力模块,以增强模型能力,以低成本挖掘性能提升潜力。
基于这些方法,作者成功地实现了一系列不同模型规模的实时端到端检测器,即YOLOv10-N / S / M / B / L / X。在标准目标检测基准上进行的广泛实验表明,YOLOv10在各种模型规模下,在计算-准确度权衡方面显著优于先前的最先进模型。如图1所示,在类似性能下,YOLOv10-S / X分别比RT-DETR R18 / R101快1.8倍/1.3倍。与YOLOv9-C相比,YOLOv10-B在相同性能下实现了46%的延迟降低。此外,YOLOv10展现出了极高的参数利用效率。YOLOv10-L / X在参数数量分别减少了1.8倍和2.3倍的情况下,比YOLOv8-L / X高出0.3 AP和0.5 AP。YOLOv10-M在参数数量分别减少了23%和31%的情况下,与YOLOv9-M / YOLO-MS实现了相似的AP。
在训练过程中,YOLOs通常利用TAL(任务分配学习) 为每个实例分配多个正样本。采用一对多的分配方式产生了丰富的监督信号,有助于优化并实现卓越的性能。然而,这也使得YOLOs 必须依赖于NMS(非极大值抑制)后处理,这导致在部署时的推理效率不是最优的。虽然之前的工作探索了一对一的匹配方式来抑制冗余预测,但它们通常会增加额外的推理开销或导致次优的性能。在这项工作中,我们为YOLOs提出了一种无需NMS的训练策略,该策略采用双重标签分配和一致匹配度量,实现了高效率和具有竞争力的性能。
效率驱动的模型设计。YOLO中的组件包括主干(stem)、下采样层、带有基本构建块的阶段和头部。主干部分的计算成本很低,因此我们对其他三个部分进行效率驱动的模型设计。
(1)轻量级的分类头。在YOLO中,分类头和回归头通常具有相同的架构。然而,它们在计算开销上存在显著的差异。例如,在YOLOv8-S中,分类头(5.95G/1.51M的FLOPs和参数计数)的FLOPs和参数计数分别是回归头(2.34G/0.64M)的2.5倍和2.4倍。然而,通过分析分类错误和回归错误的影响(见表6),我们发现回归头对YOLO的性能更为重要。因此,我们可以在不担心对性能造成太大损害的情况下减少分类头的开销。因此,我们简单地采用了轻量级的分类头架构,它由两个深度可分离卷积组成,卷积核大小为3×3,后跟一个1×1卷积。
(3)基于rank引导的模块设计。YOLOs通常对所有阶段都使用相同的基本构建块,例如YOLOv8中的bottleneck块。为了彻底检查YOLOs的这种同构设计,我们利用内在秩来分析每个阶段的冗余性。具体来说,计算每个阶段中最后一个基本块中最后一个卷积的数值秩,它计算大于阈值的奇异值的数量。图3(a)展示了YOLOv8的结果,表明深层阶段和大型模型更容易表现出更多的冗余性。这一观察表明,简单地对所有阶段应用相同的block设计对于实现最佳容量-效率权衡来说并不是最优的。为了解决这个问题,提出了一种基于秩的模块设计方案,旨在通过紧凑的架构设计来降低被证明是冗余的阶段的复杂性。
首先介绍了一种紧凑的倒置块(CIB)结构,它采用廉价的深度卷积进行空间混合和成本效益高的逐点卷积进行通道混合,如图3(b)所示。它可以作为有效的基本构建块,例如嵌入在ELAN结构中(图3(b))。然后,倡导一种基于秩的模块分配策略,以在保持竞争力量的同时实现最佳效率。具体来说,给定一个模型,根据其内在秩的升序对所有阶段进行排序。进一步检查用CIB替换领先阶段的基本块后的性能变化。如果与给定模型相比没有性能下降,我们将继续替换下一个阶段,否则停止该过程。因此,我们可以在不同阶段和模型规模上实现自适应紧凑块设计,从而在不影响性能的情况下实现更高的效率。
基于精度导向的模型设计。论文进一步探索了大核卷积和自注意力机制,以实现基于精度的设计,旨在以最小的成本提升性能。
(1)大核卷积。采用大核深度卷积是扩大感受野并增强模型能力的一种有效方法。然而,在所有阶段简单地利用它们可能会在用于检测小目标的浅层特征中引入污染,同时也在高分辨率阶段引入显著的I/O开销和延迟。因此,作者提出在深层阶段的跨阶段信息块(CIB)中利用大核深度卷积。这里将CIB中的第二个3×3深度卷积的核大小增加到7×7。此外,采用结构重参数化技术,引入另一个3×3深度卷积分支,以缓解优化问题,而不增加推理开销。此外,随着模型大小的增加,其感受野自然扩大,使用大核卷积的好处逐渐减弱。因此,仅在小模型规模上采用大核卷积。
(2)部分自注意力(PSA)。自注意力机制因其出色的全局建模能力而被广泛应用于各种视觉任务中。然而,它表现出高计算复杂度和内存占用。为了解决这个问题,鉴于普遍存在的注意力头冗余,作则提出了一种高效的部分自注意力(PSA)模块设计,如图3.(c)所示。具体来说,在1×1卷积之后将特征均匀地按通道分成两部分。只将一部分特征输入到由多头自注意力模块(MHSA)和前馈网络(FFN)组成的NPSA块中。然后,将两部分特征通过1×1卷积进行拼接和融合。此外,将MHSA中查询和键的维度设置为值的一半,并将LayerNorm替换为BatchNorm以实现快速推理。PSA仅放置在具有最低分辨率的第4阶段之后,以避免自注意力的二次计算复杂度带来的过多开销。通过这种方式,可以在计算成本较低的情况下将全局表示学习能力融入YOLOs中,从而很好地增强了模型的能力并提高了性能。
实验对比
这里就不做过多介绍啦,直接上结果!!!latency减少,性能继续增加。
Visualization Results
图4展示了作者YOLOv10在复杂且具有挑战性的场景下的可视化结果。可以看出,YOLOv10在各种困难条件下都能实现精确检测,例如低光照、旋转等。它还展示了在检测多种且密集排列的物体(如瓶子、杯子和人)方面的强大能力。这些结果表明其性能卓越。
Contribution, Limitation, and Broader Impact
总的来说,作者的贡献主要体现在以下三个方面:
- 作者提出了一种新颖的一致性双重分配策略,用于无需NMS的YOLO。设计了一种双重标签分配方法,通过一对多分支在训练过程中提供丰富的监督信息,以及通过一对一分支在推理过程中实现高效率。此外,为了确保两个分支之间的和谐监督,作者创新性地提出了连贯匹配度量,这可以很好地减少理论上的监督差距,并带来性能的提升。
- 作者提出了一种整体效率-精度驱动的模型设计策略,用于YOLO的模型架构。作者展示了新型轻量级分类头、空间-通道解耦降采样和排名引导的块设计,这些设计大大减少了计算冗余并实现了高效率。作者进一步引入了大核卷积和创新的部分自注意力模块,这些模块在低成本的条件下有效地提升了性能。
- 基于上述方法,作者推出了YOLOv10,这是一个新的实时端到端目标检测器。广泛的实验表明,YOLOv10与其他先进检测器相比,在性能和效率权衡方面达到了最先进水平。
局限性:由于计算资源的限制,作者没有在大规模数据集上进行YOLOv10的预训练,例如Objects365 [47]。此外,尽管作者在无需NMS的训练下使用一对一 Head 可以获得具有竞争力的端到端性能,但与使用NMS的一对多训练相比,仍然存在性能差距,特别是在小型模型中更为明显。例如,在YOLOv10-N和YOLOv10-S中,使用NMS的一对多训练的性能比无需NMS的训练分别高出1.0% AP和0.5% AP。作者将在未来的工作中探索进一步缩小差距并实现更高性能的方法。
Conclusion
在本文中,作者针对YOLO系列检测 Pipeline 中的后处理和模型架构进行了研究。对于后处理,作者提出了持续的双重分配以实现无需NMS的训练,从而实现高效的端到端检测。对于模型架构,作者引入了整体效率-精度驱动的模型设计策略,改进了性能与效率之间的权衡。这些改进带来了YOLOv10,这是一个新的实时端到端目标检测器。大量实验表明,与其它先进检测器相比,YOLOv10在性能和延迟方面均达到了最先进水平,充分展示了其优越性。
4.2、ONNX模型部署和性能对比
YOLOv10
是清华大学最近开源的一个实时端到端的目标检测算法,解决了以往版本YOLO
系列目标检测算法在后处理和模型架构方面的不足。通过消除非极大值抑制(NMS
)操作和优化模型架构,YOLOv10
在显著降低计算开销的同时还实现了最先进的性能。
YOLOv10
的模型架构由以下几个部分组成:
- 主干网络:使用增强版的
CSPNet
来提取图像特征,它能改善梯度流并减少计算量。 - 颈部:采用
PAN
结构汇聚不同尺度的特征,有效地实现多尺度特征融合。 - 一对多预测头:在训练过程中为每个对象生成多个预测,用来提供丰富的监督信号从而提高学习的准确性;在推理阶段不生效,从而减少计算量。
- 一对一预测头:在推理过程中为每个对象生成一个最佳预测,无需
NMS
操作,从而减少延迟并提高推理效率。
YOLOv10
的主要特点如下:
- 利用一致的双重分配来消除对
NMS
的需求,从而减少推理延迟。 - 从推理效率和准确性的角度出发全面优化各种组件,包括轻量级分类头、空间通道去耦下采样和等级引导块设计。
- 引入大核卷积和部分自注意模块,在不增加大量计算成本的情况下提高性能。
官方发布了从N
到X
各种型号的模型,以满足不同应用的需求:
-
YOLOv10-N
:用于资源极其有限环境的超小型版本。 -
YOLOv10-S
:兼顾速度和精度的小型版本。 -
YOLOv10-M
:通用的中型版本。 -
YOLOv10-B
:平衡型,宽度增加,精度更高。 -
YOLOv10-L
:大型版本,精度更高,但计算资源增加。 -
YOLOv10-X
:超大型版本,可实现最高的精度和性能。
「本文主要介绍如何基于ONNXRuntime
框架部署onnx
格式的YOLOv10
模型,以及YOLOv10
与RT-DETR
等算法的性能对比。」
1. 准备工作
首先把代码从GitHub
上clone
下来
git clone https://github/THU-MIG/yolov10.git
然后执行下面的命令用conda
创建Python
环境并安装相关的依赖库和YOLOv10
conda create -n yolov10 python=3.9
conda activate yolov10
pip install -r requirements.txt
pip install -e .
这里需要注意的是,如果使用的是低版本的pip
,可能会报类似下面的错误:
ERROR: File "setup.py" or "setup.cfg" not found. Directory cannot be installed in editable mode: /path/to/yolov10
(A "pyproject.toml" file was found, but editable mode currently requires a setuptools-based build.)
这种情况需要升级pip
的版本,最新版本24.0
实测是没有问题的。
pip install --upgrade pip
安装成功后,从GitHub的release中下载PyTorch
格式的模型权重,然后执行下面的命令就可以导出onnx
模型了。
yolo export model=yolov10n/s/m/b/l/x.pt format=onnx opset=13 simplify
2. onnx模型部署
2.1 加载onnx模型
首先导入onnxruntime
包,然后调用其API
加载模型即可:
import onnxruntime as ort
session = ort.InferenceSession("yolov10m.onnx", providers=["CUDAExecutionProvider"])
这里的providers
参数根据自己的实际情况设置,我使用的是GPU
所以设置的是"CUDAExecutionProvider"
。如果用CPU
进行推理,则需设置为"CPUExecutionProvider"
;如果有TensorRT
的环境,还可以设置为"TensorrtExecutionProvider"
。
模型加载成功后,我们可以查看一下模型的输入、输出层的属性:
for input in session.get_inputs():
print("input name: ", input.name)
print("input shape: ", input.shape)
print("input type: ", input.type)
for output in session.get_outputs():
print("output name: ", output.name)
print("output shape: ", output.shape)
print("output type: ", output.type)
结果如下:
input name: images
input shape: [1, 3, 640, 640]
input type: tensor(float)
output name: output0
output shape: [1, 300, 6]
output type: tensor(float)
从上面的打印信息可以知道,模型有一个尺寸为[1, 3, 640, 640]
的输入层和一个尺寸分别为[1, 300, 6]
的输出层。
2.2 数据预处理
用OpenCV
读入图片后,首先需要对图片做预处理:
image = cv2.imread("soccer.jpg")
print("image shape: ", image.shape)
image_height, image_width, _ = image.shape
_, _, model_height, model_width = session.get_inputs()[0].shape
input_tensor, ratio, x_offset, y_offset = preprocess(image,
image_width, image_height, model_width, model_height)
YOLOv10
在做数据预处理的时候是对原始图像做等比例缩放的,如果缩放后的图像某个维度上比目标值小,那么就需要进行填充。举个例子:假设输入图像尺寸为1920x1058
,模型输入尺寸为640x640
,按照等比例缩放的原则缩放后的图像尺寸为640x352
,那么在y
方向上还需要填充640-352=288
,即分别在图像的顶部和底部各填充144
行像素。最终实现的效果如下:
整个数据预处理的流程如下:
- 把
OpenCV
读取的BGR
格式图片转换为RGB
格式; - 计算缩放比例和需要填充的区域,把原始图片进行等比例缩放,对不足的区域进行填充让输入图片的尺寸匹配模型的输入尺寸;
- 对像素值除以
255
做归一化操作; - 把图像数据的通道顺序由
HWC
调整为CHW
; - 扩展数据维度,将数据的维度调整为
NCHW
。
实现上述功能的预处理函数preprocess
的代码如下:
def preprocess(bgr_image, src_w, src_h, dst_w, dst_h):
image = cv2.cvtColor(bgr_image, cv2.COLOR_BGR2RGB)
ratio = min(dst_w/src_w, dst_h/src_h)
border_w = int(round(src_w * ratio / 2) * 2)
border_h = int(round(src_h * ratio / 2) * 2)
x_offset = (dst_w - border_w ) // 2
y_offset = (dst_h - border_h ) // 2
image = cv2.resize(image, (border_w, border_h))
image = cv2.copyMakeBorder(
image, y_offset, y_offset, x_offset, x_offset,
cv2.BORDER_CONSTANT, value=(114, 114, 114)
)
image = image.astype(np.float32) / 255.0
image = np.transpose(image, (2, 0, 1))
input_tensor = np.expand_dims(image, axis=0)
return input_tensor, ratio, x_offset, y_offset
经过预处理后,输入数据input_tensor
的维度变为[1, 3, 640, 640]
,与模型要求的输入尺寸一致。
2.3 模型推理
准备好输入数据以后,就可以送入模型进行推理:
outputs = session.run(None, {session.get_inputs()[0].name: input_tensor})
output = np.squeeze(outputs[0])
print("output shape: ", output.shape)
YOLOv10
只有一个输出分支,所以只要取outputs[0]
的数据进行处理,去掉batch
这个维度后,模型输出的维度为300x6
。
output.shape: (300, 6)
2.4 后处理
从前文知道模型输出的维度为300x6
,其中300
表示模型在一张图片上最多能检测的目标数量,6
则表示每个目标包含4
个坐标属性(xmin,ymin,xmax,ymax
)和1
个类别置信度以及1
个类别索引。每个目标的坐标信息都是相对于模型的输入尺寸的,由于预处理的时候在边上做了填充,所以后处理的时候要把每个坐标值减掉对应的偏移值;如果要恢复到原始图像的尺寸,还需要除以预处理时使用的缩放比例系数。与RT-DETR
一样,YOLOv10
的检测结果不需要再做NMS
这些额外的后处理操作,处理过程非常简单。后处理的代码如下:
for i in range(output.shape[0]):
# 读取类别置信度
confidence = output[i][4]
# 用阈值进行过滤
if confidence > 0.5:
# 读取类别索引
label = int(output[i][5])
# 读取类坐标值,把坐标还原到原始图像
xmin = int((output[i][0] - x_offset) / ratio)
ymin = int((output[i][1] - y_offset) / ratio)
xmax = int((output[i][2] - x_offset) / ratio)
ymax = int((output[i][3] - y_offset) / ratio)
# 可视化
class_name = COCO_CLASSES[label]
box_color = np.array(COLOR_LIST[label]) * 255
box_color = (int(box_color[0]), int(box_color[1]), int(box_color[2]))
cv2.rectangle(image, (xmin, ymin), (xmax, ymax), box_color, 4)
# 省略.....
来看一下检测效果:
下面是用官方代码基于PyTorch
推理的结果:
可以看到,ONNXRuntime
和PyTorch
推理的结果是一致的。
3. 推理耗时对比
这里的对比仅对比模型本身的推理耗时,不包含后处理操作。本文在GeForce GTX 1650 Ti
显卡上,基于ONNXRuntime
框架分别采用CUDA
和TensorRT
后端对YOLOv10
、YOLOv9
和RT-DETR
的各个模型进行测试,数据精度统一采用FP32
,模型输入尺寸统一设置为640x640
。各模型的推理耗时(单位为毫秒)测试结果如下:
模型 | CUDA | TensorRT |
yolov10n | 10 | 7 |
yolov10s | 17 | 13 |
yolov10m | 34 | 27 |
yolov10b | 44 | 37 |
yolov10l | 55 | 46 |
yolov10x | 80 | 64 |
yolov9-c | 52 | 41 |
yolov9-e | 106 | 82 |
rtdetr_r18vd_6x | 52 | 26 |
rtdetr_r34vd_6x | 66 | 36 |
rtdetr_r50vd_6x | 94 | 52 |
rtdetr_r50vd_m_6x | 66 | 40 |
rtdetr_r101vd_6x | 133 | 89 |
YOLOv10
的论文里说YOLOv10-S
比RT-DETR-R18
快1.8
倍,YOLOv10-X
比RT-DETR-R101
快1.3
倍,YOLOv10-B
的推理延迟比YOLOv9-C
减少了46%
。从我测试的结果来看,YOLOv10-S/X
不止比RT-DETR-R18/R101
快一点几倍,YOLOv10-B
则没有比YOLOv9-C
快那么多。
总的来说,YOLOv10
的性能确实比之前的模型要强一些,新一代卷王名不虚传。
4. 参考资料
- YOLOv10: Real-Time End-to-End Object Detection
- https://docs.ultralytics/zh/models/yolov10/
我自己的原文哦~ https://blog.51cto/whaosoft/12830682
1、Yolo5
1.1、YOLOv5~DNN模块部署
一、什么是模型部署?
在典型的机器学习和深度学习项目中,我们通常从定义问题陈述开始,然后是数据收集和准备(数据预处理)和模型构建(模型训练),对吧?但是,最后,我们希望我们的模型能够提供给最终用户,以便他们能够利用它。模型部署是任何机器学习项目的最后阶段之一,可能有点棘手。如何将机器学习模型传递给客户/利益相关者?模型的部署大致分为以下三个步骤:
- 模型持久化
持久化,通俗得讲,就是临时数据(比如内存中的数据,是不能永久保存的)持久化为持久数据(比如持久化至数据库中,能够长久保存)。那我们训练好的模型一般都是存储在内存中,这个时候就需要用到持久化方式,在Python中,常用的模型持久化方式一般都是以文件的方式持久化。
- 选择适合的服务器加载已经持久化的模型
- 提高服务接口,拉通前后端数据交流
二、案例,运行操作:
准备ONNX模型
我们在tests/testdata下准备了一个分类模型mnasnet0_5.onnx,可用于测试。
通过如下手段可以获取更多的ONNX模型:
- 可以从OpenMMLab/PyTorch导出ONNX模型:model-convert-guide.md
- 从ONNX Model Zoo获取模型:https://github/onnx/models
ONNX Model Zoo的模型opset版本都较低,可以通过tools下的convert_onnx_opset_version.py将opset转换为11:
python convert_onnx_opset_version.py --input_model input_model.onnx --output_model output_model.onnx --output_opset 11
转换opset具体请参考:onnx-model-opset-convert-guide.md
准备测试图片
测试图片使用任何格式均可。我们在tests/testdata下准备了cat0.png和cat1.jpg(ImageNet 的验证集图片):
任意大小的图片都可以正常运行,如果想要resize到224 x 224的话,可以修改程序里的如下变量:
const bool resize_input = false; // 想要resize的话,修改为true即可
测试推理服务
运行
pplnn-build/samples/cpp/run_model/classification <image_file> <onnx_model_file>
推理完成后,会得到如下输出:
image preprocess succeed!
[INFO][2021-07-23 17:29:31.341][simple_graph_partitioner:107] total partition(s) of graph[torch-jit-export]: 1.
successfully create runtime builder!
successfully build runtime!
successfully set input data to tensor [input]!
successfully run network!
successfully get outputs!
top 5 results:
1th: 3.416199 284 n02123597 Siamese cat, Siamese
2th: 3.049764 285 n02124075 Egyptian cat
3th: 2.989676 606 n03584829 iron, smoothing iron
4th: 2.812310 283 n02123394 Persian cat
5th: 2.796991 749 n04033901 quill, quill pen
不难看出,这个程序正确判断猫是真猫。至此OpenPPL的安装与图像分类模型推理已完成。另外,在pplnn-build/tools目录下有可执行文件pplnn,可以进行任意模型推理、dump输出数据、benchmark等操作,具体用法可使用--help选项查看。大家可以基于该示例进行改动,从而更熟悉OpenPPL的用法。
三、DNN模块部署Yolov5
用opencv的dnn模块做yolov5目标检测的程序,包含两个步骤:1)、把pytorch的训练模型pth文件转换到onnx文件;2)、opencv的dnn模块读取onnx文件做前向计算。
1)、把pytorch的训练模型pth文件转换到onnx文件
yolov5官方代码:https://github/ultralytics/yolov5
这套程序里的代码比较乱,在pytorch里,通常是在py文件里定义网络结构的,但是官方代码是在yaml文件定义网络结构,利用pytorch动态图特性,解析yaml文件自动生成网络结构。
在yaml文件里有depth_multiple和width_multiple,它是控制网络的深度和宽度的参数。这么做的好处是能够灵活的配置网络结构,但是不利于理解网络结构,假如你想设断点查看某一层的参数和输出数值,那就没办法了。
因此,在编写的转换到onnx文件的程序里,网络结构是在py文件里定义的。其次,在官方代码里,还有一个奇葩的地方,那就是pth文件。起初,下载官方代码到本地运行时,torch.load读取pth文件总是出错,后来把pytorch升级到1.7,就读取成功了。可以看到版本兼容性不好,这是它的一个不足之处。设断点查看读取的pth文件里的内容,可以看到ultralytics的pt文件里既存储有模型参数,也存储有网络结构,还储存了一些超参数,包括anchors,stride等等。
self.register_buffer('anchors', a) #shape(nl,na,2)
self.register_buffer('anchor_grid', a.clone().view(self.nl, 1, -1, 1, 1, 2))
尝试过把这两行代码改成:
self.anchors = a
self.anchor_grid = a.clone().view(self.nl, 1, -1, 1, 1, 2)
程序依然能正常运行,但是torch.save保存模型文件后,可以看到pth文件里没有存储anchors和anchor_grid了,在百度搜索register_buffer,解释是:pytorch中register_buffer模型保存和加载的时候可以写入和读出。在这两行代码的下一行:
self.m = nn.ModuleList(nn.Conv2d(x, self.no * self.na, 1) for x in ch) # output conv
它的作用是做特征图的输出通道对齐,通过1x1卷积把三种尺度特征图的输出通道都调整到num_anchors*(num_classes+5)。阅读Detect类的forward函数代码,可以看出它的作用是根据偏移公式计算出预测框的中心坐标和高宽,这里需要注意的是,计算高和宽的代码:
pwh = (ps[:, 2:4].sigmoid() * 2) ** 2 * anchors[i]
没有采用exp操作,而是直接乘上anchors[i],这是yolov5与yolov3v4的一个最大区别(还有一个区别就是在训练阶段的loss函数里,yolov5采用邻域的正样本anchor匹配策略,增加了正样本。其它的是一些小区别,比如yolov5的第一个模块采用FOCUS把输入数据2倍下采样切分成4份,在channel维度进行拼接,然后进行卷积操作,yolov5的激活函数没有使用Mish)。
现在可以明白Detect类的作用是计算预测框的中心坐标和高宽,简单来说就是生成proposal,作为后续NMS的输入,进而输出最终的检测框。我觉得在Detect类里定义的1x1卷积是不恰当的,应该把它定义在Detect类的外面,紧邻着Detect类之前定义1x1卷积。
在官方代码里,有转换到onnx文件的程序:
python models/export.py --weights yolov5s.pt --img 640 --batch 1
在pytorch1.7版本里,程序是能正常运行生成onnx文件的。观察export.py里的代码,在执行torch.onnx.export之前,有这么一段代码:
# Input
img = torch.zeros(opt.batch_size, 3, *opt.img_size) # image size (1, 3, 320, 192) iDetection
# Update model
for k, m in model.named_modules():
m._non_persistent_buffers_set = set() # pytorch 1.6.0 compatibility
if isinstance(m, modelsmon.Conv): # assign export-friendly activations
if isinstance(m.act, nn.Hardswish):
m.act = Hardswish()
elif isinstance(m.act, nn.SiLU):
m.act = SiLU()
# elif isinstance(m, models.yolo.Detect):
# m.forward = m.forward_export #assign forward (optional)
model.model[-1].export = True # set Detect() Layer export = True
y = model(img) # dry run
注意其中的for循环,我试验过注释掉它,重新运行就会出错,打印出的错误如下:
由此可见,这段for循环代码是必需的。SiLU其实就是swish激活函数,而在onnx模型里是不直接支持swish算子的,因此在转换生成onnx文件时,SiLU激活函数不能直接使用nn.Module里提供的接口,而需要自定义实现它。
2)、opencv的dnn模块读取.onnx文件做前向计算
在生成onnx文件后,就可以用opencv的dnn模块里的cv2.dnn.readNet读取它。然而,在读取时,出现了如下错误:
其实是:
其次,在models\yolo.py里的Detect类里,也有切片操作,代码如下:
y[..., 0:2] = (y[..., 0:2] * 2. - 0.5 + self.grid[i].to(x[i].device)) * self.stride[i]
y[..., 2:4] = (y[..., 2:4] * 2) ** 2 * self.anchor_grid[i] # wh
前面说过,Detect类的作用是计算预测框的中心坐标和高宽,生成proposal,这个是属于后处理的,因此不需要把它写入到onnx文件里。
总结一下,按照上面的截图代码,修改Focus类,把Detect类里面的1x1卷积定义在紧邻着Detect类之前的外面,然后去掉Detect类,组成新的model,作为torch.onnx.export的输入:
torch.onnx.export(model, inputs, output_onnx, verbose=False, opset_version=12, input_names=['images'], output_names=['out0', 'out1', 'out2'])
最后生成的onnx文件,opencv的dnn模块就能成功读取了,接下来对照Detect类里的forward函数,用python或者C++编写计算预测框的中心坐标和高宽的功能。
在github上,地址是https://github/hpc203/yolov5-dnn-cpp-python
四、后处理模块
后处理模块,python版本用numpy array实现的,C++版本的用vector和数组实现的,整套程序只依赖opencv库(opencv4版本以上的)就能正常运行,彻底摆脱对深度学习框架pytorch,tensorflow,caffe,mxnet等等的依赖。用openvino作目标检测,需要把onnx文件转换到.bin和.xml文件,相比于用dnn模块加载onnx文件做目标检测是多了一个步骤的。因此,我就想编写一套用opencv的dnn模块做yolov5目标检测的程序,用opencv的dnn模块做深度学习目标检测,在win10和ubuntu,在cpu和gpu上都能运行,可见dnn模块的通用性更好,很接地气。
生成yolov5s_param.pth 的步骤:
首先下载https://github/ultralytics/yolov5的源码到本地,在yolov5-master主目录(注意不是我发布的github代码目录)里新建一个.py文件,把下面的代码复制到.py文件里。
import torch
from collections import OrderedDict
import pickle
import os
device = 'cuda' if torch.cuda.is_available() else 'cpu'
if __name__=='__main__':
choices = ['yolov5s', 'yolov5l', 'yolov5m', 'yolov5x']
modelfile = choices[0]+'.pt'
utl_model = torch.load(modelfile, map_location=device)
utl_param = utl_model['model'].model
torch.save(utl_param.state_dict(), os.path.splitext(modelfile)[0]+'_param.pth')
own_state = utl_param.state_dict()
print(len(own_state))
numpy_param = OrderedDict()
for name in own_state:
numpy_param[name] = own_state[name].data.cpu().numpy()
print(len(numpy_param))
with open(os.path.splitext(modelfile)[0]+'_numpy_param.pkl', 'wb') as fw:
pickle.dump(numpy_param, fw)
运行这个.py文件,这时候就可以生成yolov5s_param.pth文件。之所以要进行这一步,我在上面讲到过:ultralytics的.pt文件里既存储有模型参数,也存储有网络结构,还储存了一些超参数,包括anchors,stride等等的。torch.load加载ultralytics的官方.pt文件,也就是utl_model = torch.load(modelfile, map_location=device)这行代码,在这行代码后设断点查看utl_model里的内容,截图如下:
可以看到utl_model里含有既存储有模型参数,也存储有网络结构,还储存了一些超参数等等的,这会严重影响转onnx文件。此外,我还发现,如果pytorch的版本低于1.7,那么在torch.load加载.pt文件时就会出错的。
因此在程序里,我把模型参数转换到cpu.numpy形式的,最后保存在.pkl文件里。这时候在win10系统cpu环境里,即使你的电脑没有安装pytorch,也能通过python程序访问到模型参数。
五、pytorch转onnx常见坑:
- onnx只能输出静态图,因此不支持if-else分支。一次只能走一个分支。如果代码中有if-else语句,需要改写。
- onnx不支持步长为2的切片。例如a[::2,::2]
- onnx不支持对切片对象赋值。例如a[0,:,:,:]=b, 可以用torch.cat改写
- onnx里面的resize要求output shape必须为常量。可以用以下代码解决
if isinstance(size, torch.Size):
size = tuple(int(x) for x in size)
此外,在torch.onnx.export(model, inputs, output_onnx)的输入参数model里,应该只包含网络结构,也就是说model里只含有nn.Conv2d, nn.MaxPool2d, nn.BatchNorm2d, F.relu等等的这些算子组件,而不应该含有后处理模块的。图像预处理和后处理模块需要自己使用C++或者Python编程实现。
在明白了这些之后,在转换生成onnx文件,你需要执行两个步骤,第一步把原始训练模型.pt文件里的参数保存到新的.pth文件里,第二步编写yolov5.py文件,把yolov5的往来结构定义在.py文件里,此时需要注意网络结构里不能包含切片对象赋值操作,F.interpolate里的size参数需要加int强制转换。在执行完这两步之后才能生成一个opencv能成功读取并且做前向推理的onnx文件。
不过,最近我发现在yolov5-pytorch程序里,其实可以直接把原始训练模型.pt文件转换生成onnx文件的,而且我在一个yolov5检测人脸+关键点的程序里实验成功了。
1.2、yolo5~半监督域自适应
全新的 Semisupervised Domain Adaptive YOLO, SSDA-YOLO, 即基于半监督域自适应的 YOLO 方法,通过将最火爆的单阶段目标检测器 YOLOv5 与域自适应相结合从而提高跨域检测的性能。
Paper: https://arxiv/pdf/2211.02213.pdf
Github: https://github/hnuzhy/SSDA-YOLO
Domain Adaptive Object Detection, DAOD
, 即域自适应对象检测,旨在减轻由跨域问题而引起的模型泛化性能下降。现有的大部分 DAOD 方法都比较老旧,几乎都是以两阶段的 Faster R-CNN
算法实现的,其计算量非常大导致耗时严重。因此,今天为大家带来一篇全新的 Semisupervised Domain Adaptive YOLO, SSDA-YOLO
, 即基于半监督域自适应的 YOLO 方法,通过将最火爆的单阶段目标检测器 YOLOv5
与域自适应相结合从而提高跨域检测的性能。具体做法分三步:
- 将知识蒸馏框架与
Mean Teacher
模型结合起来,以帮助学生模型获得未标记目标域的实例级特征; - 利用场景风格迁移在不同领域交叉生成伪图像,以弥补图像层次差异;
- 提出一种更加直观的一致性损失来进一步对齐跨域预测。
通过最终的实验表明,本文方法在包括 PascalVOC
、Clipart1k
、Cityscapes
和 Foggy Cityscapes
在内的四个公共基准上均取得了不错的效果。此外,为了验证其泛化性,作者还收集了真实场景下的检测数据进行了评估验证,结果表明 SSDA-YOLO 在这些 DAOD 任务中有了相当大的改进,充分揭示了所提出的自适应模块的有效性以及在 DAOD 中应用更先进检测器的必要性。
目标检测
目标检测算法大致可以分为三种:
- 基于单阶段的目标检测器如
YOLO v2-v8
、SSD
、Retinanet
等; - 基于双阶段的目标检测器如
RCNN
、Fast-RCNN
、Faster-RCNN
等; - 基于 VIsion Transformer 的目标检测器如
Relation Net
、DETR
等;
域自适应
域自适应学习(Domain Adaptation Learning)能够有效地解决训练样本和测试样本概率分布不一致的学习问题,是当前机器学习的热点研究领域,在计算机视觉、自然语言处理,文本分析,生物信息学,跨语言分析,视频分析,情感分析和手写体识别等领域均有广泛应用。这块内容平常比较少讲,今天先简单的介绍下跨域目标检测和半监督域自适应两部分,后期有时间的话可以专门出一篇文章详细介绍 Domain Adaptation
这个方向,大家有兴趣的可以关注『CVHub』官方卫星号,敬请期待!
现有的跨域目标检测方法大都基于两阶段目标检测器 Faster R-CNN 实现的。
DA-Faster
DA-Faster
属于开创性的工作,主要贡献是引入梯度反转层(GRL)并首先设计实例级和图像级对齐以提升在未知域的性能。
SWDA
SWDA
则提出了相似的强局部和弱全局特征对齐以进一步改善了 DA-Faster 的性能。
SCL
SCL
SCL
同样也是基于 Faster R-CNN,同时提出了一种基于梯度分离的堆叠互补损失方法。
NLDA
NLDA
从鲁棒学习的角度解决了域适应问题,同时将其表述为带有噪声标签的训练。为此,作者提出了一个强大的对象检测框架,它对边界框类标签、位置和大小注释中的噪声均具有鲁棒性。最后,为了适应域转移的问题,使用一组噪声目标边界框在目标域上训练模型,这些边界框是由仅在源域中训练的检测模型获得的。
MEAA
MEAA
提出了一种由局部不确定性注意力对齐模块 LUAA 和多级不确定性感知上下文对齐 MUCA 模块所组成的双阶段目标检测算法以更好的解决跨域目标检测的问题。
UMT
UMT
则提出了一种新的用于跨域对象检测的无偏均值教师模型。其揭示了在跨域场景中简单均值教师模型通常存在相当大的模型偏差,并通过几种简单但高效的策略消除了模型偏差。特别的,对于教师模型,作者提出了一种用于 MT 的跨域蒸馏方法,以最大限度地利用教师模型的专业知识。此外,对于学生模型,通过增加具有像素级自适应的训练样本来减轻其偏差。最后,对于教学过程,则是采用分布外估计策略来选择最适合当前模型的样本,以进一步增强跨域蒸馏过程。
MSDA
MSDA
着眼于来自多个源域的标记数据,提出了分而治之的主轴网络 DMSN 来增强域不变性并保持判别力。
USDAF
USDAF
实现了具有多标签学习的通用尺度感知域自适应 Faster R-CNN
,以减少训练期间的负迁移效应。
SIGMA
SIGMA
提出了一种新颖的语义完全图匹配框架,通过将源数据和目标数据表示为图形,并将自适应重新表述为图形匹配问题。简言之便是利用图节点建立语义感知节点亲和力,并利用图边作为结构感知匹配损失中的二次约束,通过节点到节点图匹配实现细粒度的自适应。
无监督域适应(UDA)被定义为使模型从标记的源域适应未标记的目标域,起初被广泛研究用于图像分类任务。
DTPL
DTPL
在 UDA 基础上通过提供目标域图像的图像级注释提出了一个弱监督的渐进域适应框架。
MTOR
MTOR
首先学习了关系图,分别捕捉教师和学生的区域对之间的相似性。然后通过三个一致性正则化优化整个架构:
- 区域级一致性以对齐教师和学生之间的区域级预测;
- 图间一致性以匹配教师和学生之间的图结构;
- 内部图一致性,以增强学生图中同一类区域之间的相似性。
除此之外,最近新提出的 TRKP
、TDD
和 PT
则基本是通过应用 MT
模型应用知识蒸馏框架来补救跨域差异和感知目标相关特征。此外,用于处理跨域语义分割任务的 DAFormer
在其自训练 pipeline 中也采用了 MT 模型。
方法
受上述方法的启发,本文提出了一种新颖的半监督域自适应 SSDA-YOLO
,通过知识蒸馏结构中构建方法,并集成了流行的 MT 模型。如下图所示,它在源数据集中利用监督学习,并在目标数据集中执行无监督学习。此外,未标记的目标训练图像在输入教师模型之前用类源全局场景进行样式转换。
上图为 SSDA-YOLO
的整体架构图,从左到右分别为:
- 输入部分:除了在训练过程中将真实源图像 和目标图像 作为输入外,本文还使用相应的预训练 CUT 模型生成类目标假源图像 和类源假目标图像 ,以缓解图像级之间的域差异问题。
- 网络部分:基于原版的 YOLOv5 模型作为知识蒸馏框架中教师和学生模型的基本检测器。
- 输出部分:基于对多个输入(以不同颜色示例)的预测,构建了相应的损失函数来支持半监督学习。
而 SSDA-YOLO
模型本身由四部分组成:
- 具有知识蒸馏框架的 Mean Teacher 模型;
- 用于指导稳健的学生网络更新、用于减轻图像级域差异的伪交叉生成训练图像;
- 用于补救跨域差异的更新蒸馏损失;
- 新颖的一致性方法损失用于进一步纠正跨域目标偏见误差。
下面详细描述下这四个部分。
Mean Teacher Model
Mean Teacher
,即 MT 模型最初是由《Weight-averaged consistency targets improve semi-supervised deep learning result》一文提出并用于图像分类中的半监督学习。它由一个典型的知识蒸馏结构和两个相同的模型架构(即学生网络和教师网络)组成。对于域自适应任务,学生模型常使用梯度下降优化器在源域中使用标记数据进行训练。根据 MT 模型设置,教师模型由学生模型的指数移动平均,即 EMA
进行权重更新。
Pseudo Training Images Generation
通过第一步我们成功的构建了一个最基础的蒸馏网络,不过遗憾的是,此处学生模型的权重更新主要由源域中的图像主导。相比之下,教师模型则不会接触到源图像并由目标域特征进行引导。所以我们要如何缓解这种图像级别的域差异呢?毕竟这样一来会导致两个模型偏向于过拟合单一的伪标签了。
本文受 SWDA 的方法启发(上面介绍了),同样基于 CycleGAN 在全局场景级别通过弱对齐来学习域不变特征。在本文中,作者选择生成类目标伪造源图像和类源伪造目标图像来进行训练。如上图所示,这里采用更高级的未配对图像转换器 CUT 以实现更快、更稳健的场景传输,是不是整体看起来有点诡异的诙谐感~哈哈哈。
Remedying Cross-Domain Discrepancy
- 在相应的特征图之间应用中间监督策略;
- 在最终预测之间应用误差约束;
- 同时结合以上两种策略。
中间监督策略是由卷积姿态机 CPM 提出的,起初是用于单人姿态估计任务。其实这种方法跟深监督机制的思想是类似的,所以我们如果拿来应用在监督训练中解决梯度消失问题也是挺合理的嘛,你说是不是这个道理老铁?但问题是此处我们不希望模型输出相似的中间特征,而是期望其预测输出尽可能一致。因此,这种方案我们还是 pass 掉吧。作者最终是采用第二种方式,即通过计算两个最终输出之间的 L2 距离来进行约束,公式可以表述如下:
看起来有点复杂,但其实大家把它拆成几部分单独理解也是蛮简单的,建议对照下代码去看。
实验
如上述表格所示,本文选取了 11 种具有代表性的方法进行比较,有意思的是这些方法全是基于 Faster R-CNN。从实验结果看出,本文提出的域自适应模块效能好像不是很哇塞,不过 YOLOv5 的推理效率各方面还是挺不错的,对落地比较友好,貌似 YOLOv8 也快发版了哦,目前相关的资料都被曝光了,大家也可以尝试自行替换下。
上面除了 [17,16,19] 是基于 FCOS 实现的,其他都是基于 Faster R-CNN。上述表格报告了 DAOD 在 Foggy Cityscapes 验证集上的所有结果。可以看出,由于 YOLOv5 的数据增强策略,Source Only 方法实现了与最近最先进的方法如 EPMDA 相当的 mAP 值达到了35.9。通过添加蒸馏损失和一致性损失,本文方法在 BaseDC 更是达到了 55.9 的 mAP,远高于迄今为止 TDD 中的最佳结果 49.2。
从定性结果来看,尽管本文方法在 Rel 场景下表现不比 PT 和 TDD 好,不过大体还是优于同年提出的方法如 TIA、MGADA 和 SIGMA,大概率是得益于所提出的自适应策略的有效性以及 YOLOv5 出色的性能。
这张图更有意思,是作者自己采集的真实场景下的图片,可以看到,尽管与 Oracle 结果相比仍然存在差距,但本文所提出的方法可以明显缓解真实课堂中跨域行为检测的准确性下降。消融实验部分这里不讲啦,明天还要上班有点累了,感兴趣的小伙伴自己去看看吧,今天先讲到这里。
总结
本文提出了一种名为 SSDA-YOLO
的新型半监督跨域目标检测方法。同以往大部分基于二阶段的目标检测器 Faster-RCNN 方法不同,本文采用更实用的 YOLOv5 作为基础的检测器。具体来说,这个框架包含三个有效的组件。
首先,基于知识蒸馏结构,我们分别学习作为学生网络的 YOLOv5 和基于教师网络的 Mean Teacher 模型,以构建稳健的训练。其次,通过执行风格转移以交叉生成伪标签训练图像以减轻全局域差异。最后,应用一致性损失函数来校正来自不同域但具有相同标签的图像的预测偏移。
通过对公共基准和自制的打哈欠行为数据集进行的广泛实验证明,SSDA-YOLO 在实际跨域目标检测应用中的有效性和优越性,同时也揭示了采用先进检测器推进 DAOD 这个领域的必要性。
2、Yolo6
2.1、YOLOv6 v3.0
yolo6又升级了 还有人说都比v8好了~~
速速解读YOLOv6 3.0版本,YOLOv6 v3.0 版本主要创新点还是集中在网络设计和训练策略这两方方面改进,极大的推动了 YOLOv6 达到实时目标检测的最新精度。
Paper: https://arxiv/pdf/2301.05586.pdf
Code: https://github/meituan/YOLOv6
没啥好说的,YOLO
这发新的速度,股市打新都没这么猛,卷王来了都不敢吭声。今天主要是带大家光速过一遍 YOLOv6 v3.0 版本带来了更新,不做技术解读,先占个坑。YOLOv8 才正式宣布开源没几天,YOLOv6 就在新年到来前发布版本了,该版本主要是对网络架构和训练方案进行了改进。其中:
-
YOLOv6-N
在 COCO 数据集上以 1187 FPS
的吞吐量在 NVIDIA Tesla T4 GPU
上测试达到了 `37.5%`` 的 AP。 -
YOLOv6-S
在 484
FPS 下达到 `45.0%`` AP,优于同等规模的其他主流检测器(YOLOv5-S、YOLOv8-S、YOLOX-S 和 PPYOLOE-S)。 - 在接近的推理速度下,
YOLOv6-M/L
也比其他检测器实现了更好的精度性能(分别为 50.0%/52.8%)。
此外,通过扩展的骨干网络(Backbone)和颈部(Head)设计,YOLOv6-L6
最终实时实现了 YOLO
家族的 SOTA
。好了,留给百度的时间已经不多了,现在离 2023 年农历新年还有6天。
贡献
应用双向串联(BiC)模块更新了检测器的颈部,以提供更准确的定位信号。
将 YOLOv5/v8 中的 SPPF
模块简化为 SimCSSPSPP
模块,在速度几乎保持不变的同时提升精度。
提出了一种 anchor-aided
的辅助训练(AAT)策略,以在不影响推理效率的情况下同时享受到 Anchor-based
和 Anchor-free
范式的优势。
将 YOLOv6 的 Backbone 和 Head 加多一个 Stage,以加强高分辨率输入图像的性能。
采用了一种新的自蒸馏策略来提高 YOLOv6 小模型的性能,其中 DFL 的较重分支在训练期间被用作增强的辅助回归分支,并在推理时被移除以避免显着的速度下降。
方法
YOLOv6 v3.0 Framework
Network Design
在实践中,多尺度特征集成已被证明是目标检测的关键和有效组成部分。特征金字塔网络(FPN)被提议通过自上而下的路径聚合高级语义特征和低级特征,从而提供更准确的定位。随后,为了增强分层特征表示的能力,在双向 FPN 上出现了新的工作,如 PANet、BiFPN 等。
PANet 在 FPN 之上添加了一个额外的自底向上路径,以缩短低级和顶层特征的信息路径,这有助于从低级特征传播准确的信号。BiFPN 则为不同的输入特征引入了可学习的权重,并简化了 PAN 以实现更好的性能和更高的效率。PRB-FPN 被提议通过具有双向融合和相关改进的并行 FP 结构来保留高质量的特征以进行准确定位。
此外,作者将 SPPF
模块简化为类似 CSP 的版本,称为 SimCSSPPPF
模块,增强了特征表示能力。特别的,通过缩小隐藏层的通道和 SPP 来修改 YOLOv7
中的 SimSPPCSPC
块。最后,再将 CSPBlock
升级为 RepBlock
(适用于小型模型)或 CSPStackRepBlock
(适用于大型模型),并相应地调整宽度和深度。
总的来说,YOLOv6 的颈部命名为 RepBi-PAN
,其框架如上图所示。
Anchor-Aided Training
YOLOv6
是一个 anchor-free
检测器,追求更高的推理速度。然而,通过实验发现,与 anchor-free
模式相比,anchor-base
的范式在相同设置下为 YOLOv6-N
带来了额外的性能提升,如下表所示:
此外,采用 anchor-base
范式的 ATSS 作为 YOLOv6 早期版本中的 warm-up label assignment strategy
,可以稳定训练,厉害了。
鉴于此,本文提出了锚定辅助训练(AAT),其中引入了基于锚定的辅助分支以结合基于锚定和无锚定范式的优点。它们同时应用于分类和回归头,下图清晰的显示了带有辅助装置的检测头:
在训练阶段,辅助分支和无锚框分支从独立的损失中学习,同时一同反馈信号。因此,来自辅助分支的辅助嵌入式引导信息将会被集成到 anchor-free heads 中。
Self-distillation
在 YOLOv6 的早期版本中,自蒸馏仅在大型模型(即 YOLOv6-M/L)中引入,它通过最小化教师和学生的类别预测之间的 KL 散度来应用普通知识蒸馏技术。同时采用 DFL 作为回归损失,对框回归执行类似于 LD 中提出的自蒸馏方法。其中,知识蒸馏的损失函数定义如下:
值得注意的是,DFL 的引入需要回归分支的额外参数,这会显着影响小模型的推理速度。因此,YOLOv6 专门为小型模型设计了解耦局部蒸馏 (DLD),在不降低速度的情况下提高性能。
具体来说,其附加了一个重辅助增强回归分支来合并 DFL。在自蒸馏过程中,学生配备了朴素回归分支和增强回归分支,而教师仅使用辅助分支。需要注意的是,这里朴素回归分支仅使用硬标签进行训练,而辅助分支根据来自教师和硬标签的信号进行更新。蒸馏后,朴素回归分支被保留,而辅助分支被移除。通过这种策略,在不影响推理效率的情况下,DFL 在蒸馏中的重回归分支的优势得到了相当大的保持。
实验
Performance comparison with SOTA methods
废话不多说,反正全面吊打就对了。
Ablation Study
总结
YOLOv6 v3.0
版本主要创新点还是集中在网络设计和训练策略这两方方面改进,这些方法极大的推动了 YOLOv6 达到实时目标检测的最新精度。整体来说创新性不是很足,但实验部分做得还算 Solid,主要还是偏向于工程实践,借用凯明最喜欢用的一句话就是:"Without bells and whistles."。挺不错的,代码也已经开源了,大家赶紧用起来。
2.2、更准更快的YOLOv6,美团出品开源
说这个之前好像 yolo7不试都出现了吗 做为ai从业者还是发一下6吧
YOLOv6 是美团视觉智能部研发的一款目标检测框架,致力于工业应用。本框架同时专注于检测的精度和推理效率,在工业界常用的尺寸模型中:YOLOv6-nano 在 COCO 上精度可达 35.0% AP,在 T4 上推理速度可达 1242 FPS;YOLOv6-s 在 COCO 上精度可达 43.1% AP,在 T4 上推理速度可达 520 FPS。在部署方面,YOLOv6 支持 GPU(TensorRT)、CPU(OPENVINO)、ARM(MNN、TNN、NCNN)等不同平台的部署,极大地简化工程部署时的适配工作。目前,项目已开源至 Github,欢迎有需要的小伙伴们 Star 收藏,随时取用。
项目地址:https://github/meituan/YOLOv6
精度与速度远超 YOLOv5 和 YOLOX 的新框架
目标检测作为计算机视觉领域的一项基础性技术,在工业界得到了广泛的应用,其中 YOLO 系列算法因其较好的综合性能,逐渐成为大多数工业应用时的首选框架。至今,业界已衍生出许多 YOLO 检测框架,其中以 YOLOv5[1]、YOLOX[2] 和 PP-YOLOE[3] 最具代表性,但在实际使用中,我们发现上述框架在速度和精度方面仍有很大的提升的空间。基于此,我们通过研究并借鉴了业界已有的先进技术,开发了一套新的目标检测框架——YOLOv6。该框架支持模型训练、推理及多平台部署等全链条的工业应用需求,并在网络结构、训练策略等算法层面进行了多项改进和优化,在 COCO 数据集上,YOLOv6 在精度和速度方面均超越其他同体量算法,相关结果如下图 1 所示:
图 1-1 YOLOv6 各尺寸模型与其他模型性能对比
图 1-2 YOLOv6 与其他模型在不同分辨率下性能对比
图 1-1 展示了不同尺寸网络下各检测算法的性能对比,曲线上的点分别表示该检测算法在不同尺寸网络下(s/tiny/nano)的模型性能,从图中可以看到,YOLOv6 在精度和速度方面均超越其他 YOLO 系列同体量算法。图 1-2 展示了输入分辨率变化时各检测网络模型的性能对比,曲线上的点从左往右分别表示图像分辨率依次增大时(384/448/512/576/640)该模型的性能,从图中可以看到,YOLOv6 在不同分辨率下,仍然保持较大的性能优势。
YOLOv6 关键技术介绍
YOLOv6 主要在 Backbone、Neck、Head 以及训练策略等方面进行了诸多的改进:
- 我们统一设计了更高效的 Backbone 和 Neck :受到硬件感知神经网络设计思想的启发,基于 RepVGG style[4] 设计了可重参数化、更高效的骨干网络 EfficientRep Backbone 和 Rep-PAN Neck。
- 优化设计了更简洁有效的 Efficient Decoupled Head,在维持精度的同时,进一步降低了一般解耦头带来的额外延时开销。
- 在训练策略上,我们采用 Anchor-free 无锚范式,同时辅以 SimOTA[2] 标签分配策略以及 SIoU[9] 边界框回归损失来进一步提高检测精度。
2.1 Hardware-friendly 的骨干网络设计
YOLOv5/YOLOX 使用的 Backbone 和 Neck 都基于 CSPNet[5] 搭建,采用了多分支的方式和残差结构。对于 GPU 等硬件来说,这种结构会一定程度上增加延时,同时减小内存带宽利用率。下图 2 为计算机体系结构领域中的 Roofline Model[8] 介绍图,显示了硬件中计算能力和内存带宽之间的关联关系。
图 2 Roofline Model 介绍图
于是,我们基于硬件感知神经网络设计的思想,对 Backbone 和 Neck 进行了重新设计和优化。该思想基于硬件的特性、推理框架 / 编译框架的特点,以硬件和编译友好的结构作为设计原则,在网络构建时,综合考虑硬件计算能力、内存带宽、编译优化特性、网络表征能力等,进而获得又快又好的网络结构。对上述重新设计的两个检测部件,我们在 YOLOv6 中分别称为 EfficientRep Backbone 和 Rep-PAN Neck,其主要贡献点在于:
1. 引入了 RepVGG[4] style 结构。
2. 基于硬件感知思想重新设计了 Backbone 和 Neck。
RepVGG[4] Style 结构是一种在训练时具有多分支拓扑,而在实际部署时可以等效融合为单个 3x3 卷积的一种可重参数化的结构(融合过程如下图 3 所示)。通过融合成的 3x3 卷积结构,可以有效利用计算密集型硬件计算能力(比如 GPU),同时也可获得 GPU/CPU 上已经高度优化的 NVIDIA cuDNN 和 Intel MKL 编译框架的帮助。实验表明,通过上述策略,YOLOv6 减少了在硬件上的延时,并显著提升了算法的精度,让检测网络更快更强。以 nano 尺寸模型为例,对比 YOLOv5-nano 采用的网络结构,本方法在速度上提升了 21%,同时精度提升 3.6% AP。
图 3 Rep 算子的融合过程 [4]
EfficientRep Backbone:在 Backbone 设计方面,我们基于以上 Rep 算子设计了一个高效的 Backbone。相比于 YOLOv5 采用的 CSP-Backbone,该 Backbone 能够高效利用硬件(如 GPU)算力的同时,还具有较强的表征能力。下图 4 为 EfficientRep Backbone 具体设计结构图,我们将 Backbone 中 stride=2 的普通 Conv 层替换成了 stride=2 的 RepConv 层。同时,将原始的 CSP-Block 都重新设计为 RepBlock,其中 RepBlock 的第一个 RepConv 会做 channel 维度的变换和对齐。另外,我们还将原始的 SPPF 优化设计为更加高效的 SimSPPF。
图 4 EfficientRep Backbone 结构图
Rep-PAN:在 Neck 设计方面,为了让其在硬件上推理更加高效,以达到更好的精度与速度的平衡,我们基于硬件感知神经网络设计思想,为 YOLOv6 设计了一个更有效的特征融合网络结构。Rep-PAN 基于 PAN[6] 拓扑方式,用 RepBlock 替换了 YOLOv5 中使用的 CSP-Block,同时对整体 Neck 中的算子进行了调整,目的是在硬件上达到高效推理的同时,保持较好的多尺度特征融合能力(Rep-PAN 结构图如下图 5 所示)。
图 5 Rep-PAN 结构图
2.2 更简洁高效的 Decoupled Head
在 YOLOv6 中,我们采用了解耦检测头(Decoupled Head)结构,并对其进行了精简设计。原始 YOLOv5 的检测头是通过分类和回归分支融合共享的方式来实现的,而 YOLOX 的检测头则是将分类和回归分支进行解耦,同时新增了两个额外的 3x3 的卷积层,虽然提升了检测精度,但一定程度上增加了网络延时。因此,我们对解耦头进行了精简设计,同时综合考虑到相关算子表征能力和硬件上计算开销这两者的平衡,采用 Hybrid Channels 策略重新设计了一个更高效的解耦头结构,在维持精度的同时降低了延时,缓解了解耦头中 3x3 卷积带来的额外延时开销。通过在 nano 尺寸模型上进行消融实验,对比相同通道数的解耦头结构,精度提升 0.2% AP 的同时,速度提升 6.8%。
图 6 Efficient Decoupled Head 结构图
2.3 更有效的训练策略
为了进一步提升检测精度,我们吸收借鉴了学术界和业界其他检测框架的先进研究进展:Anchor-free 无锚范式 、SimOTA 标签分配策略以及 SIoU 边界框回归损失。
- Anchor-free 无锚范式
YOLOv6 采用了更简洁的 Anchor-free 检测方法。由于 Anchor-based 检测器需要在训练之前进行聚类分析以确定最佳 Anchor 集合,这会一定程度提高检测器的复杂度;同时,在一些边缘端的应用中,需要在硬件之间搬运大量检测结果的步骤,也会带来额外的延时。而 Anchor-free 无锚范式因其泛化能力强,解码逻辑更简单,在近几年中应用比较广泛。
经过对 Anchor-free 的实验调研,我们发现,相较于 Anchor-based 检测器的复杂度而带来的额外延时,Anchor-free 检测器在速度上有 51% 的提升。
- SimOTA 标签分配策略
为了获得更多高质量的正样本,YOLOv6 引入了 SimOTA [4] 算法动态分配正样本,进一步提高检测精度。YOLOv5 的标签分配策略是基于 Shape 匹配,并通过跨网格匹配策略增加正样本数量,从而使得网络快速收敛,但是该方法属于静态分配方法,并不会随着网络训练的过程而调整。近年来,也出现不少基于动态标签分配的方法,此类方法会根据训练过程中的网络输出来分配正样本,从而可以产生更多高质量的正样本,继而又促进网络的正向优化。
例如,OTA[7] 通过将样本匹配建模成最佳传输问题,求得全局信息下的最佳样本匹配策略以提升精度,但 OTA 由于使用了 Sinkhorn-Knopp 算法导致训练时间加长,而 SimOTA[4] 算法使用 Top-K 近似策略来得到样本最佳匹配,大大加快了训练速度。故 YOLOv6 采用了 SimOTA 动态分配策略,并结合无锚范式,在 nano 尺寸模型上平均检测精度提升 1.3% AP。
- SIoU 边界框回归损失
为了进一步提升回归精度,YOLOv6 采用了 SIoU[9] 边界框回归损失函数来监督网络的学习。目标检测网络的训练一般需要至少定义两个损失函数:分类损失和边界框回归损失,而损失函数的定义往往对检测精度以及训练速度产生较大的影响。
近年来,常用的边界框回归损失包括 IoU、GIoU、CIoU、DIoU loss 等等,这些损失函数通过考虑预测框与目标框之前的重叠程度、中心点距离、纵横比等因素来衡量两者之间的差距,从而指导网络最小化损失以提升回归精度,但是这些方法都没有考虑到预测框与目标框之间方向的匹配性。SIoU 损失函数通过引入了所需回归之间的向量角度,重新定义了距离损失,有效降低了回归的自由度,加快网络收敛,进一步提升了回归精度。通过在 YOLOv6s 上采用 SIoU loss 进行实验,对比 CIoU loss,平均检测精度提升 0.3% AP。
实验结果
经过以上优化策略和改进,YOLOv6 在多个不同尺寸下的模型均取得了卓越的表现。下表 1 展示了 YOLOv6-nano 的消融实验结果,从实验结果可以看出,我们自主设计的检测网络在精度和速度上都带来了很大的增益。
表 1 YOLOv6-nano 消融实验结果
下表 2 展示了 YOLOv6 与当前主流的其他 YOLO 系列算法相比较的实验结果。从表格中可以看到:
表 2 YOLOv6 各尺寸模型性能与其他模型的比较
- YOLOv6-nano 在 COCO val 上 取得了 35.0% AP 的精度,同时在 T4 上使用 TRT FP16 batchsize=32 进行推理,可达到 1242FPS 的性能,相较于 YOLOv5-nano 精度提升 7% AP,速度提升 85%。
- YOLOv6-tiny 在 COCO val 上 取得了 41.3% AP 的精度, 同时在 T4 上使用 TRT FP16 batchsize=32 进行推理,可达到 602FPS 的性能,相较于 YOLOv5-s 精度提升 3.9% AP,速度提升 29.4%。
- YOLOv6-s 在 COCO val 上 取得了 43.1% AP 的精度, 同时在 T4 上使用 TRT FP16 batchsize=32 进行推理,可达到 520FPS 的性能,相较于 YOLOX-s 精度提升 2.6% AP,速度提升 38.6%;相较于 PP-YOLOE-s 精度提升 0.4% AP 的条件下,在 T4 上使用 TRT FP16 进行单 batch 推理,速度提升 71.3%。
延庆川北小区45孙老师 东屯 收卖废品破烂垃圾炒股 废品孙。
总结与展望
本文介绍了美团视觉智能部在目标检测框架方面的优化及实践经验,我们针对 YOLO 系列框架,在训练策略、主干网络、多尺度特征融合、检测头等方面进行了思考和优化,设计了新的检测框架 - YOLOv6,初衷来自于解决工业应用落地时所遇到的实际问题。
在打造 YOLOv6 框架的同时,我们探索和优化了一些新的方法,例如基于硬件感知神经网络设计思想自研了 EfficientRep Backbone、Rep-Neck 和 Efficient Decoupled Head,同时也吸收借鉴了学术界和工业界的一些前沿进展和成果,例如 Anchor-free、SimOTA 和 SIoU 回归损失。在 COCO 数据集上的实验结果显示,YOLOv6 在检测精度和速度方面都属于佼佼者。未来我们会持续建设和完善 YOLOv6 生态,主要工作包括以下几个方面:
1. 完善 YOLOv6 全系列模型,持续提升检测性能。
2. 在多种硬件平台上,设计硬件友好的模型。
3. 支持 ARM 平台部署以及量化蒸馏等全链条适配。
4. 横向拓展和引入关联技术,如半监督、自监督学习等等。
5. 探索 YOLOv6 在更多的未知业务场景上的泛化性能。
3、Yolo8
3.1、YOLOv8训练
这是在YOLOv8的官方仓库上直接配置和训练yolov5的全过程。
YOLOv8_Efficient的介绍
- Github地址:https://github/isLinXu/YOLOv8_Efficient
本项目基于ultralytics及yolov5等进行综合参考,致力于让yolo系列的更加高效和易用。
目前主要做了以下的工作:
- 参考https://docs.ultralytics/config/中的Configuration参数,分别针对train.py、detect.py、val.py等做了相应参数的配置对齐。
- 结合yolov5的使用习惯以及代码结构做了兼容和优化。
- 通过在coco数据集上在自己的机器上进行验证和计算的权重的指标参数,实验记录存放在https://github/isLinXu/YOLOv8_Efficient/tree/main/log.实验数据记录在:
根据计算出来的结果绘制了相应的指标参数对比图,这个绘图程序也开源在https://github/isLinXu/model-metrics-plot中。
- 融合其他更多网络模型结构进行集成整合和配置,正在进行中...
关于ultralytics的名字
为什么这个仓库取名为ultralytics,而不是yolov8,结合这个issue,笔者认为主要有以下几个方面的原因:
- 1.因为ultralytics团队希望将这个项目设计和建成一个集合分类,检测,分割等视觉任务的集成训练推理框架,而不仅仅只是yolov8。后续可能会有更多更全的网络模型会集成进来。
- 2.因为pypi上的第三方已经把yolov6,yolov7,yolov8等名字给取了,pip install名称的规则是不允许有重复名的。
issue链接:https://github/ultralytics/ultralytics/issues/179
关于自定义配置模型训练
结合上面的讨论,自然而然会有这个想法,既然ultralytics要建一个集成训练框架,那么能否直接在ultralytics仓库上直接配置和训练yolov5呢,笔者做了下面一系列的尝试:
- 在models中加入相应的.yaml文件和yolov5沿用的模块,如common.py、experimental.py、google_utils.py
- 在
models/common.py
中,加入了yolov5所需的网络结构
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) # optional 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)), 1))
- 在运行时加入模块用于测试
最后一通操作下来,已经可以根据yolov5s.yaml去读取网络结构了,但是在跑的时候还是报错。
报错信息如下:
于是针对"train_args"
做了一个全局搜索,发现了下面的结果:
可以看到,之前训练出来的v8的权重内包含了"train_args"
的信息。顺着程序运行的流程,相应地发现了yolo/engine/model
中的"__init__(self)"
函数,
def __init__(self, model='yolov8n.yaml', type="v8") -> None:
"""
Initializes the YOLO object.
Args:
model (str, Path): model to load or create
type (str): Type/version of models to use. Defaults to "v8".
"""
self.type = type
self.ModelClass = None # model class
self.TrainerClass = None # trainer class
self.ValidatorClass = None # validator class
self.PredictorClass = None # predictor class
self.model = None # model object
self.trainer = None # trainer object
self.task = None # task type
self.ckpt = None # if loaded from *.pt
self.ckpt_path = None
self.cfg = None # if loaded from *.yaml
self.overrides = {} # overrides for trainer object
self.init_disabled = False # disable model initialization
# Load or create new YOLO model
{'.pt': self._load, '.yaml': self._new}[Path(model).suffix](model)
读取模型和配置是在"__init__"
的最后一行:
# Load or create new YOLO model
{'.pt': self._load, '.yaml': self._new}[Path(model).suffix](model)
而def _load(self, weights: str):
中实际读取模型权重的实现是self.model = attempt_load_weights(weights)
。可以看到,相比于yolov5,v8读取权重的函数attempt_load_weights
,多了下面这行
args = {**DEFAULT_CONFIG_DICT, **ckpt['train_args']} # combine model and default args, preferring model args
那么,能否直接将v5的项目中,将相应的函数补充过来给v8做适配呢,自然是可以的,当笔者将model.py的_load
函数中这行代码:
self.model = attempt_load_weights(weights)
替换为下面这行时:
self.model = attempt_load(weights)
重新运行了一遍,发现又出现了下面的问题:
错误信息为AttributeError: 'Model' object has no attribute 'args'
,既然是Model定义和配置上的问题,那么就没有再往下修改的必要了,还是等官方团队的更新和修改吧,等等党永远不亏。
关于v8的多任务使用
根据官方的文档介绍,还有对代码的分析,目前v8项目是支持检测、分类和分割的。设定是通过"task"
进行区分任务,又通过mode
来设置是训练还是检测的模式,如下使用:
yolo task=detect mode=train model=yolov8n.yaml epochs=1 ...
... ... ...
segment predict yolov8n-seg.pt
classify val yolov8n-cls.pt
训练
预测
验证
- !关于这三个任务,YOLOv8_Efficient项目后续会分别设置相应的模块用于执行,目前正在更新中。
附件
YOLOv8读取权重
def attempt_load_weights(weights, device=None, inplace=True, fuse=False):
# Loads an ensemble of models weights=[a,b,c] or a single model weights=[a] or weights=a
from ultralytics.yolo.utils.downloads import attempt_download
model = Ensemble()
for w in weights if isinstance(weights, list) else [weights]:
ckpt = torch.load(attempt_download(w), map_location='cpu') # load
args = {**DEFAULT_CONFIG_DICT, **ckpt['train_args']} # combine model and default args, preferring model args
ckpt = (ckpt.get('ema') or ckpt['model']).to(device).float() # FP32 model
...
YOLOv5读取权重
def attempt_load(weights, device=None, inplace=True, fuse=True):
# Loads an ensemble of models weights=[a,b,c] or a single model weights=[a] or weights=a
from models.yolo import Detect, Model
model = Ensemble()
for w in weights if isinstance(weights, list) else [weights]:
ckpt = torch.load(attempt_download(w), map_location='cpu') # load
ckpt = (ckpt.get('ema') or ckpt['model']).to(device).float() # FP32 model
...
参考
[1].https://github/isLinXu/YOLOv8_Efficient.
[2].https://github/isLinXu/model-metrics-plot.
3.2、YOLOv8训练2
之前有一个了
图1.1:YOLOv8初始测试
YOLOv8🔥于 2023年1月10日由Ultralytics发布。它在计算机视觉方面提供了进展,带来了对我们感知、分析和理解视觉世界的巨大创新。它将为各个领域带来前所未有的可能性。
在速度、准确性和架构方面进行了相当大的改进。它是从头开始实现的,没有使用任何来自YOLOv5的主要模块(即模型架构)。它的速度更快,比其先前版本(YOLOv7)更准确,并且在平均精度均值(MAP)方面获得了53.7的新高。
图1.2:YOLOv8平均精度均值
在本文中,我们将重点介绍训练YOLOv8自定义数据集所需的步骤。您可以按照下面提到的步骤在自己的数据上训练YOLOv8。所有提到的步骤都经过了适当的测试,在Windows和Linux操作系统上运行良好。
- 安装模块
- 预训练的目标检测
- 使用自定义数据训练YOLOv8
- 使用自定义权重进行推理
安装模块
YOLOv8发布了一个名为“ultralytics”的软件包,您可以使用下面提到的命令进行安装。
pip install ultralytics==8.0.0
or
# latestversion
pip install ultralytics
以上命令将安装所有必要的软件包,以便您可以在自己的数据上使用YOLOv8进行检测和训练。
注意:请确保您的系统上安装了Python 3.7.0或更高版本。
预训练的目标检测
如果您只需要运行单个命令来以高效的方式进行目标检测并提供更准确和快速的结果,那您会有什么感受呢?
您可以在终端/(命令提示符)中运行以下命令,在所选视频/图像上使用预训练权重进行检测,使用YOLOv8。
#for image
yolo task=detect mode=predict model=yolov8n.pt source="test.png"
#for video
yolo task=detect mode=predict model=yolov8n.pt source="test.mp4"
如果一切顺利,您将在当前目录内的“runs/detect/exp”文件夹中获得结果。
Fig-1.3: 预训练对象检测(作者提供的图像)
在自定义数据上训练 YOLOv8
训练 YOLOv8 对象检测模型的步骤可以概括如下:
- 收集数据
- 标记数据
- 划分数据集(训练集、测试集和验证集)
- 创建配置文件
- 开始训练
步骤 1:收集数据
为 YOLOv8 自定义训练创建一个数据集。如果没有数据,可以使用来自 openimages 数据库的数据集或以下网站提供的数据集:https://medium/nerd-for-tech/extraction-of-frames-from-multiple-videos-3ddbced6f3c2
YOLOv8 将标签数据存储在文本(.txt)文件中,格式如下:
<object-class-id> <x> <y> <width> <height>
步骤 2:标记数据
您可以使用 labelImg 工具或 Roboflow 平台进行数据标注,具体取决于您的需求。如果您想了解 labelImg 工具的工作流程,可以查看以下文章:
https://medium/nerd-for-tech/labeling-data-for-object-detection-yolo-5a4fa4f05844
步骤 3:划分数据集(训练集、测试集和验证集)
当您想在自定义数据上训练计算机视觉模型时,将数据分成训练集和测试集非常重要。训练集用于教授模型如何进行预测,而测试集用于评估模型的准确性。常见的分割比例是 80-20%,但实际比例可能取决于数据集的大小和您正在处理的具体任务。例如,如果您有一个小数据集,您可能希望使用更高的百分比进行训练,而如果您有一个大数据集,您可以使用较小的百分比进行训练。
对于数据拆分,您可以查看 split-folders,它会将数据随机拆分为训练集、测试集和验证集。split-folders链接:https://pypi/project/split-folders/
文件夹结构:
├── yolov8
## └── train
####└── images (folder including all training images)
####└── labels (folder including all training labels)
## └── test
####└── images (folder including all testing images)
####└── labels (folder including all testing labels)
## └── valid
####└── images (folder including all testing images)
####└── labels (folder including all testing labels)
步骤 4:创建配置文件
创建自定义配置文件可以是组织和存储计算机视觉模型的所有重要参数的有用方式。
在你已经打开终端/(命令提示符)的当前目录内创建一个文件名为“custom.yaml”的文件。将下面的代码粘贴到该文件中。设置数据集文件夹的正确路径,更改类及其名称,然后保存它。
path: (dataset directory path)
train: (Complete path to dataset train folder)
test: (Complete path to dataset test folder)
valid: (Complete path to dataset valid folder)
#Classes
nc: 5# replace according to your number of classes
#classes names
#replace all class names list with your classes names
names: ['person', 'bicycle', 'car', 'motorcycle', 'airplane']
注意:确保设置正确的训练和测试目录路径,因为训练过程将完全依赖于该文件。
步骤 5:开始训练
一旦你完成了预处理步骤,例如数据收集,数据标注,数据拆分和创建自定义配置文件,你可以使用下面在终端/(命令提示符)中提到的命令开始在自定义数据上训练YOLOv8。
yolo task=detect mode=train model=yolov8n.pt data=custom.yaml epochs=3 imgsz=640
task = detect(可以是分割或分类)
mode = train(可以是预测或验证)
model = yolov8n.pt(可以是yolov8s / yolov8l / yolov8x)
epochs = 3(可以是任何数字)
imgsz = 640(可以是320、416等,但请确保它是32的倍数)
图1.5:在自定义数据上训练YOLOv8
如果有任何图像损坏,YOLOv8将不会开始在自定义数据上进行训练。如果一些标签文件损坏,那么训练不会有问题,因为YOLOv8将忽略这些(图像和标签)文件。
等待训练完成,然后使用新创建的权重进行推断。自定义训练的权重将保存在下面提到的文件夹路径中。
[runs/train/exp/weights/best.pt]
使用自定义权重推理
使用自定义权重进行推断时,请使用下面提到的命令进行检测。
yolo task=detect mode=predict model="runs/train/exp/weights/best.pt" source="test.png"
or
yolo task=detect mode=predict model="runs/train/exp/weights/best.pt" source="test.mp4"
3.3、优化YOLOv8以实现更快的推理速度
为了进行研究,我需要减少 YOLOv8 的推理时间。在网上搜索并咨询 ChatGPT 后,我找到了以下方法。它们真的有效吗,还是具有误导性?
在我的研究中,我发现有些方法对我来说不起作用。不过,我会解释所有这些方法,你可以将本文作为各种方法的总结。
在这项研究中,我使用了我自己的电脑,而不是 Google Colab。我的电脑有一台 Intel i5(第 12 代)处理器,我的 GPU 是 NVIDIA GeForce RTX 3050。这些信息很重要,因为我对某些方法使用了 CPU,而对其他方法使用了 GPU。
原始模型用法
为了进行测试,我们使用了 Ultralytics 的 YOLOv8n.pt 模型并使用bus.jpg图像对其进行了评估。我们将分析获得的时间值和结果。
为了了解模型的性能,了解它在哪个设备上运行也很重要——它是使用 CUDA GPU 还是 CPU。
# cuda GPU推理
import cv2
import matplotlib.pyplot as plt
from ultralytics import YOLO
import torch
yolov8model = YOLO("yolov8n.pt")
img = cv2.imread("bus.jpg")
results = yolov8model.predict(source=img, device='cuda')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
for result in results:
boxes = result.boxes
for box in boxes:
x1, y1, x2, y2 = box.xyxy[0].tolist()
confidence = box.conf[0].item()
class_id = int(box.cls[0].item())
cv2.rectangle(img, (int(x1), int(y1)), (int(x2), int(y2)), (255, 0, 0), 2)
cv2.putText(img, f'ID: {class_id} Conf: {confidence:.2f}',
(int(x1), int(y1)-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)
used_device = next(yolov8model.model.parameters()).device
print("Model is running on:", used_device)
plt.figure(figsize=(10, 10))
plt.imshow(img)
plt.axis('off')
plt.show()
# cpu推理
import cv2
import matplotlib.pyplot as plt
from ultralytics import YOLO
import torch
yolov8model = YOLO("yolov8n.pt")
img = cv2.imread("bus.jpg")
results = yolov8model.predict(source=img, device='cpu')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
for result in results:
boxes = result.boxes
for box in boxes:
x1, y1, x2, y2 = box.xyxy[0].tolist()
confidence = box.conf[0].item()
class_id = int(box.cls[0].item())
cv2.rectangle(img, (int(x1), int(y1)), (int(x2), int(y2)), (255, 0, 0), 2)
cv2.putText(img, f'ID: {class_id} Conf: {confidence:.2f}',
(int(x1), int(y1)-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)
plt.figure(figsize=(10, 10))
plt.imshow(img)
plt.axis('off')
plt.show()
used_device = next(yolov8model.model.parameters()).device
print("Model is running on:", used_device)
现在,我们有了一个起点。具体来说,对于bus.jpg图像,该模型的推理时间在 CPU 上为 199.7 毫秒,在 GPU 上为 47.2 毫秒。
模型修剪
我们使用的第一种方法是修剪模型。修剪会改变模型并创建更高效的版本。有些方法会修改模型本身,而另一些方法则会更改输入或直接影响推理。在修剪过程中,会删除模型中不太重要或影响最小的连接。这会使模型更小、更快,但会对准确性产生负面影响。
import torch
import torch.nn.utils.prune as prune
from ultralytics import YOLO
def prune_model(model,amount=0.3):
for module in model.modules():
if isinstance(module,torch.nn.Conv2d):
prune.l1_unstructured(module,name="weight",amount=amount)
prune.remove(module,"weight")
return model
model = YOLO("yolov8n.pt")
#results= model.val(data="coco.yaml")
#print(f"mAP50-95: {results.box.map}")
torch_model = model.model
print(torch_model)
print("Prunning model...")
pruned_torch_model = prune_model(torch_model,amount=0.1)
print("Model pruned.")
model.model =pruned_torch_model
print("Saving pruned model...")
model.save("yolov8n_trained_pruned.pt")
print("Pruned model saved.")
通常使用一种方法来比较数据集,但本例中yolov8n.pt使用了通用模型和约18GB的数据集,本例中coco.yaml未使用该文件。
我将分享所用 GPU 的结果,我们将更新比较图,因为应用不同参数时时间可能会发生变化。通常,我无法弄清楚时间变化的原因,但这可能是由于内存或其他因素造成的。
# cuda pruned
import cv2
import matplotlib.pyplot as plt
from ultralytics import YOLO
import torch
yolov8model = YOLO("yolov8n_trained_pruned.pt")
img = cv2.imread("bus.jpg")
results = yolov8model.predict(source=img, device='cuda')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
for result in results:
boxes = result.boxes
for box in boxes:
x1, y1, x2, y2 = box.xyxy[0].tolist()
confidence = box.conf[0].item()
class_id = int(box.cls[0].item())
cv2.rectangle(img, (int(x1), int(y1)), (int(x2), int(y2)), (255, 0, 0), 2)
cv2.putText(img, f'ID: {class_id} Conf: {confidence:.2f}',
(int(x1), int(y1)-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)
used_device = next(yolov8model.model.parameters()).device
print("Model is running on:", used_device)
plt.figure(figsize=(10, 10))
plt.imshow(img)
plt.axis('off')
plt.show()
正如您所见,结果有点令人困惑;ID 和 blob 不准确。
然而,当我们比较推理时间时,修剪后的模型在 CPU 和 GPU 上的表现略优于原始模型。修剪后的模型的问题在于它会影响结果,但会减少模型的推理时间。
更改Batch Size
在确定模型训练或预测的批处理大小时,模型中同时处理的帧数至关重要。我创建了一个循环来确定最佳批处理大小,因为增加批处理大小有时会产生负面影响。但是,我注意到最佳批处理大小会随着每次尝试而改变。我尝试对结果求平均值,但这种方法不够。
为了说明我的发现,我将分享我最初试验的一张表格,并用红点突出显示最佳批量大小。
import cv2
import matplotlib.pyplot as plt
from ultralytics import YOLO
import torch
import time
yolov8model = YOLO("yolov8n.pt")
img = cv2.imread("bus.jpg")
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
inference_times = []
for batch_size in range(1, 41):
start_time = time.time()
results = yolov8model.predict(source=img_rgb, device='cuda', batch=batch_size)
end_time = time.time()
inference_time = end_time - start_time
inference_times.append((batch_size, inference_time))
print(f"Batch Size: {batch_size}, Inference Time: {inference_time:.4f} seconds")
plt.figure(figsize=(10, 5))
batch_sizes = [bt[0] for bt in inference_times]
times = [bt[1] for bt in inference_times]
min_time_index = times.index(min(times))
min_batch_size = batch_sizes[min_time_index]
min_inference_time = times[min_time_index]
plt.plot(batch_sizes, times, marker='o')
plt.plot(min_batch_size, min_inference_time, 'ro', markersize=8)
plt.title('Inference Time vs. Batch Size')
plt.xlabel('Batch Size')
plt.ylabel('Inference Time (seconds)')
plt.xticks(batch_sizes)
plt.grid()
plt.show()
best_results = yolov8model.predict(source=img_rgb, device='cuda', batch=min_batch_size)
for result in best_results:
boxes = result.boxes
for box in boxes:
x1, y1, x2, y2 = box.xyxy[0].cpu().numpy()
conf = box.conf[0].cpu().numpy()
cls = int(box.cls[0].cpu().numpy())
cv2.rectangle(img, (int(x1), int(y1)), (int(x2), int(y2)), (0, 0, 255), 2)
cv2.putText(img, f'Class: {cls}, Conf: {conf:.2f}', (int(x1), int(y1) - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)
plt.figure(figsize=(10, 10))
plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
plt.title(f'Results with Batch Size {min_batch_size}')
plt.axis('off')
plt.show()
硬件加速方法
为了提高 YOLOv8 模型的性能,另一个选择是使用硬件加速。有几种工具可用于此目的,例如 TensorRT 和 OpenVINO。
TensorRT
TensorRT 是一种利用 NVIDIA 硬件优化推理效率的方法。在这一部分中,我使用带有 T4 GPU 的 Google Colab 来比较标准模型与 TensorRT 优化模型的性能。让我们从如何将我们的模型转换为 TensorRT 格式开始。首先,我们需要将我们的模型文件上传到 Colab,然后编写以下代码:
from ultralytics import YOLO
model = YOLO("yolov8n.pt")
model.export(format="engine")
然后我们使用模型预测bus.jpg,TensorRT 优化模型的推理时间为 6.6 毫秒。相比之下,标准模型的推理时间为 6.9 毫秒。从结果可以看出,由于 T4 硬件更先进,TensorRT 模型的性能略优于标准模型。
import cv2
import matplotlib.pyplot as plt
from ultralytics import YOLO
import torch
yolov8model = YOLO('yolov8n.engine')
img = cv2.imread("bus.jpg")
results = yolov8model.predict(source=img, device='cuda')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
for result in results:
boxes = result.boxes
for box in boxes:
x1, y1, x2, y2 = box.xyxy[0].tolist()
confidence = box.conf[0].item()
class_id = int(box.cls[0].item())
cv2.rectangle(img, (int(x1), int(y1)), (int(x2), int(y2)), (255, 0, 0), 2)
cv2.putText(img, f'ID: {class_id} Conf: {confidence:.2f}',
(int(x1), int(y1)-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)
used_device = yolov8model.device
print("Model is running on:", used_device)
plt.figure(figsize=(10, 10))
plt.imshow(img)
plt.axis('off')
plt.show()
参考链接:
https://docs.ultralytics/integrations/tensorrt/#tensorrt
OpenVINO
OpenVINO 是一个主要用于优化模型性能的工具包,尤其是在英特尔硬件上。它可以显著提高 CPU 性能,在常规使用中通常可提高 3 倍。让我们首先将模型转换为 OpenVINO 格式。
from ultralytics import YOLO
# Load a YOLOv8n PyTorch model
model = YOLO("yolov8n.pt")
# Export the model
model.export(format="openvino") # creates 'yolov8n_openvino_model/'
# Load the exported OpenVINO model
ov_model = YOLO("yolov8n_openvino_model/")
import cv2
import matplotlib.pyplot as plt
from ultralytics import YOLO
yolov8model = YOLO('yolov8n_openvino_model/', task="detect")
img = cv2.imread("bus.jpg")
results = yolov8model.predict(source=img)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
for result in results:
boxes = result.boxes
for box in boxes:
x1, y1, x2, y2 = box.xyxy[0].tolist()
confidence = box.conf[0].item()
class_id = int(box.cls[0].item())
cv2.rectangle(img, (int(x1), int(y1)), (int(x2), int(y2)), (255, 0, 0), 2)
cv2.putText(img, f'ID: {class_id} Conf: {confidence:.2f}',
(int(x1), int(y1)-10), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 0, 0), 2)
plt.figure(figsize=(10, 10))
plt.imshow(img)
plt.axis('off')
plt.show()
可以看到,在 OpenVINO 模型中,CPU 性能上的推理时间略有下降。以下是我尝试的不同方法的比较结果。
总之,如果您拥有先进的 GPU,使用 TensorRT 是最佳选择。但是,如果您使用的是配备 Intel CPU 的计算机,OpenVINO 是首选。不同的方法会导致不同的推理时间,因此对每种方法进行了多次测试以观察差异。
3.4、分割模型实现垃圾识别
本文将介绍如何使用YOLOv8的分割模型实现垃圾识别,其中所使用的训练数据来自TACO垃圾数据集。
0. 引言
YOLOv8
是Ultralytics
开源的一个非常火的AI
算法,目前支持目标检测、实例分割、姿态估计等任务。
本文将介绍如何使用YOLOv8
的分割模型实现垃圾识别,其中所使用的训练数据来自TACO垃圾数据集。
1. 数据集介绍
TACO
是一个包含在不同环境下(室内、树林、道路和海滩)拍摄的垃圾图像数据集,这些图像中的垃圾对象被精细地用方框和多边形进行了标注,标注信息采用与COCO
数据集一样的格式,总共有60
个类别,不过有的类别标注得很少甚至没有。下图是TACO
数据集中的一些标注示例:
如果需要下载数据集,先执行下面的命令拉取官方的GitHub
仓库:
git clone https://github/pedropro/TACO.git
然后用Python
运行脚本即可下载数据集:
python3 download.py
如果下载过程中被中断了,只需重新执行download
脚本即可继续下载。
2.训练模型
2.1 转换标注格式
TACO
数据集原始的标注信息被保存在一个名为annotations.json
的文件中,在使用该数据集训练YOLOv8
分割模型前,需要先把原始的标注信息转换为YOLOv8
要求的格式。YOLOv8
分割模型训练时需要的标注格式如下:
<id> <x_1> <y_1> ... <x_n> <y_n>
一个对象的标注信息放在一行,首先是该对象类别的id
(从0
开始算),接着将多边形各点像素坐标的x
和y
值依次排列,其中x
和y
的值需要分别除以图像的宽度和高度进行归一化,一幅图像的所有标注信息放在一个与图像同名的txt
文件中。
进行格式转换后,txt
文件中的内容类似于这样:
5 0.5183 0.4892 0.5480 0.4840 0.4840 0.5627 0.4840 0.5724 0.4853 0.5822 0.4879 0.5900
7 0.6227 0.5211 0.6232 0.5250 0.5074 0.6154 0.5081 0.6183 0.5107 0.5068 0.6120 0.6290
用于格式转换的关键Python
代码如下:
img = cv2.imread(image_path)
height, width, _ = img.shape
label_writer = open(label_path, "w")
for annotation in annotations:
category_id = annotation["category_id"]
seg_labels = []
for segmentation in annotation["segmentation"]:
points = np.array(segmentation).reshape((int(len(segmentation) / 2), 2))
for point in points:
x = point[0] / width
y = point[1] / height
seg_labels.append(x)
seg_labels.append(y)
label_writer.write(str(category_id) + " " + " ".join([str(a) for a in seg_labels]) + "\n")
label_writer.close()
2.2 创建配置文件
首先仿照ultralytics/cfg/datasets/coco128-seg.yaml
创建一个TACO
数据集的配置文件taco-seg.yaml
,文件内容如下:
path: /home/test/TACO/data #数据集所在的目录
train: train.txt # 训练集路径,相对于path目录
val: val.txt # 验证集路径,相对于path目录
test: test.txt # 测试集路径,相对于path目录,可以不写
# 类别id和名称
names:
0: Aluminium foil
1: Battery
2: Aluminium blister pack
3: Carded blister pack
4: Other plastic bottle
5: Clear plastic bottle
6: Glass bottle
7: Plastic bottle cap
8: Metal bottle cap
9: Broken glass
10: Food Can
...
数据集的设置的方式有几种形式,我的方式是建立images
和labels
两个目录,分别用于存放图像和txt
标注文件,然后把数据集按照8:1:1
的比例划分训练集、验证集、测试集,再把三个数据集图片的绝对路径分别写入train.txt
、val.txt
和test.txt
三个文件中。所以上面的taco-seg.yaml
文件中设置的路径path
就是train.txt
、val.txt
和test.txt
这三个文件所在的目录,这三个文件中包含的是对应数据集中图片的绝对路径,类似于这样:
/home/test/TACO/data/images/batch_13/000077.jpg
/home/test/TACO/data/images/batch_11/000032.jpg
/home/test/TACO/data/images/batch_15/000073.jpg
配置好数据集后,还要设置模型参数。首先将ultralytics/cfg/models/v8/yolov8-seg.yaml
文件拷贝一份,命名为yolov8-seg-taco.yaml
,然后把文件中的类别数量nc
从80
改为TACO
数据集的60
:
...
# Parameters
nc: 60 # number of classes
scales: # model compound scaling constants, i.e. 'model=yolov8n-seg.yaml' will call yolov8-seg.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, 768]
l: [1.00, 1.00, 512]
x: [1.00, 1.25, 512]
...
其他有关模型结构的参数如果没有必要就不需要修改了。
2.3 训练
训练YOLOv8
可以使用命令行也可以编写Python
代码实现,个人觉得还是使用命令行比较方便,所以本文采用命令行的方式进行训练,调用的命令如下:
yolo task=segment mode=train data=taco-seg.yaml model=yolov8n-seg-taco.yaml epochs=100 batch=16 imgsz=640 device=0 name=taco-seg
这里data
参数用于指定数据集配置文件,model
参数用于指定模型配置文件,如果不知道有哪些参数可以参考ultralytics/cfg/default.yaml
文件,这个文件里面包含所有需要的参数。需要注意的是,我这里指定的模型配置文件名为yolov8n-seg-taco.yaml
,但是前面我创建的文件名为yolov8-seg-taco.yaml
,这是为什么呢?因为我这里想使用的模型是yolov8n
。假如我想使用yolov8x
模型,那么训练的时候设置参数model=yolov8x-seg-taco.yaml
就可以了。
训练的结果保存在runs/segment/taco-seg
目录下,其中权重保存在该目录下的weights
文件夹中。
3. 结果
训练完成后,我们可以调用命令测试一下模型的效果:
yolo task=segment mode=predict model=runs/segment/taco-seg/weights/best.pt source=/home/test/TACO/data/images/batch_9/000096.jpg show=True
下面是我在测试集的两张图片上测试的结
3.5、YOLO8~Pose Estimation
不知不觉间,YOLOv8已经发布三个月了,等待中的YOLOv8论文没来,昨天官方默默又加了新模型:姿态估计。
说好的"目标检测"工业界标杆,正向着“CV全家桶”阔步向前。
现在你可以用YOLOv8做目标检测、实例分割、图像分类、目标跟踪、姿态估计了,也许还有更多惊喜在后面。
要想使用最新的姿态估计功能,你需要更新到最新版的YOLOv8:
pip install --upgrade ultralytics
官方的模型可以在这里下载:
https://github/ultralytics/assets/releases
其实你也可以不用下载,如果你仅调用官方模型,程序运行时没找到模型的话,它会自己下载的,就是这么贴心!(不过要保证你的网络良好,亲测国内略慢)
模型是在COCO数据集训练的,目前支持的是人体的检测和姿态估计。
CV君用一张网络图片测试一下:
yolo pose predict model=yolov8n-pose.pt source='http://neweuropeans/wp-content/uploads/2016/01/2015_Chisinau_Crossing_Europe_Poike-Stomps_MG_1047.jpg' show=True save=True
其中pose指定任务类型,predict代表我们是要做推断,模型这里我选择的是最轻量级的YOLOv8n-pose,”show=True save=True“代表显示并保存。
运行结果:
Ultralytics YOLOv8.0.68 Python-3.9.13 torch-1.13.1+cu117 CUDA:0 (NVIDIA GeForce GTX 1080 Ti, 11264MiB)
YOLOv8n-pose summary (fused): 187 layers, 3289964 parameters, 0 gradients, 9.2 GFLOPs
Downloading http:\neweuropeans\wp-content\uploads\2016\01\2015_Chisinau_Crossing_Europe_Poike-Stomps_MG_1047.jpg to 2015_Chisinau_Crossing_Europe_Poike-Stomps_MG_1047.jpg...
100%|████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████████| 564k/564k [00:00<00:00, 1.29MB/s]
image 1/1 E:\download\2015_Chisinau_Crossing_Europe_Poike-Stomps_MG_1047.jpg: 448x640 17 persons, 24.6ms
Speed: 0.0ms preprocess, 24.6ms inference, 7.0ms postprocess per image at shape (1, 3, 640, 640)
Results saved to runs\pose\predict
我的GPU是1080Ti,这张1500x977大小的图片推断时间24.6ms。
如果要用YOLOv8调用摄像头的话,也非常简单:
yolo pose predict model=yolov8n-pose.pt source=0 show=True save=True
官方在COCO数据集上做了更多测试:
总计发布了YOLOv8n-pose、YOLOv8s-pose...YOLOv8x-pose-p6等6个模型,在A100上的推断速度从1.18ms到10.04ms,模型参数3.3M到99.1M。
方便在不同硬件和算力的平台上使用。
当然从CPU上的测试结果看,还不是一个CPU实时的算法。
不过更有价值的可能是,要训练我们自己的模型,是非常方便的。
按照coco128-pose.yaml的"样例"组织好数据并修改coco128-pose.yaml后,你只需要一句命令:
yolo pose train data=coco128-pose.yaml model=yolov8n-pose.pt epochs=100 imgsz=640
相关文档:https://docs.ultralytics/tasks/pose/
写在最后:YOLOv8的功能越来越多,而且相比于其他开源库,对于工业界来说更友好,涵盖训练、评估、推断、部署全流程,是快速进行项目开发的首选。
下一个进入YOLOv8 "CV全家桶"会是谁呢?Face?
4、Yolo10
4.1、yolo10
好多人应该都发了 我也跟风一下~~ yolo更新好快啊~ 我还停留在5
过去几年里,YOLOs因在计算成本和检测性能之间实现有效平衡而成为实时目标检测领域的主流范式。研究人员针对YOLOs的结构设计、优化目标、数据增强策略等进行了深入探索,并取得了显著进展。然而,对非极大值抑制(NMS)的后处理依赖阻碍了YOLOs的端到端部署,并对推理延迟产生负面影响。此外,YOLOs中各种组件的设计缺乏全面和彻底的审查,导致明显的计算冗余并限制了模型的性能。这导致次优的效率,以及性能提升的巨大潜力。在这项工作中,我们旨在从后处理和模型架构两个方面进一步推进YOLOs的性能-效率边界。为此,我们首先提出了用于YOLOs无NMS训练的持续双重分配,该方法同时带来了竞争性的性能和较低的推理延迟。此外,我们为YOLOs引入了全面的效率-准确性驱动模型设计策略。我们从效率和准确性两个角度全面优化了YOLOs的各个组件,这大大降低了计算开销并增强了模型能力。我们的努力成果是新一代YOLO系列,专为实时端到端目标检测而设计,名为YOLOv10。广泛的实验表明,YOLOv10在各种模型规模下均达到了最先进的性能和效率。例如,在COCO数据集上,我们的YOLOv10-S在相似AP下比RT-DETR-R18快1.8倍,同时参数和浮点运算量(FLOPs)减少了2.8倍。与YOLOv9-C相比,YOLOv10-B在相同性能下延迟减少了46%,参数减少了25%。代码链接:https://github/THU-MIG/yolov10。
YOLOv10有哪些改进?
首先通过为无NMS的YOLOs提出一种持续双重分配策略来解决后处理中的冗余预测问题,该策略包括双重标签分配和一致匹配度量。这使得模型在训练过程中能够获得丰富而和谐的监督,同时消除了推理过程中对NMS的需求,从而在保持高效率的同时获得了竞争性的性能。
其次,为模型架构提出了全面的效率-准确度驱动模型设计策略,对YOLOs中的各个组件进行了全面检查。在效率方面,提出了轻量级分类头、空间-通道解耦下采样和rank引导block设计,以减少明显的计算冗余并实现更高效的架构。
在准确度方面,探索了大核卷积并提出了有效的部分自注意力模块,以增强模型能力,以低成本挖掘性能提升潜力。
基于这些方法,作者成功地实现了一系列不同模型规模的实时端到端检测器,即YOLOv10-N / S / M / B / L / X。在标准目标检测基准上进行的广泛实验表明,YOLOv10在各种模型规模下,在计算-准确度权衡方面显著优于先前的最先进模型。如图1所示,在类似性能下,YOLOv10-S / X分别比RT-DETR R18 / R101快1.8倍/1.3倍。与YOLOv9-C相比,YOLOv10-B在相同性能下实现了46%的延迟降低。此外,YOLOv10展现出了极高的参数利用效率。YOLOv10-L / X在参数数量分别减少了1.8倍和2.3倍的情况下,比YOLOv8-L / X高出0.3 AP和0.5 AP。YOLOv10-M在参数数量分别减少了23%和31%的情况下,与YOLOv9-M / YOLO-MS实现了相似的AP。
在训练过程中,YOLOs通常利用TAL(任务分配学习) 为每个实例分配多个正样本。采用一对多的分配方式产生了丰富的监督信号,有助于优化并实现卓越的性能。然而,这也使得YOLOs 必须依赖于NMS(非极大值抑制)后处理,这导致在部署时的推理效率不是最优的。虽然之前的工作探索了一对一的匹配方式来抑制冗余预测,但它们通常会增加额外的推理开销或导致次优的性能。在这项工作中,我们为YOLOs提出了一种无需NMS的训练策略,该策略采用双重标签分配和一致匹配度量,实现了高效率和具有竞争力的性能。
效率驱动的模型设计。YOLO中的组件包括主干(stem)、下采样层、带有基本构建块的阶段和头部。主干部分的计算成本很低,因此我们对其他三个部分进行效率驱动的模型设计。
(1)轻量级的分类头。在YOLO中,分类头和回归头通常具有相同的架构。然而,它们在计算开销上存在显著的差异。例如,在YOLOv8-S中,分类头(5.95G/1.51M的FLOPs和参数计数)的FLOPs和参数计数分别是回归头(2.34G/0.64M)的2.5倍和2.4倍。然而,通过分析分类错误和回归错误的影响(见表6),我们发现回归头对YOLO的性能更为重要。因此,我们可以在不担心对性能造成太大损害的情况下减少分类头的开销。因此,我们简单地采用了轻量级的分类头架构,它由两个深度可分离卷积组成,卷积核大小为3×3,后跟一个1×1卷积。
(3)基于rank引导的模块设计。YOLOs通常对所有阶段都使用相同的基本构建块,例如YOLOv8中的bottleneck块。为了彻底检查YOLOs的这种同构设计,我们利用内在秩来分析每个阶段的冗余性。具体来说,计算每个阶段中最后一个基本块中最后一个卷积的数值秩,它计算大于阈值的奇异值的数量。图3(a)展示了YOLOv8的结果,表明深层阶段和大型模型更容易表现出更多的冗余性。这一观察表明,简单地对所有阶段应用相同的block设计对于实现最佳容量-效率权衡来说并不是最优的。为了解决这个问题,提出了一种基于秩的模块设计方案,旨在通过紧凑的架构设计来降低被证明是冗余的阶段的复杂性。
首先介绍了一种紧凑的倒置块(CIB)结构,它采用廉价的深度卷积进行空间混合和成本效益高的逐点卷积进行通道混合,如图3(b)所示。它可以作为有效的基本构建块,例如嵌入在ELAN结构中(图3(b))。然后,倡导一种基于秩的模块分配策略,以在保持竞争力量的同时实现最佳效率。具体来说,给定一个模型,根据其内在秩的升序对所有阶段进行排序。进一步检查用CIB替换领先阶段的基本块后的性能变化。如果与给定模型相比没有性能下降,我们将继续替换下一个阶段,否则停止该过程。因此,我们可以在不同阶段和模型规模上实现自适应紧凑块设计,从而在不影响性能的情况下实现更高的效率。
基于精度导向的模型设计。论文进一步探索了大核卷积和自注意力机制,以实现基于精度的设计,旨在以最小的成本提升性能。
(1)大核卷积。采用大核深度卷积是扩大感受野并增强模型能力的一种有效方法。然而,在所有阶段简单地利用它们可能会在用于检测小目标的浅层特征中引入污染,同时也在高分辨率阶段引入显著的I/O开销和延迟。因此,作者提出在深层阶段的跨阶段信息块(CIB)中利用大核深度卷积。这里将CIB中的第二个3×3深度卷积的核大小增加到7×7。此外,采用结构重参数化技术,引入另一个3×3深度卷积分支,以缓解优化问题,而不增加推理开销。此外,随着模型大小的增加,其感受野自然扩大,使用大核卷积的好处逐渐减弱。因此,仅在小模型规模上采用大核卷积。
(2)部分自注意力(PSA)。自注意力机制因其出色的全局建模能力而被广泛应用于各种视觉任务中。然而,它表现出高计算复杂度和内存占用。为了解决这个问题,鉴于普遍存在的注意力头冗余,作则提出了一种高效的部分自注意力(PSA)模块设计,如图3.(c)所示。具体来说,在1×1卷积之后将特征均匀地按通道分成两部分。只将一部分特征输入到由多头自注意力模块(MHSA)和前馈网络(FFN)组成的NPSA块中。然后,将两部分特征通过1×1卷积进行拼接和融合。此外,将MHSA中查询和键的维度设置为值的一半,并将LayerNorm替换为BatchNorm以实现快速推理。PSA仅放置在具有最低分辨率的第4阶段之后,以避免自注意力的二次计算复杂度带来的过多开销。通过这种方式,可以在计算成本较低的情况下将全局表示学习能力融入YOLOs中,从而很好地增强了模型的能力并提高了性能。
实验对比
这里就不做过多介绍啦,直接上结果!!!latency减少,性能继续增加。
Visualization Results
图4展示了作者YOLOv10在复杂且具有挑战性的场景下的可视化结果。可以看出,YOLOv10在各种困难条件下都能实现精确检测,例如低光照、旋转等。它还展示了在检测多种且密集排列的物体(如瓶子、杯子和人)方面的强大能力。这些结果表明其性能卓越。
Contribution, Limitation, and Broader Impact
总的来说,作者的贡献主要体现在以下三个方面:
- 作者提出了一种新颖的一致性双重分配策略,用于无需NMS的YOLO。设计了一种双重标签分配方法,通过一对多分支在训练过程中提供丰富的监督信息,以及通过一对一分支在推理过程中实现高效率。此外,为了确保两个分支之间的和谐监督,作者创新性地提出了连贯匹配度量,这可以很好地减少理论上的监督差距,并带来性能的提升。
- 作者提出了一种整体效率-精度驱动的模型设计策略,用于YOLO的模型架构。作者展示了新型轻量级分类头、空间-通道解耦降采样和排名引导的块设计,这些设计大大减少了计算冗余并实现了高效率。作者进一步引入了大核卷积和创新的部分自注意力模块,这些模块在低成本的条件下有效地提升了性能。
- 基于上述方法,作者推出了YOLOv10,这是一个新的实时端到端目标检测器。广泛的实验表明,YOLOv10与其他先进检测器相比,在性能和效率权衡方面达到了最先进水平。
局限性:由于计算资源的限制,作者没有在大规模数据集上进行YOLOv10的预训练,例如Objects365 [47]。此外,尽管作者在无需NMS的训练下使用一对一 Head 可以获得具有竞争力的端到端性能,但与使用NMS的一对多训练相比,仍然存在性能差距,特别是在小型模型中更为明显。例如,在YOLOv10-N和YOLOv10-S中,使用NMS的一对多训练的性能比无需NMS的训练分别高出1.0% AP和0.5% AP。作者将在未来的工作中探索进一步缩小差距并实现更高性能的方法。
Conclusion
在本文中,作者针对YOLO系列检测 Pipeline 中的后处理和模型架构进行了研究。对于后处理,作者提出了持续的双重分配以实现无需NMS的训练,从而实现高效的端到端检测。对于模型架构,作者引入了整体效率-精度驱动的模型设计策略,改进了性能与效率之间的权衡。这些改进带来了YOLOv10,这是一个新的实时端到端目标检测器。大量实验表明,与其它先进检测器相比,YOLOv10在性能和延迟方面均达到了最先进水平,充分展示了其优越性。
4.2、ONNX模型部署和性能对比
YOLOv10
是清华大学最近开源的一个实时端到端的目标检测算法,解决了以往版本YOLO
系列目标检测算法在后处理和模型架构方面的不足。通过消除非极大值抑制(NMS
)操作和优化模型架构,YOLOv10
在显著降低计算开销的同时还实现了最先进的性能。
YOLOv10
的模型架构由以下几个部分组成:
- 主干网络:使用增强版的
CSPNet
来提取图像特征,它能改善梯度流并减少计算量。 - 颈部:采用
PAN
结构汇聚不同尺度的特征,有效地实现多尺度特征融合。 - 一对多预测头:在训练过程中为每个对象生成多个预测,用来提供丰富的监督信号从而提高学习的准确性;在推理阶段不生效,从而减少计算量。
- 一对一预测头:在推理过程中为每个对象生成一个最佳预测,无需
NMS
操作,从而减少延迟并提高推理效率。
YOLOv10
的主要特点如下:
- 利用一致的双重分配来消除对
NMS
的需求,从而减少推理延迟。 - 从推理效率和准确性的角度出发全面优化各种组件,包括轻量级分类头、空间通道去耦下采样和等级引导块设计。
- 引入大核卷积和部分自注意模块,在不增加大量计算成本的情况下提高性能。
官方发布了从N
到X
各种型号的模型,以满足不同应用的需求:
-
YOLOv10-N
:用于资源极其有限环境的超小型版本。 -
YOLOv10-S
:兼顾速度和精度的小型版本。 -
YOLOv10-M
:通用的中型版本。 -
YOLOv10-B
:平衡型,宽度增加,精度更高。 -
YOLOv10-L
:大型版本,精度更高,但计算资源增加。 -
YOLOv10-X
:超大型版本,可实现最高的精度和性能。
「本文主要介绍如何基于ONNXRuntime
框架部署onnx
格式的YOLOv10
模型,以及YOLOv10
与RT-DETR
等算法的性能对比。」
1. 准备工作
首先把代码从GitHub
上clone
下来
git clone https://github/THU-MIG/yolov10.git
然后执行下面的命令用conda
创建Python
环境并安装相关的依赖库和YOLOv10
conda create -n yolov10 python=3.9
conda activate yolov10
pip install -r requirements.txt
pip install -e .
这里需要注意的是,如果使用的是低版本的pip
,可能会报类似下面的错误:
ERROR: File "setup.py" or "setup.cfg" not found. Directory cannot be installed in editable mode: /path/to/yolov10
(A "pyproject.toml" file was found, but editable mode currently requires a setuptools-based build.)
这种情况需要升级pip
的版本,最新版本24.0
实测是没有问题的。
pip install --upgrade pip
安装成功后,从GitHub的release中下载PyTorch
格式的模型权重,然后执行下面的命令就可以导出onnx
模型了。
yolo export model=yolov10n/s/m/b/l/x.pt format=onnx opset=13 simplify
2. onnx模型部署
2.1 加载onnx模型
首先导入onnxruntime
包,然后调用其API
加载模型即可:
import onnxruntime as ort
session = ort.InferenceSession("yolov10m.onnx", providers=["CUDAExecutionProvider"])
这里的providers
参数根据自己的实际情况设置,我使用的是GPU
所以设置的是"CUDAExecutionProvider"
。如果用CPU
进行推理,则需设置为"CPUExecutionProvider"
;如果有TensorRT
的环境,还可以设置为"TensorrtExecutionProvider"
。
模型加载成功后,我们可以查看一下模型的输入、输出层的属性:
for input in session.get_inputs():
print("input name: ", input.name)
print("input shape: ", input.shape)
print("input type: ", input.type)
for output in session.get_outputs():
print("output name: ", output.name)
print("output shape: ", output.shape)
print("output type: ", output.type)
结果如下:
input name: images
input shape: [1, 3, 640, 640]
input type: tensor(float)
output name: output0
output shape: [1, 300, 6]
output type: tensor(float)
从上面的打印信息可以知道,模型有一个尺寸为[1, 3, 640, 640]
的输入层和一个尺寸分别为[1, 300, 6]
的输出层。
2.2 数据预处理
用OpenCV
读入图片后,首先需要对图片做预处理:
image = cv2.imread("soccer.jpg")
print("image shape: ", image.shape)
image_height, image_width, _ = image.shape
_, _, model_height, model_width = session.get_inputs()[0].shape
input_tensor, ratio, x_offset, y_offset = preprocess(image,
image_width, image_height, model_width, model_height)
YOLOv10
在做数据预处理的时候是对原始图像做等比例缩放的,如果缩放后的图像某个维度上比目标值小,那么就需要进行填充。举个例子:假设输入图像尺寸为1920x1058
,模型输入尺寸为640x640
,按照等比例缩放的原则缩放后的图像尺寸为640x352
,那么在y
方向上还需要填充640-352=288
,即分别在图像的顶部和底部各填充144
行像素。最终实现的效果如下:
整个数据预处理的流程如下:
- 把
OpenCV
读取的BGR
格式图片转换为RGB
格式; - 计算缩放比例和需要填充的区域,把原始图片进行等比例缩放,对不足的区域进行填充让输入图片的尺寸匹配模型的输入尺寸;
- 对像素值除以
255
做归一化操作; - 把图像数据的通道顺序由
HWC
调整为CHW
; - 扩展数据维度,将数据的维度调整为
NCHW
。
实现上述功能的预处理函数preprocess
的代码如下:
def preprocess(bgr_image, src_w, src_h, dst_w, dst_h):
image = cv2.cvtColor(bgr_image, cv2.COLOR_BGR2RGB)
ratio = min(dst_w/src_w, dst_h/src_h)
border_w = int(round(src_w * ratio / 2) * 2)
border_h = int(round(src_h * ratio / 2) * 2)
x_offset = (dst_w - border_w ) // 2
y_offset = (dst_h - border_h ) // 2
image = cv2.resize(image, (border_w, border_h))
image = cv2.copyMakeBorder(
image, y_offset, y_offset, x_offset, x_offset,
cv2.BORDER_CONSTANT, value=(114, 114, 114)
)
image = image.astype(np.float32) / 255.0
image = np.transpose(image, (2, 0, 1))
input_tensor = np.expand_dims(image, axis=0)
return input_tensor, ratio, x_offset, y_offset
经过预处理后,输入数据input_tensor
的维度变为[1, 3, 640, 640]
,与模型要求的输入尺寸一致。
2.3 模型推理
准备好输入数据以后,就可以送入模型进行推理:
outputs = session.run(None, {session.get_inputs()[0].name: input_tensor})
output = np.squeeze(outputs[0])
print("output shape: ", output.shape)
YOLOv10
只有一个输出分支,所以只要取outputs[0]
的数据进行处理,去掉batch
这个维度后,模型输出的维度为300x6
。
output.shape: (300, 6)
2.4 后处理
从前文知道模型输出的维度为300x6
,其中300
表示模型在一张图片上最多能检测的目标数量,6
则表示每个目标包含4
个坐标属性(xmin,ymin,xmax,ymax
)和1
个类别置信度以及1
个类别索引。每个目标的坐标信息都是相对于模型的输入尺寸的,由于预处理的时候在边上做了填充,所以后处理的时候要把每个坐标值减掉对应的偏移值;如果要恢复到原始图像的尺寸,还需要除以预处理时使用的缩放比例系数。与RT-DETR
一样,YOLOv10
的检测结果不需要再做NMS
这些额外的后处理操作,处理过程非常简单。后处理的代码如下:
for i in range(output.shape[0]):
# 读取类别置信度
confidence = output[i][4]
# 用阈值进行过滤
if confidence > 0.5:
# 读取类别索引
label = int(output[i][5])
# 读取类坐标值,把坐标还原到原始图像
xmin = int((output[i][0] - x_offset) / ratio)
ymin = int((output[i][1] - y_offset) / ratio)
xmax = int((output[i][2] - x_offset) / ratio)
ymax = int((output[i][3] - y_offset) / ratio)
# 可视化
class_name = COCO_CLASSES[label]
box_color = np.array(COLOR_LIST[label]) * 255
box_color = (int(box_color[0]), int(box_color[1]), int(box_color[2]))
cv2.rectangle(image, (xmin, ymin), (xmax, ymax), box_color, 4)
# 省略.....
来看一下检测效果:
下面是用官方代码基于PyTorch
推理的结果:
可以看到,ONNXRuntime
和PyTorch
推理的结果是一致的。
3. 推理耗时对比
这里的对比仅对比模型本身的推理耗时,不包含后处理操作。本文在GeForce GTX 1650 Ti
显卡上,基于ONNXRuntime
框架分别采用CUDA
和TensorRT
后端对YOLOv10
、YOLOv9
和RT-DETR
的各个模型进行测试,数据精度统一采用FP32
,模型输入尺寸统一设置为640x640
。各模型的推理耗时(单位为毫秒)测试结果如下:
模型 | CUDA | TensorRT |
yolov10n | 10 | 7 |
yolov10s | 17 | 13 |
yolov10m | 34 | 27 |
yolov10b | 44 | 37 |
yolov10l | 55 | 46 |
yolov10x | 80 | 64 |
yolov9-c | 52 | 41 |
yolov9-e | 106 | 82 |
rtdetr_r18vd_6x | 52 | 26 |
rtdetr_r34vd_6x | 66 | 36 |
rtdetr_r50vd_6x | 94 | 52 |
rtdetr_r50vd_m_6x | 66 | 40 |
rtdetr_r101vd_6x | 133 | 89 |
YOLOv10
的论文里说YOLOv10-S
比RT-DETR-R18
快1.8
倍,YOLOv10-X
比RT-DETR-R101
快1.3
倍,YOLOv10-B
的推理延迟比YOLOv9-C
减少了46%
。从我测试的结果来看,YOLOv10-S/X
不止比RT-DETR-R18/R101
快一点几倍,YOLOv10-B
则没有比YOLOv9-C
快那么多。
总的来说,YOLOv10
的性能确实比之前的模型要强一些,新一代卷王名不虚传。
4. 参考资料
- YOLOv10: Real-Time End-to-End Object Detection
- https://docs.ultralytics/zh/models/yolov10/