ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

1793张车辆检测数据集YOLO训练全流程:避坑、调参与部署指南

1793张车辆检测数据集YOLO训练全流程:避坑、调参与部署指南 简介面向车辆检测模型训练的 YOLO 三类别数据集包含汽车、公交车、卡车共 1793 张实景道路图像适合自动驾驶、交通监控、安全预警等场景下的模型迁移学习与性能调优。资源整体约 542.36MB文件总数达 5380 个其中 1793 张 jpg 图像用于输入训练与可视化验证1793 个 xml 文件对应 VOC 格式的边界框标注1794 个 txt 文件为 YOLO 格式的归一化标签并附带 classes.txt 类别清单目录按 images-car_detest-1793、ann_xml-car、labels 清晰分离可直接接入主流训练框架。已有 349 人学习使用。相比单类别车辆检测该数据集覆盖汽车、公交车、卡车三种目标多类别标注能提升模型对复杂道路环境的区分能力也能为类别不均衡、遮挡等实战问题提供调优样本开发者按需完成数据清洗、格式转换、超参数调整与验证集评估后即可快速构建具备实时识别能力的车辆检测系统。1. 1793张三类别车辆数据集到底值不值得当作YOLO的训练起点把1793张的car-detect-dataset直接丢进YOLO训练十有八九第一轮就翻车不是mAP50卡在0.6上不去就是bus和truck两个类别的AP差出一个太平洋。这个标题看起来只是一个小规模数据集但它几乎是每个做YOLO车辆检测的人都会遇到的第一道坎——图片量不大、场景偏单一、类别分布还明显失衡。它真正的作用不是喂出一个量产级模型而是用最低成本验证你的数据管线、训练链路和调参思路是否靠谱。适合刚把yolo环境搭建起来的初学者也适合要快速出车辆检测原型验证的工程朋友。下面按拆数据、训练、调参、避坑、验证的顺序把这个数据集从头到尾盘一遍。2. 先拆数据集car/bus/truck的目录结构、标注格式与类别分布怎么看拿到手的第一步不是急着训练而是把这个数据集的“家底”摸清楚。车辆检测数据集的翻车点往往不在模型而在目录结构、标注格式和类别顺序这三个最容易被忽略的地方。2.1 拿到car-detect-dataset先做三件事核对目录、核对标注、核对类别名这种命名带“car-detect-dataset”的数据集最常见的压缩包结构是images和labels两个兄弟目录外加一个data.yaml或classes.txt。先把目录结构打出来tree -L 2 car-detect-dataset/正常你会看到这样的布局car-detect-dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yaml这里要注意train/val的比例不同作者的切分习惯不一样。我见过不少小数据集只有train和val两个目录没有test集这种情况下训练时务必别把val混进训练集否则后面报出来的val指标全是自欺欺人。另外如果压缩包里只有一个总的images和labels目录没有预设train/val划分你需要自己按8:2或者9:1切分并生成对应的train.txt/val.txt路径清单ultralytics的训练接口才认。接着看标注格式。YOLO格式的txt每行是“class x_center y_center width height”全部归一化到01和COCO的像素坐标不一样导入前不需要额外转换。但需要确认一点坐标是相对于原图还是做过letterbox。作者没写说明时默认相对原图即可。用head抽查几条head -5 labels/train/000001.txt cat data.yamldata.yaml里通常写三类car、bus、truck但偶尔也有数据集把motorcycle算进去或者把van直接并进car。这一步的价值在于确认三个类别到底叫什么、顺序是什么。训练时模型输出层的类别数nc和推理时的类别名都取决于这个列表的排序排序一变你后面评估结果全得重来。2.2 类别分布一失衡mAP全是幻觉用Python脚本算出每类目标数1793张图听起来不多但数量从来不是小数据集的第一问题真正的问题藏在labels里car占掉七成目标bus和truck只在几十张图里出现。这样的分布在loss层面不会报错但训练出来的模型会天然“偏向”多数类val指标看起来不错一旦部署到公交车站或者高速卡车场景就露馅。拿到数据集的第一件事是用脚本把三类目标数算出来from pathlib import Path from collections import Counter label_dirs [Path(labels/train), Path(labels/val)] counter Counter() for d in label_dirs: for txt in d.glob(*.txt): for line in txt.read_text().strip().splitlines(): try: cls int(line.split()[0]) counter[cls] 1 except (IndexError, ValueError): print(f跳过异常文件: {txt}, 内容: {line}) for cls, num in sorted(counter.items()): print(f类别 {cls}: {num} 个目标)这个脚本不依赖任何深度学习库Python 3.8以上就能跑。cls是txt里的第一列也是data.yaml里class顺序的下标如果打印出类别5、6这种越界编号说明yaml里类别名和txt对不上需要先统一。目标数算完之后把每类数量除以总数当某一类占比低于10%时第4章的copy-paste回填和类权重就有了明确靶子。YOLO的损失函数由分类损失、回归损失和置信度损失三块组成类别不均衡主要压在分类损失那一项。少数类样本少反向传播时梯度贡献小模型自然倾向把不确定目标归到多数类里。这不是YOLO的缺陷是所有单阶段检测器在小样本长尾分布下的通病。2.3 一张图三条链images/labels/data.yaml的对应关系先过一遍另一个常见翻车点在这里images里有1793张图labels里却只有1790个txt或者txt文件名和图片名对不上。YOLO训练时找不到对应标注会在日志里悄悄忽略那张图而不是报错最后表现为“训练正常但mAP上不去”。排查脚本给一个from pathlib import Path img_dir Path(images/train) lab_dir Path(labels/train) img_names {p.stem for p in img_dir.glob(*.jpg)} lab_names {p.stem for p in lab_dir.glob(*.txt)} print(缺标注的图片:, len(img_names - lab_names)) print(无图片的标注:, len(lab_names - img_names))再把两个集合的差集打印出来人工看几张通常会发现三件事有的是png后缀和jpg后缀对不上有的图片本身就是模糊帧或纯背景还有的标注文件是空文件——0字节。及时把缺标注的图片移到unlabeled/目录备份比花一晚上调参有价值得多。3. 用YOLOv8在三类别车辆数据集上跑通训练环境、命令与预训练权重拆完数据下一步就是把训练跑起来。很多人卡在环境配置这一步其实小数据集训练不需要追新关键是锁版本、用预训练权重、给一组合理的训练参数。3.1 环境配置别求新用conda锁住ultralytics版本yolo环境配置是新手的第一道坎。网上一堆“一键部署脚本”和“yolo最新版本更新内容”的帖子但我给团队搭训练环境时更习惯用conda新建独立环境然后把ultralytics版本固定。原因很简单ultralytics迭代太快小版本之间默认超参和回调函数都会变今天能跑的脚本下周pip update之后可能就在某个数据增强环节报错。固定版本的做法conda create -n yolo_env python3.10 -y conda activate yolo_env pip install -U pip pip install ultralytics pip install torch torchvision --index-url https://download.pytorch.org/whl/cu118 pip freeze | grep -E ultralytics|torch这里选Python 3.10和CUDA 11.8的组合兼容性覆盖大多数训练卡如果你手里是V100这种老而弥坚的卡cu118对应的torch版本支持也很好。pip freeze之后把输出的ultralytics和torch版本号记进项目说明里这是整个训练可复现的最小前提。以后换机器重装用pip install ultralytics你记录的版本号就能找回同一个环境。3.2 把yolo预训练模型下载挂到train命令里迁移学习的正确打开方式从零训练一个YOLOv81793张图远远不够。常见做法是下载COCO上预训练好的yolov8n.pt或yolov8s.pt然后在这个数据集上接着训练这就是迁移学习。预训练权重里已经包含大量车辆纹理、边缘、阴影的低层特征你真正要训练的是输出层把“80类通用目标”压缩成“car/bus/truck三类”的分类能力。训练命令cd /path/to/project yolo detect train datacar-detect-dataset/data.yaml \ modelyolov8n.pt \ epochs100 \ imgsz640 \ batch8 \ projectruns/detect \ namecar3cls注意modelyolov8n.pt这里指的不是本地路径时ultralytics会自动把预训练模型下载到当前目录相当于帮我们完成了yolo预训练模型下载这一步。第一次跑会看到下载进度条等它下载完再开始训练。YOLOv8会根据data.yaml里nc3自动重构最后一层不需要手动修改任何模型文件。选yolov8n而不是yolov8s是考虑到1793张图的数据量n版本参数量最小不容易在小数据集上过拟合如果验证集mAP一直卡住再往上升级到s版本而不是一开始就上l。3.3 epochs、batch、imgsz怎么配1793张图的一次有效训练参数很多人纠结“1793张到底训练多少轮”我的建议是epochs给100到200配合早停让模型自己决定什么时候停。batch的设置要看显存8G显存用batch8对640输入已经比较紧V100级别的大显存卡batch提到32没压力但对小数据集batch太大会让bn层统计量过稳容易欠拟合。我一般在小数据集上固定batch16然后只动学习率。第二次训练命令yolo detect train datacar-detect-dataset/data.yaml \ modelyolov8n.pt \ epochs150 \ imgsz640 \ batch16 \ lr00.005 \ patience20 \ projectruns/detect \ namecar3cls_v2lr0从默认的0.01降到0.005是数据量小于2000张时最值得做的一次调整。原因在于预训练权重已经处在COCO的一个局部最优附近学习率太大会把特征层冲乱学习率小一点更像是“微调”而不是“重新训练”。训练日志里如果看到val/mAP50每轮都在涨但涨得越来越慢说明路径是对的如果前20个epoch一点不动才是数据和标注的问题。参数建议值调整方向epochs100~200mAP还在涨就继续稳定后加成patience早停batch8~16显存16G以上用16否则用8imgsz640小目标多可试960显存压力大幅上升lr00.005欠拟合可回升到0.01过拟合降到0.003patience20连续20轮无提升自动停止4. 把1793张图用出2000张的效果数据增强、超参调整与送检策略数据集小不代表只能接受小结果。数据增强、类权重、回填策略都是在这个量级下能真正拉开差距的做法。这一章讲怎么在小数据集上把每张图的价值榨干。4.1 mosaic、hsv、fliplr车辆检测里最影响效果的增强项YOLOv8默认开了不少数据增强mosaic1.0、hsv_h0.015、hsv_s0.7、fliplr0.5等。在车辆检测场景中mosaic是把四张图拼成一张对密集小目标场景拼图边缘会频繁裁切目标本体bus这种长车型被从中间裁开后标签只剩一半模型学到的是“半截公交车”。所以重点是调整而不是关闭。通过命令行参数覆盖默认值yolo detect train datacar-detect-dataset/data.yaml \ modelyolov8n.pt \ epochs150 imgsz640 batch16 \ mosaic0.8 hsv_h0.01 hsv_s0.3 hsv_v0.3 \ fliplr0.5 scale0.5 \ projectruns/detect namecar3cls_augmosaic降到0.8保留一部分拼接增强的多样性但减少半截目标的比例hsv_s和hsv_v从默认的0.7降到0.3因为车辆检测更依赖形状和边缘颜色扰动过强会让红车和绿车之间的分类边界被干扰。scale0.5表示随机缩放幅度控制在50%以内避免把车缩得面目全非。这几个参数是车辆检测场景里最影响效果的增强项改完一轮再看验证集通常mAP会有一到两个点的变化。4.2 用copy-paste回填和类权重拉平三类别分布第2章统计完典型的分布是car有1400个目标、bus只有80个、truck有200个。bus太少训练出来的AP天然低。直接办法是copy-paste数据增强把bus目标的像素块连同标注框随机粘贴到没有bus的图上。常见做法是训练前离线生成扩展图而不是去改ultralytics内部代码import random import cv2 from pathlib import Path src_dir Path(labels/train) img_dir Path(images/train) out_img Path(images/train_aug) out_lab Path(labels/train_aug) out_img.mkdir(exist_okTrue) out_lab.mkdir(exist_okTrue) BUS_CLS 1 # 假设data.yaml里bus的class id1 for txt in src_dir.glob(*.txt): lines txt.read_text().strip().splitlines() bus_boxes [l for l in lines if l.split()[0] str(BUS_CLS)] if not bus_boxes: continue img cv2.imread(str(img_dir / (txt.stem .jpg))) h, w img.shape[:2] for l in bus_boxes: cls, xc, yc, bw, bh map(float, l.split()) x1, y1 int((xc - bw/2) * w), int((yc - bh/2) * h) x2, y2 int((xc bw/2) * w), int((yc bh/2) * h) patch img[max(0, y1):y2, max(0, x1):x2] if patch.size 0: continue new_img img.copy() pw, ph x2 - x1, y2 - y1 paste_x random.randint(0, max(1, w - pw)) paste_y random.randint(0, max(1, h - ph)) new_img[paste_y:paste_y ph, paste_x:paste_x pw] patch new_xc (paste_x pw/2) / w new_yc (paste_y ph/2) / h new_bw pw / w new_bh ph / h out_name f{txt.stem}_cp_{x1}_{y1}.jpg cv2.imwrite(str(out_img / out_name), new_img) with open(out_lab / (out_name.replace(.jpg, .txt)), w) as f: f.write(f{cls} {new_xc:.4f} {new_yc:.4f} {new_bw:.4f} {new_bh:.4f}\n)这个脚本是离线增强的思路优点是看得见、可审计生成的每张图都可以抽查缺点是需要额外磁盘空间一张图生成一张扩展图bus样本量能翻两到三倍。运行后把train_aug目录和原train目录一起写进data.yaml的train路径即可。注意脚本里粘贴坐标做了边界截断保护避免把目标贴到图片外面导致标注越界。除了数据回填还可以在data.yaml里给少数类配上更高的损失权重让分类损失在bus和truck上更敏感。不过ultralytics对类别权重的支持版本不同直接改yaml不一定生效在小数据集上我一般优先做copy-paste回填因为它的效果稳定可控。4.3 bn崩溃不是玄学小数据集训练里最典型的两个NaN来源yolo训练中bn崩溃是很多人问过的问题。现象是训练到某个epochloss突然变成nan之后val mAP50直接掉到0。第一次碰到我也觉得是玄学后来排查下来两个原因最常见。第一个是batch太小。batch4或2时bn层统计的样本太少方差估计极端反向传播时梯度爆炸。解决方式是batch至少8显存实在不够就把imgsz降到480而不是继续减batch。第二个是学习率太大导致损失曲线在某个点发散小数据集上更明显把lr0降到0.005以下同时加上warmup_epochs3让前几个epoch用低学习率预热。排查时看训练日志里的P、R、mAP50三个值grep val runs/detect/car3cls_v2/results.csv | tail -20如果loss出现nan之前P和R已经剧烈振荡基本可以断定是学习率问题如果一路正常直接跳到nan先查显卡驱动和torch的cuda版本这种隐蔽的nan很多时候是CUDA 12的驱动配了cu118的轮子导致的。5. 三类别车辆训练避坑标注错位、类别不均衡与混淆矩阵异常排查小数据集训练最花时间的往往不是训练本身而是排查那些“指标看着还行、一挖全是问题”的隐藏坑。这一章写四个最常踩的坑每个都按现象、原因、解决的顺序来。5.1 car被标成bus用推理回放快速定位标注错位现象训练完val mAP50有0.87但precision只有0.72看混淆矩阵发现car和bus之间大面积串类。原因数据集里部分图片的标注质量差常见的是把面包车标成bus、把轿车尾部标成car或者van被不同标注员分到了不同类别。YOLO不会帮你纠错它只是把错标当成真值学进去。解决用训练好的模型对val集做一次推理把预测框和真值框画在同一张图上生成对比图再人工抽检。只画预测框看不出标注错位必须把真值框叠上去from ultralytics import YOLO import cv2 from pathlib import Path model YOLO(runs/detect/car3cls_v2/weights/best.pt) val_dir Path(images/val) lab_dir Path(labels/val) out_dir Path(debug_visual) out_dir.mkdir(exist_okTrue) for img_path in val_dir.glob(*.jpg): results model.predict(str(img_path), conf0.25, saveFalse) img cv2.imread(str(img_path)) for r in results: for box in r.boxes: x1, y1, x2, y2 map(int, box.xyxy[0].tolist()) cls int(box.cls[0]) name model.names[cls] cv2.rectangle(img, (x1, y1), (x2, y2), (0, 0, 255), 2) cv2.putText(img, name, (x1, y1-5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2) txt lab_dir / (img_path.stem .txt) if txt.exists(): h, w img.shape[:2] for line in txt.read_text().strip().splitlines(): cls, xc, yc, bw, bh map(float, line.split()) x1, y1 int((xc - bw/2) * w), int((yc - bh/2) * h) x2, y2 int((xc bw/2) * w), int((yc bh/2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.imwrite(str(out_dir / f{img_path.stem}_debug.jpg), img) print(对比图已输出到 debug_visual/)红色是预测框、绿色是真值框。绿色框和红色框形状差异太大是回归还没收敛或者标框画歪了两个框重合但类别对不上就是标注错位。抽看30张左右如果错位比例超过5%建议直接基于这份图修订标签再继续调参就是浪费时间。5.2 混淆矩阵总合不唯一别自己吓自己先看归一化与采样现象有人打开runs里的confusion_matrix.png按行或按列求和发现数字对不上样本总数怀疑训练过程出错。原因ultralytics输出的混淆矩阵默认是未归一化的计数矩阵而且只统计了验证集里“模型输出过且参与NMS匹配”的目标。被NMS抑制掉的重复框、被裁到图外的标注、以及低于conf阈值的预测都不计入。所以总数不等于标签总数是完全正常的yolo混淆矩阵总合不唯一不是bug。解决想快速验证模型可靠性不要去看总和直接看三件事第一是results.csv里的mAP50和mAP50-95曲线第二是每类的AP第三是把混淆矩阵的显示模式改成归一化。曲线图脚本import pandas as pd import matplotlib matplotlib.use(Agg) import matplotlib.pyplot as plt df pd.read_csv(runs/detect/car3cls_v2/results.csv) plt.plot(df[epoch], df[metrics/mAP50(B)], labelmAP50) plt.plot(df[epoch], df[metrics/mAP50-95(B)], labelmAP50-95) plt.legend() plt.savefig(train_curves.png) print(df.tail(3)[[epoch, metrics/mAP50(B), metrics/mAP50-95(B)]])看完曲线再回到confusion_matrix.png只要对角线颜色明显比非对角线深且每类AP差距在15%以内基本不用纠结计数总和。如果非对角线成片发亮再按5.1的方式去回放错检。5.3 小目标漏检车辆远了看不清从增强和阈值两头补现象验证集里远处小车基本没框近处大巴很准car和bus的AP差距明显。原因1793张图里如果原图分辨率不高远处车辆可能只有十几像素YOLOv8下采样到640输入后这些目标在特征图上只剩1到2个格子特征太弱。另一个原因是推理时默认conf0.25把低置信度的真目标也滤掉了。解决分两步。训练侧把imgsz提到960可能有效代价是显存占用变大不适合的话就保留640但把scale增强从0.5收到0.3避免随机缩放让原本就小的车变得更小。推理侧把conf降到0.1yolo detect predict modelruns/detect/car3cls_v2/weights/best.pt \ sourceimages/val \ conf0.1 iou0.45 \ save_txtTrue注意conf降下来之后误检框也会变多。需要同时把iou保持在0.45以上用NMS把重叠的重复框压掉再按业务需求决定最终置信度阈值。5.4 空标注图混进数据集loss正常的mAP暴跌元凶现象训练loss一路下降但val的mAP50在中途突然掉到0恢复得很慢。原因数据集中混了一部分没有标注的图片常见是压缩包里有非车辆帧、模糊帧作者忘了删。这些图进入训练后模型在对应位置没有正样本把它当成“不输出”的样本逐渐学会对整张图输出低置信度一旦val集里也混了这种图mAP就会被拉低。解决训练前跑一遍空标注扫描把没有txt或txt只有0字节的图片全部隔离find labels -name *.txt -size 0 -exec ls {} \; empty_labels.txt find images -name *.jpg | while read img; do [ ! -f labels/$(basename ${img%.jpg}).txt ] echo $img done missing_labels.txt两条命令把空标注和缺失标注都列出来统计数量。如果只多出十几张直接移动到unlabeled/备份如果多出上百张这个数据集就要重新清洗否则训练结果没有任何参考价值。6. 训练完先别急着部署mAP验证、错检回放与边缘设备导出技巧模型训练完很多人直接拿best.pt就去部署结果到了现场误检率高得没法看。最后这一章讲清楚验证和导出这两步怎么做才算完。6.1 验收线怎么设mAP、每类AP与错检回放门槛训练结束后先跑一次官方验证命令yolo detect val modelruns/detect/car3cls_v2/weights/best.pt \ datacar-detect-dataset/data.yaml看输出里的mAP50和mAP50-95。对1793张的小数据集我的习惯是mAP50到0.85以上、mAP50-95到0.6以上才算合格同时每类AP的差值不超过15%。如果bus的AP比car低一半再调参收益有限回去做数据回填比继续训练更有价值。验收时优先看每类AP而不是总mAP总mAP会被多数类拉高掩盖少数类的问题。6.2 从pt到onnx再到rknn边缘部署的导出链路很多车辆检测场景最终要落到Jetson、rk3588或树莓派这类边缘设备上。yolo边缘部署监控误检率高常常不是模型训练问题而是量化精度损失。rk3588上的常见做法是先导出onnx再转rknnyolo export modelruns/detect/car3cls_v2/weights/best.pt \ formatonnx imgsz640 opset12导出后在rknn-toolkit2里做量化量化时准备200到300张代表实际场景的图片做校准集别拿val集里的原图凑数否则量化后的精度评估会虚高。树莓派这类纯CPU设备建议直接用n模型的onnx跑onnxruntime不要转TensorRTCPU上精度反而稳定。导出前后各跑一遍同一段视频对比两类漏检率差距在1%以内才算合格。早期我拿这个数据集时觉得分布无所谓直接默认参数训完mAP50到了0.93结果被拉到公交站试跑bus的AP只有0.41回放才发现问题全出在标注错位和类别不均衡上。从那以后我固定了一条习惯不管多小的数据集先统计类别分布再写训练脚本。希望帮到你。本文还有配套的精品资源点击获取
返回列表