简介:这是一份面向YOLO系列目标检测学习者的行人车辆标注数据集,适用于yolov5、yolov8、yolov9、yolov7、yolov10及yolo11等主流算法,可直接用于模型训练与验证测试,适合入门练手或课程项目实践。资源包共994个文件,包含331张jpg图像、331个txt标签、331个xml标签以及1个yaml配置文件,压缩包约19.4MB,其中txt为YOLO格式标注,xml为VOC格式标注,两种格式分别存放便于按需选用,data.yaml已划分好数据集,省去手动整理环节。标注采用归一化中心点与宽高比例,类别索引从0开始,涵盖汽车与行人两类目标。目前已有83人学习下载,读者可借此快速搭建训练流程、验证数据增强与模型调参效果,并对比YOLO与VOC两种标注格式的转换与使用差异,为后续自定义数据集标注与迁移学习提供参考。
1. 331张图像的行人车辆数据集:小样本YOLO训练到底能不能落地
手上拿到一个标注好的行人车辆数据集,331张图像,带YOLO格式标签,类别就两个:汽车和人。第一反应大概率是——这么点数据,能训出能用的模型吗?我一开始也这么想,但实际跑过几轮之后发现,这个量级在特定场景下完全能打,关键看你怎么用。
这个数据集解决的核心问题是:快速验证一个行人车辆检测方案是否可行,而不是直接上生产。适合谁?适合刚入门YOLO想跑通全流程的人,适合需要快速做demo验证产品思路的团队,也适合做边缘设备部署前想先摸清模型下限的工程师。331张图像听起来少,但如果场景集中、标注质量过关,配合迁移学习和合理的数据增强,mAP能到可用的水平。下面把我从数据检查到训练调参再到部署验证的完整路径拆开讲,每一步都给出可复现的操作。
2. 拿到331张带标签图像后,先做这三项数据体检
2.1 标签格式确认与类别映射
YOLO格式的标签是每张图对应一个txt文件,每行格式为类别索引 中心x 中心y 宽度 高度,坐标都是归一化到0到1之间的浮点数。331张图像带标签,第一步不是急着训练,而是确认标签文件和图像文件是否一一对应,以及类别索引到底对应什么。
常见做法是写个脚本快速统计:
import os from pathlib import Path from collections import Counter img_dir = Path("images") label_dir = Path("labels") img_files = {p.stem for p in img_dir.glob("*.jpg")} label_files = {p.stem for p in label_dir.glob("*.txt")} # 检查配对情况 only_img = img_files - label_files only_label = label_files - img_files print(f"仅有图像无标签: {len(only_img)}") print(f"仅有标签无图像: {len(only_label)}") # 统计类别分布 class_counter = Counter() for lbl in label_dir.glob("*.txt"): with open(lbl) as f: for line in f: parts = line.strip().split() if parts: class_counter[int(parts[0])] += 1 print("类别分布:", dict(class_counter))这段代码做两件事:一是找出图像和标签不匹配的文件,二是统计每个类别的标注框数量。如果发现某个类别只有几十个框,那训练时就要考虑过采样或者调整损失权重。参数方面,img_dir和label_dir按实际路径改,类别索引从0开始,通常0是行人、1是汽车,但一定要跟数据集提供方确认,别自己猜。
2.2 图像尺寸分布与标注框尺寸统计
331张图像的尺寸可能不统一,直接resize到640×640会带来什么问题?小目标变得更小,大目标比例失真。先统计一下原始尺寸和标注框的宽高分布:
import cv2 import numpy as np widths, heights = [], [] box_areas = [] for img_path in img_dir.glob("*.jpg"): img = cv2.imread(str(img_path)) h, w = img.shape[:2] widths.append(w) heights.append(h) lbl_path = label_dir / (img_path.stem + ".txt") if lbl_path.exists(): with open(lbl_path) as f: for line in f: parts = line.strip().split() if len(parts) == 5: bw, bh = float(parts[3]) * w, float(parts[4]) * h box_areas.append(bw * bh) print(f"图像宽度范围: {min(widths)}-{max(widths)}, 均值: {np.mean(widths):.0f}") print(f"图像高度范围: {min(heights)}-{max(heights)}, 均值: {np.mean(heights):.0f}") print(f"标注框面积中位数: {np.median(box_areas):.0f} 像素")如果标注框面积中位数低于32×32像素,就属于小目标检测范畴,训练时输入分辨率不能降太多,建议保持640甚至更高。如果图像本身分辨率差异大,比如有的1920×1080有的640×480,那就要在数据加载时统一处理策略——要么全部letterbox到640×640,要么分组训练。
2.3 数据增强策略的取舍
331张图像做增强,不是越多越好。我一般会开这几项:随机水平翻转(概率0.5)、随机缩放(0.5到1.5倍)、随机平移(0.1)、HSV色彩抖动(色调0.015、饱和度0.7、亮度0.4)。马赛克增强(mosaic)对小数据集提升明显,但要注意如果图像里目标本身就密集,mosaic拼接后可能造成目标截断,反而引入噪声。
提示:331张图像如果场景单一(比如都是同一路段、同一时间段拍的),增强参数可以激进一些;如果场景本身多样,增强反而可能让模型学到不相关的纹理。
类别不平衡时,可以在数据加载器里对包含稀有类别的图像做重复采样。比如行人标注框只有汽车的三分之一,那就把包含行人的图像在每个epoch里多采样一次。这个操作在YOLO的dataloader里改一下采样权重就行,不用改网络结构。
3. 用YOLOv8在331张图上跑通训练:配置文件与关键参数
3.1 数据集yaml配置与目录结构
YOLOv8要求的数据集配置文件是一个yaml,里面指定训练集、验证集路径和类别名称。331张图像建议按8:2划分,训练集264张、验证集67张。如果数据量再少,可以考虑交叉验证,但331张用固定验证集就够了。
目录结构建议这样组织:
dataset/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── data.yamldata.yaml内容:
path: ./dataset train: images/train val: images/val names: 0: person 1: car这里path是数据集根目录,train和val是相对路径。类别索引必须和标签文件里的数字对应,names里的顺序就是索引顺序。如果标签里0是car、1是person,那names也要反过来写,否则训练出来的模型会把类别搞混。
3.2 训练命令与超参数设置
用YOLOv8n或YOLOv8s这种小模型就够了,331张图像训大模型纯属浪费算力还容易过拟合。我一般用YOLOv8s,参数量适中,在边缘设备上也能跑。
yolo detect train \ data=dataset/data.yaml \ model=yolov8s.pt \ epochs=200 \ imgsz=640 \ batch=16 \ lr0=0.01 \ lrf=0.01 \ patience=50 \ augment=True \ mosaic=1.0 \ mixup=0.1 \ copy_paste=0.1 \ device=0 \ project=runs/train \ name=ped_car_331逐项说明:epochs=200是因为小数据集需要更多轮次收敛,但配合patience=50早停,验证集50轮不提升就停。lr0=0.01是初始学习率,小数据集可以适当降低到0.005避免震荡。mosaic=1.0表示始终开启mosaic增强,mixup=0.1和copy_paste=0.1是额外增强,概率低一点防止过拟合。batch=16在显存够的情况下尽量大一点,331张图一个epoch也就17个batch。
如果显存不够,把batch降到8,同时把lr0按比例降到0.005。imgsz=640是标准输入尺寸,如果标注框普遍偏小,可以提到1280,但训练时间会翻倍。
3.3 训练过程监控与早停判断
训练启动后重点看三个指标:box_loss、cls_loss和mAP50。前10个epoch loss下降快是正常的,如果50个epoch后box_loss还在0.5以上,说明模型没学好,要么是学习率太大,要么是标注有问题。
from ultralytics import YOLO model = YOLO("runs/train/ped_car_331/weights/best.pt") metrics = model.val(data="dataset/data.yaml", imgsz=640, batch=16) print(f"mAP50: {metrics.box.map50:.4f}") print(f"mAP50-95: {metrics.box.map:.4f}") print(f"每类AP: {metrics.box.ap50}")验证时注意看每类的AP,如果行人AP远低于汽车,说明行人类别样本太少或者标注质量差。331张图像里如果行人只有几十个框,AP低是正常的,可以通过增加行人类别的损失权重来缓解,在训练命令里加cls=1.5把分类损失放大。
注意:验证集不要用来调参,调参看训练集的loss曲线,验证集只在最后评估用。331张图像如果反复在验证集上调,很快过拟合。
4. 小数据集训练行人车辆检测的五个翻车现场
4.1 现象:训练loss正常下降但验证mAP始终在0.2以下
原因:标注框坐标归一化时用了错误的图像尺寸。比如图像实际是1280×720,但标注时按640×640算的,导致所有框位置偏移。331张图像如果来自不同来源,尺寸不统一,这个问题特别常见。
解决:写脚本逐张检查标注框是否超出图像边界,以及框的宽高是否合理。正常标注框的宽高应该在0.01到0.9之间,如果大量框的宽高接近1.0,说明归一化用错了分母。
4.2 现象:模型把汽车检测成行人,或者两个类别置信度都很低
原因:类别索引映射错了。标签文件里0和1对应的类别和data.yaml里的names不一致。有些数据集标注时0是car,但训练时按0是person配的。
解决:打开几个标签文件,结合图像看一眼,确认0到底对应什么。如果不确定,把两个类别的AP分别打出来,AP异常低的那个大概率是映射错了。
4.3 现象:训练到100轮后box_loss突然飙升
原因:学习率没有衰减或者衰减太慢,模型在局部最小值附近震荡。331张图像的小数据集,学习率对训练稳定性影响很大。
解决:用余弦退火学习率调度,在训练命令里加cos_lr=True,或者把lrf设成0.001让学习率降到初始值的千分之一。另外检查一下有没有脏数据,比如某张图的标注框坐标是NaN。
4.4 现象:验证集mAP比训练集低很多,差距超过0.3
原因:过拟合。331张图像训200个epoch,模型把训练集背下来了。数据增强不够或者模型太大都会导致这个问题。
解决:先看训练集和验证集的loss曲线,如果训练loss持续下降但验证loss在某个点后开始上升,就是过拟合。减小模型(YOLOv8n代替YOLOv8s)、增加增强强度、加dropout(YOLOv8里用dropout=0.1)、早停。
4.5 现象:推理时检测框大量重叠,NMS后只剩一个框
原因:标注时同一个目标被标了多个框,或者两个目标挨得太近。331张图像如果标注质量参差不齐,这个问题很常见。
解决:训练前做一次标注清洗,用IoU阈值0.7检查同一张图里有没有高度重叠的同类框,有就合并或删除。推理时把NMS的IoU阈值从0.45调到0.5到0.6之间,给密集目标留更多框。
5. 331张图像训完后的模型能干什么:验证与部署的边界
5.1 用混淆矩阵和PR曲线判断模型真实水平
训练完不要只看mAP,把混淆矩阵和PR曲线拉出来看。混淆矩阵能告诉你模型把多少汽车误判成了行人,PR曲线能看出在不同置信度阈值下的召回率和精确率。
from ultralytics import YOLO import matplotlib.pyplot as plt model = YOLO("runs/train/ped_car_331/weights/best.pt") results = model.val(data="dataset/data.yaml", plots=True, save_json=True) # 混淆矩阵和PR曲线会自动保存到runs/val目录下看混淆矩阵时重点关注对角线以外的数值。如果汽车和行人之间的误判超过10%,说明两个类别的特征区分度不够,要么增加样本,要么在损失函数里加大类间距离的惩罚。
5.2 导出ONNX并在CPU上测推理速度
331张图像训出来的模型,参数量小,导出ONNX后在CPU上也能跑。导出命令:
yolo export model=runs/train/ped_car_331/weights/best.pt format=onnx imgsz=640 simplify=True导出后用onnxruntime测一下单张推理耗时:
import onnxruntime as ort import numpy as np import time sess = ort.InferenceSession("best.onnx") input_name = sess.get_inputs()[0].name dummy = np.random.randn(1, 3, 640, 640).astype(np.float32) # 预热 for _ in range(5): sess.run(None, {input_name: dummy}) start = time.time() for _ in range(50): sess.run(None, {input_name: dummy}) print(f"平均推理耗时: {(time.time()-start)/50*1000:.1f} ms")如果CPU上单张超过100ms,说明模型还是偏大,换YOLOv8n重新训。331张图像训YOLOv8n和YOLOv8s的mAP差距通常在3到5个点以内,但速度差一倍。
5.3 什么场景下这个模型直接能用,什么场景必须补数据
331张图像训出来的行人车辆检测模型,在以下场景可以直接用:固定摄像头、光照条件稳定、目标尺度变化不大、背景单一。比如小区门口的行人车辆统计、停车场出入口的车辆计数。
以下场景必须补数据:夜间或低光照、雨雪雾天气、目标密集且遮挡严重、摄像头角度变化大。这些场景下331张图像覆盖不到,模型会漏检或误检。补数据时优先补困难样本,比如夜间行人、被部分遮挡的车辆,每类再补200到300张就能明显提升。
我自己的习惯是,每次训完模型先拿一段没参与训练的实拍视频跑一遍,看漏检和误检集中在什么场景,然后针对性地补那类数据。331张图像是个起点,不是终点。希望帮到你。
本文还有配套的精品资源,点击获取