news 2026/10/2 21:58:06

YOLOv8行人车辆检测数据集实战:从验证集划分到模型训练

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
YOLOv8行人车辆检测数据集实战:从验证集划分到模型训练

简介:面向行人与车辆检测任务的中型标注图像数据集,内含4500张训练图和500张验证图,适合使用YOLOv5等深度学习模型进行目标检测训练与效果评估。压缩包共16821个文件,其中jpg原图5607张,txt格式YOLO标注5607个,xml格式VOC标注5607个,三类文件一一对应,可直接被主流检测框架加载。资源包整体约499.51MB,已有2886人学习或下载。获取后可获得完整的图像与双重格式标注:txt便于YOLO系列快速训练,xml便于VOC格式转换或与其他工具箱衔接;每张图片均带有边界框信息,覆盖不同光照、视角和遮挡场景,能够用于验证模型鲁棒性。该数据集还可作为评估新算法性能的基准,帮助研究者在mAP等指标上横向对比,适用于自动驾驶、交通监控等场景的算法开发。

1. 拿到“行人车辆检测数据集一共4500张验证集500张”,先别急着训练

“行人车辆检测数据集一共4500张验证集500张”这个标题看着很规矩——训练集4000张、验证集500张,比例8:1,和多数目标检测项目的习惯一致。但拿到手的第一反应不该是直接开训,而是先问三个问题:4500张里行人和车辆各占多少;标注框有没有明显越界或漏标;500张验证集和4000张训练集是否来自同一批连续视频帧。这三个问题里只要有一个答错,验证集指标再好看也说明不了真实效果。下面按一套从数据分析、格式校验到YOLOv8训练评估的流程走一遍,新手能照步骤复现,老手重点看边界条件和踩坑的位置。

2. 拆开 4500 张图看内部:类别分布、标注质量与验证集划分

2.1 先确认类别定义:是“行人车辆”两类还是更细

“行人车辆检测”从标题字面看是二分类:行人一类、车辆一类。但真实项目里我建议先把 labels 目录翻一遍。常见做法是统计所有标签文件里出现过的类别编号,确认到底有几个类。用一个命令就能完成:

find labels -name "*.txt" -exec cat {} + | awk '{print $1}' | sort -un

这个命令把 labels 下所有 txt 的第一列(类别 ID)全部打印出来,排序去重。输出只有 0 和 1,说明确实是二分类;如果出现 2 或 3,说明还有额外类别。假设类别编号 0 是行人、1 是车辆,那 data.yaml 里的 names 就写['pedestrian', 'vehicle'],顺序错一个,训练和推理时的类别标签就会整体错位。

还有种更常见的情况:车辆大类下其实混杂了轿车、卡车、公交车、摩托车,但标注时统一归为 1。模型最后训练出来只能判断“是不是车”,业务里如果要求区分大车小车,回头就得重新标注。我的习惯是宁可一开始就把车辆拆成 3~4 个子类。类别拆细之后,模型更容易学到区分性特征;以后真需要细分时,也只是给旧标签重新映射一遍编号,不用推倒重来。

2.2 用 Python 统计每类框数与目标尺寸

确认类别之后,第二步是统计每类目标数量、平均每张图的框数,以及小目标占比。这些数字直接决定训练分辨率和模型选型。以下脚本按 YOLO 标签格式统计:

import os from collections import Counter from PIL import Image img_dir = "images" # 图片目录,train/val 分开时循环处理 label_dir = "labels" class_names = {0: "pedestrian", 1: "vehicle"} # 与 data.yaml 的 names 对齐 cls_count = Counter() total_boxes = 0 small_boxes = 0 empty_labels = [] for txt_name in os.listdir(label_dir): base = os.path.splitext(txt_name)[0] img_path = os.path.join(img_dir, base + ".jpg") if not os.path.exists(img_path): continue w, h = Image.open(img_path).size label_path = os.path.join(label_dir, txt_name) with open(label_path, encoding="utf-8") as f: lines = f.readlines() if not lines: empty_labels.append(txt_name) continue for line in lines: parts = line.split() try: cls_id = int(parts[0]) cx, cy, bw, bh = map(float, parts[1:]) except (ValueError, IndexError): continue cls_count[cls_id] += 1 total_boxes += 1 # YOLO 坐标是归一化值,乘回图片尺寸才是像素大小 box_w, box_h = bw * w, bh * h if box_w * box_h < 32 * 32: # COCO 约定 32x32 以下算小目标 small_boxes += 1 print("每类目标数:", dict(cls_count)) print("总框数:", total_boxes) print("小目标占比:", round(small_boxes / max(total_boxes, 1), 4)) print("空标签文件:", empty_labels[:10])

