
简介面向目标检测入门与进阶学习者这套YOLO车辆行人识别数据集以person行人和car车辆两个目标类别为核心包含5000多张真实场景图片可用于行人车辆检测模型的训练与评估。资源打包为zip格式压缩包内共有16822个文件jpg原图5607张配套的txt标签5607个、xml标签5607个两种标注文件分别保存在不同文件夹中便于直接对接YOLO系列以及Pascal VOC等常见训练框架文件组织清晰使用起来非常直观。压缩包整体约499.54MB解压后目录结构清晰可快速划分训练集与验证集。目前已有3714人学习下载数据规模适中既适合新手练习数据预处理和标注格式转换也适合研究者在此基础上进行数据增强或迁移学习等实验。整体而言这套数据集覆盖了模型训练所需的基础元素是开展车辆行人检测实践的高性价比选择。1. YOLO车辆行人识别数据集不是用来观赏的先把训练入口搭起来提到YOLO车辆行人识别数据集第一次接触目标检测的工程师容易高估它的作用能训练不代表直接用得好。车辆和行人看上去类别简单但不同来源的素材在标注口径上差异很大有的把公交车归为轿车有的把骑电动车的人画成一个完整框这类细节直接影响模型上限。这份资源实际能解决两件事把一堆散落的图片和标注整理成 YOLO 能直接读的目录和 txt 文件再把训练、验证、视频推理整个链路跑通给你一个能接着调的基线。适合两类人第一次自己训练检测模型的学生以及已经跑过 demo 但没时间从零攒数据的团队。先说结论跳过环境安装的重复劳动我把实际拆解过程中容易踩的目录组织、坐标转换、参数设置和训练坑按顺序写了一遍照着做两小时以内能把训练拉起来。2. 把标注转成YOLO的txt目录组织、坐标换算与预处理脚本2.1 先让数据集目录长成 YOLO 容易识别的样子YOLO 训练时不会直接读 XML 或 JSON它约定了一套自己的目录规则图像放在 images 下同名标注放在 labels 下训练和验证分得清清楚楚。这套规则的好处是读取快训练脚本只需要按路径扫描图片再靠文件名去 labels 里找对应 txt不需要在内存里维护一堆 JSON 结构。dataset/ ├── images/ │ ├── train/ │ │ ├── img_001.jpg │ │ └── img_002.jpg │ └── val/ │ └── img_003.jpg ├── labels/ │ ├── train/ │ │ ├── img_001.txt │ │ └── img_002.txt │ └── val/ │ └── img_003.txt └── data.yaml这里有个容易忽略的点images 和 labels 下的子目录名必须完全一致train 对 trainval 对 val。如果 images/train 有 100 张图而 labels/train 只有 80 个 txt训练不会直接报错而是跳过没有标签的图片并在日志里刷 Warning。这个警告很容易被忽略但会造成验证集和训练集目标总数对不上最后看混淆矩阵时一头雾水。我一般会在拿到资源后先做一次对齐检查遍历 images/train 下所有 jpg去掉扩展名后在 labels/train 里找同名 txt找不到就单独放一个 missing 目录。这样能快速区分是标注缺失还是文件名编码问题。Windows 上尤其要注意 jpg 和 JPG 扩展名大小写Linux 复制过来后大小写敏感容易大面积失配。2.2 从 VOC/COCO 样式标注转换为 YOLO txt 的核心逻辑YOLO 的标注格式是归一化的 cxcywh也就是目标中心点 x、中心点 y、宽度 w、高度 h数值范围在 0 到 1 之间。而很多公开数据集的原始标注是 xyxy 或 cxcywh 的绝对像素坐标格式不一。这份车辆行人识别数据集如果混用了不同来源的素材第一步就是统一成 YOLO txt。下面这段脚本能把 Pascal VOC 的 XML 转成 YOLO 的 txt# convert.py # 将Pascal VOC格式的XML标注转成YOLO训练用的TXT import os import xml.etree.ElementTree as ET # 类别映射按你数据集的类别清单顺序修改 CLASSES { car: 0, pedestrian: 1, truck: 2, bicycle: 3, bus: 4, } def convert_label(xml_path, out_dir): tree ET.parse(xml_path) root tree.getroot() # 优先读XML里的原始图像尺寸外部传入的尺寸可能被resize过 size root.find(size) img_w int(size.find(width).text) img_h int(size.find(height).text) lines [] for obj in root.iter(object): name obj.find(name).text if name not in CLASSES: continue bndbox obj.find(bndbox) x1 float(bndbox.find(xmin).text) y1 float(bndbox.find(ymin).text) x2 float(bndbox.find(xmax).text) y2 float(bndbox.find(ymax).text) # 个别标注存在x2小于x1的情况直接跳过避免生成负数宽高 if x2 x1 or y2 y1: continue cx (x1 x2) / 2 / img_w cy (y1 y2) / 2 / img_h w (x2 - x1) / img_w h (y2 - y1) / img_h lines.append(f{CLASSES[name]} {cx:.6f} {cy:.6f} {w:.6f} {h:.6f}) filename os.path.basename(xml_path).replace(.xml, .txt) with open(os.path.join(out_dir, filename), w) as f: f.write(\n.join(lines)) return lines脚本里两个地方需要注意。一是类别映射表 CLASSES 的顺序决定了训练时每个类别的 id一定要和后面 data.yaml 里的 names 保持一致否则会出现训练时认为类别 0 是 car推理时却把类别 0 显示成 pedestrian 的错乱。二是宽高计算用的是 XML 里记录的原始图像尺寸不是外部传入的尺寸。很多数据集的 XML 里长宽和实际 jpg 像素一致但经过某些工具批处理后会不一致这时候优先相信文件头信息更稳妥的做法是直接用 PIL 读取图片尺寸再换算。COCO 格式JSON同理只是解析路径不同从 annotations 里按 image_id 关联每一张图的 bboxbbox 字段是 [x, y, width, height]换算成 cxcywh 时用cx x w / 2cy y h / 2。我习惯单独写一个coco_to_yolo.py避免把两种逻辑混在一起后面排查问题更省事。2.3 用划分脚本完成 train/val 拆分并统计类别分布转换完成后下一步是划分训练集和验证集。很多数据集资源默认只给了全部图片和标签没有划分好的目录结构。直接全部拿去训练会让验证集失去意义因为模型在训练时已经见过这些图最后报出来的 mAP 虚高部署时一跑真实场景就露馅。# split_dataset.py # 将images和labels按同文件名划分到train/val目录并统计类别数量 import os import shutil import random from collections import Counter random.seed(42) base_dir dataset image_dir os.path.join(base_dir, images_all) label_dir os.path.join(base_dir, labels_all) train_img_dir os.path.join(base_dir, images, train) val_img_dir os.path.join(base_dir, images, val) train_label_dir os.path.join(base_dir, labels, train) val_label_dir os.path.join(base_dir, labels, val) for d in [train_img_dir, val_img_dir, train_label_dir, val_label_dir]: os.makedirs(d, exist_okTrue) # 只取那些既有jpg又有txt的文件名避免空标注文件混入 all_names [] for fname in os.listdir(image_dir): name, ext os.path.splitext(fname) if ext.lower() in [.jpg, .jpeg, .png]: if os.path.exists(os.path.join(label_dir, name .txt)): all_names.append(name) all_names.sort() random.shuffle(all_names) split_idx int(len(all_names) * 0.9) counts Counter() for i, name in enumerate(all_names): is_train i split_idx img_src os.path.join(image_dir, name .jpg) label_src os.path.join(label_dir, name .txt) img_dst_dir train_img_dir if is_train else val_img_dir label_dst_dir train_label_dir if is_train else val_label_dir shutil.copy(img_src, os.path.join(img_dst_dir, name .jpg)) shutil.copy(label_src, os.path.join(label_dst_dir, name .txt)) # 统计标签里的类别数量观察分布是否均衡 with open(label_src) as f: for line in f: cls_id int(line.split()[0]) counts[cls_id] 1 print(类别分布, dict(counts)) print(训练集数量, split_idx, 验证集数量, len(all_names) - split_idx)这段脚本把 90% 的数据分给训练10% 分给验证。random.seed(42) 保证每次跑出的划分结果一致方便复现实验。核心逻辑是只用那些同时存在 jpg 和 txt 的文件名避免标签缺失导致训练集和验证集统计口径不一致。不过 9:1 不一定适合所有情况。如果整份资源只有几千张图我会把比例调到 8:2验证集至少要保证每个类别有几十个实例否则最后的 mAP 波动很大。如果资源体量到几万张用 95:5 也可以验证集够看趋势就行。统计类别分布这一步非常关键。车辆行人检测里最常见的翻车现场就是行人样本太少训练完对车很准对行人基本瞎猜。看到 counts 分布后如果行人只有几百个实例宁可用额外数据源补一点也不要硬着头皮开训。3. yolov8训练自己的车辆行人数据集data.yaml、超参与损失曲线观察3.1 写一份不踩坑的 data.yamlYOLOv8 训练入口第一步是读 data.yaml。它的字段不多但路径问题最容易把新手卡住尤其是用 Windows 和 Linux 混着跑的时候。# data.yaml # 建议用绝对路径相对路径在切换工作目录时会突然失效 path: E:/datasets/vehicle_pedestrian train: images/train val: images/val names: 0: car 1: pedestrian 2: truck 3: bicycle 4: bus注意 names 的缩进和顺序。YOLOv8 要求 names 是一个有序映射类别 id 从 0 开始连续排列。如果中间跳了一个 id比如 0、1、3某些版本会直接报错某些版本会静默地把 3 当 2 处理后者更可怕因为错误不会立刻暴露。另外一个细节是 path 指向的是数据集根目录train 和 val 是相对这个根目录的路径。如果把 train 写成绝对路径E:/datasets/vehicle_pedestrian/images/trainYOLOv8 也会接受但会给后续模型导出和部署带来麻烦最好统一用相对路径风格。3.2 训练命令、显存量纲与常见的超参偏好环境装好、数据划分完成后训练命令本身不复杂yolo detect train datadata.yaml modelyolov8s.pt epochs100 imgsz640 batch8这条命令用 yolov8s 预训练权重作为起点。为什么不用 yolov8nn 模型推理速度快但检测小目标和远处行人时精度通常不够而车辆行人识别里“远处的小人”往往是最难的部分。s 模型比 n 大一截但精度提升可观在普通消费级显卡上也能跑。如果显存只有 6G把 batch 降到 4 就好不需要换回 n。epochs 设 100 是个人习惯。这个数据量下模型在 60 到 80 个 epoch 之间基本上收敛完100 留了余量。imgsz 用 640因为 YOLO 预训练权重就是在 640 下做的保持同等输入分辨率才能发挥预训练的优势。如果原始图片分辨率很高想提高小目标召回可以先跑一版 640再用 768 或 896 做一次 finetune。开启训练后有三个地方要盯紧train/box_loss、val/box_loss、以及 mAP 曲线。这里有个容易误读的地方YOLOv8 的输出会把 box_loss、cls_loss、dfl_loss 分开列不要只看一个 total loss因为不同 loss 的量纲不同加权后的 total 曲线看起来平稳不代表每个分支都健康。3.3 用损失曲线和预测框分布判断是否跑歪了我习惯把训练日志导出的 results.csv 拉出来画图# plot_loss.py # 读取YOLOv8训练产生的results.csv并绘制关键指标 import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/detect/train/results.csv) # 用epoch序号替代默认的step df[epoch] range(len(df)) fig, ax plt.subplots(1, 2, figsize(12, 4)) ax[0].plot(df[epoch], df[train/box_loss], labeltrain box) ax[0].plot(df[epoch], df[val/box_loss], labelval box) ax[0].set_title(Box Loss) ax[0].legend() ax[0].grid(True) ax[1].plot(df[epoch], df[metrics/mAP50(B)], labelmAP50) ax[1].set_title(mAP50) ax[1].legend() ax[1].grid(True) plt.show()正常收敛的曲线是训练损失和验证损失一起向下掉最后都趋平。如果 train/box_loss 一直下降val/box_loss 却在某个 epoch 后掉头向上第一反应不是调学习率而是检查是不是验证集里混进了训练图片或者标注本身有问题。这个数据集资源如果是从多个来源拼接的拆分时没有按来源去重同一张图在不同来源里路径不同就会造成这种数据泄漏。除了看曲线还要看 val 阶段生成的混淆矩阵。YOLOv8 会在验证时自动把预测结果和真实框做比对输出一个 N x N 的矩阵。重点关注对角线以外的位置如果 car 被大量预测成 truck说明这两个类别在标注边界上本身就有重叠如果 pedestrian 经常被预测成 background说明行人目标太小或遮挡太重数据增强方向要调整。4. 训练阶段的避坑记录标签错位、小目标漏检与被吃掉的数据4.1 标签与图像文件同名但内容不对齐现象训练能跑完验证时画出来的真实框明显偏移框住的位置和物体对不上甚至出现框宽高大于图像尺寸的异常值。原因最常见的是合并多个来源数据时文件名相同但内容不同。比如 A 来源的img_001.jpg是一辆轿车B 来源的img_001.xml标注的却是行人按文件名拼接后训练脚本读取到的标签和图像完全错位。另一种情况是坐标转换时用了错误的尺寸基准XML 里写的是 1920x1080但实际图片已经被压缩成 1280x720又没有重新换算坐标。解决训练前随机抽样 20 张图把标签框画回到图上肉眼检查。这一步听着原始但最有效。画框脚本只用 OpenCV 读图和 txt 坐标还原成像素坐标后直接画矩形。不要只看 loss 低就跳过这步loss 低只能说明模型拟合了当前标签不能说明标签拟合了现实。4.2 小目标漏检远处行人几乎全部漏掉现象验证集 mAP50 看起来有 0.8 以上但视频里远处的人或骑电动车的人基本检测不到近处车辆倒是一直很准。原因小目标在 640 输入尺寸下经过多次下采样特征图上的像素区域太小信息丢失严重。车辆行人识别数据集里如果充斥着大尺寸的近距离车辆模型会倾向于学习这类特征小目标自然被无视。同时模型默认的 anchor 或动态 anchor 分配策略会抑制小框前景框数量在损失里占比太低。解决三步走。第一步把 imgsz 从 640 提高到 768 或 896推理时也保持相同尺寸第二步开启针对小目标的增强例如 mosaic、mixup 的强度适当降低让模型在更真实的单目标场景里学习第三步在训练后用小框召回率指标评估不要只看 mAP50。另外验证时可以把 conf 阈值从默认 0.25 降到 0.1先看看是不是阈值把低置信度的目标卡掉了如果降低阈值后小目标能出来说明问题在置信度校准不在目标框定位。4.3 验证损失反弹过拟合还是数据泄漏现象训练 40 个 epoch 后 train/box_loss 还在下降val/box_loss 突然向上拐呈经典“开口”形状。原因数据集太小或验证集分布太偏。这个车辆行人识别数据集如果总图片量不足 5000 张模型在两三个 epoch 内就能背下训练集的分布后面就是纯粹的记忆过程。另一个常被忽略的原因是验证集划分方式如果按文件名哈希取模划分来自同一段视频连续帧的图像会同时出现在训练集和验证集里验证结果虚高但如果划分时太刻意避免又会造成验证集和训练集场景差异过大验证损失居高不下。解决先确认划分是按随机文件名而不是按视频片段。如果资源里有连续帧序列应该先把序列按视频维度分组再整体划分。其次开 early stoppingYOLOv8 默认 patience 为 100对小数据集我会调到 15 到 20val loss 连续 20 个 epoch 不降就保存 best.pt 结束训练。不要手动干预学习率让策略自己触发。4.4 混淆矩阵总和对不上不是玄学而是坐标命名问题现象验证集明明有 2000 个真实目标混淆矩阵每一行加起来却不到 2000或者背景列异常大换一次验证集矩阵数字分布完全不同。原因YOLO 混淆矩阵的计算依赖置信度和 IoU 阈值低于阈值的所有预测都归入背景类这是触发总数对不上的第一个原因。第二个原因是 names 顺序和训练时不一致训练时定义类别 0 是 car验证时却把 0 定义成了 pedestrian混淆矩阵看起来就会像是不同模型跑出来的结果。解决验证时使用训练阶段自动保存的 data.yaml不要手动新写一个。混淆矩阵本来就是统计性的不需要追求对角线数量严格等于真实总数但背景列巨大是个预警信号说明你的预测框和真实框的 IoU 重叠度普遍过低。这时候要做的是检查框坐标精度而不是调阈值。4.5 BatchNorm 崩溃loss 突然变成 NaN 或者断崖式上涨现象训练过程正常走完 20 个 epoch某一步开始 train/box_loss 突然变成 NaN或者没有任何征兆地跳高几个数量级再也没降回来。原因小 batch 下 BatchNorm 的均值和方差统计不稳定。车辆行人识别数据集如果类别分布极度不均某个 batch 恰好全是行人而行人数量又少BN 层的统计量会产生大幅度波动。学习率太大也会把损失顶到数值溢出的边缘。解决把 batch 调到 16 或 32确保每个 batch 里有足够的正样本。如果显存不够减小 imgsz 而不是硬撑 batch。另一个稳妥的办法是使用预训练权重作为初始化而不是 from scratch。从零训练一次性把损失打飞的概率会高很多用了预训练权重后即使梯度方向稍有偏差也有约束。遇到 NaN 时不要试图调低学习率继续跑直接删掉当前权重用最后一次正常保存的 pt 调低 lr 重新开始。5. 用视频推理验证过的模型把预测框的尺寸分布捞出来看一眼训练结束后不要直接拿一张测试图看效果了事。我习惯做一次视频推理因为视频里能看到连续帧间的稳定性比单张图片更能暴露问题。yolo detect predict modelruns/detect/train/weights/best.pt sourceroad_test.mp4 conf0.25 iou0.5 save_txtTrue save_confTrue这段命令用 best.pt 对视频逐帧推理把每个预测框写入 labels 目录下的同名 txt。参数方面conf 设 0.25 是常规值它控制保留哪些检测结果iou 设 0.5 是 NMS 的 IoU 阈值同一目标的重叠框如果 IoU 超过 0.5 会被合并。如果发现视频里同一辆车被框出两个重叠框把 iou 调低到 0.3如果发现远处目标一闪一闪地出现把 conf 调低到 0.1 看看是不是漏检。推理完别急着看视频先做一次框尺寸分布统计# analyze_boxes.py # 统计预测框的宽高分布找出可能的漏检区域 import glob from collections import Counter sizes [] categories Counter() for txt in glob.glob(runs/detect/predict/labels/*.txt): with open(txt) as f: for line in f: parts line.split() if len(parts) 5: continue cls_id int(parts[0]) w float(parts[3]) h float(parts[4]) categories[cls_id] 1 # 归一化宽高通常小于0.5如果大于这个值说明目标占了大半个画面 if w 0.1 and h 0.1: sizes.append(small) elif w 0.3 and h 0.3: sizes.append(medium) else: sizes.append(large) print(预测框规模分布, dict(Counter(sizes))) print(类别数量, dict(categories))这段统计的意义是验证模型是否真的学会了“远小人”的识别能力。如果预测框里 90% 都是 medium 和 large说明小目标漏检问题没有解决接下来要做的是用更高分辨率输入重训或者补充包含小目标的训练数据。这比盯着 mAP 数字更有价值因为 mAP 会把大量易识别的近处目标计入分母让指标显得很漂亮。有段时间我拿到任何数据集都会直接开训后来在一个车辆识别项目里被远处行人坑过一次从那以后每次训练前都强制走一遍检查流程划分前做标签对齐训练中盯 val loss 不看 train loss训练后跑视频推理并统计框尺寸分布。这套流程看着麻烦但能帮你省下大量反复训练的时间。希望帮到你。本文还有配套的精品资源点击获取