标题:YOLOv10: Real-Time End-to-End Object Detection
论文:https://arxiv.org/pdf/2405.14458
源码:https://github.com/THU-MIG/yo...
导读
YOLO(You Only Look Once)系列是当前最主流的端侧目标检测算法,由Joseph Redmon
等人首次提出,并随着时间发展,已经推出了多个版本,每个版本“似乎”都在性能和速度上有所提升。
今天为大家介绍的是 YOLOv10,这是由清华大学研究团队最新提出的,同样遵循 YOLO 系列设计原则,致力于打造实时端到端的高性能目标检测器。值得一提的是,YOLOv10 也已经被合并到 Ultralytics 官方项目。
从图中可以看出,性能是持续提升的,欢迎各位小伙伴阅读完本篇文章后在评论区留言,谈谈你对本文的看法。
方法
创新
- 双标签分配策略
众所周知,标签分配策略对于目标检测器来说是至关重要的。经过这几年的发展,前前后后也提出了许多的不同的方案,但归根结底还是围绕着正负样本去定义。通常,我们会认为与 GT 框的 IoU 大于给定阈值的便是正样本。
首先,回顾下经典的 YOLO 架构,其通过网格化的方式预定义数千个锚框(anchor),然后基于这些锚框进一步执行回归和分类任务。然而,实际场景中,我们所面临的目标其大小、长宽比、数量、位姿均各有所异,因此很难通过这种方式来提供一个完美的先验信息,尽管可以借助一些方法如 kmeans 聚类来获得一个次优的结果。
于是乎,基于 anchor-free 的目标检测器被提出来了。其标签分配策略被简化成了从网格点到目标框中心或者角点的距离。遗憾的是,无论是 anchor-based 的“框分配”策略还是 anchor-free 的“点分配”策略,其始终会面临一个 many-to-one 的窘境,即对于一个 GT 框来说,会存在多个正样本与之对应。
这便意味着 NMS 成为了一种必不可少的手段,以避免产生冗余检测框。然而,引入 NMS 一方面会增加耗时,同时也会引入一些问题,譬如当 IoU 设置不恰当时会导致一些高置信度的正确目标框被过滤掉(密集场景下)。
当然,针对这个问题,后面也提出了不少解决方案。如最容易想到的就是 two-stage 模型的 one-to-one 即一对一分配策略,我们强制只将一个 GT 框分配给一个正样本,这样就可以避免引入 NMS,可惜效率方面是个极大的劣势。
又比如 One-Net 提出的最小代价分配(Minimum Cost Assignment),即于每个 GT,仅将一个最小代价样本分配为正样本,其它均为负样本,该方法不涉及手动制定的启发式规则或者复杂的二分图匹配。这里代价是指样本与真值之间的分类代价和位置代价的总和。
另一方面,诸如 DETR 系列的检测器,其直接利用 Transformer 的全局建模能力,将目标检测看成是一个集合预测的问题。为了实现端到端的检测,其使用的标签分配策略是二分匹配,使得一个 GT 只能分配到一个正样本。
由于篇(知)幅(识)有(盲)限(区),今天我们就讲到这。回到今天的主角,YOLOv10 的一大创新点便是引入了一种双重标签分配策略,其核心思想便是在训练阶段使用一对多的检测头提供更多的正样本来丰富模型的训练;而在推理阶段则通过梯度截断的方式,切换为一对一的检测头,如此一来便不在需要 NMS 后处理,在保持性能的同时减少了推理开销。
原理其实不难,大家可以看下代码理解下:
#https://github.com/THU-MIG/yolov10/blob/main/ultralytics/nn/modules/head.py
class v10Detect(Detect):
max_det = -1
def __init__(self, nc=80, ch=()):
super().__init__(nc, ch)
c3 = max(ch[0], min(self.nc, 100)) # channels
self.cv3 = nn.ModuleList(nn.Sequential(nn.Sequential(Conv(x, x, 3, g=x), Conv(x, c3, 1)), \
nn.Sequential(Conv(c3, c3, 3, g=c3), Conv(c3, c3, 1)), \
nn.Conv2d(c3, self.nc, 1)) for i, x in enumerate(ch))
self.one2one_cv2 = copy.deepcopy(self.cv2)
self.one2one_cv3 = copy.deepcopy(self.cv3)
def forward(self, x):
one2one = self.forward_feat([xi.detach() for xi in x], self.one2one_cv2, self.one2one_cv3)
if not self.export:
one2many = super().forward(x)
if not self.training:
one2one = self.inference(one2one)
if not self.export:
return {"one2many": one2many, "one2one": one2one}
else:
assert(self.max_det != -1)
boxes, scores, labels = ops.v10postprocess(one2one.permute(0, 2, 1), self.max_det, self.nc)
return torch.cat([boxes, scores.unsqueeze(-1), labels.unsqueeze(-1)], dim=-1)
else:
return {"one2many": one2many, "one2one": one2one}
def bias_init(self):
super().bias_init()
"""Initialize Detect() biases, WARNING: requires stride availability."""
m = self # self.model[-1] # Detect() module
# cf = torch.bincount(torch.tensor(np.concatenate(dataset.labels, 0)[:, 0]).long(), minlength=nc) + 1
# ncf = math.log(0.6 / (m.nc - 0.999999)) if cf is None else torch.log(cf / cf.sum()) # nominal class frequency
for a, b, s in zip(m.one2one_cv2, m.one2one_cv3, m.stride): # from
a[-1].bias.data[:] = 1.0 # box
b[-1].bias.data[: m.nc] = math.log(5 / m.nc / (640 / s) ** 2) # cls (.01 objects, 80 classes, 640 img)
- 架构改进
- Backbone & Neck:使用了先进的结构如 CSPNet 作为骨干网络,和 PAN 作为颈部网络,优化了特征提取和多尺度特征融合。
- 大卷积核与分区自注意力:这些技术用于增强模型从大范围上下文中学习的能力,提高检测准确性而不显著增加计算成本。
- 整体效率:引入空间-通道解耦下采样和基于秩引导的模块设计,减少计算冗余,提高整体模型效率。
这块没啥好讲的,大家看一眼框架图便清楚了,懂的都懂。:)
性能
YOLOv10 在各种模型规模上显示了显著的性能和效率改进。关键比较包括:
- YOLOv10-S vs. RT-DETR-R18:YOLOv10-S 的速度提高了 1.8 倍,同时在 COCO 数据集上保持类似的平均精度(AP),参数和 FLOPs 分别减少了 2.8 倍。
- YOLOv10-B vs. YOLOv9-C:YOLOv10-B 的延迟减少了 46%,参数减少了 25%,而性能相当。
扩展性
YOLOv10 提供了多个模型规模(N、S、M、B、L、X),允许用户根据性能和资源约束选择最适合的模型。这种可扩展性确保了 YOLOv10 能够有效应用于各种实时检测任务,从移动设备上的轻量级应用到需要高精度的复杂任务。
实验
这里重点看下表3,可以看出,采用一对多的检测头性能最好(提供了更丰富的正样本监督信号),但延迟也高了许多(需要 NMS 做后处理);另外方面,一对一的检测头则性能会稍微下降,但延迟却低了不少;最终综合利用两者的优势能达到一个最优的精度-速度折衷。
实战
首先,按照官方主页将环境配置好,注意这里 python 版本至少需要 3.9 及以上,torch 版本可以根据自己本地机器安装合适的版本,默认下载的是 2.0.1:
conda create -n yolov10 python=3.9
conda activate yolov10
pip install -r requirements.txt
pip install -e .
安装完成之后,我们简单执行下推理命令测试下效果:
yolo predict model=yolov10s.pt source=ultralytics/assets/bus.jpg
Woo...Not bad! Btw, if you encounter this error message:
AttributeError: Can't get attribute 'YOLOv10DetectionModel' on <module 'ultralytics.nn.tasks' from '/home/xxx/miniconda3/lib/python3.10/site-packages/ultralytics/nn/tasks.py'>
Don't worry, just execute the following command, making sure to replace the placeholders with your local installation path:
# Linux or Mac
export PYTHONPATH=/path/to/yolov10
# Windwos
set PYTHONPATH=\path\to\yolov10
关于推理速度方面我们就不测了,不过我看有些同学测完反映说这个 latency 没有报告中的低,我想大概率是没有转换成 one-to-one 去测,应该是基于训练阶段的 one-to-many 结构测试得到一个错误的结论。
Good! 让我们尝试部署一下,譬如先导出个 onnx 模型出来看看:
yolo export model=yolov10s.pt format=onnx opset=13 simplify
好了,接下来通过执行 pip install netron
安装个可视化工具来看看导出的节点信息:
# run python fisrt
import netron
netron.start('/path/to/yolov10s.onnx')
先直接通过 Ultralytics 框架预测一个测试下能否正常推理:
yolo predict model=yolov10s.onnx source=ultralytics/assets/bus.jpg
大家可以对比下上面的运行结果,可以看出 performance 是有些许的下降。问题不大,让我们基于 onnxruntime 写一个简单的推理脚本,代码地址如下,有兴趣的可以自行查看:
# 推理脚本
https://github.com/CVHub520/X-AnyLabeling/blob/main/tools/export_yolov10_onnx.py
# onnx 模型权重
https://github.com/CVHub520/X-AnyLabeling/releases/tag/v2.3.6
好了,精度基本一致!最后再为大家介绍下X-AnyLabeling
,这是一款基于AI推理引擎和丰富功能特性于一体的强大辅助标注工具,其专注于实际应用,致力于为图像数据工程师提供工业级的一站式解决方案,可自动快速进行各种复杂任务的标定,并支持以下特性:
- 支持GPU推理加速;
- 支持图像和视频处理;
- 支持单帧和批量预测所有任务;
- 支持自定义模型和二次开发设计;
- 支持一键导入和导出主流的标签格式,如COCO\VOC\YOLO\ \DOTA\MOT\MASK;
- 支持多种图像标注样式,包括 多边形、矩形、旋转框、圆形、线条、点,以及 文本检测、识别 和 KIE 标注;
- 支持各类视觉任务,如图像分类、目标检测、实例分割、姿态估计、旋转检测、多目标跟踪、光学字符识别、图像文本描述、车道线检测、分割一切系列等。
更多介绍请查看以下文章:
X-AnyLabeling 最新 v2.3.6 版本以集成 YOLOv10 全系列模型,欢迎下载体验:
项目地址:https://github.com/CVHub520/X...
安装教程:https://github.com/CVHub520/X...
用户手册:https://github.com/CVHub520/X...
模型列表:https://github.com/CVHub520/X...
结论
YOLOv10 作为一款实时端到端目标检测模型,其通过创新的双标签分配策略和架构改进,在保持高速检测的同时显著提升了准确性和效率,提供了多个模型规模以适应不同应用场景,并通过支持ONNX和TensorRT等格式的导出,便于在多种平台上部署和推理,值得尝试。
作者:派派星
来源:CVHub
推荐阅读
- SGLang:LLM推理引擎发展新方向
- CUDA-MODE课程笔记 第7课: Quantization Cuda vs Triton
- CUDA-MODE 第一课课后实战(上)
- 一文弄懂 LLM 结构化数据生成原理
欢迎大家点赞留言,更多Arm技术文章动态请关注极术社区嵌入式AI专栏欢迎添加极术小姐姐微信(id:aijishu20)加入技术交流群,请备注研究方向。