ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Bdd100k到YOLOv5s对象检测训练包:标签转换与训练全流程

2026/10/5 14:43:26 拓冰建站 浏览量
Bdd100k到YOLOv5s对象检测训练包:标签转换与训练全流程 简介这份资源面向计算机视觉入门与进阶开发者提供在 Bdd100k 自动驾驶数据集上训练 YOLOv5s 目标检测模型的完整工程解决从数据预处理到模型训练、推理验证的全流程落地问题。压缩包共 85 个文件约 97.7MB以 py 训练与推理脚本、yaml 模型与数据配置、pt 预训练权重、ipynb 交互式笔记、jpg 训练与测试可视化图为主另含 sh 下载脚本、Dockerfile 与说明文档目录按 models、data、utils、runs 等模块划分结构清晰。资源同时给出使用预训练权重训练与从头训练两套实验记录包含损失曲线、标签分布与验证结果图并附 4K 测试视频演示链接及预处理好的 Bdd100k 数据集下载地址便于读者直接复现训练过程、对比两种策略差异并迁移到自有数据。目前已有 110 人学习下载适合希望掌握 YOLOv5 训练流程与 Bdd100k 实战的读者参考。1. 从 Bdd100k 到 YOLOv5s一份能直接跑通的对象检测训练包如果你手头正好有一批行车记录仪或路侧相机采回来的图想训一个能识别车、人、交通灯的对象检测模型大概率绕不开 Bdd100k 这个数据集。它体量大、场景杂、标注细但真正动手时你会发现光是把标注转成 YOLO 能吃的格式、把类别对齐、把预训练权重挂上就够折腾一整天。这份资源包解决的正是这段最磨人的路它把 Bdd100k 预处理脚本、YOLOv5s 训练配置、预训练权重、两个现成的训练 notebook以及从头训练和基于预训练权重训练两套完整 runs 记录都打包好了。适合刚接触 YOLOv5 想跑通全流程的新手也适合已经会训 COCO 但没碰过 Bdd100k、想看看类别映射和标签转换怎么做的熟手。下面按「资源里有什么 → 怎么一步步跑 → 哪里容易翻车」的顺序拆开讲。2. 资源结构与 Bdd100k 标签转换先搞清数据怎么进网络2.1 包里到底放了哪些能直接用的东西先把目录结构在脑子里过一遍不然后面改配置会找不到文件。核心分四块models/下是网络结构 yaml包括yolov5s.yaml、yolov5m.yaml、yolov5l.yaml、yolov5x.yaml以及这份资源专门用的custom_yolov5s.yaml和uc_data.yamldata/下是数据集配置文件有bdd100k.names、coco128.yaml、coco.yaml、voc.yamlweights/下是yolov5s.pt预训练权重和download_weights.sh根目录下是train.py、test.py、detect.py、export.py这些入口脚本加上utils/里的datasets.py、torch_utils.py、google_utils.py等工具模块。另外还有两个 notebookyolov5s_training_bdd100k.ipynb和Bdd_preprocessing.ipynb前者是训练主流程后者专门做 Bdd100k 的标签转换。runs/目录里已经存了两组训练记录exp0_yolov5s_bdd_prew是基于预训练权重跑的exp1_yolov5s_bdd是从头训的里面有results.txt、results.png、labels.png、hyp.yaml、opt.yaml和若干train_batch*.jpg、test_batch0_pred.jpg。这些记录的价值在于你可以拿自己的训练曲线跟它对判断是数据问题还是超参问题。2.2 Bdd100k 原始标注为什么不能直接喂给 YOLOv5Bdd100k 的检测标注是 JSON 格式每张图一个对象里面记着category、box2d的x1 y1 x2 y2还有attributes里的天气、时间、遮挡等。YOLOv5 要的是每张图一个.txt每行class x_center y_center width height且坐标必须归一化到 0~1。更麻烦的是类别体系不一样Bdd100k 有person、rider、car、bus、truck、train、motor、bike、traffic light、traffic sign等而这份资源里的bdd100k.names只保留了检测任务常用的若干类。如果你不先把类别映射对齐训出来的模型会把rider和person混在一起或者把traffic sign整类丢掉。Bdd_preprocessing.ipynb干的就是这件事读原始 JSON按bdd100k.names的顺序建类别到 id 的字典过滤掉不在名单里的类别再把box2d转成归一化中心点格式按 YOLOv5 的目录约定写到images/和labels/下。2.3 标签转换脚本的关键参数与执行步骤打开Bdd_preprocessing.ipynb核心逻辑集中在几个单元格。下面把关键代码抽出来方便你对照自己的路径改。import json import os from pathlib import Path # 类别顺序必须和 data/bdd100k.names 完全一致 classes [person, rider, car, bus, truck, train, motor, bike, traffic light, traffic sign] class_to_id {name: i for i, name in enumerate(classes)} def convert_bbox(size, box): 把 x1 y1 x2 y2 转成归一化的 x_center y_center w h dw 1.0 / size[0] dh 1.0 / size[1] x (box[0] box[2]) / 2.0 y (box[1] box[3]) / 2.0 w box[2] - box[0] h box[3] - box[1] return x * dw, y * dh, w * dw, h * dh def convert_label(json_path, out_dir): with open(json_path, r) as f: data json.load(f) img_w data[imgWidth] img_h data[imgHeight] lines [] for obj in data[labels]: cat obj[category] if cat not in class_to_id: continue # 不在名单里的类别直接跳过 box obj[box2d] if box[x2] box[x1] or box[y2] box[y1]: continue # 过滤零面积框 bb convert_bbox((img_w, img_h), (box[x1], box[y1], box[x2], box[y2])) lines.append(f{class_to_id[cat]} { .join([f{v:.6f} for v in bb])}) out_path Path(out_dir) / (Path(json_path).stem .txt) with open(out_path, w) as f: f.write(\n.join(lines))这段代码有三个地方值得盯住。第一classes列表的顺序就是最终模型输出的类别 id改顺序等于改标签必须和bdd100k.names逐行对应。第二convert_bbox里先算中心点再乘缩放系数顺序不能反否则框会整体偏移。第三if box[x2] box[x1]这个过滤条件看着简单但 Bdd100k 里确实存在标注框宽高为负或为零的脏数据不滤掉训练时 loss 会直接 NaN。执行时把json_path指向解压后的bdd100k/labels/det_20/det_train.json这类文件out_dir指向labels/train/跑完再检查一下生成的 txt 行数是否和图片数一致。2.4 数据集目录该摆成什么样YOLOv5 对目录结构有固定预期常见做法是bdd100k_yolo/ ├── images/ │ ├── train/ │ └── val/ ├── labels/ │ ├── train/ │ └── val/ └── bdd100k.names图片和标签文件名必须一一对应只是扩展名不同。data/uc_data.yaml里会写train: ../bdd100k_yolo/images/train、val: ../bdd100k_yolo/images/val、nc: 10、names: [person, ...]。这里nc的数字要和bdd100k.names行数一致多一个少一个都会在训练启动时报维度不匹配。如果你只做部分类别改classes列表后记得同步改nc和names三处必须一起动。3. 训练配置与启动从预训练权重到从头训的两条路3.1 custom_yolov5s.yaml 和 uc_data.yaml 改哪几行models/custom_yolov5s.yaml是在官方yolov5s.yaml基础上改的主要动的是nc。打开文件你会看到nc: 80被改成nc: 10对应 Bdd100k 的十类。anchors一般不用动YOLOv5s 的默认 anchor 是在 COCO 上聚类出来的迁移到 Bdd100k 这种道路场景仍然可用除非你的数据里小目标特别多才考虑重新聚类。uc_data.yaml是数据配置关键字段train: ../bdd100k_yolo/images/train val: ../bdd100k_yolo/images/val nc: 10 names: [person, rider, car, bus, truck, train, motor, bike, traffic light, traffic sign]路径用相对路径时是相对于train.py所在目录解析的不是相对于 yaml 文件本身这点容易搞混。如果你把数据放在别的盘直接写绝对路径最省事。nc和names必须和预处理阶段完全一致否则训练能启动但类别全错。3.2 基于预训练权重训练命令与参数含义资源里runs/exp0_yolov5s_bdd_prew就是这条路的结果。启动命令常见写法python train.py \ --weights weights/yolov5s.pt \ --cfg models/custom_yolov5s.yaml \ --data data/uc_data.yaml \ --hyp data/hyp.yaml \ --epochs 100 \ --batch-size 16 \ --img-size 640 \ --device 0 \ --name exp0_yolov5s_bdd_prew逐项说--weights加载 COCO 预训练权重只加载骨干和 neck 能对上的部分检测头因为nc变了会重新初始化--cfg指定网络结构必须和权重兼容否则加载时报 shape mismatch--data指向数据配置--hyp是超参文件资源里runs/exp0_yolov5s_bdd_prew/hyp.yaml存了当时用的那一套你可以直接拿来复现--img-size 640是训练分辨率Bdd100k 原图是 1280x720缩到 640 后小目标会变糊如果显存够可以上 960 或 1280--batch-size根据显存调16 在 11G 显存上比较稳--device 0指定第一块 GPU多卡用0,1。训练开始后终端会打印每轮 loss 和 mAP同时runs/exp0_yolov5s_bdd_prew/下会生成results.txt和results.png。3.3 从头训练什么时候该放弃预训练权重runs/exp1_yolov5s_bdd是不加--weights跑的。命令基本一样只是去掉--weights或写成--weights 。从头训的收敛慢很多前几十轮 mAP 可能一直趴在低位results.png里 loss 下降也明显更缓。那什么时候值得从头训一是你的类别和 COCO 差异极大预训练特征反而形成负迁移二是你要做消融实验需要干净的基线三是预训练权重加载时反复报错排查成本高于从头训。对 Bdd100k 这种自然道路场景COCO 预训练其实很对路我一般优先走预训练路线从头训只作为对照。资源里两组 runs 正好给你提供了这个对照把两条results.png并排看能直观感受到预训练带来的收敛加速。3.4 训练过程中该盯哪些指标results.txt每行是一轮字段包括epoch、train/box_loss、train/obj_loss、train/cls_loss、metrics/precision、metrics/recall、metrics/mAP_0.5、metrics/mAP_0.5:0.95、val/box_loss等。重点看三个train/box_loss是否稳定下降如果震荡剧烈多半是学习率或 batch size 问题metrics/mAP_0.5是否在 30 轮后开始爬升如果一直不动先查标签转换有没有把类别 id 写错val/box_loss和train/box_loss的差距差距持续拉大就是过拟合可以加数据增强或早停。labels.png是标签分布图横轴是框的宽高和中心点位置如果发现大量框挤在图像边缘或某个尺寸说明预处理阶段归一化算错了。4. 推理、验证与常见翻车点排查4.1 用 detect.py 跑单图和批量推理训练完拿weights/best.pt或last.pt做推理python detect.py \ --weights runs/exp0_yolov5s_bdd_prew/weights/best.pt \ --source inference/images \ --img-size 640 \ --conf-thres 0.25 \ --iou-thres 0.45 \ --save-txt \ --output inference/output--source可以是单张图、整个目录也可以是视频文件。--conf-thres控制置信度阈值Bdd100k 里traffic light和traffic sign目标小阈值设太高会整类漏检我一般先设 0.25 看召回再往上调。--save-txt会把检测结果按 YOLO 格式存成 txt方便你写脚本算 mAP。资源里inference/output/下已经有street.jpg、street.txt、zidane.jpg、bus.jpg这些示例输出可以拿来对照自己的结果格式。test.py则是跑验证集算 mAP 的入口命令类似把--data和--weights指对即可。4.2 避坑五条血泪经验现象一训练启动就报AssertionError: No labels found。原因通常是labels/目录路径写错或者 txt 文件名和图片名没对上比如图片是abc.jpg标签写成了abc.png.txt。解决在datasets.py里加一行打印实际扫描到的标签路径或者手动ls labels/train | head确认扩展名和命名规则。现象二loss 前几轮正常突然变 NaN。多半是标签里有零面积框或坐标超出 0~1。Bdd100k 原始标注确实存在x2x1的脏数据预处理时那个过滤条件不能省。解决重新跑一遍转换脚本加断言assert 0 x 1把越界样本单独打出来。现象三mAP 一直很低但 loss 在降。常见原因是类别 id 错位比如bdd100k.names里car在第 2 行但转换脚本里car映射到了 id 0。解决随便抽一张训练图用detect.py跑推理看输出的类别名和图上物体是否对得上对不上就回去核对classes列表顺序。现象四显存够但 batch size 上不去报 CUDA out of memory。除了降 batch还要检查--img-size是不是设太大以及workers是不是开太多导致内存泄漏。解决先把--img-size降到 640--workers设 4再逐步往上加。现象五验证集 mAP 比训练集低一大截。如果差距超过 20 个点先看val的图片是不是和train有重叠Bdd100k 官方划分是独立的但如果你自己切分时用了随机划分同一段视频的相邻帧可能同时进训练和验证造成虚高。解决按视频 id 或时间戳切分确保验证集场景和训练集不重叠。4.3 用 runs 里的记录做对照排查资源里两组 runs 不只是结果更是排查工具。当你自己的训练曲线异常时把results.png和exp0_yolov5s_bdd_prew/results.png叠在一起看如果别人的 mAP 在 50 轮爬到 0.4 而你的还在 0.1问题大概率在数据侧而不是超参侧。opt.yaml里存了那次训练的全部命令行参数hyp.yaml存了学习率、动量、权重衰减、数据增强系数你可以直接 diff 自己的配置和它找出差异项。train_batch0.jpg是训练时实际喂进网络的批次可视化框画得对不对一眼就能看出来如果框整体偏移或类别标签乱标说明标签转换有问题。5. 进阶技巧把 Bdd100k 的属性标注用起来做分层验证Bdd100k 比 COCO 多出来的一层价值是每张图带weather、timeofday、scene这些属性。默认训练流程把它们全丢了但你可以用它们做分层验证定位模型在哪种场景下最弱。具体做法在预处理阶段额外生成一个image_attributes.json记录每张验证图的weatherclear/rainy/snowy/foggy和timeofdaydaytime/night/dawn/dusk。然后写一个评估脚本按属性分组算 mAPimport json from collections import defaultdict with open(image_attributes.json) as f: attrs json.load(f) # 假设你已经有了每张图的预测和真值这里按属性分组统计 group_stats defaultdict(lambda: {tp: 0, fp: 0, fn: 0}) for img_name, meta in attrs.items(): key f{meta[weather]}_{meta[timeofday]} # 这里填入你的匹配逻辑累加 tp/fp/fn # group_stats[key][tp] ... pass for key, s in group_stats.items(): precision s[tp] / (s[tp] s[fp] 1e-6) recall s[tp] / (s[tp] s[fn] 1e-6) print(f{key}: P{precision:.3f} R{recall:.3f})跑完你会得到类似clear_daytime: P0.62 R0.58、rainy_night: P0.31 R0.22的表格。差距大的那几组就是下一步该补数据或做增强的方向。比如rainy_night差可以在hyp.yaml里调高hsv_v和hsv_s的增强幅度或者专门收集雨天夜间样本过采样。这个技巧不改变训练主流程只是在验证阶段多切一刀但能让你从「mAP 0.4」这种笼统数字落到「雨天夜间召回只有 0.22」这种可行动结论。我自己的习惯是每训完一版先跑分层验证再决定要不要继续调参否则很容易在整体 mAP 上反复微调却始终突破不了场景瓶颈。从那以后我每次拿到带属性标注的数据集都会强制走一遍分层评估哪怕多花半小时写脚本也比盲目训十轮强。希望帮到你。本文还有配套的精品资源点击获取