ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

车辆类型检测实战:YOLO数据集全流程构建与避坑指南

车辆类型检测实战:YOLO数据集全流程构建与避坑指南

做车辆类型检测这个题目,动手之后你会发现,YOLO模型本身跑通根本不难,难的是手里的数据集能不能撑起业务场景。同样一套YOLO权重,喂给它精准标注、场景均衡的车辆类型数据集,和喂给它一堆随手扒来的杂图,最终mAP能差出一大截。我前后跟过几个车辆检测类项目,包括某停车场的车型识别、某物流园区的入口车辆分类,最深的体会就是:数据集才是整个项目的隐形地基。这篇就专门围绕“YOLO数据集+车辆类型检测”这条线,从数据规划、采集清洗、标注质检,到训练调参和实测排错,把完整链路和踩过的坑一次说清楚,适合正准备做车辆识别、想自建数据集又不太确定从哪下手的同学参考。

1. 车辆类型检测为什么要“自己造”数据集

很多人第一步就卡在“用什么数据集”。网上确实能搜到不少现成的车辆数据集,但真拿过来做业务时会发现,公开数据和实际需求之间总是隔着一层。

1.1 公开数据集为什么总是“差一口”

公开的通用目标检测数据集里通常有car、truck、bus、motorcycle这几个大类,看起来够用了,但实际业务要的分法根本不是这样。比如停车场收费系统要区分“轿车、SUV、MPV、面包车”,某些园区进出管理要识别“小轿车、轻型货车、中型货车、重型半挂”,公路卡口还可能要区分“皮卡、微面、渣土车、混凝土搅拌车”。公开数据集给不了这么细的类别,强行靠一个大类“car”去做下游业务,等于把难度全甩给了后端的规则逻辑,模型根本扛不住。

另外还有一个容易忽视的问题:公开数据大多是国外场景采集的,车型比例、拍摄角度、路侧环境都和本地路况对不上。用这种数据训练出来的模型,放到本地路口一测,recall很容易掉。我的建议是:公开数据可以当“启蒙教材”用来跑通流程,但正式做项目,一定要在它基础上补充自采数据、重新定义类别,也就是走“半自建”的路线。

1.2 跑通第一版模型到底需要多少数据

这里先给出一个可以落地的估算参考。假设你的项目目标是8个车辆类型,每种类型平均要有2000到3000个标注框,模型才勉强算“见过世面”。按每张图平均出现1.5到2个目标框来算,一个类型大约需要1500张有效图片。8个类型加起来,图片总量在1.2万到2万张之间。

具体数量还要看场景复杂度,我做过的项目大致是这样一个分布:

场景类型目标类别数建议图片量建议标注框量
固定卡口/单一停车场视角5-8类8000-15000张1.5万-3万个
城市道路随动视角8-12类2万-3万张3万-5万个
园区/封闭道路监控视角6-10类1万-2万张2万-4万个

这个数量不是拍脑袋定的。太少,模型容易过拟合,换一个没见过的角度就失灵;太多,标注成本直线上升,而且边际收益会递减。先按表里的下限准备,跑出第一版模型,再用误差分析决定要不要补数据,是最经济的方式。

1.3 车辆分类体系:定粗了没用,定细了要命

分类体系的设计是数据集构建里最容易被低估的一环。分得太粗,比如只有“小汽车”和“大卡车”两类,业务规则根本没法用;分得太细,比如要精确到某品牌某型号,不仅标注员会疯掉,YOLO也很难收敛,因为同类车的不同角度差异可能比不同车型之间的差异还大。

我比较推荐的做法是“先大类、后小类、留余量”。先把车辆按用途和外形分成几个稳定的大类,比如轿车、SUV、MPV/面包车、皮卡、轻型货车、重型货车、客车、特种车辆,每一类再根据业务需要决定是否细分。分类一旦定下来,尽量不要再动,因为中途改类别意味着旧标注全部作废,这个教训我付出过不小的代价。