脚本先按文件名找到对应图片,读取原始尺寸,再解析每行 5 个数字。首列是类别编号,后面四列是归一化的中心点和宽高。把归一化宽高乘回真实宽高,就能判断小目标比例。小目标占比超过 40%,训练时输入分辨率最好提到 768 或 896;占比很低,用 640 就够。空标签文件超过 5%,说明这批数据里有大量“没有目标”的负样本图,会拖慢收敛,一般建议直接删掉或补标。

另一个关键数字是“总框数 / 有效图数”。二分类场景平均每张图只有 0.8 个框,说明画面很稀疏,模型容易欠拟合,训练轮数要适当拉长;平均 3 个以上,说明路口或街道等密集场景占大头,要重点看遮挡和小目标。

2.3 验证集 500 张应该怎么分:按序列不按文件名

4000 张训练、500 张验证的拆分比例本身没问题,约 11% 的验证占比处于 10%~20% 的常见区间。问题是这 500 张怎么从原始数据里挑出来。很多人拿到的视频检测数据集其实是连续帧,文件名叫 clip01_000123.jpg 这样。如果直接按文件名 shuffle 后随机切,相邻两帧很可能一张进训练集、一张进验证集。模型在训练时已经见过同一场景的下一帧,验证时自然“认路”,mAP 虚高。

正确的做法是按片段切分。文件名里通常有片段 ID,把同一个片段的所有帧归到同一分组,整个片段要么全部进 train,要么全部进 val。下面脚本就是按片段 ID 做的:

import os, random from collections import defaultdict random.seed(42) all_images = [n for n in os.listdir("all_images") if n.endswith((".jpg", ".png"))] groups = defaultdict(list) for name in all_images: clip_id = name.split("_")[0] # clip01_000123.jpg 的片段 ID 是 clip01 groups[clip_id].append(name) train_files, val_files = [], [] for clip_id, names in groups.items(): random.shuffle(names) split_idx = max(1, int(len(names) * 0.9)) train_files.extend(names[:split_idx]) val_files.extend(names[split_idx:]) print("train:", len(train_files), "val:", len(val_files))

注意 split_idx 在每个片段内部取 90%,整体比例接近 9:1,最后 train 总量大约 4000、val 大约 500。如果某些片段很短,只有两三帧,max(1, ...)保证这组帧至少有一帧进验证集,避免整个片段全部被分到训练集。拿到数据后先分片段再洗牌,比直接随机切可靠得多。

注意:拆分脚本里的 random.seed(42) 要固定住。换一次种子生成的验证集就变一次,前后两次训练的结果就没有可比性了。

2.4 验证集泄漏自检:先查重叠再进行下一步

不放心的话,做一次泄漏自检。最简单的是查文件名重叠:

train_names = set(os.listdir("images/train")) val_names = set(os.listdir("images/val")) print("同名文件重叠:", len(train_names & val_names))

严格来说同名文件不应该出现,结果为 0 是正常。更隐蔽的是连续帧重叠,比如训练集里有 clip01_000120,验证集里有 clip01_000121,两张图几乎一样。如果文件名含帧号,可以解析出来做差值判断:

def frame_no(name): try: return int(name.split("_")[1].split(".")[0]) except (IndexError, ValueError): return None train_frames = {frame_no(n) for n in train_names if frame_no(n) is not None} val_frames = {frame_no(n) for n in val_names if frame_no(n) is not None} near_dup = sum(any(abs(f - t) <= 2 for t in train_frames) for f in val_frames) print("验证集中与训练帧相邻 2 帧内的数量:", near_dup)

