4.5 YOLO家族 一阶段目标检测的王者
1.从两阶段到一阶段:YOLO换了个思路
说起来,我们在4.3 节聊R-CNN那一支的时候,目标检测还是个两阶段的活儿。先用一个区域提议网络在图里找出几百个看起来像物体的候选框,再一个一个细细判断框里到底是什么东西。这种慢工出细活的路子精度确实漂亮,可速度实在让人着急,一张图跑下来常常要几十甚至上百毫秒,碰到自动驾驶这种一秒钟要处理几十帧的场景就明显顶不住了。
大概在2016年,一位叫Redmon的研究者换了个思路,写了篇论文叫You Only Look Once(你只需要看一眼),这就是后来大家熟悉的YOLO。它想说的其实就一句话,检测这件事何必分两步,完全可以一刀切,让网络直接从原图一次性回归出所有的边界框和类别。说穿了,就是把检测当成一个回归问题来做,输入一张图,前向传播跑一遍,输出端就直接吐出整张图里所有物体的位置和类别。这一来,速度自然就上去了,最快能跑到每秒几十甚至上百帧,实时场景终于有了合适的工具。
我记得有阵子小张在调一个流水线计数的活儿,场景是在传送带上数螺丝,每秒钟得处理三十多帧才跟得上节拍。当时拿两阶段的Faster R-CNN去试,速度怎么都上不去,后来换成YOLO,一下就轻松过了线,那是我第一次真切感受到一阶段检测的甜头。说起来,做工程大概就是这样,合适的场景配上合适的工具,比一味追精度要受用得多。
2.YOLO v1:把图画成棋盘格
那YOLO具体是怎么做的呢,我们拿最早的v1版本来讲。它先把输入图划成 个网格,这里 表示一行或一列上网格的数量,所以整张图一共被分成 个小网格。物体中心点落在哪个网格里,那个网格就要负责预测这个物体。每个网格要输出两部分东西,一部分是若干个边界框(bounding box),每个边界框用四个数描述位置,分别记作 、 、 、 ,其中 和 表示框的中心坐标, 和 表示框的宽和高。每个边界框还配一个置信度,置信度反映这个框里到底有没有东西以及框的位置准不准。另一部分是每个网格对各类别的条件概率,记作 ,其中 表示第 个类别, 是类别编号, 也就是这个网格里确实有物体的话,它是第 类的可能性。
把这些都拼到一起,整个输出就是一个大张量。设每个网格要预测 个边界框( 表示每个网格预测的框数),数据集里一共有 个类别( 表示类别总数),那么每个网格的输出维度就是 ,这里每个框占 个数(分别是 、 、 、 和置信度),再加上 个类别概率。整张图的输出形状就是 。原文取 、 、 ,所以最终输出是 。这套设计干净利落,把一张杂乱的图变成了一组结构化的数字,往后随便怎么用都方便。
这种做法最大的好处就是快。整个网络就一条路走到底,没有任何耗时的候选区域生成步骤。代价当然也是有的。早期v1对小物体特别不友好,一群小鸟、一串远处的行人,常常漏检或者框得不够准。原因其实也好理解,网格分得粗,一个网格只能好好负责一个物体,要是好几个小物体挤在一个网格里,它就照顾不过来了。另外早期v1的精度比起Faster R-CNN也差一截,但它的速度优势实在太显眼,大家就愿意接着往前推。这件事不妨这么看,YOLO v1更像是一个开端,它把一阶段这条路打开了,后面的版本顺着这条思路一路修修补补,才有了今天的局面。
3.从v2到v3:anchor和多尺度检测
v1开了一个好头,但毛病也明显,于是接下来几代YOLO就开始打补丁。v2这代主要做了两件事。第一件事是引入了anchor(先验框)的概念。anchor是预先设定好的若干个不同形状和大小的参考框,长得像瘦长的、矮胖的、大的、小的都有。网络预测的内容变成了相对于anchor的偏移量,有了这个先验参考,预测起来就轻松多了。这件事有点像绘画起稿先打一个粗略的轮廓,再在轮廓上慢慢修细节,凭空下笔总归要难一些。第二件事是换上更好的主干网络(就是负责从图里抽取特征的那部分,CNN我们在3.3 节细讲过),让特征表达更扎实。
到了v3,又往前走了一步,引入了多尺度检测。什么叫多尺度呢,就是网络在好几种不同大小的特征图上分别做检测。大物体适合在分辨率较低的特征图上看(这种特征图每一点对应的感受野大,能看更大范围),小物体适合在分辨率较高的特征图上看(细节保留得多)。这就像看一张风景照,远处那座山要用大视野去抓,近处那朵花则要凑近了看清楚。v3还用上了叫Darknet-53的主干网络,这里的53说的是这个网络一共堆了53个卷积层。多尺度这一改,小物体检测明显改善,YOLO家族在精度上算是真正立住了。
我记得有阵子小张在帮一家种草莓的农场做病害识别,叶片上那些早期病斑特别小,用v1的思路简直没法治,换成带多尺度的v3之后,小病斑也能被稳稳框出来了。农场的老板看了效果连连点头,说这下省了不少靠人巡查的力气。其实农业里这类需求挺多,果园里找虫害、茶园里找枯叶,都是小物体扎堆的活儿,多尺度算是正好对症。
4.v4和v5:训练技巧的大丰收
到了v4和v5这两代,作者换了人,路数也变了。这两代主干结构其实没有颠覆性的创新,做的是另一件事,就是把训练目标检测能用上的技巧统统梳理一遍,能塞的都塞进去。Mosaic数据增强(把四张图拼成一张来训练,让模型一次见到更多样的场景)、新的损失函数(比如CIoU Loss,一种专门衡量两个框贴合程度的改进版损失)、各种学习率调度的花样,林林总总加起来有好几十项。这些技巧一个个试、一个个验证,最后留下一批真正管用的,全部叠到一起。
这种做法给人的启发是,深度学习有时候拼的不只是结构上的灵感,还有工程上的耐心。每一个小技巧单独看可能只涨零点几个点的精度,但全部叠起来,整体就能上一个大台阶。我记得有个篮球运动员叫科比,谈过凌晨四点的洛杉矶这件事,说的就是日复一日把基本功练扎实。这种把小事反复做好的劲头,和YOLO这两代把训练技巧一点点堆上去,其实是一个道理。v5还特别强调易用性,作者把代码工程做得格外干净,Python接口几行就能跑起来,这也是后来v5在工业界用得特别多的一个原因。说起来这也算一种隐形的能力,把好用做到极致,本身就能让一个框架走得很远。
5.v6到v8:往工程化继续走
v6、v7、v8这几代,主线大概是工程化和效率。v6、v7各自提出了不同的结构改进,比如重新设计了主干网络里下采样的方式,或者换上更高效的检测头(检测头就是网络末端负责吐出框和类别的那一段)。v8则是Ultralytics公司(这家公司一直维护着YOLO的官方实现)在2023年推出的版本,工程接口做得非常统一,目标检测、实例分割、图像分类、姿态估计这些任务都用同一套接口来调用。
这几代还有一个共同的趋势,就是在工业部署上特别下功夫。YOLO家族从一开始就和实时场景绑得很紧,而实时场景往往意味着要在边缘设备上跑(比如摄像头里那种算力有限的小芯片),所以推理速度和模型大小一直是被反复打磨的两件事。我之前翻过一本讲嵌入式AI的书,里头反复强调一个观点,能在服务器上跑得漂亮的模型不算本事,能在一颗几十块钱的芯片上稳稳跑下来的才是真功夫。YOLO这几代大概也是这种心态,一边压着模型大小,一边抠着每一毫秒的延迟。
6.v9和v10:信息和后处理
v9和v10又把视野拉回到了算法本身。v9关心的是信息在网络里流动时会不会丢。它提出了一种叫可编程梯度信息(PGI)的机制,大意是在训练时给浅层补充一些原本容易被深网络丢掉的信息,让梯度信号更完整。这件事说起来有点抽象,打个比方,就像传话游戏里,传到最后那个人常常把原话改得面目全非,PGI相当于给中间每一棒都贴了一张原话的小纸条,让信息不至于走样。v9同时还提出了一个新的主干结构叫GELAN,主打让梯度在不同层之间传得更顺,这两件事合起来,让模型在高精度区间还能再往上挪一截。
v10则做了一个更大胆的改动,直接去掉了NMS。NMS全叫非极大值抑制,是目标检测里常用的一种后处理步骤,4.3 节我们提过它的脾气。一个物体常常会被网络预测出好几个高度重叠的框,NMS做的事情就是把那些重叠太多的框里得分低的去掉,只留最好的一个。NMS的毛病是它本身是个串行的过程,速度有上限,而且要调一个叫IoU阈值的超参(IoU也就是两个框的交并比,反映它们重叠程度的一个数,阈值就是判断要不要删框的那条分界线)。v10通过改网络结构和训练方式,让模型预测的框彼此之间天然就不那么挤,从而不需要再做NMS也能给出干净的输出,端到端的速度又往前提了一截。
7.v11往后:继续打磨结构和效率
到了2024、2025这两年,又陆续出了v11、v12乃至更往后的新版本。这一阶段的特点大概是在主干里引入了注意力机制(还记得我们在3.7 节聊过的那种让网络自己学会看哪里重要的机制),或者把主干换成更新一些的结构,比如把3.8 节聊过的Transformer那一系思路和YOLO结合到一起。改进的方向依然集中在两个老朋友身上,一是更准,二是更快。
YOLO家族走到这一代,已经是一个相当成熟的体系了。每隔一段时间就有人提新版本,结构上的灵感在不同版本之间互相借鉴,社区里也形成了一套稳定的评测和对比方式。说起来这种迭代节奏本身,对一个技术家族的生命力来说是件好事,比起一次性的惊艳突破,细水长流的稳步推进往往走得更远。一阶段检测这条路,也被一代代作者踩得越来越宽。
8.速度和精度:始终在找平衡
把上面这些代版本放到一起看,会发现YOLO家族一直在做同一件事,就是在速度和精度这两头之间反复找平衡。每一代都两头兼顾,在精度和速度上各自往前挪一点点。为了让一份代码能适配不同的算力场景,作者们一般会同时发布好几个尺寸的版本,常见的有小(small)、中(medium)、大(large)、超大(extra large)这几档。小尺寸的版本参数少、跑得快,适合塞进手机或者摄像头芯片里。大尺寸的版本参数多、精度高,适合在带显卡的服务器上跑那些对精度要求高的任务。这件事用FLOPs来衡量最直观,FLOPs表示浮点运算次数,是衡量模型计算量的常用单位。一个YOLO小尺寸版本的FLOPs大概在几十亿的量级,大尺寸版本则可能到几千亿,差出几十倍。说穿了,这就是给用户一张菜单,让他根据自己的算力预算和精度要求,挑一个最划算的搭配。
举几个实际的搭配例子吧。自动驾驶的感知模块对延迟特别敏感,一辆车高速开过来,晚100毫秒检测到可能就刹不住了,这时候一般会优先选小或中尺寸的版本,把延迟压到最低。安防监控要长时间挂着跑,对稳定性要求高,但场景相对固定,中尺寸版本通常就够用。工业质检比如手机屏幕缺陷检测,缺陷特别细微,对精度要求高,往往会用大尺寸版本。农业病虫害识别,像前面说的草莓病斑,要兼顾到田间设备的算力,一般也是中尺寸版本。医学影像里自动定位CT片上的可疑结节,则要根据医院的服务器配置来选,算力够就上大尺寸求精度,算力紧就用中尺寸求平衡。每种场景都有自己的脾气,没有一刀切的最优解。
9.小结
YOLO家族从v1开始,一路走到今天,主线其实就一条,把一阶段检测这件事不断打磨。从最开始的回归思路,到anchor和多尺度,再到训练技巧的堆叠、工程接口的统一、信息和后处理的改进,每一步都解决了一个具体的痛点。它在精度上确实不如R-CNN那一家子来得细致,但胜在性价比高,换来了实时场景里不可替代的位置。说起来做技术大概就是这样,未必追求每件事都做到极致,找到那个被实际场景认可的平衡点,往往就能走得很远。
今天就先到这儿,下一篇我们聊聊DETR那一支,看Transformer是怎么把检测做成端到端的,4.6 节见。
练习
Q1. YOLO 相比两阶段的 R-CNN 家族,检测思路换了什么路子?为什么能快那么多?
YOLO 是一阶段检测,把检测当成回归问题:不分两步,让网络直接从原图一次性回归出所有边界框和类别,前向传播跑一遍输出端就吐出整张图所有物体的位置和类别。没有了耗时的候选区域生成步骤,整条路走到底,所以能跑到每秒几十上百帧,实时场景才有合适工具。
Q2. YOLO v1 取 、、,最终输出张量的形状是多少?每个网格的输出维度是怎么来的?
输出形状是 。每个网格预测 个边界框,每个框占 5 个数(、、、 和置信度),共 个;再加上 个类别条件概率,所以每个网格输出维度是 ,整张图就是 。
Q3. 早期 YOLO v1 为什么对小物体特别不友好?v3 又是怎么改善的?
因为网格分得粗,一个网格只能好好负责一个物体,几个小物体挤在一个网格里就照顾不过来,常漏检或框不准。v3 引入了多尺度检测,在好几种不同大小的特征图上分别做检测:大物体在分辨率低(感受野大)的特征图上看,小物体在分辨率高(细节保留多)的特征图上看,小物体检测因此明显改善,YOLO 家族在精度上才算真正立住。
Q4.(面试题) 请说说 YOLO 家族从 v1 到 v10,每一代主要解决了什么痛点。它一直在速度和精度之间做什么权衡?
主线是把一阶段检测不断打磨:v1 打开一阶段这条路(回归思路、画网格),但小物体不友好、精度差;v2 引入 anchor 和更好的主干,预测变成相对 anchor 的偏移;v3 加多尺度检测改善小物体、换上 Darknet-53;v4、v5 把训练技巧(Mosaic 增强、CIoU Loss 等)系统堆叠,v5 还强调易用性;v6 到 v8 走工程化和效率,v8 用统一接口覆盖检测、分割、分类、姿态;v9 用可编程梯度信息(PGI)防信息丢失、提出 GELAN 主干;v10 大胆去掉 NMS,让框彼此天然不挤、端到端更快。每一代都在速度和精度两头反复找平衡,并发布 small/medium/large/extra large 多档尺寸,让用户按算力预算和精度要求挑搭配(如自动驾驶优先小/中尺寸压延迟,工业质检用大尺寸求精度)。