2. 原始图片搜集与清洗:决定模型上限的脏活累活

分类体系定好之后,下一步就是找图。这一步又脏又累,但直接决定模型的天花板。我的经验是“公开数据打底、实拍抽帧为主、合成图补缺”,三条腿走路。

2.1 三种图片来源的搭配策略

先说公开数据,它最大的价值是帮你提供一部分背景多样性的基础样本,省去大量拍摄时间。缺点是场景偏差大、目标往往比较大且清晰,当作“保底”可以,不建议超过总量的30%。

主力必须是实拍抽帧。最有效的操作是把摄像头架在目标部署位置附近,录上一两周的高清视频,然后按每2到5秒抽一帧的方式取图。这么做的好处是视角、光照、车流比例都贴近真实场景,模型学到的就是将来部署时看到的东西。我当时在某停车场项目里就是连续录了7天入场口视频,抽了将近1.5万帧,这稿数据让后面的测试集表现非常稳定。

合成图像适合用来补稀缺类别。比如重型半挂、混凝土搅拌车这些相对少见的目标,实拍很难攒够量,可以借助3D车辆模型渲染不同光照和背景的图片凑数。但注意合成图比例要克制,我的安全线是15%,再多模型容易学到渲染特有的一些假特征,在真实照片上反而掉精度。

2.2 清洗阶段必须狠心删掉的五类图

这一环节非常容易被跳过,但跳过的后果都被记在了后面训练账上。我总结了一张“删除清单”:

  • 严重运动模糊的图,目标轮廓已经看不清,标注也是瞎标。
  • 目标太小,一辆车在整张图里只占二三十个像素,模型根本学不到有效特征。
  • 过曝或欠曝的图,比如逆光把车身压成一团黑的。
  • 目标被大面积遮挡、可见部分不足一半的图,这种图标注难度大、学习价值低。
  • 同一辆车在连续帧里的高度重复样本,如果不做去重,等于把某些固定目标反复喂给模型,容易造成过拟合。

不要心疼,删掉这些噪声样本只会让数据集更健康。从我看过的不少数据包来看,清洗后保留70%到80%是正常的,真正精品的部分才值得进入训练集。

2.3 场景覆盖计划表:别让模型只会看白天

很多第一次做车辆检测的人,会把大量注意力放在白天顺光、晴天场景上,结果模型一到晚上就失灵。这不是模型的问题,是数据构成没有按场景做预算。我给自己定过一个经验比例,这里分享出来:

场景维度建议占比
白天顺光/侧光40%-50%
夜间/弱光20%-25%
黄昏/逆光/阴影15%-20%
雨雾等恶劣天气5%-10%

夜间图是最值得提前投入的,因为夜间漏检往往发生在关键业务场景里,比如停车场的夜间收费、园区的夜间安防。如果训练集中夜间图占比不足10%,实测recall大概率要栽跟头。这个比例建议在采集开始前就写进计划表,按目标比例去划分拍摄或挑选时间,而不是等数据攒完了再调整。

3. 标注规范与质量管控:精度刺客藏在这里

标注质量对YOLO数据集的影响,比很多人想象中大得多。框偏了3个像素、类别标错了5%,训练出来可能就换来1到2个点的mAP损失。尤其是车辆类型检测,轿车、SUV、皮卡这些类之间边界模糊,标注稍微不规范,模型就会学得稀里糊涂。

3.1 标注工具与YOLO标签格式

目前YOLO系列在工程里使用度最高的是YOLOv8/YOLOv11这套框架,配套的标注格式也早就标准化了。单张图片对应一个同名txt文件,每行表示一个目标:

class_id center_x center_y width height

其中坐标值都是相对于图片宽高归一化到0-1之间的小数。比如一张640x960的图,一辆车中心点在(320, 480),宽160,高240,对应标签就是类ID 0.5 0.5 0.25 0.25。这个格式初看很别扭,但它是YOLO高效训练的基础,因为省去了运行时解码的负担。