数量超过验证集总张数的 5%,就要考虑重新划分。没有帧 ID 可解析时,按文件修改时间排序做同样检查,也能抓出一部分问题。这一步做踏实了,后面训练结果才有讨论价值。

3. 整理成 YOLOv8 可直接训练的数据集:目录、校验与训练参数

3.1 目录结构与 data.yaml 的最小配置

数据体检通过后,把数据集整理成 YOLO 工程最常见的目录结构:

datasets/ ├── data.yaml ├── images/ │ ├── train/ # 4000 张 │ └── val/ # 500 张 └── labels/ ├── train/ └── val/

data.yaml 写在 datasets 根目录下,内容如下:

path: /home/your_name/datasets # 建议写绝对路径,避免工作目录不同导致路径解析失败 train: images/train # 相对 path 的图片目录 val: images/val nc: 2 names: 0: pedestrian 1: vehicle

这里 val 只写了图片目录,没有写 labels/val,YOLOv8 会按同名规则自动在 labels 下找对应 txt。一旦 val 目录下的图片在 labels 目录里找不到同名文件,训练时会提示 “found no labels” 并跳过这些图。所以 labels/val 和 images/val 的文件名必须一一对应,后缀不同没关系,前缀必须一致。

3.2 标签数值校验:越界、空文件、异常宽高比

YOLO 标签四列坐标都是相对图片尺寸的 0~1 浮点数。实际数据转换过程中最常见的错误是:从 VOC XML 或 COCO JSON 转换时忘记除以原图宽高,导致中心点落在 1 以外,或者框宽度大于 1。训练时这些异常框会把损失函数带偏。用下面脚本刷一遍:

import os label_dir = "labels/train" # 对 val 也跑一遍 bad_records = [] for txt_name in os.listdir(label_dir): with open(os.path.join(label_dir, txt_name), encoding="utf-8") as f: lines = f.readlines() if not lines: bad_records.append((txt_name, "empty")) continue for line in lines: parts = line.split() if len(parts) != 5: bad_records.append((txt_name, "wrong_len")) break cls, cx, cy, w, h = map(float, parts) if not (0 <= cx <= 1 and 0 <= cy <= 1 and w > 0 and h > 0): bad_records.append((txt_name, "out_of_range")) break if cx + w / 2 > 1.01 or cy + h / 2 > 1.01 or cx - w / 2 < -0.01 or cy - h / 2 < -0.01: bad_records.append((txt_name, "box_cross_border")) break print("异常数量:", len(bad_records)) for rec in bad_records[:20]: print(rec)

单行越界属于硬伤,cx + w / 2 > 1这类属于“框画出画面”。画面边缘被截断的行人或车辆,标注员常习惯把框往外拉,结果半截框落在图外。处理办法有两个:一是把越界部分裁剪回 0~1 范围,保留可见区域;二是干脆删掉这条框。我的经验是,被截断超过一半的目标直接删,保留一半以上的目标裁掉越界部分,比硬留着更容易训练。

3.3 启动训练:模型选型与超参数设置

目录和标签都干净之后,开始训练。以 YOLOv8 为例,命令是:

yolo detect train \ model=yolov8m.pt \ data=data.yaml \ epochs=100 \ imgsz=768 \ batch=16 \ workers=4 \ device=0 \ patience=30

model 用 yolov8m 起步。行人车辆检测里目标尺度跨得很大,近处一辆车能占满半幅画面,远处行人只有几十个像素,n 和 s 的浅网络容易丢细节,l 和 x 在 4000 张图上又容易过拟合,m 是折中。如果部署目标是边缘设备,改回 s,精度不足时再换 m。

imgsz 取 768 而不是默认 640,前提是第 2.2 节统计结果显示小目标占比不低。小目标占比低就改回 640,省显存也省时间。batch 16 是 16G 左右显存时的常见值,OOM 报错先减小 batch,不要先降 imgsz,因为 imgsz 对最终精度影响更直接。patience=30 表示连续 30 个 epoch 验证集指标不涨就提前停,4000 张图一般跑到 60~100 epoch 就收敛了,不用一直等满 100。

提示:显存不够时先减 batch,再考虑降 imgsz。imgsz 从 768 降到 640,小目标召回会明显下降,这个代价要心里有数。

