
计算机视觉这两年火得不行但很多人一上来就掉进“调包训练”的坑里玩了一个月连目标检测到底在解决什么问题都说不清楚。目标检测是计算机视觉最核心的方向之一既要“找到目标在哪”又要“判断目标是什么”比图像分类难了一个维度。这篇文章是把目标检测的底子给你捋清楚从问题定义、经典流派到核心组件、训练实操适合刚入门深度学习但被各种术语绕晕的开发者也适合做了几年工程但一直没系统梳理过检测技术演进的从业者。读完你会明白R-CNN、YOLO这些名字背后的设计逻辑以及为什么检测模型训练起来比分类模型麻烦那么多。1. 目标检测到底在解决什么问题1.1 检测任务的数学表达与物理意义目标检测的一句话定义是给定一张图像找出其中所有属于预定类别集合的目标实例并为每个实例输出一个边界框Bounding Box和对应的类别标签。数学上一张输入图像可以表示为 (I \in \mathbb{R}^{H \times W \times 3})检测模型要输出一个集合[ D {(b_i, c_i, s_i)}_{i1}^{N} ]其中 (b_i (x_{min}, y_{min}, x_{max}, y_{max})) 是目标的边界框坐标(c_i) 是类别编号(s_i) 是置信度分数。听起来很简单但真正做起来麻烦得很。这里面有两个核心难点第一个难点是“尺度问题”。同一类目标在图像里可能大得占满整个画面也可能小到只有十几个像素。比如行人检测站在镜头前的人和远处的人尺寸可能差几十倍。模型要同时对这两种极端尺度保持敏感这本身就是个很大的挑战。第二个难点是“密度问题”。目标可能密集排列、互相遮挡比如拥挤的地铁车厢里的人群每个目标只有头部一小块露出来。模型不仅要找到每个目标还要把它们区分开不能把两个人框到一起。还有第三个麻烦的地方一张图像里目标的“个数”是不固定的。这就是检测和分类的另一个本质区别——分类的输出维度是固定的就那几个类别检测输出的是不定长集合。你想想全连接层往那一放输出向量的长度是固定的但目标数量是变化的这中间的矛盾就需要特殊设计来解决。也正因为这个不定长输出的特性检测模型的结构设计远比分类网络复杂跑通一个训练实验的时间和算力成本也高得多。1.2 困难的根源坐标回归与不确定输出长度既然检测难那到底难在什么地方我拆开说。坐标回归的本质是输出四个连续数值把目标的真实位置用浮点数框出来。这比分类里的离散标签要麻烦得多。分类输出的是一个概率分布比如“是猫的概率0.9是狗的概率0.1”梯度更新方向是明确的。但回归输出的四个坐标每一个都需要和真实坐标做距离计算这个距离函数怎么定义、量纲怎么统一、不同尺度的目标怎么归一化全是坑。再说输出长度不定。训练时你标注了一张图里面有5个目标另一张图里有10个目标模型输出该是5×5张量还是10×5张量这个问题从R-CNN时代就开始用各种间接方式规避先框出一堆候选区域再逐个判断是不是目标最后再做坐标修正绕了一大圈。直到后来的DETR这种基于Transformer的方法才算是从架构层面上正面解决了“集合预测”这个问题。还有一个细节容易被忽略边界框的真实坐标是绝对像素值还是归一化后的相对值用得最多的是除以图像宽高做归一化。为什么为了对不同分辨率的输入具有一致性。要是图像是1280×720一个目标在x方向占200个像素换算成归一化坐标就是200/1280≈0.156。模型直接去回归这个0到1之间的小数数值范围稳定损失函数也好设计。1.3 评价指标体系的核心逻辑搞检测的人见面第一句话就是问“你mAP多少”。mAPmean Average Precision是检测领域最核心的评价指标但很多人只知道这个名字不清楚它背后的统计意义。先拆解一步AP是某个类别上的平均精度。简单来说模型输出了一堆带置信度分数的框从高到低排序逐个判断这个框和某个真实框的IoUIntersection over Union是否大于某个阈值通常是0.5是就是TPTrue Positive不是就是FPFalse Positive。把Precision和Recall的关系画成一条曲线曲线下的面积就是AP。mAP就是把所有类别的AP取平均。关键点在于mAP是“排序”敏感的指标。它默认你会按置信度从高到低输出检测结果而且希望置信度高的框确实更准。这也是为什么模型最后一层输出的分数非常关键——它不只是“判断对不对”还要“排得出先后”。很多人训练时发现loss已经很低了但是mAP提不上去就是因为置信度排序质量差高分框反而落在错误位置上。顺便说一下mAP一般会标“0.5”或“0.5:0.95”之类的后缀。mAP0.5表示IoU阈值取0.5mAP0.5:0.95表示把IoU阈值从0.5到0.95每隔0.05取一个共10个阈值算平均。后者是更严格的指标对框的定位精度要求高得多。做工程落地时mAP0.5足够用了但写论文、刷榜单基本都是mAP0.5:0.95的天下。2. 传统方法与深度学习之前的日子2.1 滑动窗口与手工特征时代目标检测不是深度学习火了以后才有的20年前就有大量的研究和工程实践。只是那个时代受限于算力和特征表达检测算法的思路非常“朴素”。传统方法的经典套路是这样的用固定尺寸的窗口从头到尾扫过整张图像每个位置都提取一个特征向量然后丢进分类器判断窗口里有没有目标如果有输出目标类别和框位置。这招叫滑动窗口检测听起来简单粗暴实际上也确实是暴力枚举。特征方面最出名的是HOGHistogram of Oriented Gradients方向梯度直方图特征。它的核心思想是把图像分成小格子统计每个格子里梯度的方向分布形成直方图。这个特征对局部形状和纹理信息有较强的表达能力尤其是对行人这种竖直状态的目标非常有效所以当年在行人检测上表现优秀。紧接着有人把块状特征进一步扩展搞出了DPMDeformable Part Model可变部件模型。DPM的思路很有意思一个目标由若干个部件组成比如人由头、躯干、四肢组成每个部件单独检测再把部件之间相对位置的约束关系也建模出来综合判断整个目标是不是存在。这个思路本质上就是后来的Attention机制的雏形或者说像极了把整体问题拆分成子任务再综合决策的过程。但滑动窗口的致命弱点显而易见计算量巨大。一张图里有成千上万个窗口位置每个位置都要提取一次特征再跑一次分类器。而且固定尺寸的窗口天然处理不了多尺度问题你只能跑金字塔多尺度变换也就是把图像缩放成多个分辨率每个分辨率都滑一遍窗口计算量再翻好几倍。那时候我的个人体会是训练一个模型要跑一整夜推理一张图也要秒级离实时差得远。2.2 selective search 与创新思路滑动窗口不行就有人想为什么非要穷举所有窗口能不能先选出“可能含目标的区域”再只对这些区域跑检测这个想法催生了Region Proposal区域提议这个概念其中最火的算法是selective search。selective search的思路是用图像分割的办法先把像素聚类成小的超像素块再逐步融合相似的相邻块形成一系列由小到大的候选区域。通过这种方式候选框的数量从滑动窗口的上万个缩减到两三千个计算压力减轻不少。而且这些候选框往往符合目标的真实轮廓比固定网格的滑动窗口更贴近语义边界。我记得那时候业内讨论最多的问题不是“特征怎么设计”而是“怎么减少候选区的数量同时不丢召回”。selective search给出的答案是用颜色、纹理、大小、形状五种相似度指标融合来做层次化分组相关信息保留得比较完整但速度依然很慢——一张图提取候选区域往往要一两秒。今天回看这一段历史可以说没有selective search的铺垫就没有后来的R-CNN系列。但传统方法终究是走到头了瓶颈卡在“特征表达能力”上——不管怎么设计特征提取器都很难覆盖各种光照、遮挡、姿态变化。深度学习的出现直接把特征编码交给了卷积网络来自动学习这才真正掀翻了整个检测领域的玩法。3. 两阶段流派从R-CNN到Faster R-CNN3.1 R-CNN的开创性思路与笨重代价2014年R-CNN的出现给检测领域扔下了一颗炸弹。它做的事情现在看很简单先跑selective search提取约2000个候选区域然后把每个候选区域裁剪出来缩放到固定尺寸扔进预训练好的卷积网络比如AlexNet再然后VGG提取特征最后用SVM分类器判断类别。就这么一套组合拳在Pascal VOC数据集上把mAP提升到了前所未有的高度跟传统方法拉开了巨大的差距。但R-CNN的缺陷也非常明显2000个候选区域要分别过一遍卷积网络推理一张图像要几十秒。训练还要多阶段分开做先训特征提取网络再训SVM分类器最后训回归器每个环节还要保存中间特征磁盘占用大得吓人。当时有人吐槽它“像一台笨重的机器虽然功率大但跑起来浑身发抖”。3.2 Fast R-CNN与Faster R-CNN的效率革命R-CNN系列真正走上正轨是Fast R-CNN用了一个非常巧妙的架构调整不再把每个候选区域单独送进卷积网络而是先把整张图过一遍卷积主干得到一个共享的特征图再对特征图上的候选区域做ROI Pooling把不同尺寸的区域池化到固定尺寸的特征输出最后统一接全连接分类和回归。这样一来卷积计算只做一次共享计算推理速度大幅提升。再往后Faster R-CNN甚至把候选区域提取也放进了网络结构里引入RPNRegion Proposal Network区域提议网络这样一个轻量级子网络直接在卷积特征图上生成候选区域。这次改动不仅是速度提升更重要的是整个训练流程从多阶段变成了端到端真正意义上完成了“检测基础网络候选区域提取分类回归”的架构统一。两阶段流派的优势在哪里精度高。候选区域筛选相当于给模型一个“先粗筛后精细”的机会分类和回归都在高质量的候选区域上进行类别不平衡问题天然缓解。缺点是速度上不去即使到了Faster R-CNN时代在GPU上推理一张1080P图像也需要100毫秒以上离视频实时检测还有不小的距离。3.3 锚点机制的原理与影响讲到Faster R-CNN就绕不开锚点anchor这个概念它也是整个深度学习检测体系中最核心的设计之一。锚点是什么简单来说就是预先设定的一组不同大小、不同长宽比的参考框。比如在特征图的每个位置放9个锚点框长宽比有1:1、1:2、2:1三种面积有128²、256²、512²三种。检测模型的任务不是直接回归目标的绝对坐标而是预测目标相对于这些锚点框的偏移量。这个把“绝对预测”转成“相对预测”的设计高明在哪里目标尺寸分布太广、绝对坐标范围太大直接回归难度高锚点相当于把搜索空间缩小到了“在这附近微调”的程度回归起来容易得多。锚点数量怎么定这个说实话很多时候是经验值加调参。锚点数太多正负样本不平衡会更严重训练速度也被拖慢锚点数太少召回率容易掉下来。我在实际项目里通常这样取值先统计标注数据里所有目标的宽高分布然后做一次聚类分析看看目标尺寸集中在哪几档再根据聚类中心去定制锚点框。比如某个项目里目标普遍偏小我就把面积档位调低把128改为64效果立刻好了不少。这招在很多检测框架里都支持手动配置锚点值得每个做检测的人熟练掌握。4. 单阶段流派YOLO的进化与关键设计4.1 YOLO把检测变成单次回归问题两阶段流派虽然精度高但速度一直被诟病。YOLOYou Only Look Once的诞生就是要彻底解决这个问题把检测当成一个单次回归问题输入图像输出边界框坐标和类别概率一气呵成。第一版YOLO的做法是把图像划分成S×S的网格每个网格负责预测固定数量的边界框以及这些框的置信度和类别概率。整个模型就是一个卷积网络从输入到输出直接映射没有任何候选区域筛选的中间步骤。速度真的能跑到实时一张图像几毫秒到几十毫秒搞定。但初代YOLO的问题也是明摆着的每个网格只能预测两个框S通常是7对密集小目标效果很差网格划分是固定的目标恰好横跨网格边界时就找不到语义中心加上没有多尺度机制小目标召回率低得可怜。我当时拿YOLOv1跑VOC数据集看到漏检率和小目标错检率真的有点尴尬。4.2 YOLOv3的架构分水岭YOLOv3是个特别重要的节点很多人就是从这里开始接触YOLO系列的。为什么说它是分水岭因为v3从底层架构上做了三件大事。第一引入了多尺度预测。v3在三个不同尺度的特征图上分别做检测大的特征图对应小目标小的特征图对应大目标。这个设计解决了单尺度对尺度变化敏感的固有问题也是后面几乎所有检测框架的标准配置。第二用了FPNFeature Pyramid Network特征金字塔网络那种自顶向下的路径融合方式把深层语义信息逐层回传到浅层特征上让浅层特征图不仅“看得细”还“懂得多”。小目标的检测能力大幅提升。第三在主干网络上选择了Darknet-53去掉了很多池化层改用步长为2的卷积来做下采样。这样做的好处是特征信息保留更好梯度传播路径更通畅训练更稳。YOLOv3的损失函数也值得一提它把损失分成三块坐标回归损失、置信度损失、类别损失。置信度损失用的是二分类交叉熵判断“这个格子里面到底有没有目标”类别损失用多标签交叉熵有的框架用softmax但v3是sigmoid因为一个框可能同时属于多个类别语义比如“人”和“行人”。YOLOv3的论文里有一个细节我特别喜欢就是它指出边框坐标的回归目标应该用sigmoid函数压缩到0到1之间直接预测网格内的相对位置而不是预测任意浮点数。原始YOLO直接回归坐标导致前期预测值可能跑到离网格很远的地方训练不稳定v3用sigmoid套了一层相当于给回归加了边界约束实操中确实发现训练更稳。4.3 Anchor-Free潮流与后续演进YOLOv3之后检测领域出现了一个很重要的思想转向anchor-free。之前所有人都默认锚点是必需品但一批工作开始质疑能不能不定义锚点直接让模型输出目标中心点和尺寸比较有代表性的方向是角点检测和中心点检测。比如有些方法把目标框拆成左上角和右下角的角点用两个分支去预测角点热力图再配对成框。CSPDarknet之后的YOLOv4、YOLOv5其实是把锚点机制的细节打磨到了极致——包括用聚类方式自动设计锚点尺寸、在损失函数里加CIoU Loss来优化边框的几何相似度等。到了YOLOX时代干脆转向anchor-free把解耦头拿去只保留回归中心和尺寸两个分支推理速度又提了一档。之后的YOLOv6、v7、v8等版本更多是围绕训练策略改进比如引入更优的标签分配策略、强化的数据增强Mosaic、MixUp等以及更高效的主干网络设计。从工程落地来看YOLO系列仍然是性价比最高的选择因为它只需一个模型文件不经任何后处理优化在GPU上就能轻松实时跑1080P视频。5. 训练自己的检测模型先过这几道坎5.1 数据标注与格式转换说到训练检测模型第一步永远是数据。检测任务比分类任务对数据的要求高好几个量级因为你不仅要知道“有什么”还要标注“在哪”。数据标注的格式五花八门Pascal VOC用XML文件COCO用JSONYOLO系列用txt文件每行是“类别 中心x 中心y 宽 高”的格式坐标相对图像的宽高做了归一化。刚开始做检测项目的人最常见的坑在标注工具里标的是像素坐标转换成YOLO格式时忘了归一化结果训练出来的模型输出框全跑偏。另外标注的时候目标边缘要不要紧贴轮廓我的经验是紧贴轮廓更利于模型学习到目标的真实几何信息但也不建议贴得太紧——稍微留出1%到2%的余量让框的边界处包含一点点背景上下文模型对边界位置的回归会更平稳。还有一个数据层面的问题类别不平衡。在目标检测里有的类别特别多有的类别只出现几次。训练时模型很容易偏向样本量大的类别小类别的召回率极低。处理方式有几个对图像做重复采样增强、对少数类做针对性数据增强例如多裁剪、多旋转、或者调整损失函数里不同类别的权重。不要指望靠一堆技巧一步到位先观察模型在验证集上的每类AP其实就是看每一类的mAP哪个类别低就优先处理哪个。5.2 anchors的选择与学习率、batch size的规律检测模型训练里最容易被忽视但影响极大的两块锚点设置和超参数选择。锚点这块前面提过可以聚类得到。具体说下怎么操作把训练标注里所有真实框的宽高取出来做k-means聚类k通常是9聚类中心就是9组锚点尺寸。值得注意的是聚类用的距离函数不能是欧氏距离而要用IoU的补数即希望锚点框与真实框的IoU尽可能大。手工指定的锚点框和聚类得到的锚点框相比在mAP上的差异可以达到两三个点。学习率这一块也比分类任务更敏感。检测模型一般训练迭代轮数多当batch size取16、输入尺寸640×640时初始学习率设定在0.01到0.02之间比较安全。但关键是预热warm-up阶段前几百步把学习率从很小的值逐步拉升到目标值避免前期因为学习率太高导致模型震荡甚至发散。这个技巧我在实际使用中觉得比什么都重要尤其是用大规模预训练权重起步的时候。梯度裁剪也是一道安全网。检测头部分损失函数数值可能忽大忽小特别是早期阶段锚点和特征匹配不稳定时clip_norm设个10按梯度全局范数裁剪能避免偶发的loss突爆。很多教程不讲这个但我在跑第一个检测模型时确实被loss突然变成nan坑过一次开了梯度裁剪就再没出现过。5.3 标签分配与损失函数训练中的隐藏战场检测模型训练的另一大核心是标签分配label assignment——也就是每个预测框到底“负责”哪个真实框。如果分配错了损失函数再先进也白搭。早期的方法是IoU阈值匹配预测框和某个真实框的IoU 0.5就认为这个预测框负责该真实框。双阶段模型配合RPN可以这样玩因为候选框质量高。单阶段模型如果没有锚点筛选这样分配会导致大量难例因为大量预测框根本落不到真实框附近。后来YOLOv5时代开始流行一种动态分配策略对于一个真实框不再只看单个IoU阈值而是把与其IoU较高的前k个预测框都分配给它再通过模型自身的置信度来加权计算损失。这种“先粗筛再精调”的动态分配机制让正样本数量在训练过程中自动调整对不同尺度和形状的目标适应能力更强。损失函数选得好不好影响也很明显。我自己常用的组合是CIoU Loss边界框回归 BCE二分类置信度 BCE多标签分类。CIoU Loss相比老旧的Smooth L1 Loss考虑了重叠面积、中心点距离、宽高比例三个维度回归精度提升不少。但使用CIoU时要注意如果训练数据里目标宽高比特别极端比如宽高比超过10的条状目标CIoU的反向传播容易不稳定这时可以退回IoU Loss或GIoU Loss多试几个版本再定型。5.4 过拟合与欠拟合的现实表现检测模型过拟合和分类模型不一样它通常不会显示“训练loss一直降、验证loss先降后升”那么干净的信号——验证集的mAP可能前期上升、后期甚至停滞或微降但看起来loss表现还挺好。我遇到过的最典型情况是训练集mAP已经到80了验证集mAP才55而且验证集上的框要么多检了预测了很多背景框要么漏检某些目标完全没预测出来。这些基本就是过拟合的信号了。此时先别慌优先级依次是加大数据增强特别是随机翻转、随机尺度缩放、色彩抖动这类能增加样本多样性的操作降低模型复杂度比如把主干网络从最大规模换成轻量版加早停机制在验证mAP连续10轮不涨时触发。欠拟合的表现往往是验证集和训练集mAP都低此时说明模型容量不足或训练时间不够。优先加训练周期其次增大模型容量而不是盲目调学习率。6. 常用工具选型与实战路径选择6.1 主流框架速览与选型建议现在做目标检测常用的框架基本是这几类Ultralytics的YOLO系列、MMDetection、Detectron2以及用来做自定义实验的PaddleDetection。Ultralytics YOLO走得最远官方仓库开箱即用命令行几行代码就能训练、验证、导出模型老本行也延续得好。如果目标是快速验证一个点子或者做工程项目落地我会优先选这个因为它的默认配置在大多数情况下都足够好自动锚点聚类、自动数据增强策略、自适应灰度处理、多尺度训练都有内置。MMDetection的特点是模块化做得极致可以方便地把不同主干、不同检测头、不同标签分配策略组合在一起。论文复现重度用户会喜欢它但它配置复杂依赖版本众多新手配置环境时很容易卡住。我的建议是如果你想深入研究检测算法、对比不同模块的优劣MMDetection再合适不过如果只想快速解决一个实际业务问题别在MMDetection上浪费时间。Detectron2是另一个面向研究的框架用起来也重但我发现它的可视化工具做得很好——可以把预测框、特征图、热力图一步到位画出来做算法调试非常方便。PaddleDetection在国内生态里做得最完备针对国产硬件和国产深度学习框架做了大量适配部署到CPU、移动端都很顺手。如果生产环境是国产化栈这个框架是最平滑的选择。6.2 端到端的实践路径建议如果你是第一次做目标检测项目我给你一条最稳妥的路径第一步用Ultralytics YOLO跑通一个最小样例。选COCO128这个小子集跑5到10轮训练目标只有一个把环境搭建、数据加载、训练循环、模型导出这整条链路彻底跑通。第二步对标自己的业务数据。准备好标注数据后用YOLO自带的自动聚类设置锚点关闭预训练权重直接把模型训练到收敛得到第一版性能基线。不要上来就用预训练模型加冻层训练这样能帮助你判断数据量和任务复杂度。第三步优化性能。检查各类AP如果小目标AP偏低就加Mosaic增强、增大输入分辨率如果定位精度差就换CIoU或引入辅助训练分支。最后一步部署。打包成ONNX格式或者转成TensorRT测一下推理延迟和内存占用。工业生产一定要关注这一环模型在离线评测里的mAP再高上不了线都是白搭。我的经验是整个流程走完一个中等规模数据几千张图的项目大概需要一到两周。其中数据清洗和标注占掉六成时间训练调参反而比较快。别一上来就想搞创新模型结构先把基线跑通后面再慢慢迭代才有参照系。7. 常见问题与排查技巧实录7.1 训练不收敛的排查清单训练过程中loss一直不降或者直接变成nan是检测项目最高频且最磨人的问题。我把常见原因和排查顺序整理成了一张表你按顺序检查基本能定位现象可能原因排查优先级loss前几步就nan学习率过高、梯度爆炸先设小lr0.0001复训一次loss卡住完全不降标签分配有误、类别编号错乱可视化验证loss看类别标签是否正确训练loss和验证loss同时很高数据预处理与标注格式不匹配检查是否做了归一化、坐标是否越界过拟合早期出现训练好验证差数据量不足、增强不够加大Mosaic、随机翻转等增强强度收敛了但单类AP特别低样本数量太少、目标框太小针对性补充数据或做裁剪增强关于nan还有一个隐蔽原因锚点框宽高出现0或负值。训练数据里如果有标注错误的框比如宽高为0或坐标端点反了回归目标就会异常梯度自然出问题。标注清洗这步真的不能省。7.2 推理阶段的典型问题模型训练好了推理阶段也会遇到新问题。第一个问题一张图输出几百个框重叠严重。这是NMS非极大值抑制阈值设置不合理导致。NMS的核心逻辑是按置信度排序选中最高分框把所有与它IoU大于阈值的框抑制掉再继续从剩下框里选最高分的。阈值取0.4到0.6之间比较常见。阈值取太小0.2同类的不同目标挨得近时容易被一起抑制掉导致漏检取太大0.8重叠框太多视觉上新检出很多重复框。调NMS阈值是最快的“收益手段”之一。第二个问题小目标完全检测不到但大目标正常。这种情况大概率不是模型问题而是输入尺寸太小。YOLOv5默认输入640×640小目标在这么小的分辨率里可能只占十几个像素特征都丢了。加大输入尺寸比如1280×1280开启Mosaic增强往往立竿见影。第三个问题模型在一张图上能跑通但输入视频流时偶尔卡顿或内存涨。这还是推理管线问题一般是没启用批处理、没做预处理缓存。视频检测最好用流式处理把每帧预处理做成队列别在检测循环里做大量numpy运算。说实话这些排查经验都是在项目里被喷出来的坑攒出来的。测下来的感受是80%的训练问题出在数据上15%出在超参数只有5%是模型结构本身的问题。数据没搞干净再好的网络也白搭。关于目标检测的“概览上篇”我最后再分享一点个人心得做检测项目最忌讳的是“只见树木不见森林”。一上来就扎进某个框架的调参细节里被各种API牵着鼻子走结果连模型为什么这样设计都没搞懂。你如果能理解这个领域的演进逻辑——从滑动窗口到R-CNN再到YOLO——可能比多调出几个点mAP更有价值因为后者换个数据集就失效前者却能让你在面对新任务时做出正确的方案选择。下一篇再聊检测模型的部署优化与实战数据调优到时候把端侧加速、剪枝量化和失败案例分析一并展开。