标注工具我常用的是LabelImg,轻量、免费,标注界面直接,支持输出YOLO格式。如果项目多人协作,也可以考虑在线标注平台。选工具的核心标准就两条:能直接导出YOLO格式,能多人分任务并且保留复核记录。

3.2 标注规则细化:遮挡、紧贴、跨栏车怎么标

这里写几条我反复强调的规则,也是最容易踩的坑:

第一,标注框要紧贴目标的“可见部分”,被遮挡的部分不要硬脑补。比如一辆SUV被路牌挡了一半,框只能框住看得见的一半,类别按可辨识的特征来定。第二,不许一个框里塞两个目标,两辆车在画面里哪怕只隔一个像素,也要分两个框。第三,一辆车被柱子分成前后两段,只要看得出是同一辆连着通过,就按一个目标标,框取前后部分的外接整体,这类情况属于“跨栏车”,标注规范必须提前写明。

还有一个容易被忽略的是:类别定义的“判例”。比如皮卡和轻型货车,外形接近,标注员很容易反复横跳。我的做法是写死判例规则:以货斗或货厢是否封闭、驾驶室与货箱的连接方式为准,并且配几张典型示例图放到标注规范文档里。有判例和没判例,标注一致性差别非常大。

3.3 双重质检与预标注流水线

质量管控上,只靠一个标注员闷头标,后面模型肯定会抗议。我的标准流程是“初标+复标+抽检”三层。初标完成后,换一个人对标注结果做全量复核,重点看类别是否正确、框是否紧贴;复核完之后,项目负责人再从每批次随机抽10%到15%检查,抽检一致率低于95%就退回重标。

比较高效的提速手段是“预标注”。先用一个已经训练过的YOLO模型对原始图片跑一遍推理,生成初始框,标注员只需要修正框的位置和确认类别,速度能提升一半以上。我在物流园区的项目里就是用上一版模型去预标注新一代数据,新数据标注时间压缩了近40%,而且框的贴合度明显更稳定。

4. 训练配置与调参思路:让YOLO跑出该有的效果

数据集这块土建工程做完,训练就是真正检验成果的时候。车辆类型检测的训练配置有一些固定套路,照着做基本不会翻车,我会把关键点拆开讲清楚。

4.1 训练框架与预训练权重的选择

现在主流做法是用ultralytics这套YOLO框架,它把数据加载、增强、训练、验证、导出全部打包进了一条命令,对新手非常友好。安装环境的时候建议直接装支持CUDA的PyTorch版本,用GPU训练,CPU训练同一份数据可能要慢几十倍,基本没法做迭代实验。

预训练权重一定要加载官方提供的通用目标检测预训练模型,不要从头训练。原因很简单:通用预训练权重是在海量数据上先学出了基础的纹理、边缘、形状和物体概念,其中已经包含对车辆这类目标的初步认知。在它的基础上微调,相当于一个已经会开车的人去考特定车型的驾照,和从零学起完全是两个量级的事。车辆类型检测和通用检测高度相关,能迁移的知识尤其多。

4.2 一组适合车辆检测的起点参数

训练参数没有绝对最优,但有一套非常适合车辆类型检测的起手参数,我放在下面:

参数推荐值说明
imgsz640速度与精度平衡;远端小目标多时试1280
batch16-32由显存决定,尽量不超过显存的80%
epochs150中途看验证集loss和mAP决定是否提前停
optimizerSGD微调阶段实测比AdamW更稳,泛化更好
lr00.01使用预训练权重微调时的常用起始值

这组参数在我做过的车辆类型任务里表现都比较稳。imgsz是最值得花时间尝试的一项,如果部署端允许更高的分辨率,从640提到960或1280,对远处小目标的召回提升非常明显,代价是训练和推理耗时同步上升。batch一般往大了调,能稳定梯度,但如果显存报错就果断减半。epochs不建议一上来就设300,先150轮跑通,看验证集指标变化再决定是否加跑。

