简介:面向道路破损检测场景的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_ok=True) 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 是 0,D44 是 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 torch==1.12.1 torchvision==0.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", path="best.pt", source="local", force_reload=True ) model.conf = 0.25 model.iou = 0.45 model.agnostic = True img = "test_images/road_001.jpg" results = model(img, size=1280) results.print()这里source="local"表示使用本地 clone 的 YOLOv5 仓库,避免每次加载都联网拉 GitHub 源码。size=1280是关键参数,很多人在代码里忽略了它,导致模型用默认 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.yaml:
train: 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"], label="train box") plt.plot(df["epoch"], df["val/box_loss"], label="val box") plt.plot(df["epoch"], df["metrics/mAP@0.5"], label="mAP@0.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重试即可。我用这种方式重训了道路破损模型,最终 mAP@0.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", path="best.pt", source="local") results = model("test_images/", size=1280) df = results.pandas().xyxy[0] for cls in df["class"].unique(): sub = df[df["class"] == cls] print(f"class {cls}: conf mean={sub['confidence'].mean():.2f}, " f"max={sub['confidence'].max():.2f}, min={sub['confidence'].min():.2f}")根据这些统计,把 D40、D44 的阈值设为 0.3,D0w0 设为 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 中的mAP@0.5:0.95指标,这个指标反映边界框精确度。如果 mAP@0.5 很高但 mAP@0.5:0.95 偏低,说明框的位置还差一点,可以在已有权重基础上用更低学习率再训练 10 个 epoch,只微调 box 分支。这个步骤能用最少的时间把框的精度补回来,也算是对这份训练好的权重价值最大化的收尾。
本文还有配套的精品资源,点击获取