简介:这是一份专为YOLO系列目标检测算法准备的车辆数据集,覆盖卡车、小型车、摩托车、公交车四类常见车辆,共2129张带标签图像。数据集已完成训练集、验证集与测试集的划分,并内置data.yaml配置文件,可直接对接YOLOv5、YOLOv7、YOLOv8、YOLOv9、YOLOv10及YOLO11等主流版本,免去环境适配的繁琐步骤。包内同时提供YOLO格式的txt标签和VOC格式的xml标签,分别存放于独立文件夹,txt标注按class、x_center、y_center、width、height的归一化坐标记录目标信息,xml则保留完整物体框与类别信息,便于不同流程下的格式转换与调试。整个压缩包共含2000个文件,以1589个xml和411个txt为主,体积仅62.4MB,轻量易下载,适合目标检测初学者快速跑通训练链路,也可供有车辆识别需求的开发者进行迁移学习或性能测试。目前已有111人学习下载,在标注规范、划分完整的前提下,是一份即拿即用的高性价比数据资源。
1. 拿到yolo车辆数据集的第一步:先别急着训练,先搞清这2129张图里是什么
一个包含2129张带标签图像的yolo算法车辆数据集,类目覆盖卡车、小型车、摩托车、公交车,听起来是拿来就能训的现成资源。但实际跑过的人都知道,这种zip包真正的问题从来不在数量够不够,而在标签是否真的和图像对得上、类别顺序是不是和训练脚本里写的一致、解压出来的文件是不是出现了乱码和损坏。如果你直接丢给YOLO训练,轻则loss异常,重则模型把摩托车认成公交车,排查时还得回头翻原始标注,血泪经验非常典型。
这个数据集的典型使用场景是路侧监控、停车场管理、高速卡口这类固定视角下的车辆检测。四类目标里有大目标也有小目标,卡车和公交车的框通常很大,摩托车的框则小到在640分辨率下容易被忽略,这让它很适合用来验证YOLO系列算法在小目标与大目标混合情况下的表现,同时也能用来练习完整的YOLO训练流程。适合的人群有两类:一是刚入门yolo、想拿一份带标签数据跑通训练和验证链路的新手;二是已经在做车辆计数或车流统计,需要针对特定四类车辆做模型微调的一线工程师。后面章节我会按解压检查、格式整理、训练落地、踩坑排查的顺序,把这个数据集的用法讲透。
2. 解剖这个数据集:目录结构、YOLO标签格式与类名顺序怎么确认
拿到zip的第一件事不是解压了直接训,而是先做整包体检。很多人在这一步偷懒,结果后续所有报错都从这里埋下。按我的习惯,解压后要同时核对三个维度:图片和标签文件数量是否对得上、标签内容是否符合YOLO格式、类别顺序是否和你预想的一致。三个检查五分钟内做完,能省掉后面至少半天的排错时间。
2.1 解压后先做三项体检
先看整个压缩包解压后有没有配置说明文件。多数车辆数据集会附带一个classes.txt或README,记录类别顺序。如果只有图片和标签,那就需要自己推断顺序,这时标题里的“卡车-小型车-摩托车-公交车”只能作为参考,不能当依据,因为标注工具常见的导出顺序未必和标题一致。我的做法是先把解压结果列出来看看目录长什么样。
unzip yolo车辆数据集.zip -d vehicle_dataset && cd vehicle_dataset ls -la find . -type f | wc -l find . -name "*.txt" | wc -l find . -name "*.jpg" -o -name "*.jpeg" -o -name "*.png" | wc -l这段命令先解压到vehicle_dataset目录,再统计全部文件总数、txt数量和图片数量。正常情况下txt数量应该等于图片数量,因为YOLO数据集里每张图片对应一个同名同路径的标签文件。如果数字对不上,说明存在漏标图片或空标签,这类问题会在训练时表现为某个batch的targets为0,后续章节会专门讲处理方案。
统计完数量后,随手抽查几张图片和一个标签文件,确认标签可读且坐标在合理范围内。比如打开一个标签文件,里面应该是每行5个数字,第一个是类别id,后面四个是归一化坐标。如果出现文件损坏、乱码或坐标大于1的情况,说明标注格式不是标准YOLO文本,需要做格式清洗。
2.2 YOLO标签里的一行数字到底在说什么
YOLO标签格式各家工具导出略有差异,但ultralytics系和darknet系基本统一为一行五个数字:class_id、x_center、y_center、width、height,全部是相对于图像宽高的比例值,取值范围通常在0到1之间。举例来说,如果一张1920x1080的图里有一辆卡车,其左上角在像素坐标(480, 270),右下角在(1440, 810),那么x_center就是(480+1440)/2/1920=0.5,y_center是(270+810)/2/1080=0.5,width是(1440-480)/1920=0.5,height是(810-270)/1080=0.5,对应的标签行就是:0 0.5 0.5 0.5 0.5。
这里有个细节容易被忽略:归一化坐标要基于原图尺寸计算,但很多标注工具导出时已经帮你算好了,你只需要确认坐标值没有大于1或小于0。如果发现负值或超过1,通常是标注框越界后没被裁切,或者导入前经过了resize但标签没同步更新。这类脏数据最好在训练前清洗掉,而不是指望YOLO的loss把它自适应掉。
head -n 5 labels/000001.txt cat classes.txt 2>/dev/null || ls *.names用这条命令看一眼标签内容。如果classes.txt显示的顺序是0: truck, 1: car, 2: motorcycle, 3: bus,那训练时你写的类别名也必须是这个顺序。如果classes.txt缺失,而标签里出现4或者5这类超出四类的id,说明数据集里存在不属于卡车、小型车、摩托车、公交车的标注噪声,需要筛掉或重新归类。
2.3 类名顺序不是靠猜,是靠classes.txt和数据标注源头
类名顺序错位是这个类型数据集最容易翻车的地方。很多标注工具会按照标注人员添加类目的顺序分配id,而不是按文件名或你想象的自然顺序。假设标注者先标公交车,再标卡车,那标签文件里的0就代表公交车,而不是标题顺序里的卡车。这个错位在可视化阶段不容易发现,因为yolo训练时只用id,不关心类名,模型照样能收敛,但推理输出的时候类别就全乱了。
我一般会在训练前先写一个几十行的可视化脚本,把标签画到原图上,直接把每个框和类别名打印出来。这样如果类别顺序错了,一眼就能看到:摩托车的框下面写着bus,或者卡车框上面写着 motorcycle。这种验证比训练结束再看混淆矩阵要便宜得多,因为训练一个模型至少几十分钟,而可视化检查只需要几秒钟。
import cv2 import os import numpy as np img_dir = 'images' label_dir = 'labels' class_names = ['truck', 'car', 'motorcycle', 'bus'] # 以classes.txt实际内容为准 for img_name in os.listdir(img_dir)[:10]: img = cv2.imread(os.path.join(img_dir, img_name)) h, w = img.shape[:2] txt_path = os.path.join(label_dir, os.path.splitext(img_name)[0] + '.txt') if not os.path.exists(txt_path): continue for line in open(txt_path): cls, cx, cy, bw, bh = map(float, line.split()) x1 = int((cx - bw / 2) * w) y1 = int((cy - bh / 2) * h) x2 = int((cx + bw / 2) * w) y2 = int((cy + bh / 2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, class_names[int(cls)], (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 2) cv2.imwrite('check_' + img_name, img)这段代码把前10张图的检测框画出来并把类名写在框上。参数里最关键的是class_names列表的索引顺序,必须与classes.txt里的顺序严格对应,否则框画得对但名字会错,这个错在纯可视化阶段就能暴露。画框时注意坐标要先把归一化值乘回原图尺寸,再换算成整数像素,否则OpenCV的rectangle接口会报错。
3. 把车辆数据集整理成YOLO可训练结构:train/valid划分与data.yaml生成
数据体检没问题之后,下一步是把散落的数据整理成YOLO训练要求的标准结构。YOLOv5和YOLOv8默认支持的数据集目录是images里有train和val两个子目录,labels里同样有train和val两个子目录,图片文件名和标签文件名必须保持一致,且txt放在labels下同名路径中。很多车辆数据集解压出来可能是单一文件夹,甚至图片和标签混在一起,必须先划分再使用。
3.1 常见做法:统一目录结构
我一般会把数据集整理成下面的形态,这种结构在YOLOv8中被称为dataset layout,也是官方train脚本里最不容易出错的格式:
vehicle_dataset/ ├── images/ │ ├── train/ │ ├── val/ ├── labels/ │ ├── train/ │ ├── val/ ├── data.yaml一个常见的坑是images和labels的train、val划分比例不一致,比如图片按8:2切分,但标签文件仍然全部留在根目录,导致训练时标签缺失。出现这个问题的原因是很多人只移动了图片,没有同步移动同名txt。所以划分脚本要同时处理图片和标签,而不是各写一套逻辑。
3.2 一键划分脚本与参数说明
下面这个Python脚本能完成从原始目录到标准目录的划分,支持按比例随机拆分,并且在每次运行时固定随机种子,保证可复现。
import os import shutil import random src_img_dir = 'images' # 原始图片目录 src_label_dir = 'labels' # 原始标签目录 dst_root = 'yolo_dataset' # 输出根目录 val_ratio = 0.2 # 验证集比例 seed = 42 random.seed(seed) os.makedirs(f'{dst_root}/images/train', exist_ok=True) os.makedirs(f'{dst_root}/images/val', exist_ok=True) os.makedirs(f'{dst_root}/labels/train', exist_ok=True) os.makedirs(f'{dst_root}/labels/val', exist_ok=True) img_names = [f for f in os.listdir(src_img_dir) if f.lower().endswith(('.jpg', '.jpeg', '.png'))] random.shuffle(img_names) val_count = int(len(img_names) * val_ratio) val_set = set(img_names[:val_count]) for img_name in img_names: img_src = os.path.join(src_img_dir, img_name) label_src = os.path.join(src_label_dir, os.path.splitext(img_name)[0] + '.txt') if img_name in val_set: img_dst = os.path.join(dst_root, 'images', 'val', img_name) label_dst = os.path.join(dst_root, 'labels', 'val', os.path.splitext(img_name)[0] + '.txt') else: img_dst = os.path.join(dst_root, 'images', 'train', img_name) label_dst = os.path.join(dst_root, 'labels', 'train', os.path.splitext(img_name)[0] + '.txt') shutil.copy(img_src, img_dst) if os.path.exists(label_src): shutil.copy(label_src, label_dst) else: print(f'警告: {img_name} 缺少对应标签文件')这段脚本的逻辑是按文件名把图片随机分成训练集和验证集,然后同步复制对应的txt标签文件。val_ratio设为0.2意味着2129张图像大约有426张进入验证集,剩余1703张用于训练。seed参数固定为42,方便你下次用相同数据复现同一个划分结果。输出目录里的文件名使用copy而非move,属于备份保护,数据源文件未改动,万一划分有问题可以随时重跑。
脚本会在缺标签时打印警告,这是刻意设计的。如果告警数量超过几十个,说明原数据集有大量漏标图片,训练前需要把没标签的图片直接从目录里剔除,否则训练时OpenCV读取正常、但输入没有标注,loss会异常波动。
3.3 验证划分结果和生成配置文件
划分完成后,用find命令检查一下两边数量。
find yolo_dataset/images/train -type f | wc -l find yolo_dataset/labels/train -type f | wc -l find yolo_dataset/images/val -type f | wc -l find yolo_dataset/labels/val -type f | wc -l四个数字两边应该一一对应。确认后写data.yaml,这是YOLOv8训练时唯一要喂的数据集描述文件,内容如下:
path: /path/to/yolo_dataset train: images/train val: images/val names: 0: truck 1: car 2: motorcycle 3: bus需要特别注意path字段建议写绝对路径,在Windows和Linux下避免相对路径引起的“训练时找不到图片”问题。names字段的顺序必须和标签里的class_id对应,这是整个训练过程中决定模型输出语义的唯一依据。如果你的classes.txt顺序和这里不一致,必须修改names,而不是修改标签文件里的所有id,后者要把两千多个文件全部重写,风险太高。
4. 用这个车辆数据集训练YOLO:从预权重选择到训练命令落地
目录结构和data.yaml就绪后,就到了真正训练的部分。这里围绕yolo算法本身的参数选择和损失函数做展开,先回答一个新手最容易纠结的问题:该选YOLOv5还是YOLOv8,该用s还是n尺寸。我的结论是,如果机器显存够,优先YOLOv8s;如果显存只有6G左右,就用YOLOv8n,这个数据集只有2129张图,模型尺寸过大反而容易在验证集上过早过拟合,n和s之间的mAP差距会被样本量放大。
4.1 选v8s还是v5s:先看硬件再定模型尺寸
YOLOv8相比v5最大的改动在head部分,把之前的anchor-based检测头换成了anchor-free的decoupled head,并引入了DFL损失用于回归分布。对于车辆检测这种框比较规整、宽高比变化不极端的任务,v8的收敛速度通常更快,对边界框回归的稳定性更好,尤其适合这个数据集里那种大量大目标和小目标混合的情况。如果项目部署环境必须是老框架和TensorRT的旧版本,那选v5s更省事;否则优先v8。
预权重建议直接用官方提供的COCO预训练权重作为起点。虽然预训练权重里的80类物体和你的四类车不完全匹配,但它的backbone已经学会了纹理、边缘和形状特征,迁移过来的收敛速度比从随机初始化训练要快很多。对2129张图这个量级,从零训练基本不可取,因为你连几个epoch都跑不满就会在验证集上看到抖动剧烈的曲线。
训练命令各种参数里的玄学很多,最影响结果的是imgsz、batch和lr0这三个。imgsz直接决定输入分辨率,直接影响小目标车的检测能力。车辆的摩托车目标在640分辨率下只有几十个像素宽,如果imgsz降到416,这类小目标几乎会丢失。所以这个数据集我建议至少640起步。batch的大小要配合显存,batch越大BN层的统计量越稳定,但显存不够就只能缩小。
4.2 训练命令与关键参数说明
yolo detect train \ data=vehicle_dataset/data.yaml \ model=yolov8s.pt \ epochs=150 \ imgsz=640 \ batch=16 \ lr0=0.01 \ patience=20 \ device=0 \ seed=42这里几个参数说明一下。model指定预权重路径,yolov8s.pt会自动从ultralytics下载,没有外网的环境要提前下载放到同目录。epochs设150是因为数据量不大,150轮足够收敛且有提前停机的空间。patience是早停轮数,表示验证集mAP连续20轮不提升就停训,这个值避免了你晚上睡觉时训练跑到几百轮白白浪费电。device=0表示用第一张显卡,如果你的机器只有CPU,改成device=cpu,但训练时间会从半小时变成七八个小时。lr0初始学习率0.01对迁移学习是一个稳妥起点,如果发现前50轮loss不下降先不要动lr0,优先考虑是不是数据划分或标签有问题。
损失函数部分YOLOv8会自动构建:box分支用DFL和CIoU的组合,cls分支用BCE,整体loss是三者加权求和。DFL的作用是让边界框回归输出一个离散分布而不是单点,对车辆这种边界清晰的刚性目标很友好。你不需要手动改这些权重,但要会看训练日志里的box_loss和cls_loss。如果box_loss降得很快而cls_loss迟迟不降,问题基本出在标签噪声上,也就是第2章里说的类目错位或者漏标。
4.3 训练完成后的验证与推理命令
训练结束后,ultralytics会把best.pt和last.pt存到runs/detect/train/weights下面。验证时用best.pt跑一遍val集,注意这里不要再用训练集,否则mAP会虚高。
yolo detect val \ model=runs/detect/train/weights/best.pt \ data=vehicle_dataset/data.yaml \ imgsz=640 \ batch=16 \ conf=0.25 \ iou=0.5conf是置信度阈值,0.25表示只有预测概率超过0.25的框才会被保留参与mAP计算。iou是NMS的IoU阈值,0.5是检测任务的标准设定,要对比不同模型时不要随便改这个值,否则mAP数字没有可比性。val结束后日志里会打印每类的mAP50和mAP50-95,重点关注mAP50-95,它对框位置的精确程度更敏感,车辆检测里很多实际项目要求框和路面元素对齐,mAP50-95更能反映真实效果。
推理单张图片看效果,用下面的命令:
yolo detect predict \ model=runs/detect/train/weights/best.pt \ source=test_images/ \ conf=0.3 \ save=Truesource可以指向图片文件夹或视频文件,save=True会把画完框的结果存到runs/detect/predict目录。这一步的意义是看模型在真实场景里的输出,而不是只看指标。如果发现小汽车被反复漏检,先查训练集里小型车的样本数和标注质量,而不是急着调conf阈值。
5. 车辆数据集训练避坑:五条能让模型翻车的血泪经验
这个章节整理我在处理类似车辆数据集时最常遇到的五个问题,全部来自真实训练现场。每一条都按现象、原因、解决三段式来说明,建议你把第2章的可视化脚本和这里的检查脚本配套使用,把问题在训练前尽量拦截下来。
5.1 图片能打开但标签找不到:文件名编码与后缀不一致
现象:训练日志提示找不到某些图片对应的txt,或者标签文件数量少于图片数量;也有情况是Windows下解的zip包在Linux服务器训练,文件名里的中文全部变成乱码,导致OpenCV读取图片直接失败。
原因:zip包在Windows下解压默认用GBK编码,传到Linux后用UTF-8解码文件名会乱码;另一个常见原因是图片后缀大小写不一致,比如训练集里既有.jpg也有.JPG,而分割脚本只匹配了其中一种。
解决:不要直接用系统的unzip解压这个数据集,改用Python的zipfile模块,在解压时指定文件名用UTF-8重新编码。关于后缀问题,在划分脚本里统一把所有图片重命名为小写的.jpg,相应地修改txt文件名,格式不会变,但能避免后续所有路径匹配问题。
import zipfile import os with zipfile.ZipFile('yolo车辆数据集.zip', 'r') as zf: for info in zf.infolist(): # 强制用UTF-8重命名内部文件 new_name = info.filename.encode('cp437').decode('utf-8', errors='ignore') info.filename = new_name zf.extract(info, 'clean_dataset')这段代码解决了Windows和Linux之间解压乱码的最典型场景。zipfile默认把文件名按CP437解码,而中文系统写压缩包时通常是GBK,所以先把原始字节解成CP437,再转成UTF-8就能得到正确文件名。注意errors='ignore'透明丢弃无法转码的字符,如果文件名里有极特殊字符,解压后会变短,但至少不会导致整个解压中断。解压完成后再跑一遍图片-txt数量对比脚本即可。
5.2 class_id错位:把摩托车当成公交车的最大元凶
现象:训练正常、loss正常下降、验证mAP也到了0.85以上,但推理时把摩托车全部标成公交车,或者小型车和卡车偶尔互换。
原因:训练时的data.yaml里names顺序与标签文件的class_id不一致,最常见的是标注工具先建了bus这个类,后续才加truck,导出的标签里0是bus而不是truck,而你的names里0写的却是truck。
解决:用第2章的可视化脚本全量抽查一遍。如果确认顺序错位,优先改data.yaml里的names顺序,而不是去重写所有txt。因为改names只需要一次,改标签文件要遍历2129个txt并重写每个id,出错概率太高,而且以后如果重新导出标签又会乱。
5.3 小目标车辆漏标导致AP上升很慢
现象:训练曲线里car类的mAP50涨得很快,motorcycle类一直在0.3附近徘徊,而且随着epoch增加不再上升。
原因:摩托车在图像里的像素占比较小,放大缩小时边界框的标注框可能只覆盖了车身的一部分,后视镜、骑手和行人被排除在框外,导致模型学到的特征不完整;另一个原因是数据集中本来就存在大量这类目标的漏标,一张图有三辆摩托车但标签只有一个框,训练时模型看到另外两个摩托车没被惩罚,学不到应有的特征。
解决:对数据集做一次小目标过滤统计,把所有标签中宽度或高度小于16像素的目标数量列出来。如果数量超过总量10%,建议先用第3章的划分脚本,把那些小目标框过小或严重漏标的图片单独剔出去,优先保证训练集干净,后续再补标。补标阶段常见做法是用训练过的模型做预标注,再让人工只修正漏掉的目标,这样能把人力成本压下来。
5.4 样本类别不均衡:mAP很高但公交车召回极低
现象:验证集整体mAP50有0.88,但看per-class结果,bus的recall只有0.6,而car的recall是0.95。
原因:数据集2129张图里,car占了一半以上样本,bus可能只有一百多张,导致模型对bus的特征学习不足。整体mAP被多数类拉高了,掩盖了少数类的糟糕表现。
解决:不要在整体mAP上乐观,先做类别分布统计。如果bus只有一百多张图,优先做数据增强层面的类别拷贝,比如把含bus的图片复制两份并在复制时做小幅平移或缩放,增加该类的出现频率;也可以直接给分类损失加一个类别权重,比如bus的权重设为3,car设为1。需要说明的是,这种权重调整要在data.yaml里加一个额外配置项,或者直接改训练脚本,不是简单改文本能做到的,建议先做数据层面的增样,权重方式留到增样效果不够再上。
5.5 zip包解压出现CRC错误
现象:解压过程报CRC failed或unexpected end of archive,强行解压后缺失部分图片,训练时数量对账对不上。
原因:压缩包传输过程中损坏,或者下载工具断点续传导致文件不完整。CRC是zip格式的完整性校验,报CRC说明文件数据确实坏了,不是解压工具的玄学。
解决:先用下面的命令做完整性测试,再决定要不要重下。
unzip -t yolo车辆数据集.zip如果输出里有error字样,基本只能重新下载。下载后立刻再跑一遍unzip -t,确认干净再继续。注意不要对报CRC的压缩包使用zip -FF修复,那些修复出来的文件往往是乱码或半截数据,喂给训练只会产生更多脏标签。类似的道理是不要在网上搜什么zip密码移除工具,这类工具在正常数据集上没有意义,还容易引入恶意软件。
6. 评估这个车辆数据集值不值得用:做一次数据体检再决定增删
很多人在训练前不会主动去统计数据本身的质量,直到模型效果不达预期才反过来查数据。一个更高效的做法是先用一段几十行的Python脚本,算出这个数据集的类别分布、框尺寸分布、每张图的标注数量,然后根据报告决定是直接训练、做增强、还是补标注。这一步放在训练前,能让你对这个数据集的实际价值有数。
import os import cv2 img_dir = 'yolo_dataset/images/train' label_dir = 'yolo_dataset/labels/train' class_count = {0: 0, 1: 0, 2: 0, 3: 0} img_count_with_boxes = 0 bbox_sizes = [] for img_name in os.listdir(img_dir): img = cv2.imread(os.path.join(img_dir, img_name)) h, w = img.shape[:2] txt_path = os.path.join(label_dir, os.path.splitext(img_name)[0] + '.txt') if not os.path.exists(txt_path): continue lines = open(txt_path).read().strip().splitlines() if lines and lines[0]: img_count_with_boxes += 1 for line in lines: cls, cx, cy, bw, bh = map(float, line.split()) class_count[int(cls)] += 1 bbox_sizes.append((bw * w, bh * h)) print('有标注的图像数:', img_count_with_boxes) print('各类目标总数:', class_count) print('平均每张图的目标数:', len(bbox_sizes) / max(1, img_count_with_boxes)) small = sum(1 for bw, bh in bbox_sizes if bw < 32 or bh < 32) print('小目标框(<32px)占比:', small / len(bbox_sizes))这段脚本输出的三个值最有参考意义:各类目标总数能让你判断是否存在严重类不平衡;平均每张图的目标数如果小于1,说明大量图片可能没有标注或只有单个目标,模型学到的上下文信息不充分;小目标框占比则决定你要不要为了提高检测精度增加数据增强策略。如果小目标框占比超过20%,建议在训练时打开mosaic增强,并考虑在推理阶段用原图检测和大图切分检测,而不是直接缩小输入分辨率。
数据体检报告出来后,我的经验习惯是先跑一个baseline:用默认参数训练100个epoch,记录四类的AP,再针对薄弱类做一次数据增强或补标注,接着再训一次对比。这个数据集的底子是够的,但大概率存在类不平衡,前一次训练结果就是最好的证据。以我的经验,拿到这种历史数据集,第一版尽量减少改动先跑通链路,第二版再围绕数据治理做优化,不要期望一次就出完美模型。希望帮到你。
本文还有配套的精品资源,点击获取