ARTICLE DETAIL

资讯详情

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

YOLO下水道缺陷检测实战:从数据集训练到部署全流程

YOLO下水道缺陷检测实战:从数据集训练到部署全流程 简介面向YOLO系列算法目标检测的下水道缺陷数据集共包含2364张已标注图像覆盖关节偏移、障碍物、裂纹、带扣、洞、公用设施入侵、碎片等真实管廊场景下的典型缺陷类型。压缩包共2000个文件大小7.43MB其中1636个xml文件对应VOC格式标签364个txt文件对应YOLO格式标签两种标签分别存放文件名末尾带有缺陷类别名称便于按类别筛选与检查。标签内容采用YOLO标准归一化写法类别索引从0开始中心点坐标与宽高均为0到1之间的比例值可以直接用于YOLOv5、YOLOv7、YOLOv8、YOLOv9、YOLOv10、YOLO11等主流模型训练。数据集已完成训练集、验证集、测试集划分并附有data.yaml配置文件省去手动拆分的步骤。目前已有66人浏览学习适合从事市政管网检测、缺陷识别研究或希望快速获取标准目标检测样本的开发者参考。1. 下水道缺陷数据集为什么这2364张图值得训练前先过一遍做管道检测的人拿到「YOLO算法-下水道缺陷数据集-2364张图像带标签」这类资源时第一反应通常不是高兴而是怀疑够不够训标签准不准类别能不能直接用这套数据集涵盖了关节偏移、障碍物、裂纹、带扣、洞、公用设施入侵、碎片七类缺陷全部是YOLO格式标注图片批量2364张。对目标检测任务来说这个量级谈不上大但胜在缺陷类别集中、场景单一适合做预训练、模型选型验证和算法demo。如果你正卡在「没有自己的数据又想在污水管、雨水管场景里验证YOLO能不能用」的阶段这套数据就是最省时间的起点。不过要提醒一句直接用别人的标注训练是一回事把它变成你自己的模型是另一回事中间的目录整理、标签清洗、参数调整每一步都会影响最终能不能落地。2. 解压之后先做三件事看懂标签、清洗标注、重新划分数据集2.1 解压后第一件事不是训练而是确认标签格式常见做法是压缩包解压后能看到images和labels两个目录也可能图片和txt混在一起。不管哪种你先别急着跑训练先用命令看一眼文件结构和标签内容。# 解压按实际压缩包名调整 unzip YOLO算法-下水道缺陷数据集-2364张图像带标签.zip -d sewer_dataset cd sewer_dataset # 查看目录结构 find . -maxdepth 2 -type d | sort # 随机抽3个标签文件确认内容格式 for f in $(find labels -name *.txt | head -3); do echo $f cat $f done标签文件每行对应一个目标格式是class x_center y_center width height五个值全部是归一化到0~1之间的小数。class是整数从0开始对应类别顺序。这里有个关键点你看到标签文件里有小数越界比如宽度大于1或者中心点坐标接近1.5说明标注工具导出时出了问题这种样本必须在训练前剔掉或修正否则损失函数会先被这些脏数据带偏。2.2 重建目录结构YOLOv8和YOLOv5都认的train/val划分不管压缩包里原始目录怎么排我建议你统一整理成下面这个结构这是YOLO系最常见的目录约定。sewer_dataset/ ├── images/ │ ├── train/ # 约1900张 │ └── val/ # 约464张 ├── labels/ │ ├── train/ │ └── val/ └── data.yaml划分逻辑按8:2随机切但一定注意下水道CCTV视频抽帧得到的图片相邻帧之间高度相似如果随机划分验证集里大概率会出现训练集同一段管道的近似帧导致验证分数虚高。import os, random, shutil random.seed(42) image_dir images_all # 原始图片目录 label_dir labels_all # 原始标签目录 train_ratio 0.8 imgs [f for f in os.listdir(image_dir) if f.endswith(.jpg)] random.shuffle(imgs) split int(len(imgs) * train_ratio) train_imgs imgs[:split] val_imgs imgs[split:] for split_name, split_imgs in [(train, train_imgs), (val, val_imgs)]: os.makedirs(fimages/{split_name}, exist_okTrue) os.makedirs(flabels/{split_name}, exist_okTrue) for img in split_imgs: base os.path.splitext(img)[0] shutil.copy(f{image_dir}/{img}, fimages/{split_name}/{img}) if os.path.exists(f{label_dir}/{base}.txt): shutil.copy(f{label_dir}/{base}.txt, flabels/{split_name}/{base}.txt)这段脚本做了三件事先打乱图片顺序再按8:2切分最后把图片和同名txt分别拷到train/val目录。random.seed(42)保证你每次跑出来的划分结果一致方便复现。如果你的图是视频帧序列建议改成按视频片段切分不要逐帧随机切否则验证集的意义会打折扣。2.3 写data.yaml类别顺序必须和标签里的class一致data.yaml是YOLO训练时读取数据集的入口内容很简单但类别顺序错一个全部标注就全错位了。# data.yaml path: /absolute/path/to/sewer_dataset train: images/train val: images/val nc: 7 names: 0: joint_offset 1: obstacle 2: crack 3: buckle 4: hole 5: utility_invasion 6: debris注意一点压缩包里如果有classes.txt或class_names.txt以它为基准不要自己重新排顺序。我见过有人按照自己的理解重排了类别结果训练时loss在0.6左右怎么都降不下去后来发现是标签里的class值和names对不上。检查方法很简单随便打开一个标签txt看class数值是多少再去data.yaml里对应names第几个。3. 用YOLOv8训练下水道缺陷检测最小可复现命令与关键参数3.1 训练前检查先跑一次检测看标注框是否贴住目标直接开训容易翻车建议先挑十几张验证集图片把标注框画出来看看。pip install ultralytics # 把验证集图片和标签画在一起目测标注质量 yolo detect predict modelyolov8n.pt sourcesewer_dataset/images/val save_txtFalse saveFalse严格来说这条命令跑的是模型推理不是画标注框。想看标注可以写几行脚本借助OpenCV画框import cv2 img_path sewer_dataset/images/val/000123.jpg label_path sewer_dataset/labels/val/000123.txt img cv2.imread(img_path) h, w img.shape[:2] with open(label_path) as f: for line in f: cls, xc, yc, bw, bh map(float, line.split()) x1 int((xc - bw/2) * w) y1 int((yc - bh/2) * h) x2 int((xc bw/2) * w) y2 int((yc bh/2) * h) cv2.rectangle(img, (x1, y1), (x2, y2), (0, 255, 0), 2) cv2.putText(img, str(int(cls)), (x1, y1-5), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 0, 255), 2) cv2.imwrite(check_000123.jpg, img)这段代码不复杂但它能让你直观看到问题裂纹这类细长目标标注框是紧贴裂纹走向还是整个矩形框住了大块管壁后者说明标注粒度粗糙训练出来的模型预测框会偏大部署时没法用来判断缺陷精确位置。这一步不要省标注质量决定了模型上限。3.2 训练命令与参数imgsz、batch、epochs怎么定数据量只有2364张模型不建议一开始就上yolov8x先用n或s版本跑通流程确认能收敛再放大模型。yolo detect train \ modelyolov8s.pt \ datasewer_dataset/data.yaml \ epochs150 \ imgsz640 \ batch16 \ device0 \ patience30 \ projectruns/sewer \ nameexp1这里几个参数值得说清楚。imgsz640是YOLO系默认输入尺寸下水道原始图片如果是1080P640能保留多数缺陷特征。如果你发现裂纹这类细长小目标漏检严重可以把imgsz提到960或1280代价是显存占用上升batch要相应下调。batch16是在单张显卡(显存12~24GB)下的经验值显存不够时优先降batch而不是降imgsz否则小目标特征丢失后很难补回来。patience30表示验证集指标连续30个epoch不提升就提前停止省时间。loss曲线正常的话前20个epoch下降明显50个epoch后进入平台期。如果loss在0.8以上反复震荡先检查标签里的class范围是否小于nc值再看是否有空标签文件混进训练集。3.3 类别不均衡碎片和裂纹样本多带扣和洞样本少2364张图按七类缺陷分布一定不均衡。正常情况下裂纹、碎片这类常见缺陷可能占一半以上带扣、洞样本可能只有一两百。直接训练会出现多数类mAP高、少数类几乎不检出的情况。yolo detect train \ modelruns/sewer/exp1/weights/best.pt \ datasewer_dataset/data.yaml \ epochs80 \ imgsz640 \ batch16 \ cls1.2 \ dropout0.15 \ projectruns/sewer \ nameexp2_finetunecls1.2是分类损失的权重系数默认是0.5调高会加大分类错误的惩罚对类别不均衡有一定缓解。dropout0.15适合数据量不大的场景减少过拟合。另一个更直接的办法是给少数类提高采样权重YOLOv8里可以通过修改数据增强的mosaic概率来间接控制但对新手来说先调cls参数是最省事的。4. 评估与迭代mAP50只是起点真正要看的是缺陷级别的精度4.1 混淆矩阵里找短板哪些类别在互相吞训练结束后在runs/sewer/exp1/目录下可以看到confusion_matrix.png、results.png、val_batch0_pred.jpg等产物。mAP50数值好看不代表每个类别都好你要单独看混淆矩阵。常见的情况是障碍物和碎片互相混淆公用设施入侵被识别成障碍物。原因是这两类缺陷在视觉上本身接近——都是管道里出现了不该出现的东西只是纹理和位置有差异。如果混淆严重考虑合并类别把障碍物和碎片合成一类宁缺毋滥。带标签数据的价值就在这里你可以在不影响整体结构的前提下重新定义任务边界。验证集mAP50在0.75左右对下水道场景来说已经不错。但如果某个类别AP50低于0.4不要盲目加训练轮数先回去看这个类别的样本数、标注框大小分布再决定是补数据还是合并类别。4.2 用验证集逐张翻车图区分标注噪声和模型漏检YOLO训练完成后会在验证集上跑一次推理生成带预测框的图片路径一般在runs/sewer/exp1/val_batch0_pred.jpg。这是你排查模型行为的第一手材料。打开图片后看两类问题一是漏检标注框存在但模型没画框说明特征学习不到位二是误检模型画了框但标注里没有对应目标可能是把管壁纹理当成裂纹了。逐张翻车时把它们记录下来统计规律。如果某个缺陷类别频繁漏检去查它的标注框尺寸分布——用脚本统计这类目标的像素面积如果大部分都小于32x32像素那就不是模型问题是目标尺寸太小需要提高imgsz或专门做切片检测。4.3 为部署准备导出导出为TensorRT引擎时的注意事项如果你最终要跑在NVIDIA Jetson这类边缘设备上模型导出这一步的坑不比训练少。from ultralytics import YOLO model YOLO(runs/sewer/exp1/weights/best.pt) model.export(formatengine, imgsz640, halfTrue, workspace4)导出成TensorRT引擎需要本机有NVIDIA显卡和CUDA环境这个过程会做层融合和权重量化。halfTrue表示用FP16精度推理速度几乎翻倍但mAP可能下降0.5到1个百分点适合对实时性要求高的场景。workspace4指定了4GB的显存空间来做autotune值太小可能导致某些层融合失败导出报错时优先调大它。导出后的engine文件是序列化的绑定硬件换一张不同型号的显卡就得重新导出这一点不要忽略。另外TensorRT的autotune过程耗时较长像这种2364张规模的数据集模型本身不大通常几分钟内能完成但如果是yolov8x等待时间会显著拉长要有心理准备。5. 避坑标注噪声、小目标漏检与过拟合的排查记录5.1 标签文件中小数越界或类别名错位现象训练时loss在某个值附近震荡验证集mAP稳定在0.3以下看训练日志发现loss_focal和loss_cls居高不下。原因标签txt中出现了大于1的坐标值、负数、空文件或class数值大于等于nc。这些异常样本在数据加载时直接参与损失计算把梯度方向带乱。另一种可能labels目录里多了几个非txt文件被误读。解决写一个遍历脚本逐行校验每个标签文件。python - EOF import os bad_files [] for root, _, files in os.walk(sewer_dataset/labels): for f in files: path os.path.join(root, f) with open(path) as fh: for line in fh: parts line.strip().split() if len(parts) ! 5: bad_files.append((path, field_count)) break try: nums list(map(float, parts)) except ValueError: bad_files.append((path, not_float)) break if nums[0] 0 or nums[0] 6: bad_files.append((path, class_out_of_range)) break if any(v 0 or v 1 for v in nums[1:]): bad_files.append((path, coord_out_of_range)) break print(fbad files: {len(bad_files)}) for p, reason in bad_files[:20]: print(p, reason) EOF脚本结果出来后坏文件隔离到单独目录不要直接删除避免后续需要手工修正时没有原始参考。5.2 验证集分数虚高同一管道相邻帧被拆进train和val现象训练时验证集mAP有0.8但部署到真实CCTV视频流里框明显不稳定同一段缺陷时有时无。原因数据来自视频抽帧相邻帧高度相似随机划分后验证集里混入了训练集的近亲样本模型等于做了开卷考试。解决按视频片段划分数据。假设文件名带帧号比如clip01_frame_0123.jpg按clip前缀分组整个clip只能进train或val不允许跨集合。import os, random from collections import defaultdict random.seed(42) clip_groups defaultdict(list) for f in os.listdir(images_all): clip_id f.split(_frame_)[0] # 按实际命名规律调整 clip_groups[clip_id].append(f) clips list(clip_groups.keys()) random.shuffle(clips) split int(len(clips) * 0.8) train_clips set(clips[:split]) val_clips set(clips[split:]) train_imgs [img for c in train_clips for img in clip_groups[c]] val_imgs [img for c in val_clips for img in clip_groups[c]]这段代码的核心是按clip粒度切分而不是按图片切分。如果你的文件名没有明确的片段编号可以从元数据或CCTV记录里补或者按时间戳分组。这一步在视频类数据集上是必须做的否则整个评估环节都失去参考价值。5.3 裂纹类小目标漏检严重但mAP不降现象整体mAP50尚可但抽查翻车图时发现裂纹预测框总是比标注框大一圈或者完全漏检。原因裂纹是细长结构在640x640输入下如果原图只有1080P裂纹宽度可能只有几个像素特征在多次下采样后基本消失。模型检测到了模糊的痕迹但无法精确定位。解决针对这类小目标两条路。一是提高imgsz到960甚至1280代价是显存和推理耗时上升二是用SAHI这类切片推理工具对大图先切块再做检测最后合并结果。第二种方式部署时复杂度更高但精度提升明显。5.4 训练集loss下降但验证集loss反弹现象训练到第60个epoch左右train loss持续下降val loss开始回升mAP不再上涨。原因典型的过拟合。2364张图对YOLOv8s来说偏少模型在第60个epoch后开始死记训练图像特征而不是泛化缺陷的视觉模式。解决不要等到反弹才干预。训练之前就把patience30设好让early stopping兜底。训练过程中如果发现val loss连续10个epoch不降手动停掉用倒数第二个epoch的权重做验证。另一个手段是增强regularization把dropout从0.1提到0.2同时把mosaic增强概率调高增加训练样本变异度。还可以加载预训练权重进行迁移学习而不是从随机初始化开始训。6. 用迁移学习和置信度阈值调优把模型状态再向前推一步模型训完后工作并没有结束。针对下水道缺陷数据集的实际部署有两个技巧值得一试。先做迁移学习的二次训练。拿已经收敛的模型在相同数据集上用小学习率0.0001跑30个epoch只微调最后几层。这样做的意义在于第一次训练是从COCO预训练权重出发特征提取层已经能识别通用纹理和边缘二次训练专注于下水道场景的纹理分布让模型更适应管道内壁的材质、光照和缺陷形态。跑这个步骤时把model参数指向best.pt同时pretrainedFalse其余超参数保持和第一次训练一致的imgsz和batch即可。再调整置信度阈值。YOLO默认conf-thres是0.25在下水道场景里这个值偏低导致碎片、障碍物这类目标出现大量低置信度误检。你可以用验证集做一个阈值扫描yolo detect val \ modelruns/sewer/exp2_finetune/weights/best.pt \ datasewer_dataset/data.yaml \ conf0.4对比conf0.25和conf0.4的验证输出重点看precision和recall的变化。一般来说conf从0.25提到0.4时precision会明显上升recall下降有限因为没有哪个缺陷类别是极度依赖低置信度框才能检出的。最终在部署时建议把conf设在0.35到0.45之间IoU阈值保持默认0.5即可。如果你接的是实时视频流帧间还可以做简单的跟踪平滑把置信度低于阈值的检测框在连续两帧都出现时才输出这一条能滤掉不少单帧噪声。我自己的习惯是拿到任何带标签的数据集第一周不会碰训练命令先花时间看标签、看样本分布、看坏标注。数据本身就是最大的超参数处理得越细后面调参越顺。这套下水道缺陷数据集类别干净、标注质量总体在线你花两小时把目录结构整理好后续的每一步都会顺畅很多。希望帮到你。本文还有配套的精品资源点击获取
返回列表