4.3 数据增强开关别照抄默认值

YOLO框架默认开着一堆数据增强,但对车辆类型检测,有几项要专门调整:

mosaic增强(把四张图拼成一张训练)默认开启,它能让模型更鲁棒,但有个副作用:拼图后每个目标的像素占比会被大幅压缩,尤其在训练后期,会影响模型对正常尺度目标的适应。我的习惯是训练最后20轮把mosaic关掉,让模型回归到正常构图下精调。

另外像随机旋转、翻转、HSV颜色扰动这类增强,建议适度开启,不要每种都拉满。车辆是有方向性的,水平翻转没问题,但大角度旋转生成出来的“歪着跑的车”既不符合物理规律,还可能干扰模型对车身比例的学习。颜色扰动幅度也要克制,车辆的类别判断主要依赖外形结构,颜色只是一个弱线索,过度扰动会引入噪声。

4.4 精度指标怎么读:车辆检测更看重哪一项

训练结束后,很多人只盯着一行mAP50,其实不够。我推荐至少看三个指标:mAP50、mAP50-95、recall。mAP50好理解,就是IoU阈值在0.5时各类别AP的平均值,可以作为粗粒度精度的晴雨表。mAP50-95更严格,多个IoU阈值平均,能反映框位置的贴合程度,如果这个值低而mAP50高,基本能说明框偏大偏小、位置不稳。

车辆类型检测的实际业务里,我通常更看重recall。漏检一辆车,后面不管是计费、安防还是流量统计,都会出实际问题;而误检一辆车,往往还能通过前后帧或逻辑规则过滤掉。所以如果recall和precision只能保一个,先保recall,再通过后续策略去压制误检。训练日志里的混淆矩阵也很值得看,它能直接告诉你哪两类车在互相认错,这可是后面针对性补数据的地图。

5. 实测踩坑记录:三类高频问题与针对性修复

再完美的规划也躲不过实测的暴雨。我这里专门挑三个出现频率最高的坑来复盘,它们都来自真实项目经历。

5.1 夜间漏检:数据构成失衡的典型案例

某次在某园区出入口做车辆分类测试,模型白天表现不错,一到晚上recall从0.82直接掉到0.65左右,最直接的损失是夜间几辆货车漏报。一开始我怀疑是模型容量不够,先换了更大的模型试跑,提升非常有限。后来停下来做了个数据统计,发现训练集里夜间图片只占6%,而部署后的实际使用时段里夜间占了将近一半。这属于典型的训练分布和部署分布不一致。

修复办法不是去堆更大的网络,而是补充夜间数据。我把夜间的占比在一周内补到22%,重新训练后夜间recall拉回到了0.76以上。自那以后,我每做车辆类型项目,都会先画一张“场景构成表”,按部署实际情况规划白天、夜间、黄昏、雨天的图片比例,这个习惯帮我省掉了后面大量反复返工的麻烦。

5.2 中型货车被识别成轿车:类别定义模糊的连锁反应

另一个项目里,模型总是把远处的中型货车给成“轿车”类别,混淆矩阵里这两个类的交叉项特别刺眼。第一反应是数据量不够,于是又加大货车样本量,结果改善有限。后来仔细翻标注,发现一批货车的框体在远处本身就很小,标注员当时为了省事把看不清的尾部直接标成了轿车;更麻烦的是,很多两轴厢式货车和大型SUV的外形轮廓确实很像,单靠标框很难强行分开。

最后的解决办法有两步。一是把类别定义写死,明确“载货功能车辆一律归货车,即使外形像SUV也不能标轿车”;二是单独筛出货车类的所有样本做了一次全量复核,修正了其中一批错误标签。重训后货车的F1分数提了将近10个点。这个案例给我最大的教训是:类别混淆往往不是模型学不会,而是标签本来就带着矛盾。

5.3 远端小目标消失:分辨率与切片补救

