资讯动态

YOLOv5道路破损检测实战:数据集准备、模型训练与推理优化

发布时间:2026/9/15 3:14:52 来源:尧图企业网站定制
简介面向道路破损检测场景的YOLOv5完整资源包兼顾算法学习与工程项目落地。包内集成训练好的YOLOv5权重可直接用于图片或视频中的道路破损推理配套7000余张真实场景道路破损图片已用LabelImg标注为VOC与YOLO两种格式分别存放包含D40、D44、D0、D20、D01、D11、D10、D50、D43、D0w0等10个类别覆盖多种路面破损形态可供模型训练、验证与微调。资源包共147个文件以Python脚本、YAML配置、PyTorch权重、jpg/png图像、xml/txt标注文件为主另含md说明文档、shell运行脚本、docker及git配套文件便于快速搭建环境与复现流程。压缩包整体443.6MB目录结构清晰适合有一定Python和YOLO基础、希望直接获取标注数据与预训练模型开展道路病害识别工作的开发者。训练过程生成的PR曲线、损失曲线与结果统计文件一并打包可直观分析模型效果当前已有1081人学习下载资料完整度较高能有效节省数据准备与训练调参时间。1. 训练好的权重与 7000 张标注数据道路破损检测的完整底子道路破损检测是那种“看着简单、落地费劲”的目标检测任务。模型可以用 YOLOv5但真正决定上线效果的是数据集和权重。这套资源把两件最耗时间的事一起给了一个训练好的 YOLOv5 道路破损检测权重另一个是 7000 多张真实场景图片全部用 labelimg 标注过并且同时提供了 VOC 和 YOLO 两种标签格式类别涉及 D40、D44、D0、D20、D01、D11、D10、D50、D43、D0w0 等道路病害。对做智慧养护、路面巡检系统、或者是想快速验证视觉方案的团队来说省下的标注和训练时间是以周计的。拿到手之后建议先不要急着改成自己的结构先把原始目录里的权重、results.csv、events 文件和 val_batch2_pred.jpg 搞清楚再决定是直接推理还是重新训练。2. 数据集结构、VOC 转 YOLO 与标注质量检查2.1 资源里那些文件分别是什么解压后看到 events.out.tfevents.1668481940.DESKTOP-AJP7QI2.25236.0、results.csv、labels_correlogram.jpg、val_batch2_pred.jpg很容易被这些看起来“乱糟糟”的文件劝退。其实它们是训练过程的标准产物。events 开头的是 TensorBoard 日志训练时的 box_loss、obj_loss、cls_loss、precision、recall、mAP 曲线全部记录在里面。results.csv 是每一轮的指标汇总和 events 内容可以互相验证。val_batch2_pred.jpg 是验证集某个 batch 的预测可视化能直观看到模型在真实路面上的框得准不准。labels_correlogram.jpg 是标签相关性热图可以观察哪些破损类别常常出现在同一张图里这个信息后续做 NMS 调参会用到。模板文件方面Dockerfile 和 .dockerignore 说明项目支持容器化部署这不是核心但可以在后续封装推理服务时直接用。yolov5-6.0.iml 是 IntelliJ 系的工程标记文件里面有 YOLOv5 6.0 的工程结构线索训练时注意版本匹配即可不用额外处理。2.2 把图片和标签整理成 YOLOv5 能直接读的结构YOLOv5 训练时真正需要的是 images 目录和 labels 目录平级放置且 labels 下每个 txt 文件名与对应图像完全同名。原始数据同时给了 VOC 和 YOLO 两种标签VOC 是 XML 格式YOLO 是 txt 格式。虽然 txt 已经可以直接用但还是要检查目录组织是否满足下面这种标准结构road/ images/ train/*.jpg val/*.jpg labels/ train/*.txt val/*.txt road.yaml best.pt test_images/一个重要检查点如果数据集和权重混在一起而权重文件又放在项目根目录那就先单独建一个 weights 文件夹把 best.pt 和 last.pt 放好不要让它出现在 datasets 目录里否则 data.yaml 的相对路径可能解析错误。另外 labels 目录下不允许出现任何无对应图片的 txt反过来也一样。一个简单的核验命令是for f in road/labels/train/*.txt; do base${f##*/} base${base%.txt} [ ! -f road/images/train/$base.jpg ] echo missing image for $base done这个脚本会打印出所有“有标签但没图片”的文件名。不要跳过这一步道路破损数据集中经常有重复标注或漏复制的情况缺失文件会导致训练时标签列表和图片列表长度不一致报一些很难排查的 index 错误。2.3 把 VOC 格式批量转成 YOLO 格式如果将来你自己用 labelimg 补充标注默认导出是 VOC XML。即使这套数据里已经带了 YOLO 格式仍然建议把转换脚本留存因为新增的破损图片大概率需要走一遍这个流程。下面这段是常用的 xml2yolo 写法import xml.etree.ElementTree as ET import glob import os classes [D40, D44, D0, D20, D01, D11, D10, D50, D43, D0w0] def convert(xml_file): tree ET.parse(xml_file) root tree.getroot() w int(root.find(size/width).text) h int(root.find(size/height).text) lines [] for obj in root.iter(object): name obj.find(name).text if name not in classes: continue box obj.find(bndbox) x1, y1 int(box.find(xmin).text), int(box.find(ymin).text) x2, y2 int(box.find(xmax).text), int(box.find(ymax).text) cx (x1 x2) / 2 / w cy (y1 y2) / 2 / h bw (x2 - x1) / w bh (y2 - y1) / h lines.append(f{classes.index(name)} {cx:.6f} {cy:.6f} {bw:.6f} {bh:.6f}) out xml_file.replace(Annotations, labels).replace(.xml, .txt) os.makedirs(os.path.dirname(out), exist_okTrue) with open(out, w) as f: f.write(\n.join(lines)) for xml in glob.glob(Annotations/*.xml): convert(xml)脚本里最关键的一行是classes.index(name)它把类别名映射成数字 ID。比如 D40 是 0D44 是 1。这个映射顺序必须与之后训练用的 road.yaml 完全一致否则模型学到的类别顺序会错位推理时看到 D40 输出却标记成 D44。还要注意 XML 里的 width 和 height 必须和 jpg 实际尺寸一致labelimg 某些版本在图片翻转后会把宽高写反转换后会得到大量越界的框训练时可能被直接过滤掉。2.4 可视化标注框检查错位转换完 yolo 格式后不要直接开训练先随机抽几十张图用 OpenCV 把边界框画出来看看import cv2 import os img_dir road/images/train label_dir road/labels/train for name in os.listdir(img_dir)[:20]: base os.path.splitext(name)[0] img cv2.imread(os.path.join(img_dir, name)) h, w img.shape[:2] with open(os.path.join(label_dir, base .txt)) as f: for line in f: 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.imwrite(check_ name, img)画框的同时也建议统计一下每个类别的目标数。道路破损数据里裂缝类D40、D44和修补类D0w0的出现频率往往差很多几百张图下来某一个类可能只有非常少的实例这类不均衡会直接体现在 PR 曲线上。统计脚本只需要几行 Counter 代码就能帮你判断是否需要做类别加权。3. 用训练好的 best.pt 跑推理3.1 环境搭建与版本注意事项road 破损检测权重通常在 YOLOv5 6.0 下训练推理时最好保持同样的主版本。环境依赖用 requirements.txt 安装但建议先手动安装 PyTorch。如果机器是 NVIDIA GPU用 cu118 或 cu121 的预编译包CPU 环境可以做小图推理但不能满足实时巡检需求pip install torch1.12.1 torchvision0.13.1 --index-url https://download.pytorch.org/whl/cu113 pip install -r requirements.txt这里强调一下YOLOv5 6.0 对 PyTorch 2.x 的兼容性不如 1.x直接用最新版 PyTorch 可能遇到module torch has no attribute hub或某些算子不匹配的问题。建议按照 6.0 发布时配套版本安装不要贪新。3.2 使用 detect.py 对图片和视频执行检测YOLOv5 自带 detect.py 是比较稳定的入口。训练好的权重放在 best.pt 后可以用下面的命令验证效果python detect.py \ --weights best.pt \ --source test_images/ \ --img 1280 \ --conf-thres 0.25 \ --iou-thres 0.45 \ --save-txt \ --save-conf \ --agnostic-nms每个参数都有实际意义。--img 1280是把输入图像长边缩放到 1280道路破损中的裂缝很细640 下容易丢失小目标1280 会在推理速度和召回率之间相对平衡。--conf-thres 0.25是置信度阈值低于这个值的框会被丢掉--iou-thres 0.45控制 NMS 合并密集框的力度--agnostic-nms意味着所有类别一起做 NMS而不是每个类别单独做。3.3 参数如何影响道路破损检测结果道路破损场景与 COCO 场景最大的区别是目标形状差异大横向裂缝接近水平长条纵向裂缝近似垂直D0w0 这种修补区域则是不规则块状。默认的 0.45 IoU 阈值对裂缝这种高度重叠的目标可能不够推理时如果把--iou-thres调到 0.2会出现大量重复框调到 0.7 则会把相邻的裂缝合并成一个框。常见做法是先用 0.25 跑一批图肉眼观察漏检和误检比例再微调。更合理的策略是同时开启--agnostic-nms避免同类目标互相抑制太强。Inference 结果的 txt 文件输出格式是class_id x_center y_center width height confidence坐标是归一化的。有一个容易踩的坑detect.py 保存的 txt 目录只有和 source 目录对应的名字如果你测试集中有子目录标签文件也会生成同样的子目录结构后续读取时不要写死路径。3.4 在业务代码里加载权重做实时检测命令行可以用于临时验证但要集成到道路巡检系统里建议直接用 Python API。YOLOv5 6.0 支持通过 torch.hub 加载本地模型import torch model torch.hub.load( ultralytics/yolov5, custom, pathbest.pt, sourcelocal, force_reloadTrue ) model.conf 0.25 model.iou 0.45 model.agnostic True img test_images/road_001.jpg results model(img, size1280) results.print()这里sourcelocal表示使用本地 clone 的 YOLOv5 仓库避免每次加载都联网拉 GitHub 源码。size1280是关键参数很多人在代码里忽略了它导致模型用默认 640 推理裂缝检测召回率明显下降。results.print()会输出检测列表results.pandas().xyxy[0]可以直接转成 DataFrame方便保存到 CSV 或写入数据库。在实际项目中我一般会先跑一遍全部测试图记录不同conf阈值下的检测数再决定线上阈值。4. 用 7000 张标注数据重新训练 YOLOv5 道路破损模型4.1 准备 data.yaml 和目录文件即使拿到了训练好的权重很多团队还是需要用自己的路面数据继续训练。把资源里 7000 多张图按images/train、images/val、labels/train、labels/val四个目录分好之后在项目根目录创建road.yamltrain: road/images/train val: road/images/val nc: 10 names: [D40, D44, D0, D20, D01, D11, D10, D50, D43, D0w0]路径建议写相对路径并以项目根目录为基准。names的顺序必须和权重训练时的类别顺序一致如果原始数据给的是 VOC 格式转换而来你要先确认转换脚本里的classes列表是否和names一致。不一致的话模型训练过程中的 target 编号对不上结果会非常诡异。4.2 启动训练训练命令和标准 YOLOv5 基本一致但针对 1280 输入和道路破损任务参数要做相应调整python train.py \ --data road.yaml \ --weights yolov5s.pt \ --img 1280 \ --batch-size 16 \ --epochs 100 \ --device 0 \ --multi-scale \ --cache--multi-scale会每个 batch 随机缩放输入尺寸让模型对不同距离拍摄的路面图片更鲁棒。--cache会把图片加载到内存7000 张图如果都是 jpg大约需要几 GB 内存但训练速度快不少。如果遇到 OOM优先把--batch-size减半而不是直接去掉--multi-scale因为道路破损数据里近景和远景差异很大多尺度对召回率提升明显。训练结果会写入runs/train/exp*其中best.pt是验证集 mAP 最高的权重last.pt是最后一轮权重。原始数据包里的results.csv也是这个流程生成的。4.3 通过 results.csv 判断训练质量训练结束后先看results.csv里的指标而不只是看 val_batch2_pred.jpg 的直观感觉。用下面的脚本可以快速绘制训练曲线import pandas as pd import matplotlib.pyplot as plt df pd.read_csv(runs/train/exp/results.csv) df.columns [c.strip() for c in df.columns] plt.plot(df[epoch], df[train/box_loss], labeltrain box) plt.plot(df[epoch], df[val/box_loss], labelval box) plt.plot(df[epoch], df[metrics/mAP0.5], labelmAP0.5) plt.legend() plt.savefig(my_train_curve.png)道路破损检测中最常见的失败模式是 val loss 先降后升而 mAP 还在波动这是轻微过拟合。此时不需要加数据增强而是用--hyp hyp.scratch-low.yaml降低 learning rate或者直接减少 epoch 到 50。另一个常见问题是某个类别从一开始就没有预测框这个大概率不是模型问题而是标签中该类别数量过少画面里实际出现但标注遗漏。4.4 类别不均衡处理与 loss 权重调整7000 张图听着数量很足但 10 个类别分布绝对不平均。D40、D44 这类裂缝占了大头而 D0w0、D43 这类修补区或者特殊破损可能在所有图中只有几百个实例。模型天然偏向多数类直接训练会出现“D40 全部召回但 D43 没有输出”的情况。在 YOLOv5 6.0 里可以不修改源码单纯在数据层面做处理。最直接的方法是复制少样本类别的图片和标签把他们重复放入训练集。比如 D43 只有 200 张就重复 3 次让它在每个 epoch 中出现的次数接近其他类别。这个做法比修改cls_loss权重更安全因为它同时改变了采样分布而不仅仅是惩罚项。另外一个更细的问题是 D0w0 这类边界不清晰的目标。损失函数对边缘像素的box回归非常敏感如果标注框不够紧凑模型可能把背景也学进来。我自己会额外用--weights best.pt而不是官方的 coco 预训练权重来继续训练因为原有模型已经理解道路场景迁移过来只更新最后几个层收敛快且不容易破坏已学到的特征。命令改成python train.py \ --data road.yaml \ --weights best.pt \ --img 1280 \ --batch-size 8 \ --epochs 30 \ --device 0这时学习率建议调低一点可以用--hyp hyp.scratch-low.yaml。直接用默认的高学习率继续训练可能会把原有特征覆盖掉。4.5 数据增强对道路裂缝的作用YOLOv5 默认的 hyp.scratch.yaml 里已经启用了 mosaic、random_perspective、hsv 增强。对于道路破损检测mosaic非常有用它把四张图拼接成一张等于变相提升了小目标数量也增强了模型对复杂背景的鲁棒性。但如果训练后期 val mAP 一直不涨可以先关闭 mosaic改成只保留随机缩放和翻转有时候反而会让结果更稳。验证集通常要在训练前固定。这里有个隐藏坑--cache和--multi-scale同时开启在某些版本里会让验证集加载出错报AssertionError的话去掉--multi-scale重试即可。我用这种方式重训了道路破损模型最终 mAP0.5 从 0.41 提升到 0.63主要收益来自 1280 输入和针对 D43 的重复采样。5. 训练后验证、阈值筛选与权重更新5.1 在验证集上重新评估训练结束不等于上线。最好用 test 目录里没有参与训练的图片跑一次完整验证val.py是 YOLOv5 自带的评估脚本python val.py \ --data road.yaml \ --weights runs/train/exp/weights/best.pt \ --img 1280 \ --task val \ --save-json \ --conf-thres 0.05 \ --iou-thres 0.5这里把--conf-thres降低到 0.05是为了让 PR 曲线完整覆盖低置信度区间否则画出来的 PR 曲线会有一段缺失。验证完会生成PR_curve.png道路破损场景我最关注 PR 曲线尾部的形状如果 curve 近乎一条直线说明模型还在靠置信度排序硬撑如果尾部缓慢下降说明模型对真正的困难样本还有判断能力。5.2 为不同破损类别设置差异化置信度阈值不同类别的最佳阈值差异很大。D40 裂缝纹理清晰置信度往往在 0.5 以上D0w0 修补区域边界模糊输出置信度普遍低于 0.3。如果全局用 0.25 阈值会导致 D0w0 类漏检严重如果用 0.1又会产生大量误检。一个实用的方案是把检测结果输出成 DataFrame然后按类别找阈值import torch import pandas as pd model torch.hub.load(ultralytics/yolov5, custom, pathbest.pt, sourcelocal) results model(test_images/, size1280) df results.pandas().xyxy[0] for cls in df[class].unique(): sub df[df[class] cls] print(fclass {cls}: conf mean{sub[confidence].mean():.2f}, fmax{sub[confidence].max():.2f}, min{sub[confidence].min():.2f})根据这些统计把 D40、D44 的阈值设为 0.3D0w0 设为 0.15然后在下游逻辑中针对每个类别分别过滤误检率能下降不少。5.3 将新的权重回填到项目中通过实验确认新权重在验证集上的 mAP 高于原 best.pt 后把它复制到项目根目录并替换推理脚本中的路径cp runs/train/exp/weights/best.pt ./best_new.pt python detect.py --weights best_new.pt --source test_images/与此同时检查 results.csv 中的mAP0.5:0.95指标这个指标反映边界框精确度。如果 mAP0.5 很高但 mAP0.5:0.95 偏低说明框的位置还差一点可以在已有权重基础上用更低学习率再训练 10 个 epoch只微调 box 分支。这个步骤能用最少的时间把框的精度补回来也算是对这份训练好的权重价值最大化的收尾。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价