3.4 两个容易反复出现的“验证集事故”

第一个事故是把验证集路径写成了 train。data.yaml 里 val 如果写成 images/train,YOLOv8 会用训练图片做验证,mAP 直接飙到 0.9 以上。自查时先看训练日志里验证集图片数量是否等于 500。第二个事故是训练命令里再加自动切分参数。有的配置入口支持拆分参数,和 data.yaml 的 val 同时生效时,可能把本来留好的 val 又拆出一部分变成新的验证集,造成验证集只剩不到 500 张。用 data.yaml 显式指定 val 后,不再设置任何自动切分参数,保证验证集就是那固定的 500 张。

4. 训练与验证阶段最常见的 4 个坑:现象、排查、解决

训练不是启动命令就完事。下面几个坑按现象、原因、解决的顺序写,都是实际踩过的,遇到时对照着排查。

4.1 现象:loss 下降正常但 mAP 卡在 0.3 附近

训练日志里 box_loss、cls_loss 都在降,可验证集 mAP50 一直在 0.3 上下震荡,怎么调学习率都上不去。原因大概率出在数据本身:行人只有 800 个框,车辆有 6000 个框,少类的梯度在整个 batch 里被稀释。YOLOv8 的分类损失对所有类别平权,多数类主导了优化方向。

解决分两步。第一步回到第 2.2 节的统计结果看每类框数;比例超过 10:1 就属于严重不平衡。第二步对少类做过采样:把行人较多的几百张图在训练目录里复制一份,并对复制出的图片做随机裁剪、翻转、亮度扰动,让实际参与训练的少类样本翻倍。还有一种思路是给少类单独加权,但 YOLOv8 的配置入口不直接暴露,实用性不如过采样。补完重训,mAP 通常能拉到 0.5 以上。

4.2 现象:验证集 mAP 0.75,真实现场一测就翻车

验证集上各项指标都不错,拿到真实路口视频一跑,行人漏检、车辆乱框。原因极大概率是验证集和训练集同源:同一段视频的相邻帧被随机拆分,模型记住的是背景纹理而不是目标语义。这种“高分翻车”在监控视频类数据里特别常见,属于典型的验证集泄漏。

排查方法回到第 2.4 节,看验证集中有多少帧与训练集连续帧相邻。一旦确认泄漏,必须按片段重新划分。如果数据集已经按片段拆分了还是翻车,那就加一个“盲测集”:单独留出 200 张完全没参与训练的、不同时段不同路口的图片,训练完成后用盲测集评估。盲测集 mAP 比训练验证集低 0.1 以内可接受;低超过 0.2 说明模型过拟合到了训练数据分布上。

注意:盲测集图片必须和训练集来自完全不同的采集时间,否则仍然会泄漏。

4.3 现象:误检集中在“车辆被当成行人”

验证集混淆矩阵里,车辆类大量被判成行人,有的卡车侧面被框成人,摩托车被框成人。原因往往不是模型问题,是标注框画得太松:标行人的框把路边栏杆、背包、自行车都圈进去了,模型学到“框内只要有人形轮廓就算人”。车辆框把人物、车窗玻璃、地面倒影也框进去,不同类别之间特征互相污染。

解决方法是重新定义标注规范。行人框必须紧贴头、肩、脚的外轮廓,放宽一圈都会把背景学进去;车辆框以车身主体为准,车窗和保险杠要收在框线内;被遮挡超过一半的目标,项目里约定标成 ignore 类别或直接跳过。把标签刷一遍后重新训练,误检会明显下降。

4.4 现象:近处目标能检出,远处行人小目标全漏

验证集 PR 曲线上 recall 在高置信度区间掉得厉害,漏检集中在 50 米外的小像素行人。原因有两层:一是原数据里远处小目标本身就少,样本不足;二是输入分辨率 640 下,下采样后一个 20×20 像素的框只剩几个像素的特征响应,基本不可能检出。

解决路径:先看统计里的小目标占比,大于 40% 就换 imgsz=896 重新训练。换分辨率后显存不够,优先减小 batch。如果业务上必须做远距离检测,推理阶段用切片推理,把大图切成多块重叠区域分别预测再合并,小目标召回能再上一个台阶。数据侧也要补远处行人样本,光调参数解决不了样本分布缺失的问题。

