ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

下水道缺陷检测实战:YOLOv8训练2364张带标签数据集全流程

下水道缺陷检测实战:YOLOv8训练2364张带标签数据集全流程 简介面向YOLO系列算法应用的下水道缺陷检测数据集对应2364张图像中的缺陷标注信息适用于目标检测模型训练、验证与测试环节。主要缺陷类型包括关节偏移、障碍物、裂纹、带扣、洞、公用设施入侵与碎片可帮助算法工程师快速启动市政管道缺陷自动识别项目也适合教学科研场景下的目标检测实验。压缩包内共2000个文件其中1636份VOC格式xml标签与364份YOLO格式txt标签已预先按训练、验证需求划分并附带data.yaml配置文件可直接适配yolov5、yolov7、yolov8、yolov9、yolov10、yolo11等常见算法版本省去类别定义和目录调整环节。整体包体仅7.43MB轻量便捷便于快速下载与迭代实验。目前已有66人学习使用两类标签格式可满足不同训练框架的导入需求无需自行整理数据针对实际巡检场景可快速检验各版本YOLO在下水道缺陷检测上的精度差异辅助算法选型与优化。1. 下水道缺陷数据集配上 YOLO 算法2364 张带标签图为什么能直接拿来做检测管网运维团队每年靠 CCTV 机器人钻进排水管拍出几百小时视频真正能把缺陷筛出来的工程师却没几个。这套下水道缺陷数据集正好卡在这个节骨眼上它将关节偏移、障碍物、裂纹、带扣、洞、公用设施入侵、碎片这七类常见缺陷整理成 2364 张带标签图像并用 YOLO 格式的 txt 标注文件固化下来。也就是说你不必再从零做起标注工作拿到 zip 就能解压、核验、划分并直接喂给 YOLO 算法训练一个可用的缺陷检测模型。这篇笔记就按这个顺序写先教会你看懂标签和类别分布再完成数据集划分与 YOLOv8 训练命令配置接着处理损失函数和增强参数最后把几个最容易翻车的坑列给你看并附上推理结果转维修报告的小技巧。2. 先拆包核验标签2364 张图的类别分布和标注格式长什么样拿到 zip 第一件事不是解压后直接开训而是先搞清三件事图片是不是真的 2364 张、标签文件是不是对得上、七个类别的编号顺序是什么。顺序错了模型不会报错但训练完预测出来的名字会错位这种问题排查起来非常费时。2.1 七个缺陷类别分别对应管道里的哪种病理class id标签名管道现场对应物检测难点0关节偏移管节接口错位、脱节、橡胶圈外露与背景纹理差异小常靠接缝阴影判断1障碍物管道内遗留的石块、沙袋、工具尺寸跨度极大近景能占半幅图2裂纹管壁纵向或环向开裂细长小目标标注框容易带上大量背景3带扣管壁屈曲、内衬褶皱、局部鼓包边缘模糊没有清晰矩形轮廓4洞管壁穿孔、破损缺口容易被渗水、泥土或阴影遮挡5公用设施入侵第三方电缆、支管、拉索穿入管道常与洞、裂纹同时出现在一个画面6碎片碎石、砖块、垃圾、沉积物堆积数量多、彼此粘连小目标密度高这七个类别大概率来自某次 CCTV 检测项目的自定义编码不一定完全对应 CJJ 181-2012 或 PACP 缺陷编码体系。所以你要把它当成一个私有的类别集合来用不要想当然地往标准缺陷代码上套。比如 class 3 的“带扣”现场表现更像管壁屈曲或内衬褶皱而不是字面上的金属扣件class 5 的“公用设施入侵”拍到的多半是电缆或支管穿过检测井的画面。训练前把每个类别的代表图扫一遍知道它实际长什么样后面调阈值和写报告时才不会指鹿为马。2.2 看一眼标注文件YOLO 标签格式与 class id 校验脚本先解压然后数一数图片和标签文件的数量。最常见的目录结构是 images 和 labels 两个平级目录图片是 jpg标签是同名 txt。unzip -q 下水道缺陷数据集-2364张.zip -d sewer_defect find sewer_defect -type f -name *.jpg | wc -l find sewer_defect -type f -name *.txt | wc -l find sewer_defect -type f | head -20如果图片数和 txt 数不一致多数情况是某几张图没有对应缺陷标注人员没生成空 txt或者某几张图的 txt 文件名与 jpg 不一致。YOLO 训练时会自动跳过没有标签的 jpg但如果你以为 2364 张都有框实际只有 2000 张参与训练mAP 会低于预期。所以这个数量校验非常值得做。数量对得上后写个小脚本检查标签内容。YOLO 格式每行是“class x_center y_center width height”五个字段坐标均为 0 到 1 的归一化值。这里我把 class id 合法性、字段数量、边界越界一起查了import os from collections import Counter label_dir sewer_defect/labels cls_counter Counter() bad_lines [] for f in os.listdir(label_dir): if not f.endswith(.txt): continue path os.path.join(label_dir, f) with open(path, encodingutf-8) as fp: lines [ln.strip() for ln in fp if ln.strip()] if not lines: print(空标签文件:, f) for ln in lines: parts ln.split() if len(parts) ! 5: bad_lines.append((f, ln, 字段数不为5)) continue cls_id int(parts[0]) try: x, y, w, h [float(v) for v in parts[1:5]] except ValueError: bad_lines.append((f, ln, 坐标非数值)) continue if not (0 cls_id 6): bad_lines.append((f, ln, fclass id 越界: {cls_id})) if not (0 x 1 and 0 y 1 and 0 w 1 and 0 h 1): bad_lines.append((f, ln, 归一化坐标越界)) else: cls_counter[cls_id] 1 print(类别分布:, dict(sorted(cls_counter.items()))) print(异常行数:, len(bad_lines)) for item in bad_lines[:5]: print(item)脚本逻辑很简单但有两处要注意。第一class id 的上限要严格按照 nc7 来设也就是 0 到 6如果数据集里写了个 7说明标注过程出现过类别错乱必须揪出来。第二坐标必须是严格大于 0 且小于等于 1 的浮点数如果出现负数或宽度为 0训练时那个框会被 YOLO 直接忽略既不会报错也不会进 loss这种隐性脏数据只能靠脚本抓。空标签文件不一定要删YOLO 会把它们当成纯背景图参与训练对模型有时反而是好事。2.3 类别分布不均是常态训练前先做一次普查跑完上面的脚本你会得到一张类别计数表。如果 2364 张图里碎片占了 40% 以上而公用设施入侵只有几十条那就要在训练策略上做区分。YOLO 训练默认对所有类别一视同仁但数据分布倾斜会让模型偏向多数类。最直接的应对不是改损失函数而是先查清分布再决定少数类要不要复制增强或降低多数类的采样权重。我一般会把类别计数画成条形图同时统计每张图的框数量。一张图里如果有二十个碎片框训练时 loss 会被这张图主导如果这种高密度图还集中在验证集mAP 会被拉低很多。另一个要记录的是“单张图最大框数”和“框面积分布”。下水道缺陷里裂纹和洞都是小目标框面积可能只有整张图的 0.5% 甚至更低这类目标对标注质量极其敏感稍微标偏一点训练时 IoU 就达不到正样本阈值。分布普查阶段发现的这些小目标图值得单独建一个子目录后面调 imgsz 和增强参数时重点观察。很多同学拿到带标签数据集就直接跑训练遇到 mAP 低再回头找原因这时数据集里的结构问题已经被模型“消化”了你分不清是过拟合还是标注问题。所以这一步核验时间花得越细后面训练阶段越省心。3. 数据集划分与 YOLOv8 训练配置目录结构、data.yaml 和一条命令跑通训练YOLO 训练有一个非常容易迷惑新手的点图片和标签必须分别在 train 和 val 两个目录下而且目录结构是 images 与 labels 同级不是把 train 和 val 分开放在顶级目录后让标签跟着图片走。很多人在这里栽跟头导致 YOLO 报 “found no labels”。3.1 数据集目录结构images 和 labels 必须严格同级这套 zip 如果解压出来是 images 和 labels 两个平级目录那就按下面的方式建立训练集和验证集子目录cd sewer_defect mkdir -p images/train images/val labels/train labels/val然后按 8:1:1 或 7:2:1 的比例把图片和同名标签分进 train、val这里我推荐 8:1:1因为 2364 张图本身不算多验证集留 10% 已经能评估出有效 mAP。测试集可以暂时不用建先把验证集结果跑稳后面从实际 CCTV 视频里截帧做更真实的测试。划分时务必保证图片和标签文件成对移动不要只移图片不移标签。下面这个 bash 循环是我常用的做法它循环遍历验证集名单并同步复制图片和标签# 假设 val_imgs.txt 里存放验证集图片名每行一个不含扩展名 while read name; do mv images/${name}.jpg images/val/ mv labels/${name}.txt labels/val/ done val_imgs.txt移动完成后检查一下 labels/val 的文件数量应该和 images/val 的 jpg 数量一致。这种“先移动再验证”的做法比用随机脚本一次性搞定更稳妥因为一旦发现哪张图没有标签你还能及时把它留在训练集里当背景图而不是让验证集出现一个无标签样本。3.2 编写 dataset.yaml类别顺序必须与 txt 的 class id 一致在这个数据集的场景里data.yaml 就是整个训练流程的“黑匣子开关”。YOLOv8 使用一种简单的 YAML 结构这里我按七类别写一份完整配置# sewer_defect/dataset.yaml path: ./sewer_defect train: images/train val: images/val nc: 7 names: 0: joint-offset 1: obstacle 2: crack 3: buckle 4: hole 5: utility-intrusion 6: debris这里最关键的约定是 names 的索引顺序。txt 标签里 class id 写的是 0 到 6YAML 里 names 的第 0 项必须对应关节偏移第 1 项对应障碍物以此类推。如果你把 names 写成了从 1 开始的字典比如1: crack那么预测结果里 “裂纹” 会被显示成 “关节偏移”而且 loss 并不会因此变大。另外path字段建议写绝对路径或相对 dataset.yaml 所在目录的路径。在 Windows 上训练时path: ./sewer_defect有时会因为盘符问题读不到更省事的做法是直接把 path 写成C:/data/sewer_defect这种绝对路径。类别名称建议用英文或拼音中文类别名在部分可视化脚本里会出现字体编码问题推理时画框看不出标签名。你不要在这里花费额外精力留给最终报告输出时再映射成中文描述。3.3 用 YOLOv8 训练自己的数据集一条命令与先看的三个参数现在把训练跑起来环境安装一步带过重点看参数pip install ultralytics yolo detect train \ datasewer_defect/dataset.yaml \ modelyolov8n.pt \ epochs120 \ imgsz640 \ batch16 \ workers4 \ projectsewer_runs \ namedefect_v1训练开始后不要干等前十分钟重点看三样东西第一日志里 P精确率、R召回率、mAP50 是否在 epoch 5 之前有了非零值第二训练 loss 和验证 loss 的下降曲线是否同步第三images/val 里的样本有没有被正确加载。如果验证 loss 从第三个 epoch 开始就不再下降而训练 loss 仍在降那就是过拟合信号先停下来调增强参数而不是跑满 120 个 epoch。modelyolov8n.pt是 nano 版适合先从轻量模型起步因为下水道缺陷数据只有两千多张用 s 或 m 反而更容易过拟合。batch16在 8GB 显存上比较安全如果你有 12GB 以上显存可以加到 32batch 越大 BN 统计越稳。workers4在 Windows 上如果报 DataLoader worker 错误改成 0 或 2 即可这是最常见的兼容性问题之一。数据集的 2364 张图看起来不大但加上 mosaic 增强和随机仿射变换一个 epoch 的迭代次数仍然够模型在 120 epoch 内充分收敛。我建议先把 epochs 设成 120同时开启patience30这样验证集连续 30 个 epoch 不提升就自动停止节省时间也防止过拟合。4. 调参不是玄学损失函数三个分量、imgsz 与小目标增强策略很多人在这个数据集上跑出 mAP50 只有 0.3第一反应是换更大的模型但我遇到过反过来的情况用 yolov8n 在 640 分辨率下把 mAP50 从 0.4 提到 0.7靠的只是改损失函数权重和增强参数。YOLO 的损失函数常被当成不用碰的黑匣子其实它由三个可调节的分量组成每个分量对缺陷检测的敏感性完全不同。4.1 损失函数在管什么box、cls、dfl 三个权重的调节逻辑YOLOv8 的损失函数由三部分累加box loss 负责预测框和真实框的 IoU 回归cls loss 负责分类是否正确dfl loss 负责边框分布建模。命令行对应参数是box、cls、dfl默认值一般是7.5、0.5、1.5。在下水道缺陷场景里裂纹和洞的框很小box loss 权重过低会导致框回归不准mAP50 看着还行但 mAP50-95 掉得很厉害。# sewer_runs/defect_v1/hyp.yaml 中修改 loss_obox: 7.5 loss_cls: 0.5 loss_dfl: 1.5我在这套数据上倾向把 box 保持 7.5 不变把 cls 从 0.5 降到 0.4。原因是碎片和障碍物这类类别特征非常明显分类压力不大而关节偏移和带扣的边界模糊更需要 box 回归把框贴紧。如果你发现模型总是把洞和障碍物混淆再把 cls 升到 0.7 试一次。每次只改一个参数不要同时动三个否则没法定位是哪个改动带来了收益。4.2 裂纹和洞是小目标imgsz 从 640 提到 960 之前的权衡YOLOv8 默认imgsz640对大多数场景够用但下水道图像里裂纹宽度可能只有几个像素640 下缩放到模型输入后裂纹区域可能退化到 2×2 像素特征图上的响应几乎消失。把 imgsz 提到 960 或 1280 能让小目标保留更多细节代价是显存占用和训练时间同步上升。yolo detect train \ datasewer_defect/dataset.yaml \ modelyolov8n.pt \ imgsz960 \ batch8 \ epochs100 \ close_mosaic10这里batch必须从 16 降到 8因为 960 分辨率下单张图显存占用约为 640 的 2.25 倍。close_mosaic10表示最后 10 个 epoch 关闭 mosaic 增强这个参数非常关键mosaic 会把四张图拼成一张小目标被进一步缩小训练后期如果不关闭框回归精度会受影响。另外提一句YOLO 训练时会自动按 imgsz 缩放标注坐标不需要你手动改 txt 里的归一化值但如果你之前用脚本把标签都按 640 重新归一化过换 imgsz 后反而要重新核验一遍。4.3 从 2364 张图里榨出泛化能力增强参数要按缺陷场景配下水道 CCTV 图像有很强的一致性光照相对固定但旋转方向、俯仰角度和镜头距离变化很大。数据增强里对这套数据收益最大的是scale和degrees。因为检测机器人镜头可能任意旋转管道内壁圆形缺陷在画面中会以任意角度出现。yolo detect train \ datasewer_defect/dataset.yaml \ modelyolov8n.pt \ scale0.5 \ degrees90 \ fliplr0.5 \ mosaic1.0 \ mixup0.1scale0.5允许图像缩放范围在 0.5 到 1.5 倍之间对应 CCTV 镜头远近变化。degrees90是旋转增强角度上限我见过有人设 180但那会引入大量颠倒样本对判断管底沉积物反而有害。mixup0.1不要设太高下水道缺陷类别之间语义差异不算大mixup 过高会让裂纹和碎片互相融合出诡异的训练样本。增强参数调完务必保留一次原始参数的训练作为对照组。你可以分别记录两个版本的 mAP50-95而不是只看 mAP50。很多时候增强让 mAP50 下降但 mAP50-95 上升说明框回归更稳了这种情况下我选择增强版本上线。5. 这套数据集最容易翻车的 5 个坑现象、原因、解决办法这部分是血泪经验。我见过不止一个人在这套类似的数据集上白跑几十个小时最后发现是数据本身或参数配置的隐性错误。下面五条按出现频率从高到低排列每条都按现象、原因、解决写清楚。5.1 训练一切正常日志却提示 “found no labels”现象yolo 命令正常运行loss 从 2 左右开始下降但每轮 epoch 的迭代次数特别少而且验证集 mAP 一直是 0。训练日志里只有图像信息没有标签统计。原因data.yaml 里的 train 和 val 路径写成了图片目录而 YOLO 默认在图片目录的同级 labels 目录中找标签。比如 path 是sewer_defecttrain 是images/trainYOLO 会到sewer_defect/labels/train找标签。如果你的实际目录是sewer_defect/train/images和sewer_defect/train/labels只写train: images/train就会找不到标签。解决把 data.yaml 改为相对 path 下的完整结构确保 labels 和 images 平级。如果你原来的组织是images和labels分别包含 train 子目录YAML 里就写train: images/train不要带../不要写绝对路径再拼接相对路径。5.2 图片文件名带空格或中文导致标签匹配不上现象解压后图片名类似“CCTV_01 管道.jpg”训练中部分图像没有参与 loss 计算但也不报错。原因YOLO 按文件名前缀匹配 jpg 和 txt如果 jpg 和 txt 的编码或空格不一致会导致部分样本被静默跳过。中文文件名在部分 Linux 环境下还会因为 locale 编码不同产生匹配失败。解决训练前统一重命名把空格替换为下划线删除中文。处理完以后重新跑一遍 2.2 节的校验脚本确保 jpg 名和 txt 名完全一致。这个坑看起来笨但在下水道数据集这类历史数据里很常见。5.3 训练完成但七个类别中某个类别 mAP 为 0现象训练 loss 正常收敛验证集打印 per-class 指标时带扣或公用设施入侵的 precision 全为 0其他类别正常。原因这个类别的标注框数量太少比如只有 30 个框训练时正样本不足模型几乎不可能学出有效特征。更隐蔽的情况是这类别的框全部集中在同一批图片里而这批图片恰好被划分到了训练集验证集没有正样本per-class mAP 自然显示为 0。解决回到 2.3 节的类别普查结果如果某个类别数量少于 50建议先做类别重平衡复制该类别样本到训练集三份或对该类别单独做旋转平移增强。如果数量实在太少我一般会考虑合并类别比如把带扣和障碍物合并成“结构异常”保证模型至少能框出区域再由人工查看具体类型。5.4 imgsz 提高后召回率反而下降现象从 640 提升到 1280 后mAP50 下降尤其裂纹和洞的召回率从 0.6 掉到 0.4。原因一方面 batch 从 16 降到 8 后 BN 统计不稳定另一方面图像分辨率提高时如果原图本身模糊提升 imgsz 只是把噪点放大没有新增有效纹理信息。CCTV 机器人镜头离目标近时图像清晰离远时本来就糊强行放大只会让模型学到噪声。解决先用 960 试不要一步跳到 1280。如果 960 比 640 有明显收益就定在 960。同时检查验证集里模糊图像的比例这类图在推理时要单独设低置信度阈值否则会被全部过滤掉。5.5 验证集 mAP 很高实际 CCTV 视频里全是误检现象模型在数据集验证集上 mAP50 达到 0.85放到一段新的CCTV 巡检视频上把管壁接缝全框成裂纹把检修孔阴影框成洞。原因这是典型的数据泄漏。2364 张图来自相同管段的连续截帧训练集和验证集如果随机划分两张连续帧可能非常相似模型相当于记住了这些帧没有学到跨管段的泛化特征。解决用 2.1 的类别分布和文件名做一次聚类把同一段管道的图像尽量放进同一个集。最稳的做法是按视频片段划分而不是按单帧随机划分。如果 zip 内的文件名带片段编号就按编号前缀切分 train 和 val如果不带至少保证验证集图片不是训练集相邻帧。这个步骤无法靠修改训练参数弥补只能在划分阶段做对。6. 把推理结果变成维修报告置信度阈值与 CSV 输出技巧模型训练完不要只在验证集上看数字而是要拿真实 CCTV 视频跑一段把检测结果整理成维修班组能直接使用的表格。这一步的通用做法是写一个推理脚本输出带框的视频片段和分类统计表。from ultralytics import YOLO import csv import cv2 model YOLO(sewer_runs/defect_v1/weights/best.pt) video_path field/clip_01.mp4 cap cv2.VideoCapture(video_path) results [] while cap.isOpened(): ret, frame cap.read() if not ret: break preds model.predict(frame, conf0.35, imgsz960, verboseFalse) for box in preds[0].boxes: cls_id int(box.cls[0]) conf float(box.conf[0]) results.append([video_path, cls_id, round(conf, 3)]) # 可在这里把每帧画框结果写入视频 cap.release() print(目标数量:, len(results)) with open(sewer_defect_report.csv, w, newline) as f: writer csv.writer(f) writer.writerow([video, class_id, conf]) writer.writerows(results)这里有个验证技巧值得做把conf从 0.2 到 0.5 间隔 0.05 跑一遍同一段视频记录每档的检出数和误检数。误检数量下降最快的那一档往往就是当前场景的最佳阈值。下水道视频里管壁接缝和阴影很多0.35 是我的起点如果你的模型在验证集上 mAP50 很高但实际视频误检多把 conf 提高到 0.45 是更稳妥的选择。最后说一个我吃过亏的习惯模型训练完会顺手把 best.pt 当作最终模型但我现在一定会拿它跑两段未参与训练的真实 CCTV 片段一段是管道状况良好的一段是缺陷密集的。良好管段上如果出现大量框说明模型对背景过拟合缺陷密集管段上如果漏检说明验证集划分有数据泄漏。只有这两段视频表现都合理我才会进入报告输出环节。这个验证动作只需要二十分钟却能避免你把一个只在数据集上好看的模型交到维修组手里然后在现场被质疑。希望你拿到这套数据集时能从拆包核验这一步开始做扎实训练和上线都会顺很多。希望帮到你。本文还有配套的精品资源点击获取
返回列表