简介这套车辆目标检测数据集面向YOLO系列算法兼容yolov5、yolov7、yolov8、yolov9、yolov10、yolo11等主流版本主要涵盖汽车、自行车、公共汽车三类目标可直接用于模型训练、验证与测试也适合目标检测入门、算法效果对比或毕业设计等场景。压缩包内共包含2000个文件主体是969个txt标签YOLO格式与969个xml标签VOC格式分别存放在不同文件夹以便按需调用另附jpg图像样本和一个data.yaml配置文件包体大小仅57.08MB。YOLO格式的txt文件中每行记录目标类别索引、归一化后的中心点坐标及宽高比例VOC格式的xml文件沿用标准目标检测标注结构便于在传统检测流程中直接读取或二次开发。数据集已按训练、验证等用途划分与data.yaml配套即可无缝接入YOLO系列模型训练管线省去自行标注、整理标签、划分数据集和格式转换的环节。目前已有164人学习下载适合需要轻量、快速上手车辆检测任务的开发者选用。1. yolo车辆数据集从拿到压缩包到跑通训练的完整链路做目标检测项目最耗时间的往往不是调模型而是整理数据集。很多从业者拿到一个数据集压缩包解压一看——图像和标签散落一地训练集验证集没分配置文件缺失光是整理目录就要花掉半天。这个yolo车辆数据集就是冲着省时间来的969张图像汽车、自行车、公共汽车三类标签两种格式yolo的txt和voc的xml分文件夹给齐train/val划分做好了data.yaml也在压缩包里解压后改一行路径就能直接喂给yolov5、yolov8或yolov9训练。适合毕设做车辆检测、工程上快速验证模型效果、或者刚入门yolo想跑通全流程的开发者。这篇文章把从解压到出模型的完整流程拆开讲重点说清楚两种标签格式怎么互相转换、目录结构怎么摆、以及训练时常见的几个坑。2. 数据集的真实结构解压后先看懂这四件事拿到压缩包第一件事不是急着训练而是把目录结构摸清楚。这个包的核心是四块内容图像文件夹、两种格式的标签文件夹、划分好的train/val列表、以及data.yaml配置文件。先cd进解压目录用tree命令把结构打出来unzip yolo算法-车辆数据集-969张图像带标签-汽车-自行车-公共汽车.zip -d vehicle_dataset cd vehicle_dataset tree -L 2输出大致是这样├── images/ │ ├── train/ # 776张 │ └── val/ # 193张 ├── labels/ │ ├── yolo_format/ │ │ ├── train/ # 每个txt对应一张jpg │ │ └── val/ │ └── voc_format/ │ ├── train/ # 每个xml对应一张jpg │ └── val/ ├── data.yaml └── README.txt我用过不少网上流传的数据集很多压缩包只给图像和一个孤零零的标注文件划分得自己手动做。这个包好一点——train/val已经切好比例大约8:2不需要自己再写划分脚本。images和labels的目录层级一一对应yolo格式和voc格式各自的train/val都能对得上。这里有个值得注意的细节有些数据集把txt和xml混在同一个标签目录下训练时还得先筛一遍扩展名。这个包用两个独立文件夹把格式分开后面不管用yolo原生txt还是转成voc做其他框架训练都不会互相干扰。data.yaml是yolo训练的核心配置文件打开看一眼:train: ./images/train val: ./images/val nc: 3 names: [car, bicycle, bus]train和val指向图像目录的相对路径nc是类别数names按顺序列出类别名。注意这里的类别顺序必须和标签文件里的class索引完全一致——car对应0bicycle对应1bus对应2一旦改动names顺序所有标签就全废了。yolo格式的标签核心是类别索引加归一化坐标。比如img_0689_348.jpg对应的txt:0 0.685937 0.454861 0.532031 0.664583 1 0.323958 0.701736 0.193750 0.242014 2 0.510833 0.372917 0.095313 0.121528每行一个目标五个数字依次是类别索引class从0开始中心点x和y是相对于图像宽高的比例值范围0到1最后两个是框的宽高同样做了归一化。这种格式不关心图像的绝对像素尺寸换不同分辨率训练都不用改标签。voc格式的xml则走另一套路——用绝对坐标存框的左上角和右下角同时记着图像宽高。以这个数据集里的xml为例结构与标准VOC一致annotation size width1280/width height720/height depth3/depth /size object namecar/name bndbox xmin117/xmin ymin102/ymin xmax797/xmax ymax580/ymax /bndbox /object /annotation同样是车yolo格式里中心点是(0.685937, 0.454861)换算回1280×720图像就是x878像素、y327像素而xml里存的是xmin/ymin/xmax/ymax绝对坐标。两种格式只是同一标注的不同编码没有谁比谁更准用哪个取决于你后面打算跑什么框架。yolo系列框架原生吃txtfaster-rcnn、ssd这类框架常要voc的xml所以这个包把两种都给齐省了到处找转换脚本的麻烦。做车辆检测数据集类别分布这关绕不开。我统计了一下这个包的标注分布car占绝大多数可能有七百多张图像都含汽车bicycle和bus相对少——城市道路场景里车多、自行车和公交车少这是很现实的分布。训练前最好数一遍各类别目标数量类别严重不均衡时考虑对样本少的类别做增强这个后面细说。【注意】压缩包里可能还带有名为classes.txt或label.txt的文件那是给VOC格式类别映射用的yolo训练时真正认的是data.yaml里的names别搞混。3. 目录重组与格式转换拆开txt和xml互相转换的代码逻辑很多新手拿到数据集直接开训结果yolov8报AssertionError: train: No labels in ...原因往往是images和labels目录没对齐。yolo框架找标签文件有严格规则假设图像在images/train/img_001.jpg它就去labels/train/img_001.txt找对应标签目录结构拼错一个字就找不到。拿这个数据集做yolov8训练时目录结构应该组装成yolo原生要求的布局# 把标签从yolo_format中复制为labels/train、labels/val # 与images/train、images/val一一对应 mkdir -p yolov8_dataset/labels/train yolov8_dataset/labels/val cp labels/yolo_format/train/*.txt yolov8_dataset/labels/train/ cp labels/yolo_format/val/*.txt yolov8_dataset/labels/val/ # 图像同理 mkdir -p yolov8_dataset/images/train yolov8_dataset/images/val cp images/train/*.jpg yolov8_dataset/images/train/ cp images/val/*.jpg yolov8_dataset/images/val/复制完成后要验证图像与标签是否一一对应缺标签的图像训练时会被框架静默跳过浪费数据还影响效果。验证方法:import os img_dir yolov8_dataset/images/train label_dir yolov8_dataset/labels/train img_files {os.path.splitext(f)[0] for f in os.listdir(img_dir) if f.endswith(.jpg)} label_files {os.path.splitext(f)[0] for f in os.listdir(label_dir) if f.endswith(.txt)} missing img_files - label_files print(f缺标签数量: {len(missing)}) for name in list(missing)[:5]: print(name)这个脚本用集合做差集找出有图像但没标签的文件。跑完如果输出空说明一一对应如果打印出文件名检查是不是图像里有全黑或损坏的帧这类坏数据该删就删。如果要用voc格式做对比实验或跑faster-rcnntxt转xml的脚本是刚需。核心是读归一化坐标反算绝对坐标再组装成xml结构import os import xml.etree.ElementTree as ET def yolo_to_voc(txt_path, xml_path, img_w, img_h): # 归一化坐标反算绝对像素坐标 with open(txt_path) as f: lines f.readlines() annotation ET.Element(annotation) size ET.SubElement(annotation, size) ET.SubElement(size, width).text str(img_w) ET.SubElement(size, height).text str(img_h) ET.SubElement(size, depth).text 3 for line in lines: parts line.strip().split() cls, xc, yc, w, h int(parts[0]), float(parts[1]), float(parts[2]), float(parts[3]), float(parts[4]) # 反归一化中心点乘宽高得到像素坐标 xmin int((xc - w / 2) * img_w) ymin int((yc - h / 2) * img_h) xmax int((xc w / 2) * img_w) ymax int((yc h / 2) * img_h) # 越界裁剪防止标注落在图像外 xmin, ymin max(0, xmin), max(0, ymin) xmax, ymax min(img_w, xmax), min(img_h, ymax) obj ET.SubElement(annotation, object) ET.SubElement(obj, name).text [car, bicycle, bus][cls] box ET.SubElement(obj, bndbox) ET.SubElement(box, xmin).text str(xmin) ET.SubElement(box, ymin).text str(ymin) ET.SubElement(box, xmax).text str(xmax) ET.SubElement(box, ymax).text str(ymax) tree ET.ElementTree(annotation) tree.write(xml_path) # 使用前需要从图像属性读取宽高txt里没有这个信息 from PIL import Image img Image.open(images/train/img_0689_348.jpg) img_w, img_h img.size yolo_to_voc(labels/yolo_format/train/img_0689_348.txt, output.xml, img_w, img_h)逻辑拆开看读取txt每行的五个数值中心点坐标乘图像宽高还原成像素值宽高也是乘宽高还原成像素值由中心点和宽高推出左上右下坐标。越界裁剪很重要——归一化坐标在边界目标的标注经常取整产生0到1范围外的数裁剪不做好后续框架读取xml时box坐标可能为负值或超宽。用这种转换脚本前先确认你的图像分辨率是不是统一。这个数据集我看下来基本都是1280×720如果混入不同尺寸的图像脚本里读的img_w、img_h必须从PIL取实际值不能写死。有人图省事写死尺寸转出来的标注全部偏移——手动标注软件里看着框是对的训练loss就是降不下去。4. 模型训练与参数调优用yolov8s在车辆数据集上验证环境没准备好的先装依赖yolov8用pip直接装ultralytics包pip install ultralytics yolo --version装完确认输出版本号。我习惯再验证一下GPU可用性训练前把torch的cuda检查跑一遍import torch print(torch.cuda.is_available()) print(torch.cuda.get_device_name(0) if torch.cuda.is_available() else CPU only)用pycharm跑yolo项目时很多人翻车在这一步——终端没配conda环境GPU版torch装不上或者cuda toolkit和驱动版本不匹配。建议只用conda创建独立python 3.10环境再装torchpycharm里interpreter选同一个避免系统环境被搞乱。训练命令指定数据集yaml即可以yolov8s为例:yolo detect train \ datavoc_format/data.yaml \ modelyolov8s.pt \ epochs100 \ batch16 \ imgsz640 \ patience20 \ projectruns \ namevehicle_experiment参数含义拆开讲modelyolov8s.pt表示从官方预训练权重开始fine-tune比从零开始收敛快得多小数据集尤其受益epochs100是迭代轮数车辆检测任务100轮基本够batch16在12G显存的卡上跑yolov8s正合适显存小就调到8imgsz640是训练分辨率yolo默认就是640如果想检测小目标可以提到960但显存占用会显著增加patience20表示20轮没有mAP提升就提前停止防止过拟合。训练完成后的关键产物在runs/vehicle_experiment/目录下:weights/best.pt和weights/last.pt两个权重文件还有results.csv记录每轮的loss和mAP曲线。判断训练是否正常主要看几个指标——box_loss、cls_loss持续下降precision和mAP0.5在后期趋于稳定。如果loss曲线像过山车一样上下剧烈波动优先怀疑学习率过大或batch太小。验证模型用yolo detect val \ modelruns/vehicle_experiment/weights/best.pt \ datavoc_format/data.yaml验证输出会打印mAP50和mAP50-95。注意yolov8验证默认只打印不带conf_thres0.5的指标mAP50在0.8以上对这个969张的小数据集来说是正常表现。类别多或者数据更复杂时mAP掉到0.6也常见不必焦虑。推理测试用一张没训过的图yolo detect predict \ modelruns/vehicle_experiment/weights/best.pt \ sourcetest_images/street.jpg \ conf0.25 \ saveTrueconf阈值是0.25低于这个置信度的框会被丢弃。实际部署时这个值调高到0.4能大幅减少误检但也会漏掉一些遮挡严重的小目标。阈值怎么设取决于你后面怎么用——毕设展示求画面干净就调高做安防监控求不漏检就调低。5. 避坑指南几个实际踩过的数据与训练问题整理这个数据集和训练过程中遇到比较典型、值得写出来的问题有五个。第一个txt文件行尾有空格导致解析错位。现象是用labelImg或代码读txt时某行报错invalid literal for int() with base 10。原因是部分txt编辑器在每行末尾加了空格或\r而yolo解析器对行内容做了strip处理但没处理好末尾空白。解决方式很简单批量清洗:import os for fname in os.listdir(labels/yolo_format/train): path os.path.join(labels/yolo_format/train, fname) with open(path, r) as f: lines f.readlines() cleaned [] for line in lines: parts line.strip().split() # 过滤空行保留恰好5个字段的标注 if len(parts) 5: cleaned.append( .join(parts) \n) with open(path, w) as f: f.writelines(cleaned)第二data.yaml里train和val路径写成绝对路径换机器就要重改。现象是在自己电脑上训练正常把数据集打包发给同事对方一跑就报FileNotFoundError。原因就是yaml里的路径是C:\Users\xxx\...或/home/xxx/...换环境就失配。解决方法是统一改成相对路径训练前cd到数据集目录再启动或者在yaml里用${YOLO_DATASET_DIR}这类环境变量拼接。第三xml文件不完整导致labelImg打不开。现象是某个xml用xmllint验证报Premature end of data in tag annotation line。原因是中断的标注进程或转换脚本异常退出只写了一半就到尾部。解决方法是批量验证:find . -name *.xml -exec xmllint --noout {} \;第四图片中有全黑的损坏帧。现象是训练日志里某个epoch loss突变到NaN排查后发现是某个jpg是纯黑图或者图像文件只写了一部分。原因是从视频抽帧时抽到黑帧没过滤。解决方式是写脚本删掉这类图同时删掉对应的标签txt保持一一对应。检查方法用PIL的getextrema()最大最小值都是0就是全黑。第五类别不均衡导致自行车目标mAP极低。现象是总mAP50有0.82但单独看bicycle类的mAP只有0.4。根本原因还是样本少——900多张里自行车可能就两三百框。缓解手段是数据增强yolov8内置的mosaic和fliplr已经起作用了还可以进一步对样本少的类做过采样复制bicycle样本的txt和对应图像到训练集若干次或者降低car类别的loss权重。还有个容易被忽略的坑训练和验证的图像分辨率如果不一致精度会受影响。这个数据集图像就是1280×720但yolo训练时imgsz640会resize。验证时选不同的imgsz会得到不同的mAP所以对比模型时imgsz必须固定不然对比就是玄学。6. 模型导出与端侧部署验证训练完权重不代表流程结束。实际项目中best.pt往往要导出成其他格式才能上生产环境。yolov8官方支持导出onnx、tensorrt、openvino、coreml等格式但踩坑点在于不同格式对opset版本和动态轴设置的要求不一样。以导出onnx为例:yolo export modelruns/vehicle_experiment/weights/best.pt formatonnx opset12 simplifyTrue导出会生成best.onnx这个格式可以直接被ncnn、MNN等推理框架接手。参数opset12是onnx的算子版本版本太低某些op不支持太高旧版推理框架不兼容simplifyTrue会移除计算图中冗余节点缩小模型体积。导出后用onnxruntime做一个前后端一致的验证import onnxruntime as ort import cv2 import numpy as np sess ort.InferenceSession(best.onnx) input_name sess.get_inputs()[0].name # 预处理resize到640x640归一化到0-1转CHW并加batch维度 img cv2.imread(test_images/street.jpg) img cv2.cvtColor(img, cv2.COLOR_BGR2RGB) img cv2.resize(img, (640, 640)) img img.astype(np.float32) / 255.0 img np.transpose(img, (2, 0, 1))[None, ...] outputs sess.run(None, {input_name: img}) # outputs[0] shape为[1, 84, 8400]844个框坐标80个类别概率yolov8的onnx输出与v5略有不同第一维末尾的8400是不同尺度特征图上的候选框总数。如果是自己训练的3类数据集输出张量就不是84而是7——4个坐标加3个类别概率。拿模型导出时如果shape不对检查你导出的模型是不是从官方权重直接导的没有加载训练权重或者类别数本身是80不是3。这一点经常有人栽从ultralytics官方仓库下载的yolov8s.pt是80类COCO权重不是自己的3类模型推理时输出维度完全不一样。本地跑通onnx后我最常做的一件事是用opencv直接读onnx做推理不依赖yolo的推理器验证模型确实跟框架解耦net cv2.dnn.readNetFromOnnx(best.onnx) blob cv2.dnn.blobFromImage(cv2.imread(test.jpg), 1/255.0, (640, 640), swapRBTrue) net.setInput(blob) outs net.forward()这一步是模型落地的后悔药。训练时的mAP指标只能说明离线效果部署时输入尺寸、图像通道顺序、归一化方式的微小差异都可能导致检测结果几乎全丢。用opencv直读onnx跑一遍等于给模型做了一次端到端验证。从那以后我每次训练完车辆检测模型都强制走一遍导出加onnxruntime推理验证的流程确认输出框的位置和训练时的效果相差不大才交给下一个环节。这个习惯帮我避免了好几次模型交付后才发现推理结果不对的尴尬。希望你做yolo车辆检测相关项目时这份数据集和这些流程记录能帮你少走弯路。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?