5. 用 500 张验证集把结果看明白:指标读数与版本记录

5.1 训练输出里哪些文件值得看

一轮训练结束后,输出目录 runs/detect/train 里有一堆文件。重点看这几个:

文件 / 指标作用怎么判断好坏
results.csv每个 epoch 的 loss、precision、recall、mAP50、mAP50-95看 mAP 曲线是否还在爬坡,最后 10 个 epoch 有没有平台期
confusion_matrix.png类别之间的错分关系对角线越亮越好;行人列、车辆行交叉亮就说明两类在互混
PR_curve.png各类别的精确率-召回率曲线曲线越贴近右上角越好,注意小目标类的曲线是否明显下凹
val_batch1_pred.jpg验证集图片叠加模型预测框的直观图直接看漏检和误检的分布,比指标更直观
weights/best.pt验证集指标最优的权重后续回测和导出都用它,不用 last.pt

mAP50 和 mAP50-95 在行人车辆场景要分开看。mAP50 只要求预测框和真值框 IoU 超过 0.5,主要评价“有没有找到目标”;mAP50-95 把 IoU 阈值从 0.5 拉到 0.95 后平均,更严苛地评价边框贴不贴。行人和车辆对框的精度要求不同,车辆检测需要高 IoU 才能支撑后续跟踪和测距,所以 mAP50 达标后,mAP50-95 也要尽量过 0.5。

5.2 用 best.pt 回测 500 张验证集

训练日志里的指标是训练过程中的自动验证,我还习惯把 best.pt 拿出来在同样的 500 张验证集上单独回测一遍,确认推理阶段的质量:

yolo predict \ model=runs/detect/train/weights/best.pt \ source=datasets/images/val \ save=True \ save_txt=True \ save_conf=True \ conf=0.25 \ name=val_reeval

回测结果在 runs/detect/val_reeval 下。save_txt 生成的每行是“类别ID 置信度 x_center y_center width height”,和标签格式相比多了一个置信度。用这个结果和标签做一次简单匹配:计算预测框和真值框的 IoU,大于 0.5 算命中;没命中的真值框就是漏检。人工抽看漏检图片里是不是集中出现“横穿马路的行人”“被树挡了一半的车辆”这类难例,再决定是补数据还是调后处理。

5.3 验证集不变的版本记录习惯

500 张验证集相比 5000 张规模小很多,mAP 的随机波动也更明显。同一个模型跑两次,指标可能差 1~2 个点。为了让自己相信指标变化是有效果的,实验记录里要写清楚五件事:数据集版本、训练集文件数、验证集文件数、imgsz、随机种子。记录模板如下:

日期数据版本训练集验证集imgszmAP50备注
2026-01-12v1.040005007680.71按片段拆分后首次全量训练
2026-01-15v1.140005007680.75补充行人过采样后的效果
2026-01-18v1.140005008960.77仅换 imgsz,未动数据

每次只改一个变量,验证集始终保持一样,三行记录里的 mAP 变化才可解释。五件事里缺随机种子的话,即使验证集相同,数据增强的随机性也可能让结果浮动,务必把 seed 一起记下来。

6. 部署前再走一步:ONNX 导出与数据补强路线

6.1 导出 ONNX 做一次部署自测

验证集指标可接受后,将 best.pt 导出成 ONNX,提前暴露转格式时的算子兼容问题。命令:

yolo export \ model=runs/detect/train/weights/best.pt \ format=onnx \ opset=12 \ simplify=True

opset 12 兼容性最好,不要一味追新版本;simplify=True 会折叠一些冗余结构,让模型文件更小。导出后用 onnxruntime 加载它,打印输入输出名字,确认模型结构完整:

import onnxruntime as ort sess = ort.InferenceSession("best.onnx") for inp in sess.get_inputs(): print(inp.name, inp.shape) for out in sess.get_outputs(): print(out.name, out.shape)