道路监控场景下,远处驶来的车辆在画面里经常只有20到30个像素宽。imgsz=640时,这个尺寸的目标经过几次下采样,特征几乎被压没了,模型等于在盲猜。我最初试着直接把imgsz拉到1280,确实改善了远端召回,但训练和推理速度都慢得让人难受。

更实用的有效做法是把“远方区域”单独裁剪成小块作为额外训练数据。比如画面远端一条车道宽度大概是原图的1/8,那就把远端固定区域裁出来,放大到640x640再标注训练。模型在这个切片上学到的特征,相当于专门练了“远处车辆识别”,推理时整图输出结果和区域注意力形成互补,远端召回明显提升。这个方法成本低、见效快,是我在处理道路远端车辆漏检问题时最喜欢的一招。

6. 进阶优化与部署前功课:精度和速度的平衡

模型跑通之后,真正头疼的是如何在算力受限的边缘设备上保持精度。车辆类型检测的实际部署环境往往不是一台满血GPU机器,而是摄像机内置芯片或者小盒子,这就逼着你在“精度”和“速度”之间反复权衡。

6.1 用大模型蒸馏,小模型也能救回来

部署端算力有限时,YOLOv8n这种轻量模型往往是首选,但它的精度和YOLOv8x这种大模型比起来差距明显。我的经验是用大模型做“老师”,教轻量模型“学生”,也就是知识蒸馏。

具体做法不复杂:先训练一个高精度的YOLOv8x教师模型,用它跑训练集生成预测结果;再训练YOLOv8n学生模型时,除了拟合真实标注框,还同时拟合教师的预测分布,让学生不仅知道“哪里有车、是什么车”,还保留了教师对模糊边界和困难样本的判断习惯。实测下来,蒸馏后的轻量模型在mAP50上能提升2到4个点,比单纯加大训练轮数有用得多。

6.2 推理阶段的conf和iou其实还能再调

训练完成后,推理时的置信度阈值conf和NMS的IoU阈值,看着是两个不起眼的参数,实际上对最终效果影响很大。框架默认的conf=0.25、iou=0.45只能算中性起跑线,具体业务场景一定要自己调。

我习惯在验证集上做一个小网格搜索:conf从0.15到0.5每隔0.05取一个值,iou从0.3到0.7每隔0.1取一个值,分别记录precision和recall,然后按业务侧重点挑一组最优组合。比如某个平台上漏检代价更高,就压低conf到0.15到0.2,让模型多吐一些框出来;如果算法后面还有跟踪逻辑能消除误检,conf甚至可以再低一点。这个调参过程一般半小时就能跑完,别省。

6.3 导出部署时最容易翻车的三个细节

训练好的YOLO模型导出到ONNX或TensorRT,打印精度没问题,一接到摄像流就出状况,这种情况通常出在三件容易被忽略的事上:

第一是输入预处理不一致。训练时模型做的是RGB输入、letterbox缩放、0-1归一化,但很多设备SDK默认给的是BGR且没有letterbox。这一步不统一,精度立刻下降,而且极难排查。第二是TensorRT固定输入尺寸的问题。导出时如果固定了batch和长宽,部署端就必须严格按照这个尺寸去预处理,换尺寸必须重新导出。第三是INT8量化带来的精度回退。量化可以在推理速度上带来很大收益,但车辆检测这种精细分类任务对量化敏感,建议量化和不量化各跑一遍验证集,如果mAP掉得超过3个点,就老老实实用FP16。

我自己做车辆类型检测项目的习惯是:拿到需求第一周不开算法,先把拍摄机位、场景预算表和分类判例文档写好,数据开工比模型开工早,结果比一上来就调参要稳得多。另外有个小技巧,如果预算有限没有专门拍摄团队,可以从日常上下班路口的监控视频里抽帧,一个月的时间跨度足够覆盖不同光线和车流,攒出第一个版本的数据集。数据这东西,不会一朝一夕变得完美,但每补一轮,模型就实实在在聪明一点。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进