公司动态
YOLO车辆行人四类别识别:数据构建到部署实战指南
简介目标检测是计算机视觉的核心任务之一YOLO凭借其高效的单阶段检测架构成为智慧交通与安防监控领域的首选算法。然而模型性能的上限往往由数据质量决定。工程实践中类别不平衡、小目标漏检、遮挡截断是车辆行人检测的三大常见痛点。通过科学的数据采集与筛选、统一的标注规范、针对性的增强策略以及合理的训练调参能够有效提升模型在复杂路况下的泛化能力。本文以YOLO车辆行人四类别识别为例从数据集的类别定义与质量把控出发系统讲解标注工具选择、增强方法、模型训练与指标解读并给出部署阶段的关键技巧。这套方法适用于智慧交通、车流统计、安防监控及毕业设计等场景帮助开发者绕过常见数据坑快速构建可落地的检测系统。 做目标检测项目最烦的一件事不是模型选型而是数据。“YOLO车辆行人四类别识别数据集”这个项目说白了就是把道路场景里最常见、也最容易让检测模型翻车的四类目标——汽车、大型车辆、骑行者、行人——从标注规范到训练收敛整套流程走通。这篇文章我会从类别定义、数据构建、训练调参、踩坑修复一路讲到部署效果适合正在做智慧交通、安防监控或者毕设里需要车辆行人识别的朋友直接照着抄作业。先说清楚一件事模型再强喂进去的数据不干净出来就是一团糟。我在这个项目里花了接近三分之二的时间在数据侧而不是在改网络结构上。所以这篇博文的核心思路也很简单把数据管明白YOLO自然能跑出好结果。1. 项目整体设计与需求拆解1.1 四类别分类的由来很多初学者拿到需求会犯一个毛病类别越多越好看到什么就分什么。但实际上分类粒度越细标注成本越高模型之间容易混淆最终精度反而下降。这个项目叫“四类别识别”不是随随便便定的它对应的是道路监控和辅助驾驶场景里真正有区分意义的四类目标。我采用的四类定义为类别ID类别名覆盖范围典型特征0car小汽车、SUV、面包车长宽比接近1.5-2.0高度较低1truck卡车、大型客车、货车体积大轮廓方正长度较长2cyclist自行车、电动车、摩托车骑手人车一体宽度小高度介于行人与汽车之间3pedestrian行人高瘦型长宽比约0.3-0.5这个分类有讲究。如果把truck和car合并成一类卡车的漏检率会拖累整体mAP因为大目标和中小目标的尺寸分布差异太大一个锚框体系很难同时兼顾。如果把骑行者归进行人又是另一个坑骑行者运动速度快、姿态固定、还会与电动车发生遮挡和行人的特征差别非常大。四类是一个既不臃肿又有区分度的方案。大家不要小看cyclist这一类。在真实的城市道路数据里电动车的出现频率比纯自行车高得多而且它们的运动轨迹和行人完全不一样后续做轨迹预测时类别分离能省很多事。如果后续需要扩展红绿灯、交通标志等类别在这个基础上加就行不用推翻重来。1.2 数据集要解决的三个核心问题我复盘整个项目后发现所有训练效果不理想的根因最终都归结到三个问题上。第一个问题是类别极端不平衡。道路数据里汽车出现的频率远高于行人和骑行者。想象一下你站在路边拍一小时的视频汽车可能过了上千辆行人可能只有几十个。如果直接拿去训练YOLO会偏向学习car的特征对pedestrian和cyclist的泛化能力会很差。这不是网络结构能解决的是数据分布问题。第二个问题是小目标占比高。监控摄像头视角下的行人、远处的车辆占整个画面的像素比例非常小可能只有十几乘几十像素。YOLO在这类目标上的召回率天然偏低如果不刻意调整数据中小目标的比例和增强策略漏检几乎是必然的。第三个问题是遮挡与截断。路上车辆互相遮挡、行人被车挡住一半、骑行者进入画面时只露出一半这些情况在实际场景里逃不掉。如果训练数据全部是干干净净的完整目标那模型一到真实环境就傻眼。所以数据集的构建策略很明确不追求绝对数量多而是追求样本分布的合理性、场景的多样性和遮挡情况的覆盖度。这三个问题也是后面所有数据操作、训练调参的出发点。2. 数据集构建采集、筛选与标注实操2.1 图片来源与质量门槛数据从哪里来有两条路一条是开源数据集一条是自采数据。我这边的做法是两者结合。开源方面可以考虑BDD100K、Cityscapes、UA-DETRAC这类带车辆和行人标注的数据集。这些都是公开资源使用时需要注意License。如果只是做研究或毕设大部分开源数据集的学术用途许可就够了如果是商业项目就得逐条核对条款甚至用自有数据重训。在这个环节省事后面容易惹麻烦这是合规问题不是技术问题。自采数据更重要。我建议花几天时间在不同时段、不同天气、不同路口补拍一些视频然后抽帧。补拍的重点不是数量而是覆盖雨天、逆光、傍晚、夜间、十字路口、人行横道、商圈门口这些场景下的车辆行人状态差异很大。把开源数据当作基础把自采数据当作补充混合起来比单用任何一边都稳。数据质量筛选是很多人忽略的一步。一定要做去重视频抽帧后连续帧之间的目标几乎一样直接训练会导致严重过拟合。我的做法是用感知哈希算法做相似度过滤把相邻且相似度超过阈值比如0.85的帧只保留一张。还有模糊帧可以用拉普拉斯算子的方差来判断方差值很低就说明图像太模糊弃掉。建议每张图保留一个质量记录表列清楚来源、场景、天气、亮度、目标数、目标尺寸分布。这一步看似琐碎后面分析badcase时能起大作用。我整理数据时发现当时晴天的数据占比接近80%雨天样本极少后面模型一到雨天就崩这就是数据分布观察不仔细的代价。2.2 标注工具与标注规范标注工具这块我的选择比较固定。小型项目用X-AnyLabeling它基于Python开发支持YOLO格式直接导出操作界面顺手适合一个人干活。多人协作的大规模标注就用CVAT能在服务器上部署分发任务、审核结果都很方便。Roboflow也可以但它强在后续的增强和版本管理纯标注环节我更习惯本地工具。标注规范必须提前定死不然多人协作时每个人都有自己的理解数据就乱了。我总结了以下几条规矩边界框必须紧紧贴合目标可见部分不预留边距。如果目标被遮挡只标注可见部分不画想象出来的完整轮廓。遮挡超过70%的目标可以不标。这个阈值要写在规范里吗我不会写死因为场景影响很大。我的实际标准是如果我认为人眼都很难判断这是什么东西那就别标标了就是噪声。骑行者标注时框要覆盖人和车整体不要只框人或者只框车。镜面反射、广告牌里的车和人不标。这是负样本素材不是正样本。为了保持一致性同一个人完成同一批次图片的标注避免多人标准不统一。标注完了不是结束一定要抽检。我每次从结果中随机抽5%以上的图片自己人工核对边界框的位置和类别是否准确。另外可以计算一类指标不同标注者对同一批图的边界框IoU如果平均IoU低于0.8说明标注标准没有对齐得返工或重新讲解规则。2.3 数据增强与类别均衡数据增强是YOLO训练里最不能省的一环。YOLOv8自带的增强策略里mosaic是效果最明显的它把四张图拼在一起训练有效提升了模型对小目标和被截断目标的鲁棒性。HSV扰动可以改变色调、饱和度和亮度相当于免费模拟不同光照条件。但有两项增强要特别小心。一个是垂直翻转在车辆行人检测里我不建议开车辆倒着放的世界不符合物理规律。另一个是旋转角度过大比如超过30度会让车辆变成奇怪的形状反而干扰学习。我的经验是只做小幅旋转正负15度以内。类别不均衡的解法首先不是靠损失函数硬调而是先把数据调均衡。我统计了项目数据集中类别的实例数当时是car约3.2万、truck约0.9万、cyclist约1.1万、pedestrian约1.5万。truck和cyclist明显偏少。我的做法是对这两个类别使用复制增强找出含truck和cyclist的图片复制后做几组强随机增强亮度变换、小角度旋转、裁剪再放回训练集。这样类别数量拉到比较接近的水平又不至于引入完全重复的样本。loss权重调整也可以辅助但我建议放在数据调整之后再试。如果数据本身就不均衡光改loss权重是往水里压皮球压住一头另一头又浮起来了。3. 从标注到训练完整代码与训练配置3.1 数据格式转换与目录组织标注完成后第一步是把各种标注格式统一转成YOLO格式。YOLO的标签格式很简单每张图对应一个同名txt文件每行是“类别ID 归一化中心x 归一化中心y 归一化宽度 归一化高度”。拿VOC XML转YOLO举个例子核心逻辑是这样import os import xml.etree.ElementTree as ET def convert_voc_bbox(size, box): dw 1.0 / size[0] dh 1.0 / size[1] x (box[0] box[1]) / 2.0 y (box[2] box[3]) / 2.0 w box[1] - box[0] h box[3] - box[2] return (x * dw, y * dh, w * dw, h * dh) def convert_annotation(xml_file, out_txt): tree ET.parse(xml_file) root tree.getroot() size root.find(size) w int(size.find(width).text) h int(size.find(height).text) lines [] for obj in root.findall(object): name obj.find(name).text if name not in class_mapping: continue cls_id class_mapping[name] box obj.find(bndbox) xmin float(box.find(xmin).text) ymin float(box.find(ymin).text) xmax float(box.find(xmax).text) ymax float(box.find(ymax).text) bbox convert_voc_bbox((w, h), (xmin, xmax, ymin, ymax)) lines.append(f{cls_id} .join([f{v:.6f} for v in bbox])) with open(out_txt, w) as f: f.write(\n.join(lines))上面class_mapping就是类别名称到ID的映射。转完后要检查两类错误一是坐标是否越界比如某个值小于0或大于1二是txt文件是否为空空文件代表这张图没有任何目标一般需要剔除。目录结构我统一采用YOLO标准布局datasets/ ├── images/ │ ├── train/ │ ├── val/ │ └── test/ ├── labels/ │ ├── train/ │ ├── val/ │ └── test/ └── data.yamldata.yaml是配置文件里面指定路径和类别path: /path/to/datasets train: images/train val: images/val test: images/test names: 0: car 1: truck 2: cyclist 3: pedestrian数据划分比例我建议train和val按8比2test单独留出来。测试集最好从不同场景的原始数据里单独抽取而不是从混合数据里直接随机切这样才能真实反映模型在没见过的场景下的表现。3.2 训练参数怎么定模型我选YOLOv8m比n和s精度高比l和x训练成本低在四类目标的场景里属于性价比最高的选择。当然如果你对速度要求极高比如要在嵌入式设备上实时跑可以换成yolov8n不过后面要想清楚算力换精度是亏是赚得看具体需求。训练命令长这样yolo detect train datadata.yaml modelyolov8m.pt epochs200 imgsz640 batch16 device0几个关键参数我说一下我的选择逻辑。imgsz640是通用起点。如果小目标问题严重可以尝试提到768甚至896分辨率高了小目标像素多了召回率自然会涨但显存占用和推理耗时也会涨。我试过从640到768mAP50涨了约2个点但推理时间增加了约30%。值不值视项目情况而定。epochs200听起来多但YOLOv8有early stopping机制如果连续几十个epoch验证集指标不再提升会自动停下。我实际训练中大概到130-150个epoch就收敛了所以200只是安全值。batch16是基于我的显卡显存定的。如果你的卡只有8G显存batch16配合640分辨率可能会爆显存可以降到8或者开梯度累积。不要小看batch它对训练稳定性影响很大batch太小梯度噪声大不容易收敛batch太大显存放不下只能加并行卡或降分辨率。优化器用默认的SGD或AdamW都行。YOLOv8默认是SGD加动量配合预热和余弦退火学习率策略效果很稳。你要是新手不要乱改学习率默认0.01起步就行改小了半天不收敛改大了直接loss起飞。3.3 训练曲线与评价指标怎么读训练完不要急着看mAP先看曲线。训练日志里会输出每轮的train_loss、val_loss和各项指标。我最关注这四样precision、recall、mAP50、mAP50-95。mAP50是IoU阈值0.5时的平均精度宽容度高适合看模型“大方向”对不对。mAP50-95则是0.5到0.95每间隔0.05算一次再取平均严格得多对边界框的定位精度很敏感。两个指标一起看才能判断模型是框得准还是框得糊。判断过拟合的标志也很简单train_loss持续下降但val_loss从某个epoch开始不掉反升同时val的mAP开始震荡或下滑。这时候不要再去延长训练而是应该回看数据是不是训练集和验证集有重复是不是样本多样性不够模型复杂度太高比如用了yolov8x但数据量只有几千张训练曲线还有一个容易踩的坑mAP在训练中后期波动非常大。比如前50个epoch mAP50一直缓慢上升到了80个epoch突然掉到很低下一个epoch又涨回去。这通常不是模型问题而是验证集太小或者数据分布不均导致的统计波动。把验证集数量加大或者用多次推理取平均的做法能缓解。我自己的习惯是每次训练完至少看三张图PR曲线、混淆矩阵、验证集上的badcase可视化。混淆矩阵能直观看到car和truck之间有没有系统性混淆比如真实truck被预测成car的比例高不高。PR曲线看recall在高precision区间是否突然断崖式下跌如果是说明有相当一部分难样本没搞定。4. 常见问题与排查技巧实录4.1 漏检与误检到底谁在捣乱这个部分如果我不写估计你们的训练效果会卡在一个很尴尬的位置。漏检小目标是最常见的问题。监控画面里的行人只有二十几个像素yolov8m简直睁眼瞎。我尝试过的解决办法有四种。第一种是把imgsz调大到768或896给模型更多像素细节。第二种是开启SAHI切片推理把大图切成若干小图分别推理再合并对小目标效果立竿见影。第三种是在数据里增强小目标比例比如用马赛克增强时把小图放大拼到大图上。第四种是换用专门设计的小目标检测头模型这个改动成本高建议前三招用完再决定。误检的问题往往出在负样本上。我遇到过最经典的一个case模型把路边竖着的交通指示牌、树干当成行人。原因是训练数据里行人的形态和这些物体在某些角度下太像了而我又没有提供足够的负样本告诉模型“这个不是”。解决办法是在数据集中加入一批纯背景图或者难负样本图标注文件为空让模型学会在特定场景下不该框任何东西。另外还有一个容易忽略的点类别间遮挡导致漏检。比如car和truck并列停在路边模型只框了一个另一个被当成前者的延伸。这个靠改模型结构很难解决我能给的建议是在标注时把密集场景的样本单独抽一批出来重训或者调低NMS的IoU阈值让重叠目标更容易被保留而不是被抑制掉。4.2 类别不均衡与相似类别混合模型在你不知道的情况下做了很多妥协。比如truck和car的边界在模型眼里并不清晰长头的皮卡、面包车到底算car还是truck这种主观模糊直接反映在混淆矩阵里truck的真实标签有一定比例被预测成car。解决这个问题的第一原则语义边界要清晰。如果样本里包含大量面包车和皮卡建议把这些归入truck而不是car因为它们的体积更接近卡车。同时在标注阶段就要统一标淮并且做一致性审查。补充数据时优先补充最容易混淆的类别。如果类别不均衡问题已经发生先别急着改模型。统计一下train集的类别直方图看低样本类的数量到底少多少。如果少得不多过采样就够如果少得离谱那就得去补数据单纯的增强只是延缓问题不是解决问题。增强出来的数据相似性高对泛化帮助有限。4.3 训练不收敛或效果差训练loss出现NaN或者不断升高第一件要做的事不是调学习率而是检查数据。打开几张训练图确认标签坐标有没有越界、类别ID有没有写错、图片是否损坏。在我的经验里超过一半的训练崩溃是数据问题不是模型问题。如果数据正常但loss就是不降我会把学习率从默认的0.01降到0.001试试有时模型在较大初始学习率下震荡厉害。还有可能的原因是训练集太小模型还不够复杂就过拟合了。这时候减少epochs、加大数据增强强度、或者切换到更小的模型从m降到s都是可行的方向。我遇到过一种特别隐蔽的问题数据增强太强导致训练集和验证集分布严重不一致。mosaic增强里的随机裁剪可能把目标切成不完整的碎片虽然模型鲁棒性提上去了但推理时遇到的都是完整目标反而出现诡异偏差。如果训练loss一直降不到理想值可以试着关掉部分增强项看看是不是增强过度。4.4 推理部署的坑训练完了模型要部署。YOLOv8导出ONNX是最常见路径一条命令的事yolo export modelbest.pt formatonnx imgsz640导出之后要在ONNX Runtime或TensorRT上做推理验证。这里有两个坑。第一个是动态batch和动态分辨率的设置如果不小心导成了固定shape换输入尺寸时会报错要注意导出时opset版本和dynamic axes参数。第二个是TensorRT在低精度FP16甚至INT8下精度损失特别是小目标精度掉得非常明显建议在性能允许的情况下保留FP16不要动INT8除非你能接受精度损失和大量calibration。推理时的NMS阈值和置信度阈值同样关键。置信度设太高比如0.7以上漏检增加设太低比如0.1误检爆炸。我的起步配置是conf_thres0.25、iou_thres0.45然后再根据场景微调。车辆行人检测这种安全相关场景宁可稍微多一点误检也不要漏检所以我会偏向把conf阈值调低到0.2左右。5. 真实场景效果与扩展方向5.1 性能数据实测参考下面这组数据是yolov8m、imgsz640、batch16训练200轮后在独立测试集上的参考结果。不同数据集分布下会不一样但能给你一个心理预期。类别PrecisionRecallmAP50mAP50-95car0.9190.8830.9340.745truck0.8710.8420.9020.681cyclist0.8350.7910.8610.602pedestrian0.7980.7440.8230.554从数据可以看出car类指标最好cyclist和pedestrian明显偏低。这不是偶然而是和数据分布、目标尺寸、类内形态多样性直接相关。如果你自己的模型跑到类似水平说明当前模型能力和数据质量已经进入瓶颈区。继续无限堆训练数据收益不会太大下一步得从模型结构、损失函数、后处理策略上想办法。在推理速度上把yolov8m转成TensorRT FP16后在单张RTX 3090上对1080P视频流可以做到约90 FPS。如果只是做离线分析这个速度完全够如果要上24小时实时监控可能需要换成yolov8n或者把分辨率降到480再配多进程并行。5.2 扩展方向跟踪、统计与多任务四类别识别只是感知层的第一步。真正落到业务上还需要目标跟踪。我用ByteTrack接在YOLO检测结果后面做车流量统计、行人轨迹绘制效果可靠。ByteTrack的优点是逻辑简单、不依赖ReID特征纯靠检测框的IoU和状态机关联跑起来开销很低。另外一个扩展方向是多任务学习。既然模型已经能检测出行人和车辆可以在同一骨干网络后挂多个头比如一个头做检测另一个头做车道线分割。在工程上YOLOv8-seg可以同时输出边界框和分割掩码对需要精确定位车辆轮廓的场景很有用比如违章压线检测、停车位占用判断。还有一类扩展是少样本增量学习。因为道路场景变化多端比如新路口出现了新类型的车辆你要尽快让模型认识它。全量重训成本太高。我尝试过在预训练权重基础上冻结大部分层只对新增类别重新训练最后几个检测头这样只需要很少的新样本就能在旧能力衰减很小的情况下学会新类别。如果后面想把模型往自动驾驶感知方向推那现在的四类就远远不够了还要加上红绿灯、交通标志、车道线、可行驶区域等信息。不过那已经不是“车辆行人识别”这个项目的范畴了。四类别识别能帮你把整个感知pipeline跑通一次后面加类别、加任务是水到渠成的事。我自己的体会是这个项目的关键不在模型有多新而在于你是否愿意花时间把数据分布理解透。车辆行人这类目标形态变化大、场景依赖强没有哪个模型是装上就能用的。把数据做扎实把每一个badcase拆解开YOLO就能给你一个相当可用的结果。真正做部署时再回头调一调阈值、提一提分辨率这套方案基本能顶住大多数真实场景的考验。本文还有配套的精品资源点击获取