这一步只解决“模型能不能跑”,解决不了“模型在真实现场准不准”。用同一个 ONNX 在 GPU 和边缘设备上各测一遍推理时间,实测帧率达不到业务要求时,再考虑换 s 或 m 的量化版本,重新做验证。

6.2 只有 4500 张时的数据补强方向

4500 张这个规模,足够支撑一个固定场景的行人和车辆检测原型,但离开放场景还有差距。优先补三类数据:夜间和逆光场景、雨天和镜头起雾场景、不同俯视角度(杆式、枪机、球机)的场景。每类补 300~500 张硬样本并重新划分验证集,提升通常比调超参数更明显。如果采集困难,用已训练模型在视频上跑一遍,把置信度低于 0.3 或明显漏检的片段抽帧,人工确认后加入数据集,相当于让模型帮自己筛难例。

我现在的习惯是,拿到任何数据集先花半小时做一遍“数据体检”,把类别统计、小目标占比、验证集划分方式、空标签数量写在数据集根目录的 notes.txt 里。训练前只对照三句话检查:类别平不平衡、验证集有没有同源、小目标够不够多。三条都过了,再谈调参和部署。这个顺序帮我避开了大多数看不见的黑匣子问题。希望帮到你。

本文还有配套的精品资源,点击获取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/2 21:56:49

MySQL SSL加密访问配置实战:从证书生成到强制加密完整指南

给MySQL配置SSL加密访问&#xff0c;这事儿我拖了大半年才动手。理由其实很实在&#xff1a;数据库在内网&#xff0c;觉得没人会无聊到去监听交换机流量。直到有一次闲着没事&#xff0c;在自己搭的测试环境里用Wireshark看了一轮客户端和服务端的交互&#xff0c;结果一条UPD…

作者头像 李华
网站建设 2026/10/2 21:53:32

Unity点击事件与UI穿透冲突的通用修正方案

站在Unity开发者的角度&#xff0c;点击事件和UI“打架”这个问题&#xff0c;尤其是“UI弹出时穿透点击到场景物体”“按钮连点触发多次”这两个症状&#xff0c;几乎每个项目都会遇到。我最早做2D手游的时候就吃过亏&#xff1a;玩家疯狂点“关闭”按钮&#xff0c;结果把按钮…

作者头像 李华
网站建设 2026/10/2 21:50:01

Linux磁盘占用排查:du命令从基础到实战完全指南

今天聊一下 Linux 下最常用的磁盘占用排查命令&#xff1a;du。不管你是在生产服务器上看到报警“磁盘空间不足”&#xff0c;还是本地开发环境莫名撑爆了根分区&#xff0c;总得先搞清楚哪个文件、哪个目录把空间给吃掉了。du 就是干这个的。 这个命令的核心能力是递归统计文…

作者头像 李华
网站建设 2026/10/2 21:49:50

SpringBoot+Vue在线教育系统源码:从环境搭建到二次开发全攻略

把这套项目真正跑起来之前&#xff0c;我先说一句大实话&#xff1a;网上号称"可直接运行"的源码很多&#xff0c;但绝大多数你都要花一晚上解决数据库版本、端口冲突、前端代理这三个问题。这套在线教育系统信息管理系统源码&#xff0c;SpringBoot 后端 Vue 前端 …

作者头像 李华
网站建设 2026/10/2 21:49:01

npm install 报错排查全指南:从原理到实战

开门见山说个很多人的困惑&#xff1a;一条npm install敲下去&#xff0c;运气好一杯水没喝完就装完了&#xff0c;运气差能卡在进度条上一个小时&#xff0c;弹出的报错还千奇百怪。更烦的是&#xff0c;同一个项目在别人电脑上一次过&#xff0c;到你手里就翻车。问题的根源在…

作者头像 李华
网站建设 2026/10/2 21:46:42

COLMAP点云可视化与6D位姿对齐:从二进制解析到Open3D/PCL实战

简介&#xff1a;这是一款面向三维重建与计算机视觉开发者的点云可视化工具&#xff0c;主要解决Colmap重建结果、pcd/ply点云以及6D位姿R|t难以直观查看的问题。工具支持加载Colmap输出的images、cameras、points3D、project四类文件&#xff0c;也兼容pcd、ply格式点云&#…

作者头像 李华